WIP eTemplate2.0 DTD

This commit is contained in:
ralf 2024-04-19 12:23:10 +02:00
parent c04cf276b7
commit d1b3786b2a
6 changed files with 7404 additions and 1059 deletions

127
doc/etemplate2-rng.php Normal file → Executable file
View File

@ -1,3 +1,4 @@
#!/usr/bin/env php
<?php <?php
/** /**
* eTemplate2 XML schema * eTemplate2 XML schema
@ -31,9 +32,10 @@ if (!file_exists(($file = __DIR__."/etemplate2/etemplate2.rng")))
die("Missing file '$file', you need to generate it from 'etemplate2.dtd' e.g. with PHPStorm!"); die("Missing file '$file', you need to generate it from 'etemplate2.dtd' e.g. with PHPStorm!");
} }
$grammar = new SimpleXMLElement(file_get_contents($file)); $grammar = new SimpleXMLElement(file_get_contents($file));
$widgets = getByName($grammar, 'Widgets'); $widgets_choice = getByName($grammar, 'Widgets')->choice;
/** /**
* Manually overwriting problems / errors in what we automatically generate * Manually overwriting problems / errors in what we automatically generate
* Use class-name (e.g. Et2Button) so all descends inherit the fix, use-tag to fix only specific widget.
* @todo fix in TS sources * @todo fix in TS sources
*/ */
$overwrites = [ $overwrites = [
@ -44,7 +46,9 @@ $overwrites = [
'id' => 'string', // commented out with some reasoning in Et2Widget 'id' => 'string', // commented out with some reasoning in Et2Widget
//'data' => null, // ToDo: not sure, but AFAIK this is no attribute, but somehow listed in each widget //'data' => null, // ToDo: not sure, but AFAIK this is no attribute, but somehow listed in each widget
'width' => 'string', 'width' => 'string',
'height' => 'string',
'span' => 'string', // actually int|"all" 'span' => 'string', // actually int|"all"
'slot' => 'string',
], ],
], ],
'Et2InputWidget' => [ 'Et2InputWidget' => [
@ -109,7 +113,34 @@ $overwrites = [
'et2-split' => [ 'et2-split' => [
'.children' => 'Widgets', '.children' => 'Widgets',
], ],
'et2-select-*' => 'et2-select', // seems like et2-select-* widgets are NOT parsed 'et2-url-email' => [
'.attrs' => [
'emailDisplay' => 'string', // can't see that anywhere in the code, but in addressbook.index.xet
],
],
'et2-nextmatch-header-custom' => [
'.attrs' => [
'emptyLabel' => 'string',
],
],
'Et2Button' => [
'.attrs' => [
'image' => 'string',
'noSubmit' => 'boolean',
],
],
'Et2Select' => [
'.attrs' => [
'rows' => 'int',
'tabindex' => 'int',
'allowFreeEntries' => 'boolean',
],
],
'et2-email' => [
'.attrs' => [
'onTagClick' => 'function',
],
],
]; ];
/** /**
@ -118,13 +149,75 @@ $overwrites = [
// make overlay the only allowed start element // make overlay the only allowed start element
removeNode($grammar->start->choice); removeNode($grammar->start->choice);
$grammar->start->addChild('ref')->addAttribute('name', 'overlay'); $grammar->start->addChild('ref')->addAttribute('name', 'overlay');
// overlay can only container template, not all widgets // fix legacy widgets: attribute-name => (array of) widgets
getByName($grammar, 'overlay')->element->zeroOrMore->ref->attributes()['name'] = 'template'; $missing_legacy_attributes = [
getByName($grammar, 'attlist.vfs-upload')->addChild('optional') 'callback' => 'vfs-upload',
->addChild('attribute')->addAttribute('name', 'callback'); 'statustext' => 'tab',
// add statustext to tab 'minWidth' => 'column',
getByName($grammar, 'attlist.tab')->addChild('optional') 'maxWidth' => 'column',
->addChild('attribute')->addAttribute('name', 'statustext'); 'id' => [
'.optional' => false,
'nextmatch-header', 'nextmatch-sortheader', 'nextmatch-customfields', 'nextmatch',
],
'template' => ['.optional' => false, 'nextmatch'],
'header_left' => 'nextmatch',
'header_row' => 'nextmatch',
'header_right' => 'nextmatch',
'disabled' => 'nextmatch',
'onselect' => 'nextmatch',
'span' => ['nextmatch', 'nextmatch-header', 'nextmatch-customfields', 'nextmatch-sortheader'],
'class' => ['nextmatch','nextmatch-header', 'nextmatch-customfields', 'nextmatch-sortheader'],
'label' => [
'.optional' => false,
'nextmatch-header', 'nextmatch-sortheader',
],
'sortmode' => [
'.values' => ['ASC', 'DESC'],
'.default' => 'ASC',
'nextmatch-sortheader',
],
];
foreach($missing_legacy_attributes as $attribute => $widgets)
{
foreach((array)$widgets as $key => $widget)
{
if (!is_int($key)) continue; // .(values|default)
// widget not found add it plus it's attribute-list
if (!getByName($grammar, $widget))
{
$widgets_choice->addChild('ref')->addAttribute('name', $widget);
($define = $grammar->addChild('define'))->addAttribute('name', $widget);
($element = $define->addChild('element'))->addAttribute('name', $widget);
$element->addChild('ref')->addAttribute('name', 'attlist.'.$widget);
$element->addChild('empty'); // no children allowed
$grammar->addChild('define')->addAttribute('name', 'attlist.'.$widget);
}
// add (optional) attribute
if (!is_array($widgets) || (!isset($widgets['.optional']) || $widgets['.optional'] === true))
{
$attr = getByName($grammar, 'attlist.'.$widget)->addChild('optional')
->addChild('attribute');
}
else
{
$attr = getByName($grammar, 'attlist.'.$widget)->addChild('attribute');
}
$attr->addAttribute('name', $attribute);
// add values and/or default
if (is_array($widgets) && isset($widgets['.values']))
{
$choice = $attr->addChild('choice');
foreach($widgets['.values'] as $value)
{
$choice->addChild('value', $value);
}
}
if (is_array($widgets) && isset($widgets['.default']))
{
$attr->addAttribute('a:defaultValue', $widgets['.default'], 'http://relaxng.org/ns/compatibility/annotations/1.0');
}
}
}
// build a hashed version of all classes, members and attributes to e.g. find ancestors // build a hashed version of all classes, members and attributes to e.g. find ancestors
$classes = []; $classes = [];
@ -175,7 +268,7 @@ foreach($data['modules'] as $module)
$attrs = $element->addChild('ref'); $attrs = $element->addChild('ref');
$attrs->addAttribute('name', 'attlist.'.$export['name']); $attrs->addAttribute('name', 'attlist.'.$export['name']);
// add to widgets // add to widgets
$widgets->choice->addChild('ref')->addAttribute('name', $export['name']); $widgets_choice->addChild('ref')->addAttribute('name', $export['name']);
// add the element-attributes // add the element-attributes
$attrs = $grammar->addChild('define'); $attrs = $grammar->addChild('define');
@ -193,10 +286,11 @@ foreach($data['modules'] as $module)
} }
else else
{ {
$list = $element->addChild('oneOrMore'); // zeroOrMore for e.g. empty boxes?
// add allowed children // add allowed children
foreach((array)$overwrites[$export['name']]['.children'] as $child) foreach((array)$overwrites[$export['name']]['.children'] as $child)
{ {
$element->addChild('ref')->addAttribute('name', $child); $list->addChild('ref')->addAttribute('name', $child);
} }
} }
@ -206,7 +300,7 @@ foreach($data['modules'] as $module)
} }
$remove = []; $remove = [];
foreach($widgets->choice->children() as $widget) foreach($widgets_choice->children() as $widget)
{ {
if (preg_match($overwrites['.remove'], $name=(string)$widget->attributes()['name'])) if (preg_match($overwrites['.remove'], $name=(string)$widget->attributes()['name']))
{ {
@ -261,8 +355,8 @@ echo preg_replace('#<choice>
*/ */
function removeWidget(string $name) function removeWidget(string $name)
{ {
global $grammar, $widgets; global $grammar, $widgets_choice;
if (removeByName($widgets->choice, $name)) if (removeByName($widgets_choice, $name))
{ {
removeByName($grammar, $name); removeByName($grammar, $name);
removeByName($grammar, 'attlist.'.$name); removeByName($grammar, 'attlist.'.$name);
@ -381,7 +475,10 @@ function attributes(array $class, ?SimpleXMLElement $attrs=null)
{ {
$default = substr($default, 1, -1); $default = substr($default, 1, -1);
} }
$attribute->addAttribute('a:defaultValue', $default, 'http://relaxng.org/ns/compatibility/annotations/1.0'); if ($default !== 'undefined') // do NOT add undefined, it's the default anyway
{
$attribute->addAttribute('a:defaultValue', $default, 'http://relaxng.org/ns/compatibility/annotations/1.0');
}
} }
switch ($attr['type']['text'] ?? 'any') switch ($attr['type']['text'] ?? 'any')
{ {

View File

@ -26,9 +26,9 @@ function replace(string, terms)
} }
export default { export default {
globs: ["api/js/etemplate/**/Et2*/*.ts"], globs: ["api/js/etemplate/**/Et2*.ts","api/js/etemplate/Et2Nextmatch/**/*.ts"],
/** Globs to exclude */ /** Globs to exclude */
exclude: [],//, 'et2_*.ts', '**/test/*', '**/*.styles.ts', '**/*.test.ts'], exclude: ["api/js/etemplate/**/test/*"],//, 'et2_*.ts', '**/test/*', '**/*.styles.ts', '**/*.test.ts'],
dev: false, dev: false,
litelement: true, litelement: true,
plugins: [ plugins: [
@ -195,4 +195,4 @@ export default {
*/ */
] ]
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@
--> -->
<!ENTITY % Widgets "template|grid|vbox|box|details|hbox|groupbox|caption|split|button|buttononly|colorpicker|description|label|entry|contact-value|contact-account|contact-template|infolog-value|tracker-value|records-value|textbox|hidden|searchbox|int|integer|float|passwd|url|url-email|url-phone|url-fax|menupopup|listbox|select|select-cat|select-percent|select-priority|select-country|select-state|select-year|select-month|select-day|select-dow|select-hour|select-number|select-app|select-lang|select-bool|select-timezone|select-bitwise|menulist|checkbox|radio|radiogroup|date|date-time|date-timeonly|date-duration|date-since|date-time_today|time_or_date|date-range|diff|dropdown_button|styles|link-to|link-apps|link-entry|link|link-string|link-list|link-add|select-account|taglist|taglist-account|taglist-email|taglist-cat|taglist-thumbnail|taglist-state|customfields|customfields-list|favorites|html|htmlarea|tabbox|button-timestamp|timestamper|toolbar|tree|tree-cat|historylog|hrule|image|appicon|avatar|lavatar|iframe|file|progress|portlet|ajax_select|vfs|vfs-name|vfs-path|vfs-mime|vfs-size|vfs-mode|vfs-uid|vfs-gid|vfs-upload|vfs-select|video|audio|barcode|itempicker|script|countdown"> <!ENTITY % Widgets "template|grid|vbox|box|details|hbox|groupbox|caption|split|button|buttononly|colorpicker|description|label|entry|contact-value|contact-account|contact-template|infolog-value|tracker-value|records-value|textbox|hidden|searchbox|int|integer|float|passwd|url|url-email|url-phone|url-fax|menupopup|listbox|select|select-cat|select-percent|select-priority|select-country|select-state|select-year|select-month|select-day|select-dow|select-hour|select-number|select-app|select-lang|select-bool|select-timezone|select-bitwise|menulist|checkbox|radio|radiogroup|date|date-time|date-timeonly|date-duration|date-since|date-time_today|time_or_date|date-range|diff|dropdown_button|styles|link-to|link-apps|link-entry|link|link-string|link-list|link-add|select-account|taglist|taglist-account|taglist-email|taglist-cat|taglist-thumbnail|taglist-state|customfields|customfields-list|favorites|html|htmlarea|tabbox|button-timestamp|timestamper|toolbar|tree|tree-cat|historylog|hrule|image|appicon|avatar|lavatar|iframe|file|progress|portlet|ajax_select|vfs|vfs-name|vfs-path|vfs-mime|vfs-size|vfs-mode|vfs-uid|vfs-gid|vfs-upload|vfs-select|video|audio|barcode|itempicker|script|countdown">
<!ELEMENT overlay (%Widgets;)*> <!ELEMENT overlay (template)*>
<!ELEMENT template (%Widgets;)*> <!ELEMENT template (%Widgets;)*>
<!ATTLIST template <!ATTLIST template
template CDATA #IMPLIED template CDATA #IMPLIED
@ -59,13 +59,14 @@ attributes CDATA #IMPLIED
class CDATA #IMPLIED class CDATA #IMPLIED
width CDATA #IMPLIED> width CDATA #IMPLIED>
<!ELEMENT rows (row)*> <!ELEMENT rows (row)*>
<!ELEMENT row (%Widgets;)> <!ELEMENT row (%Widgets;)+>
<!ATTLIST row <!ATTLIST row
class CDATA #IMPLIED class CDATA #IMPLIED
height CDATA #IMPLIED height CDATA #IMPLIED
valign CDATA #IMPLIED valign CDATA #IMPLIED
disabled CDATA #IMPLIED disabled CDATA #IMPLIED
part CDATA #IMPLIED part CDATA #IMPLIED
id CDATA #IMPLIED
> >
<!ATTLIST grid <!ATTLIST grid
border CDATA #IMPLIED border CDATA #IMPLIED
@ -2984,7 +2985,7 @@ customfields CDATA #IMPLIED
fields CDATA #IMPLIED fields CDATA #IMPLIED
value CDATA #IMPLIED value CDATA #IMPLIED
type_filter CDATA #IMPLIED type_filter CDATA #IMPLIED
private (true|false) "undefined" private (true|false) #IMPLIED
sub_app CDATA #IMPLIED sub_app CDATA #IMPLIED
onchange CDATA #IMPLIED onchange CDATA #IMPLIED
prefix CDATA "#" prefix CDATA "#"
@ -3017,7 +3018,7 @@ customfields CDATA #IMPLIED
fields CDATA #IMPLIED fields CDATA #IMPLIED
value CDATA #IMPLIED value CDATA #IMPLIED
type_filter CDATA #IMPLIED type_filter CDATA #IMPLIED
private (true|false) "undefined" private (true|false) #IMPLIED
sub_app CDATA #IMPLIED sub_app CDATA #IMPLIED
onchange CDATA #IMPLIED onchange CDATA #IMPLIED
prefix CDATA "#" prefix CDATA "#"
@ -3158,7 +3159,7 @@ add_tabs (true|false) "false"
tab_height CDATA #IMPLIED tab_height CDATA #IMPLIED
align_tabs (v|h) "h" align_tabs (v|h) "h"
> >
<!ELEMENT tabs (tab)> <!ELEMENT tabs (tab)*>
<!ELEMENT tab EMPTY> <!ELEMENT tab EMPTY>
<!ATTLIST tab <!ATTLIST tab
id CDATA #IMPLIED id CDATA #IMPLIED

View File

@ -147,7 +147,7 @@
<element name="overlay"> <element name="overlay">
<ref name="attlist.overlay"/> <ref name="attlist.overlay"/>
<zeroOrMore> <zeroOrMore>
<ref name="Widgets"/> <ref name="template"/>
</zeroOrMore> </zeroOrMore>
</element> </element>
</define> </define>
@ -295,7 +295,9 @@
<define name="row"> <define name="row">
<element name="row"> <element name="row">
<ref name="attlist.row"/> <ref name="attlist.row"/>
<ref name="Widgets"/> <oneOrMore>
<ref name="Widgets"/>
</oneOrMore>
</element> </element>
</define> </define>
<define name="attlist.row" combine="interleave"> <define name="attlist.row" combine="interleave">
@ -314,6 +316,9 @@
<optional> <optional>
<attribute name="part"/> <attribute name="part"/>
</optional> </optional>
<optional>
<attribute name="id"/>
</optional>
</define> </define>
<define name="attlist.grid" combine="interleave"> <define name="attlist.grid" combine="interleave">
<optional> <optional>
@ -11611,7 +11616,7 @@
<attribute name="type_filter"/> <attribute name="type_filter"/>
</optional> </optional>
<optional> <optional>
<attribute name="private" a:defaultValue="undefined"> <attribute name="private">
<choice> <choice>
<value>true</value> <value>true</value>
<value>false</value> <value>false</value>
@ -11729,7 +11734,7 @@
<attribute name="type_filter"/> <attribute name="type_filter"/>
</optional> </optional>
<optional> <optional>
<attribute name="private" a:defaultValue="undefined"> <attribute name="private">
<choice> <choice>
<value>true</value> <value>true</value>
<value>false</value> <value>false</value>
@ -12254,7 +12259,9 @@
<define name="tabs"> <define name="tabs">
<element name="tabs"> <element name="tabs">
<ref name="attlist.tabs"/> <ref name="attlist.tabs"/>
<ref name="tab"/> <zeroOrMore>
<ref name="tab"/>
</zeroOrMore>
</element> </element>
</define> </define>
<define name="attlist.tabs" combine="interleave"> <define name="attlist.tabs" combine="interleave">