|
From: <net...@us...> - 2013-01-23 20:31:20
|
Revision: 1353
http://openautomation.svn.sourceforge.net/openautomation/?rev=1353&view=rev
Author: netzkind
Date: 2013-01-23 20:31:08 +0000 (Wed, 23 Jan 2013)
Log Message:
-----------
simplish preview of the whole configuration, implementation of sequence in editor, re-commit sequenced version of xsd (originally by hausl), other stuff (see DEVELOPMENT.txt)
upgrade-scripts to pull config to current version of library, speed-improvements on the editor (might be improved upon)
backups of config on saving, backups of config on upgrade; other stuff i can remember right now :)
Modified Paths:
--------------
CometVisu/trunk/visu/editor/DEVELOPMENT.txt
CometVisu/trunk/visu/editor/bin/save_config.php
CometVisu/trunk/visu/editor/init.js
CometVisu/trunk/visu/editor/lib/Configuration.js
CometVisu/trunk/visu/editor/lib/Editor.css
CometVisu/trunk/visu/editor/lib/Editor.js
CometVisu/trunk/visu/editor/lib/Messages.js
CometVisu/trunk/visu/editor/lib/Schema.js
CometVisu/trunk/visu/visu_config.xml
CometVisu/trunk/visu/visu_config.xsd
CometVisu/trunk/visu/visu_config_2d3d.xml
CometVisu/trunk/visu/visu_config_demo.xml
CometVisu/trunk/visu/visu_config_demorss.xml
CometVisu/trunk/visu/visu_config_metal.xml
Added Paths:
-----------
CometVisu/trunk/visu/backup/
CometVisu/trunk/visu/upgrade/
CometVisu/trunk/visu/upgrade/ConfigurationUpgrader.class.php
CometVisu/trunk/visu/upgrade/index.php
CometVisu/trunk/visu/visu_config_previewtemp.xml
Modified: CometVisu/trunk/visu/editor/DEVELOPMENT.txt
===================================================================
--- CometVisu/trunk/visu/editor/DEVELOPMENT.txt 2013-01-21 08:25:57 UTC (rev 1352)
+++ CometVisu/trunk/visu/editor/DEVELOPMENT.txt 2013-01-23 20:31:08 UTC (rev 1353)
@@ -1,33 +1,36 @@
=== KNOWN BUGS AND LIMITATIONS ===
* loading the XSD is done in Javascript. The Schema-Loader knows a certain set an structure of an XSD. If "unknown"
XSD-features are being used in the schema, the Schema-Loader may not know about them, and the editor will thus fail.
-* the Schema care a little for bounds. It does, however, not support 'multi-dimensional'-bounds.
-* user-feedback is done using window.alert() - this works accross all devices and browsers, has no ux-issues on small
- screens, and enforces the users attention.
-* XSD is fucked up. minOccurs on elements in a choice are ignored if said choice is bounded otherwise
+
=== ROADMAP ===
* MS1 - done
- OOP-representation of the schema/XSD
- OOP-representation of the configuration
- validate configuration against XSD
-* MS2 - week 46/47 2012
+* MS2 - done
- browser loads configuration, checks it against an XSD, warns if not compliant
- editor is displayed in browser, nodes are shown as a tree
- node-attributes can be edited
- node children can be added, removed
-* MS3 - week 52 2012
+* MS3 - done
- nodes have copy&paste, move.
- configuration can be saved
- attributes like rrd, addr can be selected from a list; said list is loaded from the server
- attributes like styling, mapping can be selected from a dynamic list; list is based on current configuration
-* MS4 - early 2013
- - better check bounds of configuration, including multi-dimensional bounds, sequences?
- - preview for a selected node without the need of replacing the configuration on the server
+* MS4 - done
+ - better check bounds of configuration, including multi-dimensional bounds, sequences
+ - preview of the complete configuration without the need of replacing the configuration on the server
+ - integrated sequences, force specific order of elements
+ - use xsd:appinfo to mark attributes as <xsd:appinfo>level:expert</xsd:appinfo> to hide them in non-complex mode
+ - use xsd:documentation, respecting xml:lang, to show documentation next to attributes
+ - configurationUpgrader for anything below version 1 to version 1.
+* no MS planned
+ - preview for a SINGLE node without the need of replacing the configuration on the server
=== TODO ===
@@ -41,10 +44,13 @@
- done
* MS4:
- - allow for multi-dimensional bounds, as well as sequence?
+ - done
+
+* future:
- frontend: preview node incl. children
+
=== IDEAS ===
-* read xsd:annotation and xsd:documentation including xml:lang for tooltips/...
* transparently exchange whitespaces with for text-nodes to make sure the user gets what he entered ...
+* backups on saving
Modified: CometVisu/trunk/visu/editor/bin/save_config.php
===================================================================
--- CometVisu/trunk/visu/editor/bin/save_config.php 2013-01-21 08:25:57 UTC (rev 1352)
+++ CometVisu/trunk/visu/editor/bin/save_config.php 2013-01-23 20:31:08 UTC (rev 1353)
@@ -30,6 +30,7 @@
*/
define('CONFIG_FILENAME', '../../visu_config%s.xml');
+define('BACKUP_FILENAME', '../../backup/visu_config%s-%s.xml');
// get everything the user has provided ...
$strJson = (true === isset($_POST['data'])) ? $_POST['data'] : null;
@@ -49,6 +50,20 @@
exitWithResponse(false, 'config-file is not writeable by webserver-process; please chmod/chown config-file \'' . $strConfigFQFilename . '\' (\'' . $strConfigFilename. '\').');
}
+// create a backup, but not for the previewtemp
+if ($strConfigCleaned !== '_previewtemp') {
+ // generate the backups filename
+ $strBackupFilename = sprintf(BACKUP_FILENAME, $strConfigCleaned, date('YmdHis'));
+ $strBackupFQDirname = realpath(dirname($strBackupFilename));
+
+ if (false === is_writeable($strBackupFQDirname)) {
+ exitWithResponse(false, 'backup-file is not writeable, please chmod/chown directory \'' . $strBackupFQDirname . '\'');
+ }
+
+ // make a copy of the file for backup-purposes
+ copy($strConfigFQFilename, $strBackupFilename);
+}
+
// bail out if no json/configuration-data was provided
if (true === empty($strJson)) {
exitWithResponse(false, 'no configuration-content given');
Modified: CometVisu/trunk/visu/editor/init.js
===================================================================
--- CometVisu/trunk/visu/editor/init.js 2013-01-21 08:25:57 UTC (rev 1352)
+++ CometVisu/trunk/visu/editor/init.js 2013-01-23 20:31:08 UTC (rev 1353)
@@ -49,23 +49,6 @@
alert(result.message);
});
-
- $(document).unbind('configuration_saving_error').bind('configuration_saving_error', function (event, result) {
- // something went wrong
- // we can not fix it, so let's simply inform the user, and leave.
- alert(result.message);
- });
-
-
- $(document).unbind('configuration_saving_success').bind('configuration_saving_success', function (event) {
- // everything is cool, configuration was saved
- // @TODO: maybe implement some sort of "traffic light" that goes to green when the configuration was saved?
- var tmpResult = new Result(false, Messages.configuration.saved);
- alert(tmpResult.message);
- });
-
-
-
$(document).unbind('schema_loaded').bind('schema_loaded', function () {
try {
Modified: CometVisu/trunk/visu/editor/lib/Configuration.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/Configuration.js 2013-01-21 08:25:57 UTC (rev 1352)
+++ CometVisu/trunk/visu/editor/lib/Configuration.js 2013-01-23 20:31:08 UTC (rev 1353)
@@ -95,15 +95,21 @@
/**
* send the configuration to the server for saving
*
+ * @param filename string the filename to save to (optional, e.g. for saving to a temporary file)
* @return boolean success
*/
- _config.save = function () {
+ _config.save = function (filename) {
+ if (filename == undefined) {
+ // if no filename is given, use the one that we had for loading the file
+ filename = _filename;
+ }
+
var data = _config.getAsSerializable();
$.ajax('editor/bin/save_config.php',
{
dataType: 'json',
data: {
- config: _filename,
+ config: filename,
data: JSON.stringify(data),
},
type: 'POST',
@@ -379,6 +385,15 @@
};
/**
+ * return an id unique to this element
+ *
+ * @return integer the unique id
+ */
+ _element.getUID = function () {
+ return _uid;
+ };
+
+ /**
* set the SchemaElement for this ConfigurationElement.
* Goes recursive.
*
@@ -471,101 +486,32 @@
return false;
}
-
- // counting all our sub-elements
- var allSubElements = {};
-
- // populate this list with all allowed elements (with a counting of 0 (zero))
- var allowedSubElements = _schemaElement.getAllowedElements();
- if (allowedSubElements != undefined) {
- $.each(allowedSubElements, function (name, item) {
- allSubElements[name] = 0;
- });
- }
- delete allowedSubElements;
-
-
- if (_element.children.length > 0) {
- // if we have children, check them
-
- // go over list of all elements, and see if some are not valid
- // that's it
- $.each(_element.children, function (i, child) {
- if (isValid == false) {
+
+ var regexString = _schemaElement.getChildrenRegex(';');
+ var regExp = regexFromString(regexString);
+
+ var childrenString = '';
+ $.each(_element.children, function (i, child) {
+ if (child.name == '#text') {
+ // this is a text
+ if (false === _schemaElement.isMixed && false === _schemaElement.isTextContentAllowed()) {
+ informListeners('invalid', {type: 'text_not_allowed'});
+ isValid = false;
return;
}
-
- var childName = child.name;
- isValid = isValid && _schemaElement.isChildElementAllowed(childName);
+ // #text-nodes are not part of the string to match
- if (isValid == false) {
- informListeners('invalid', {type: 'element_disallowed', item: childName});
- return;
- }
-
- // count the number of occurences for this element
- if (undefined == allSubElements[childName]) {
- allSubElements[childName] = 0;
- }
- ++allSubElements[childName];
-
- // go recursive.
- isValid = isValid && child.isValid();
- });
- }
-
- if (false === isValid) {
- // bail out, if we already know that we failed
- return false;
- }
-
- // secondly, check with the bounds of their parent (i.e. their 'choice')
- // is NOT capable of multi-dimensional-choice-bounds (like in complexType mapping)
- var childBounds = _schemaElement.getChildBounds();
- if (childBounds != undefined) {
- // check bounds only if we have them :)
-
- // check all allowed and used elements against their personal bounds.
- // only if the choice-bounds of our parent are not undefined! (undefined = no choice, or multi-dimensional)
- // @FIXME: this is not how XSD-specifications say this works!
-// $.each(allSubElements, function (name, count) {
-// var bounds = _schemaElement.getSchemaElementForElementName(name).getBounds();
-//
-// if (bounds.min > count) {
-// // this element does not appear often enough
-// informListeners('invalid', {type: 'element_bounds_under', item: name});
-// isValid = false;
-// }
-//
-// if (bounds.max < count) {
-// // this element does not appear often enough
-// informListeners('invalid', {type: 'element_bounds_over', item: name});
-// isValid = false;
-// }
-// });
-
-
-
- if (childBounds.min > _element.children.length) {
- // less elements than defined by the bounds
- informListeners('invalid', {type: 'element_bounds_under'});
- isValid = false;
+ } else {
+ childrenString += child.name + ';';
}
-
- if (childBounds.max != 'unbounded') {
- if (childBounds.max < _element.children.length) {
- // more elements than defined by the bounds
- informListeners('invalid', {type: 'element_bounds_over'});
- isValid = false;
- }
- }
- }
-
- if (false === isValid) {
- // bail out, if we already know that we failed
+ });
+
+ if (false == regExp.test(childrenString)) {
+ // the children of this element do not match what the regex says is valid
+ informListeners('invalid', {type: 'regex_not_matched', regex: regexString, children: childrenString});
return false;
}
-
+
if (_element.children.length == 0 || _schemaElement.isMixed) {
// if this element has no children, it appears to be a text-node
// also, if it may be of mixed value
@@ -673,9 +619,84 @@
// set the parent of the new child!
childNode.setParentNode(_element);
+
+ // sort the child-nodes
+ sortChildNodes();
};
/**
+ * the list of sort-values for our children; needed and filled by sortChildNodes*
+ * @var object
+ */
+ var childSortValues = undefined;
+
+ /**
+ * re-sort child-elements based on a given sorting we might have from our schema
+ */
+ var sortChildNodes = function () {
+ // make a copy of the children-list
+ var tmpChildren = $.extend([], _element.children);
+
+ // get the sortValues once
+ if (childSortValues == undefined) {
+ childSortValues = _schemaElement.getAllowedElementsSorting();
+ }
+
+ // do the actual sorting
+ tmpChildren.sort(sortChildNodesHelper);
+
+ // set the children-list to the newly created, sorted, one
+ _element.children = tmpChildren;
+ };
+
+ /**
+ * the comparison-function that helps the sorting
+ *
+ * @param a mixed whatever sort gives us
+ * @param b mixed whatever sort gives us
+ * @return integer -1, 0, 1 - depending on sort-order
+ */
+ var sortChildNodesHelper = function (a, b) {
+ var aSortvalue = childSortValues[a.name];
+ var bSortvalue = childSortValues[b.name];
+
+ if (aSortvalue == undefined || bSortvalue == undefined) {
+ // undefined means: no sorting available
+ return 0;
+ }
+
+ if (aSortvalue == bSortvalue) {
+ // identical means 'no sorting necessary'
+ return 0;
+ }
+
+ // we need to go through the complete list of values the sorting is composed of,
+ // to find the first one that distinguishes a from b
+
+ // first, typecast to string!
+ if (typeof aSortvalue != 'string') {
+ aSortvalue = aSortvalue.toString();
+ }
+ if (typeof bSortvalue != 'string') {
+ bSortvalue = bSortvalue.toString();
+ }
+
+ var aSortvaluesList = aSortvalue.split('.');
+ var bSortvaluesList = bSortvalue.split('.');
+
+ for (var i = 0; i < aSortvaluesList.length; ++i) {
+ if (aSortvaluesList[i] < bSortvaluesList[i]) {
+ return -1;
+ } else if (aSortvaluesList[i] > bSortvaluesList[i]) {
+ return 1;
+ }
+ }
+
+ // if nothing else matched, then they are treated equal
+ return 0;
+ };
+
+ /**
* set the parent of this element.
* Needed for duplication, as we need to re-set the parent
*
@@ -721,6 +742,9 @@
// add the child
_element.children.splice(position, 0, child);
+
+ // we need to sort it afterwards, maybe the arbitrary position was too arbitrary :)
+ sortChildNodes();
}
/**
@@ -777,29 +801,22 @@
}
// the bounds of ourself, this means the maximum number of children as defined by a choice
- var myChildBounds = _schemaElement.getChildBounds();
- if (myChildBounds != undefined && myChildBounds.max >= _element.children.length) {
+ var myChildBounds = _schemaElement.getBoundsForElementName(childName);
+
+ var childCount = 0;
+
+ $.each(_element.children, function (i, child) {
+ if (child.name == childName) {
+ ++childCount;
+ }
+ });
+
+ if (myChildBounds != undefined && myChildBounds.max <= childCount) {
// no more children are allowed.
// sorry, you can not enter
return false;
}
- // the bounds of the specific element (child-to-be)
- var typeBounds = _schemaElement.getSchemaElementForElementName(childName).getBounds();
-
- // count the number of elements of this type we already have
- var count = 0;
- $.each(_element.children, function (i, item) {
- if (item.name == childName) {
- ++count;
- }
- });
-
- if (count >= typeBounds.max) {
- // if there are more elements of this type than allowed, then we're not ok
- return false;
- }
-
// no reason why to disallow the child-to-be
return true;
};
@@ -812,30 +829,27 @@
*/
_element.initFromScratch = function () {
+ // set the default-value, if one is given
+ if (_schemaElement.defaultValue != undefined) {
+ _element.setTextValue(_schemaElement.defaultValue);
+ }
+
+ // set the default-value to the attributes, if given
+ $.each(_schemaElement.allowedAttributes, function (attributeName, attributeSchema) {
+ if (attributeSchema.defaultValue != undefined) {
+ _element.setAttributeValue(attributeName, attributeSchema.defaultValue);
+ }
+ });
+
// empty our list of children (this is init, after all)
_element.children = [];
- // create a list of all elements that define their own min-occurences
- var neededElements = {};
- var allowedSubElements = _schemaElement.getAllowedElements();
- if (allowedSubElements != undefined) {
- $.each(allowedSubElements, function (name, item) {
- var bounds = item.getBounds();
-
- if (bounds.min > 0) {
- neededElements[name] = bounds.min;
- }
- });
- }
- delete allowedSubElements;
-
+ var requiredElements = _schemaElement.getRequiredElements();
+
// create elements for all items with min-occurences
- $.each(neededElements, function (name, count) {
- // create the appropriate amount of children of each needed type
- for (var i = 0; i < count; ++i) {
- // create a new child; this auto-recurses!
- _element.createChildNode(name);
- }
+ $.each(requiredElements, function (i, name) {
+ // create a new child; this auto-recurses!
+ _element.createChildNode(name);
});
};
@@ -1098,4 +1112,22 @@
*/
var listeners = [];
+ /**
+ * unique id
+ * @var integer
+ */
+ var _uid = uniqueID();
+
};
+
+/**
+ * creator of unique IDs
+ * source: http://arguments.callee.info/2008/10/31/generating-unique-ids-with-javascript/
+ * no license mentioned.
+ */
+var uniqueID = function() {
+ var id = 1; // initial value
+ return function() {
+ return id++;
+ }; // NOTE: return value is a function reference
+}(); // execute immediately
\ No newline at end of file
Modified: CometVisu/trunk/visu/editor/lib/Editor.css
===================================================================
--- CometVisu/trunk/visu/editor/lib/Editor.css 2013-01-21 08:25:57 UTC (rev 1352)
+++ CometVisu/trunk/visu/editor/lib/Editor.css 2013-01-23 20:31:08 UTC (rev 1353)
@@ -455,6 +455,11 @@
background-color: #D88F8F;
}
+#config .element > .attributes > .attribute .documentation {
+ display: inline-block;
+ font-size: 85%;
+}
+
#config .element .value.notset {
opacity: 0.3;
filter: alpha(opacity=30);
Modified: CometVisu/trunk/visu/editor/lib/Editor.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/Editor.js 2013-01-21 08:25:57 UTC (rev 1352)
+++ CometVisu/trunk/visu/editor/lib/Editor.js 2013-01-23 20:31:08 UTC (rev 1353)
@@ -50,21 +50,133 @@
*/
var _config = config;
+
/**
- * save the configuration, but validate it first
+ * filename-suffix for the preview-configuration
+ * @var string
*/
- var saveHandler = function () {
- if (false === _config.isValid()) {
- // configuration is not valid
- alert(Messages.editor.notSavingInvalidConfiguration);
- return;
+ var _previewSuffix = 'previewtemp';
+
+ /**
+ * disable the feedback on a successful 'save configuration'
+ * will be honoured once only, and then reset!
+ * @var boolean
+ */
+ var savingPreview = false;
+
+ /**
+ * is the expert-mode active?
+ * @var boolean
+ */
+ var isExpert = false;
+
+ var clickHandler = function (event) {
+ var $button = $(this);
+
+ if ($button.is('.save')) {
+ // save configuration
+ if (false === _config.isValid()) {
+ // configuration is not valid
+ alert(Messages.editor.notSavingInvalidConfiguration);
+ return;
+ }
+
+ // save the configuration
+ _config.save();
}
- // save the configuration
- _config.save();
+ if ($button.is('.preview')) {
+ // preview configuration
+
+ if ($('iframe#preview').length > 0) {
+ // there already is a preview - so toggle back and return to editing
+ $('iframe#preview').remove();
+ $('ul#config').show();
+ $('.menu .button.preview').removeClass('active');
+ return;
+ }
+
+ if (false === _config.isValid()) {
+ // configuration is not valid
+ alert(Messages.editor.notSavingInvalidConfiguration);
+ return;
+ }
+
+ savingPreview = true;
+ $(document).one('configuration_saving_success', previewShowHandler);
+
+ // save the configuration
+ _config.save('visu_config_' + _previewSuffix + '.xml');
+ }
+
+
+ if ($button.is('.expert')) {
+ // show/hide expert-attributes
+ $('#config').find('.expert').toggle();
+ $button.toggleClass('active');
+
+ isExpert = $button.hasClass('active');
+ }
};
+
+
+ /**
+ * eventHandler
+ *
+ * @param event jQuery-Event
+ */
+ var eventHandler = function (event, result) {
+ switch (event.type) {
+ case 'configuration_saving_error':
+ // something went wrong
+ // we can not fix it, so let's simply inform the user, and leave.
+ alert(result.message);
+ break;
+ case 'configuration_saving_success':
+ // everything is cool, configuration was saved
+ // @TODO: maybe implement some sort of "traffic light" that goes to green when the configuration was saved?
+
+ if (false === savingPreview) {
+ // only show feedback if this is not saving a preview ...
+ var tmpResult = new Result(false, Messages.configuration.saved);
+ alert(tmpResult.message);
+ }
+
+ break;
+ }
+
+ // re-enable saving success feedback to 'true'
+ savingPreview = false;
+ };
/**
+ * preview the configuration.
+ * this handler is called AFTER the preview-configuration has already been saved
+ */
+ var previewShowHandler = function () {
+ // remove a preview if there is one (should never happen, but better be safe than sorry)
+ $('iframe#preview').remove();
+
+ // mark the button as being active
+ $('.menu .button.preview').addClass('active');
+
+
+ // hide the editing-view
+ $('ul#config').hide();
+
+ // create, and render the preview
+ var $preview = $('<iframe />')
+ $preview.attr({id: 'preview', src: 'index.html?config=' + _previewSuffix});
+
+
+ var height = $(window).height() - $('#editor .menu').height() - $('#editor .menu').position().top;
+
+ $preview.css({width: '100%', height: height + 'px', border: 'none'});
+
+ $('#editor').append($preview);
+ };
+
+ /**
* render the Editor.
* Will render the editor in the specified DOMNode.
*
@@ -96,15 +208,35 @@
.addClass('save')
.html(Messages.editor.ui.save.text)
.attr('title', Messages.editor.ui.save.tooltip)
- .click(saveHandler);
+ .click(clickHandler);
$menu.append($save);
-
+
+ var $expert = $('<span />')
+ .addClass('button')
+ .addClass('expert')
+ .html(Messages.editor.ui.expert.text)
+ .attr('title', Messages.editor.ui.expert.tooltip)
+ .click(clickHandler);
+ $menu.append($expert);
+
+ var $preview = $('<span />')
+ .addClass('button')
+ .addClass('preview')
+ .html(Messages.editor.ui.preview.text)
+ .attr('title', Messages.editor.ui.preview.tooltip)
+ .click(clickHandler);
+ $menu.append($preview);
+
$editor.append($menu);
$editor.append($container);
$target.append($editor);
+
+ // register event-handlers
+ $(document).bind('configuration_saving_error', eventHandler);
+ $(document).bind('configuration_saving_success', eventHandler);
};
var rememberedElement = {
@@ -144,6 +276,15 @@
_editor.getRememberedElementOptions = function () {
return rememberedElement.options;
};
+
+ /**
+ * find out if expert attributes are to be visible
+ *
+ * @return boolean
+ */
+ _editor.areExpertAttributesVisible = function () {
+ return isExpert;
+ }
}
/**
@@ -177,6 +318,12 @@
var KEYCODE_ESCAPE = 27;
/**
+ * cache of children
+ * @var array
+ */
+ var _childrenCache = {};
+
+ /**
* everything to do with buttons
* @var object
*/
@@ -274,8 +421,8 @@
// Spacer
$html.append($menuitem.clone().addClass('spacer'));
- // cut, copy, paste, sort (like move with same parent)
- $.each(['cut', 'copy', 'paste', 'sort'], function (i, item) {
+ // cut, copy, paste
+ $.each(['cut', 'copy', 'paste'], function (i, item) {
$tmpItem = $menuitem.clone();
$tmpItem.addClass(item).click(UIElements.clickHandler);
$tmpItem.attr('title', Messages.editor.ui[item].tooltip);
@@ -283,8 +430,21 @@
$html.append($tmpItem);
delete $tmpItem;
});
+
+ // sort (like move with same parent); only if the parent allows for sorting!
+ // (check tyepof getConfigurationElement, as the root-element has no such thing)
+ if (typeof _parent.getConfigurationElement == 'function'
+ && true === _parent.getConfigurationElement().getSchemaElement().areChildrenSortable()) {
+ $tmpItem = $menuitem.clone();
+ $tmpItem.addClass('sort').click(UIElements.clickHandler);
+ $tmpItem.attr('title', Messages.editor.ui['sort'].tooltip);
+ $tmpItem.text(Messages.editor.ui['sort'].text);
+ $html.append($tmpItem);
+ delete $tmpItem;
+ }
+
// search for the paste-button, and tell it which elements to allow
var $paste = $html.find('.menuitem.paste');
$.each(_element.getAllowedElements(), function (name) {
@@ -446,8 +606,7 @@
var newElement = originalElement.getDuplicateForParent(_element);
_element.appendChildNode(newElement);
- var newEditorElement = new EditorConfigurationElement(_self, newElement);
- _self.appendChildNode(newEditorElement);
+ _self.redrawHTML();
if (options.type == 'cut') {
// remove the original element if type was cut
@@ -497,16 +656,16 @@
var sortableElement = _self.getConfigurationElement();
// clone the ConfigurationElement, and create a new EditorConfigurationElement with that
- var sortedElement = $.extend(true, {}, sortableElement);
+ sortableElement.remove();
+ var sortedElement = sortableElement.getDuplicateForParent(_element);
_parent.getConfigurationElement().addChildAtPosition(sortedElement, position);
// then: remove ourselves (the old node), before re-arranging the DOM (or this will fail)
_self.remove();
+ // redraw our parents HTML
+ _parent.redrawHTML();
- var sortedEditorElement = new EditorConfigurationElement(_parent, sortedElement);
- _parent.appendChildNodeAtPosition(sortedEditorElement, position);
-
// clear rememberedElement afterwards
_parent.rememberElement(undefined, undefined);
}
@@ -582,6 +741,22 @@
// user-feedback. there is nothing we could add ... (MS2)
return undefined;
}
+
+ // sort the elements in the select-list
+ var $options = $select.find('option');
+
+ $options.sort(function (a, b) {
+ if (a.text > b.text) return 1;
+ if (a.text < b.text) return -1;
+ return 0;
+ });
+
+ // we need to re-append the items to the select-list
+ $select.empty().append($options);
+ delete $options;
+
+ // select the 'select one'-entry
+ $select.val(AddChild.settings.valueDefault);
$selector.append($select);
@@ -618,16 +793,13 @@
}
// create a new Configuration-Node
- var childNode = _element.createChildNode(name);
+ _element.createChildNode(name);
- // create a new Editor-Node
- var childEditorNode = new EditorConfigurationElement(_self, childNode);
-
// and kill the messenger.. uh, remove the select-list from view
$select.closest('span.' + AddChild.settings.cssClass).remove();
- // append the EditorNodes HTML to the DOM
- _self.appendChildNode(childEditorNode);
+ // redraw the HTML
+ _self.redrawHTML();
},
/**
@@ -652,6 +824,22 @@
* cache the html of the attributes
*/
$attributes: undefined,
+
+ /**
+ * placeholder
+ */
+ $htmlPlaceholder: undefined,
+
+ /**
+ * get the placeholder so we can add the attributes later on to save time at startup
+ *
+ * @return jquery-object the HTML-placeholder
+ */
+ getPlaceholderAsHTML: function () {
+ Attributes.$htmlPlaceholder = $('<span />');
+
+ return Attributes.$htmlPlaceholder;
+ },
/**
* get this elements HTML for its attributes
@@ -670,15 +858,39 @@
var $attributes = $('<ul />').addClass('attributes');
$.each(allAttributes, function (key, value) {
var isOptional = undefined;
+ var isExpert = false;
+ var attributeDocumentation = '';
if (schemaElement != undefined) {
var schemaAttribute = schemaElement.allowedAttributes[key];
+
+ if (typeof schemaAttribute == 'undefined') {
+ throw 'unknown attribute ' + key + ' for element ' + schemaElement.name;
+ }
+
isOptional = schemaAttribute.isOptional;
+
+ var properties = schemaAttribute.getAppinfo();
+ isExpert = properties.indexOf('level:expert') != -1;
+ delete properties;
+
+ var documentation = schemaAttribute.getDocumentation();
+ if (documentation.length > 0) {
+ attributeDocumentation = documentation[0];
+ }
+ delete documentation;
}
var $attribute = $('<li />').addClass('attribute');
$attribute.addClass('attributeType_' + key);
+ if (true === isExpert) {
+ $attribute.addClass('expert');
+ if (_parent.areExpertAttributesVisible() == false) {
+ $attribute.hide();
+ }
+ }
+
// name of the attribute
var $name = $('<span />').addClass('name').html(key);
if (true === isOptional) {
@@ -686,6 +898,7 @@
} else {
$name.addClass('required');
}
+
$attribute.append($name);
delete $name;
@@ -698,7 +911,18 @@
$value = $('<span />').addClass('value').addClass('notset');
}
$value.attr('title', Messages.editor.ui.clickToEdit.tooltip);
+
$attribute.append($value);
+
+ // if we have documentation for this attribute, append it
+ if (attributeDocumentation != '') {
+ var $attributeDocumentation = $('<span />');
+ $attributeDocumentation.addClass('documentation');
+ $attributeDocumentation.html(attributeDocumentation);
+ $attribute.append($attributeDocumentation)
+ delete $attributeDocumentation;
+ }
+
// attach a click-handler
$value.bind('click', Attributes.clickHandler);
delete $value;
@@ -867,7 +1091,15 @@
*/
toggleDisplay: function () {
var $attributes = Attributes.$attributes;
-
+
+
+ if (typeof $attributes == 'undefined') {
+ // inject the actual HTML
+ $attributes = Attributes.getAsHTML();
+ Attributes.$htmlPlaceholder.replaceWith($attributes);
+ Attributes.$htmlPlaceholder = undefined;
+ }
+
// first hide
if ($('ul.attributes:visible').not($attributes).length > 0) {
@@ -881,11 +1113,17 @@
$attributes.slideToggle('fast');
}
-
return;
},
/**
+ * hide all visible attributes, whoever they may belong to
+ */
+ hideAll: function () {
+ $('ul.attributes:visible').slideToggle('fast');
+ },
+
+ /**
* mark an attribute as invalid.
*
* @param attributeName string name of the attribute
@@ -1266,18 +1504,15 @@
* toggle displaying this element as being 'the active one'
*/
var toggleActive = function () {
- if (Attributes.$attributes == undefined) {
- // no attributes to be had
- return;
- }
-
- Attributes.toggleDisplay();
-
+
var $name = _html.find('.name').first();
// clean up!
$('.name.active').not($name).removeClass('active');
$name.toggleClass('active');
+
+ Attributes.toggleDisplay();
+
};
/**
@@ -1325,7 +1560,16 @@
return _parent.getRememberedElement();
};
+ /**
+ * find out if expert attributes are to be visible
+ *
+ * @return boolean
+ */
+ _self.areExpertAttributesVisible = function () {
+ return _parent.areExpertAttributesVisible();
+ }
+
/**
* get the options that were stored alongside the remembered element
*
@@ -1336,40 +1580,6 @@
};
/**
- * append a child to the HTML-DOM
- *
- * @param childEditorNode object EditorConfigurationElement to be added
- */
- _self.appendChildNode = function (childEditorNode) {
- // append the EditorNodes HTML to the DOM
- _html.find('ul.children').first().append(childEditorNode.getAsHTML());
- _self.redrawChildrenButton();
- };
-
-
- /**
- * append a child to the HTML-DOM at an arbitrary position
- *
- * @param childEditorNode object EditorConfigurationElement to be added
- * @param position integer array-index at which to insert said childEditorNode
- */
- _self.appendChildNodeAtPosition = function (childEditorNode, position) {
- var $nodes = _html.find('ul.children').first().children('li');
- if (position >= $nodes.length) {
- // am not able to insert after the end of our list ...
- // or at the end of the list (length - 1)
- // append the EditorNodes HTML to the DOM
- $nodes.last().after(childEditorNode.getAsHTML());
- } else {
- // add the EditorNodes HTML to the DOM
- $nodes.eq(position).before(childEditorNode.getAsHTML());
- }
-
- _self.redrawChildrenButton();
- };
-
-
- /**
* remove this EditorConfigurationElement from the DOM, and remove the _element from its parent
*/
_self.remove = function () {
@@ -1400,7 +1610,6 @@
_html.find('ul.children > li').hide();
}
};
-
/**
* get the current ConigurationElement
@@ -1455,13 +1664,34 @@
};
/**
+ * reset the HTML-Cache of this Editor-Node.
+ * This is needed as we are not in the position to decide which element to place where exactly -
+ * that is up to the configuration.
+ * So when we add a child to the configuration, we need to re-set and re-draw our HTML/DOM
+ */
+ _self.redrawHTML = function () {
+ _html.replaceWith(_self.getAsHTML(false, true));
+ };
+
+ /**
* create the html of us. recursive!
* caches the result
*
- * @return jquery-object the HTML to display for this node and its children
+ * @param allowCache boolean is it ok to ge the HTML from cache?
+ * @param childrenVisible boolean shall the element be uncollapsed?
+ * @return jquery-object the HTML to display for this node and its children
*/
- _self.getAsHTML = function () {
- if (_html != undefined) {
+ _self.getAsHTML = function (allowCache, childrenVisible) {
+ if (typeof allowCache == 'undefined' || allowCache == undefined) {
+ allowCache = true;
+ }
+
+ if (typeof childrenVisible == 'undefined' || childrenVisible == undefined) {
+ childrenVisible = false;
+ }
+
+ if (true === allowCache && _html != undefined) {
+ // caching is allowed, so use it!
return _html;
}
@@ -1486,7 +1716,7 @@
// get the html for our attributes (and those that are allowed but not set)
// depending on that, we set/add buttons, that is why we do it so early
- var $attributes = Attributes.getAsHTML();
+ var $attributes = Attributes.getPlaceholderAsHTML();
// define, which buttons to show
// attributes only, if there are some that are not immediately visible
@@ -1496,7 +1726,6 @@
var allowedChildren = schemaElement.getAllowedElements();
var menuConfig = {
- attributes: ($attributes != undefined && $attributes.find('li:not(.visible)').length > 0),
children: !$.isEmptyObject(allowedChildren),
remove: _element.isRemovable(),
};
@@ -1524,17 +1753,46 @@
if ($attributes != undefined && $attributes.length > 0) {
// only append attributes if we have some
$innerHTML.append($attributes);
- $name.css({cursor: 'pointer'});
}
+
+ $name.css({cursor: 'pointer'});
- // append this elements children (immediate first, the recurse)
- var $children = $('<ul />').addClass('children').hide();
+ // append this elements children (immediate first, then recurse)
+ var $children = $('<ul />').addClass('children');
+
+ var newChildrenCache = {};
$.each(_element.getChildren(), function (i, node) {
- var element = new EditorConfigurationElement(_self, node);
+
+ var element;
+
+ var nodeUID = node.getUID();
+ if (typeof _childrenCache[nodeUID] != 'undefined') {
+ // use the cached element
+ element = _childrenCache[nodeUID];
+ } else {
+ // go create a new element
+ element = new EditorConfigurationElement(_self, node);
+
+ // get the configurationElements UID
+ nodeUID = element.getConfigurationElement().getUID();
+ }
- $children.append(element.getAsHTML());
+ // get the elements HTML, and allow for caching
+ $children.append(element.getAsHTML(true));
+
+ // fill the new cache
+ newChildrenCache[nodeUID] = element;
});
+
+ // replace the old cache with the new cache
+ _childrenCache = newChildrenCache;
+ delete newChildrenCache;
+
+ if (false === childrenVisible) {
+ $children.hide();
+ }
+
$innerHTML.append($children);
delete $children;
Modified: CometVisu/trunk/visu/editor/lib/Messages.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/Messages.js 2013-01-21 08:25:57 UTC (rev 1352)
+++ CometVisu/trunk/visu/editor/lib/Messages.js 2013-01-23 20:31:08 UTC (rev 1353)
@@ -30,6 +30,7 @@
*/
var Messages = {
+ language: 'en',
validity: {
configurationInvalid: 'The configuration appears to be not valid. ' +
'Please check with \'check_config.php\' for details.\n' +
@@ -106,6 +107,14 @@
tooltip: 'validate config and save',
text: 'save',
},
+ preview: {
+ tooltip: 'validate config, save it temporarily, and preview it',
+ text: 'preview',
+ },
+ expert: {
+ tooltip: 'show/hide attributes for advanced configuration',
+ text: 'complex',
+ },
},
},
schema: {
Modified: CometVisu/trunk/visu/editor/lib/Schema.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/Schema.js 2013-01-21 08:25:57 UTC (rev 1352)
+++ CometVisu/trunk/visu/editor/lib/Schema.js 2013-01-23 20:31:08 UTC (rev 1353)
@@ -33,8 +33,6 @@
*/
-// @TODO: read and parse annotations (documentation, xml: lang); use separate Class? (MS5+)
-
/**
* starting-point for a javascript-representation of the XSD
*
@@ -94,6 +92,12 @@
}
/**
+ * cache for referenced nods
+ * @var object
+ */
+ var referencedNodeCache = {};
+
+ /**
* dive into the schema and find the element that is being pulled in by a ref.
* Do so recursively.
* referenced nodes can be top-level-nodes only!
@@ -103,6 +107,10 @@
* @return object jQuery-object of the ref'ed element
*/
_schema.getReferencedNode = function (type, refName) {
+ if (typeof referencedNodeCache[type] != 'undefined' && typeof referencedNodeCache[type][refName] != 'undefined') {
+ return referencedNodeCache[type][refName];
+ }
+
var selector = 'xsd\\:schema > xsd\\:' + type + '[name="' + refName + '"]';
selector = fixNamespace(selector);
var $ref = $(selector, $xsd);
@@ -112,27 +120,73 @@
$ref = _schema.getReferencedNode(type, $ref.attr('ref'));
}
+ if (typeof referencedNodeCache[type] == 'undefined') {
+ referencedNodeCache[type] = {};
+ }
+
+ // fill the cache
+ referencedNodeCache[type][refName] = $ref;
+
return $ref;
}
/**
+ * cache for getTypeNode
+ * @var object
+ */
+ var typeNodeCache = {};
+
+ /**
* get the definition of a type, be it complex or simple
*
* @param type string Type of type to find (either simple or complex)
* @param name string Name of the type to find
*/
_schema.getTypeNode = function (type, name) {
+
+ if (typeof typeNodeCache[type] != 'undefined' && typeof typeNodeCache[type][name] != 'undefined') {
+ return typeNodeCache[type][name];
+ }
+
var selector = fixNamespace('xsd\\:' + type + 'Type[name="' + name + '"]');
var $type = $(selector, $xsd);
if ($type.length != 1) {
throw 'schema/xsd appears to be invalid, ' + type + 'Type "' + name + '" can not be found';
}
-
+
+ if (typeof typeNodeCache[type] == 'undefined') {
+ typeNodeCache[type] = {};
+ }
+
+ // fill the cache
+ typeNodeCache[type][name] = $type;
+
return $type;
}
/**
+ * cache for #text-SchemaElement
+ * @var object
+ */
+ var textNodeSchemaElement = undefined;
+
+ /**
+ * get a SchemaElement for a #text-node
+ *
+ * @return object SchemaElement for #text-node
+ */
+ _schema.getTextNodeSchemaElement = function () {
+ if (textNodeSchemaElement == undefined) {
+ // text-content is always a simple string
+ var tmpXML = $('<element />', _schema.getSchemaDOM()).attr({name: '#text', type: 'xsd:string'});
+ textNodeSchemaElement = new SchemaElement(tmpXML, _schema);
+ }
+
+ return textNodeSchemaElement;
+ }
+
+ /**
* get the DOM for this Schema
*
* @return object DOM
@@ -437,7 +491,96 @@
return Messages.schema.complexType;
}
+
/**
+ * cache for getAppinfo
+ * @var array
+ */
+ var appinfoCache = undefined;
+
+ /**
+ * get the appinfo information from the attribute, if any
+ *
+ * @return array list of texts, or empty list if none
+ */
+ _attribute.getAppinfo = function () {
+ if (undefined != appinfoCache) {
+ return appinfoCache;
+ }
+
+ var appinfo = [];
+
+ // any appinfo this element itself might carry
+ $.each($node.find(fixNamespace('> xsd\\:annotation > xsd\\:appinfo')), function (i, appinfoNode) {
+ var appinfoNodeText = $(appinfoNode).text();
+ appinfo.push(appinfoNodeText);
+ });
+
+ if ($node.is('[ref]')) {
+ // the attribute is a reference, so take appinfo from there, too
+
+ var refName = $node.attr('ref');
+ var $ref = _schema.getReferencedNode('attribute', refName);
+
+ $.each($ref.find(fixNamespace('> xsd\\:annotation > xsd\\:appinfo')), function (i, appinfoNode) {
+ var appinfoNodeText = $(appinfoNode).text();
+ appinfo.push(appinfoNodeText);
+ });
+ }
+
+ // fill the cache
+ appinfoCache = appinfo;
+
+ return appinfo;
+ };
+
+ /**
+ * cache for getDocumentation
+ * @var array
+ */
+ var documentationCache = undefined;
+
+ /**
+ * get the documentation information from the attribute, if any
+ *
+ * @return array list of texts, or empty list if none
+ */
+ _attribute.getDocumentation = function () {
+ if (undefined != documentationCache) {
+ return documentationCache;
+ }
+
+ var documentation = [];
+
+ var lang = Messages.language;
+ var selector = '> xsd\\:annotation > xsd\\:documentation[xml\\:lang=' + lang + ']';
+
+ // any appinfo this element itself might carry
+ $.each($node.find(fixNamespace(selector)), function (i, documentationNode) {
+ var documentationNodeText = $(documentationNode).text();
+ documentation.push(documentationNodeText);
+ });
+
+ if ($node.is('[ref]')) {
+ // the attribute is a reference, so take appinfo from there, too
+
+ var refName = $node.attr('ref');
+ var $ref = _schema.getReferencedNode('attribute', refName);
+
+ $.each($ref.find(fixNamespace(selector)), function (i, documentationNode) {
+ var documentationNodeText = $(documentationNode).text();
+ documentation.push(documentationNodeText);
+ });
+ }
+
+ // fill the cache
+ documentationCache = documentation;
+
+ return documentation;
+ };
+
+
+ /**
* get the list of values that are valid for this attribute, if it is an enumeration
*
* @return array list of string of valid values
@@ -472,8 +615,16 @@
* @var string the name
*/
_attribute.name = getAttributeName($node);
+
/**
+ * a default-value for this element
+ * defaults to undefined
+ * @var string
+ */
+ _attribute.defaultValue = $node.attr('default');
+
+ /**
* we have our own type
* @var object SchemaSimpleType of the attribute, for validating purposes
*/
@@ -489,6 +640,12 @@
*/
var SchemaElement = function (node, schema) {
var _element = this;
+
+ /**
+ * type of this object
+ * @var string
+ */
+ _element.type = 'element';
/**
* Get the name of a schema-element
@@ -561,41 +718,39 @@
}
// allowed sub-elements
- // can be either choice, simpleContent or sequence (the latter is not supported)
+ // can be either simpleContent, or (choice|sequence|group|all)?
+ // 'all' is not supported yet.
if ($type.children(fixNamespace('xsd\\:simpleContent')).length > 0) {
// it's simpleContent? Then it's either extension or restriction
// anyways, we will handle it, as if it were a simpleType
allowedContent._text = new SchemaSimpleType($type.children(fixNamespace('xsd\\:simpleContent')), _schema);
- } else if ($type.children(fixNamespace('xsd\\:choice')).length > 0) {
- // we have a choice. great
- // as per the W3C, choice may only appear once per element/type
- var tmpDOMChoice = $type.children(fixNamespace('xsd\\:choice')).get(0);
-
- allowedContent._choice = new SchemaChoice(tmpDOMChoice, _schema);
- delete tmpDOMChoice;
- } else if ($type.children(fixNamespace('xsd\\:group')).length > 0) {
- // we have a group
- // as per the W3C, group may only appear once per element/type (not true for group in choices!)
- // also, a group and a choice can not BOTH appear in an element/type
-
- var tmpDOMGroup = $type.children(fixNamespace('xsd\\:group'))[0];
+ } else if ($type.children(fixNamespace('xsd\\:choice, xsd\\:sequence, xsd\\:group')).length > 0) {
+ // we have a choice, group or sequence. great
+ // as per the W3C, only one of these may appear per element/type
- var subGroup = new SchemaGroup(tmpDOMGroup, _schema);
+ var tmpDOMGrouping = $type.children(fixNamespace('xsd\\:choice, xsd\\:sequence, xsd\\:group')).get(0);
- if (true === subGroup.hasChoice()) {
- allowedContent._choice = subGroup.getChoice();
+ // create the appropriate Schema*-object and append it to this very element
+ switch (tmpDOMGrouping.nodeName) {
+ case 'xsd:choice':
+ case 'choice':
+ allowedContent._grouping = new SchemaChoice(tmpDOMGrouping, _schema);
+ break;
+ case 'xsd:sequence':
+ case 'sequence':
+ allowedContent._grouping = new SchemaSequence(tmpDOMGrouping, _schema);
+ break;
+ case 'xsd:group':
+ case 'group':
+ allowedContent._grouping = new SchemaGroup(tmpDOMGrouping, _schema);
+ break;
}
-
- delete tmpDOMGroup;
- delete subGroup;
+ delete tmpDOMGrouping;
} else if ($type.is('[type]') && $type.attr('type').match(/^xsd:/)) {
// this is a really simple node that defines its own baseType
allowedContent._text = new SchemaSimpleType($type, _schema);
- } else if ($type.children(fixNamespace('xsd\\:sequence')).length > 0) {
- // sequence is not yet supported
- throw 'schema/xsd is not compatible, unsupported node ' + $e;
} else {
// no type, no children, no choice - this is an element with NO allowed content/children
return allowedContent;
@@ -616,6 +771,37 @@
}
/**
+ * are this elements children sortable? this is not the case if a sequence is used, e.g.
+ *
+ * @return boolean are children sortable?
+ */
+ _element.areChildrenSortable = function () {
+ if (allowedContent._grouping == undefined) {
+ return undefined;
+ }
+
+ // the inverse of "do the elements have a given order?"
+ return !allowedContent._grouping.elementsHaveOrder;
+ };
+
+
+ /**
+ * get a list of required elements.
+ * if an element is required multiple times, it is listed multiple times
+ *
+ * @return array list of required elements
+ */
+ _element.getRequiredElements = function () {
+ if (allowedContent._grouping != undefined) {
+ // we do have a grouping as a child
+ return allowedContent._grouping.getRequiredElements();
+ }
+
+ // there is no grouping, hence no elements defined as children
+ return [];
+ };
+
+ /**
* get a list of all allowed elements for this element
*
* @return object list of SchemaElement-elements, key is the name
@@ -624,40 +810,61 @@
var allowedContent = _element.getAllowedContent();
var allowedElements = {};
- if (allowedContent._choice != undefined) {
- $.extend(allowedElements, allowedContent._choice.getAllowedElements(true));
+ if (allowedContent._grouping != undefined) {
+ $.extend(allowedElements, allowedContent._grouping.getAllowedElements());
}
if (true === _element.isMixed) {
// mixed elements are allowed to have #text-nodes
- var tmpXML = $('<element />', _schema.getSchemaDOM()).attr({name: '#text', type: 'xsd:string'});
- var tmpSchemaElement = new SchemaElement(tmpXML, _schema);
- allowedElements['#text'] = tmpSchemaElement;
+ allowedElements['#text'] = _schema.getTextNodeSchemaElement();
}
return allowedElements;
}
/**
+ * get the sorting of the allowed elements.
+ *
+ * @return object list of allowed elements, with their sort-number as value
+ */
+ _element.getAllowedElementsSorting = function () {
+ if (allowedContent._grouping != undefined) {
+ return allowedContent._grouping.getAllowedElementsSorting();
+ }
+
+ return undefined;
+ }
+
+ /**
* get the bounds for this elements children (as defined by a choice)
*
* @return object bounds ({min: x, max: y})
*/
_element.getChildBounds = function () {
- if (allowedContent._choice == undefined) {
+ if (allowedContent._grouping == undefined) {
// no choice = no idea about bounds
return undefined;
}
- if (true === allowedContent._choice.hasSubChoice()) {
+ if (true === allowedContent._grouping.hasMultiLevelBounds()) {
// if our choice has sub-choices, then we have not fucking clue about bounds (or we can not process them)
return undefined;
}
- return allowedContent._choice.bounds;
+ return allowedContent._grouping.getBounds();
}
+ /**
+ * get the bounds for a specific element-name
+ * will go through all of the groupings-tree to find out, just how many elements of this may appear
+ *
+ * @param childName string name of the child-to-be
+ * @return object {min: x, max: y}
+ */
+ _element.getBoundsForElementName = function (childName) {
+ return allowedContent._grouping.getBoundsForElementName(childName);
+ };
/**
* get this elements bounds (bounds defined FOR THIS ELEMENT)
@@ -714,7 +921,7 @@
*
* @return boolean
*/
- var isTextContentAllowed = function () {
+ _element.isTextContentAllowed = function () {
if (_element.isMixed == true) {
// mixed means that we allow for text-content
return true;
@@ -742,27 +949,106 @@
_element.isChildElementAllowed = function (child) {
if (child == '#text') {
// text-nodes are somewhat special :)
- return isTextContentAllowed();
+ return _element.isTextContentAllowed();
}
// first, get a list of allowed content (don't worry, it's cached)
var allowedContent = _element.getAllowedContent();
- if (allowedContent._choice == undefined) {
+ if (allowedContent._grouping == undefined) {
// when there is no choice, then there is no allowed element
return false;
}
// see, if this child is allowed with our choice
- if (allowedContent._choice != undefin...
[truncated message content] |