|
From: <net...@us...> - 2012-10-21 08:28:10
|
Revision: 1067
http://openautomation.svn.sourceforge.net/openautomation/?rev=1067&view=rev
Author: netzkind
Date: 2012-10-21 08:28:02 +0000 (Sun, 21 Oct 2012)
Log Message:
-----------
starting a new, solid editor; MS1 (see DEVELOPMENT.txt); this is work in progress, so non-usability is guaranteed.
Added Paths:
-----------
CometVisu/trunk/visu/editor/
CometVisu/trunk/visu/editor/DEVELOPMENT.txt
CometVisu/trunk/visu/editor/index.html
CometVisu/trunk/visu/editor/lib/
CometVisu/trunk/visu/editor/lib/Configuration.js
CometVisu/trunk/visu/editor/lib/DumpConfig.js
CometVisu/trunk/visu/editor/lib/Schema.js
Added: CometVisu/trunk/visu/editor/DEVELOPMENT.txt
===================================================================
--- CometVisu/trunk/visu/editor/DEVELOPMENT.txt (rev 0)
+++ CometVisu/trunk/visu/editor/DEVELOPMENT.txt 2012-10-21 08:28:02 UTC (rev 1067)
@@ -0,0 +1,49 @@
+=== 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 and the Editor do not care about bounds. This means it does not check if the schema only allows
+ for a certain number of children in an element.
+
+
+=== ROADMAP ===
+* MS1 - done
+ - OOP-representation of the schema/XSD
+ - OOP-representation of the configuration
+ - validate configuration against XSD
+
+* MS2 - week 46/47 2012
+ - 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 49/50 2012
+ - 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 actual configuration
+
+* MS4 - week 52 2012
+ - preview for a selected node without the need of replacing the configuration on the server
+
+
+=== TODO ===
+* MS1:
+ - done
+
+* MS2:
+ - frontend: show tree
+ - frontend: make node attributes editable
+ - frontend: make node removeable
+ - frontend: make node children addable, removable
+
+* MS3:
+ - frontend: have copy&paste for nodes incl. children
+ - frontend: have move for nodes incl. children (drag&drop?; mind touch-devices!)
+ - frontend: save configuration
+ - backend (save the xml; php)
+ - cross-reference: have select-lists for mapping, stylings, ... defined by the config itself
+
+* MS4:
+ - frontend: preview node incl. children
+
Added: CometVisu/trunk/visu/editor/index.html
===================================================================
--- CometVisu/trunk/visu/editor/index.html (rev 0)
+++ CometVisu/trunk/visu/editor/index.html 2012-10-21 08:28:02 UTC (rev 1067)
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <script src="../dependencies/jquery.js" type="text/javascript"></script>
+ <script src="lib/Schema.js" type="text/javascript"></script>
+ <script src="lib/Configuration.js" type="text/javascript"></script>
+ <script src="lib/DumpConfig.js" type="text/javascript"></script>
+
+ </head>
+ <body>
+ <div>TODO write content</div>
+ <h1>Editor</h1>
+ <p><b>all of this is work in progress</b></p>
+ <p>once this is ready, it will be an xsd-aware, form-driven editor for the CometVisu-configuration with a
+ tree-like representation of the configuration</p>
+ <p>WYSIWIG will be dropped in favour of the xsd-awareness, which will hopefully make this editor withstand
+ even massive changes to the visu, the templateengine or the configuration-schema. Any plugin and feature
+ which is well defined in the xsd will be supported out-of-the-box (as long as no Ninja-XSD is used...)</p>
+
+ <pre>
+var config;
+var schema;
+
+$(document).unbind('configuration_loaded').bind('configuration_loaded', function () {
+ // configuration is loaded
+ var schemaFilename = config.getSchemaFilename();
+ console.log(schemaFilename);
+ schema = new Schema(schemaFilename);
+});
+
+// the good:
+var config = new Configuration('../visu_config_metal.xml'); // <== metal-config is valid
+// the bad:
+// var config = new Configuration('../visu_config_demo.xml'); // <== demo-config is invalid
+
+$(document).unbind('schema_loaded').bind('schema_loaded', function () {
+ try {
+ config.setSchema(schema);
+ } catch (e) {
+ console.error('configuration and schema do not mix (invalid config?), can not proceed');
+ return;
+ }
+
+ if (false === config.isValid()) {
+ console.error('configuration is not valid, can not proceed');
+ } else {
+ console.log('config loaded and valid');
+ }
+
+ // var dc = new DumpConfig(config);
+ // dc.dump();
+
+});
+ </pre>
+
+ </body>
+</html>
Added: CometVisu/trunk/visu/editor/lib/Configuration.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/Configuration.js (rev 0)
+++ CometVisu/trunk/visu/editor/lib/Configuration.js 2012-10-21 08:28:02 UTC (rev 1067)
@@ -0,0 +1,380 @@
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Javascript-Representation of a configuration
+ *
+ * These classes are used to load a visu-configuration, making it's attributes and nodes available in Javascript
+ *
+ *
+ * LICENSE: This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
+ *
+ * @category editor
+ * @package CometVisu
+ * @author Julian Makowski (julian at makowskis dot de)
+ * @copyright 2012 Julian Makowski
+ * @license GPLv3 or later, http://opensource.org/licenses/gpl-license.php
+ * @version SVN: $Id$
+ * @link http://cometvisu.de
+ * @since 2012-10-10
+ * @requires Schema.js
+ */
+
+/**
+ * loader
+ *
+ * @param filename string full name of the configuration file
+ */
+var Configuration = function (filename) {
+ if (filename == undefined || filename == '' || !filename.match(/\.xml$/)) {
+ throw 'no, empty or invalid filename given, can not instantiate without one';
+ }
+
+ var _config = this;
+ var _filename = filename;
+
+ /**
+ * the Schema-object associated with this Configuration
+ * @var object
+ */
+ var _schema = {};
+
+ /**
+ * the configuration
+ * @var object
+ */
+ var $xml = {};
+
+ /**
+ * a list of our child-nodes
+ * @var array
+ */
+ _config.rootNodes = [];
+
+ /**
+ * load and cache the configuration/xml from the server
+ */
+ _config.load = function () {
+ $.ajax(_filename,
+ {
+ dataType: 'xml',
+ success: function (data) {
+ $xml = $(data);
+
+ // parse the data, to have at least a list of root-level-elements
+ parseXML();
+
+ // tell everyone that we are done!
+ $(document).trigger('configuration_loaded');
+ }
+ }
+ );
+ }
+
+ /**
+ * get the filename of the schema/xsd associated with this Configuration
+ *
+ * @return string filename (including path) to the schema/xsd
+ */
+ _config.getSchemaFilename = function () {
+ // extract schema-name
+ var schemaName = $xml.children().attr('xsi:noNamespaceSchemaLocation');
+
+ if (schemaName == undefined || schemaName == '') {
+ throw 'no schema/xsd found in root-level-element, can not run without one';
+ }
+
+ // path is the same as the one from the configuration, so let's throw out the filename, and voila, path.
+ var schemaPath = _filename.replace(/\/[^\/]*$/, '');
+
+ var schemaFilename = schemaPath + '/' + schemaName;
+
+ return schemaFilename;
+
+ }
+
+ /**
+ * set the Schema-object for this configuration
+ *
+ * @param schema object Schema-object
+ */
+ _config.setSchema = function (schema) {
+ if (schema == undefined || typeof schema != 'object') {
+ throw 'internal problem: improper usage of Configuration (not an object)';
+ }
+
+ _schema = schema;
+
+
+ $.each(_config.rootNodes, function (i, element) {
+ var elementName = element.name;
+
+ if (typeof _schema.allowedRootElements[elementName] == 'undefined') {
+ throw 'schema is not valid for this configuration, can not find root-level element ' + elementName;
+ }
+
+ var childSchemaElement = _schema.allowedRootElements[elementName];
+ element.setSchemaElement(childSchemaElement);
+ });
+ }
+
+ /**
+ * check and report if the loaded configuration is valid by the standards of an already set schema
+ *
+ * @return boolean valid?
+ */
+ _config.isValid = function () {
+ if ($.isEmptyObject(_schema)) {
+ throw 'internal problem: improper usage of Configuration (no schema set)';
+ }
+
+ var isValid = true;
+
+ $.each(_config.rootNodes, function (i, element) {
+ var elementName = element.name;
+ // if no element of this name is allowed at root level, this config is not valid. period.
+ if (typeof _schema.allowedRootElements[elementName] == 'undefined') {
+ // not valid
+ isValid = false;
+ return;
+ }
+
+ // check this element for validity
+ isValid = isValid && element.isValid();
+ });
+
+ return isValid;
+ }
+
+ /**
+ * Parse the already loaded XML
+ */
+ var parseXML = function () {
+
+ // start with the root-nodes, and then go down through all of the configuration ...
+ // the going-down-part is done by ConfigurationElement itself
+ $xml.children().each(function () {
+ var node = new ConfigurationElement(this, _config);
+ _config.rootNodes.push(node);
+ });
+ }
+
+ _config.load();
+
+}
+
+// @TODO: need a way to find a SchemaElement for a ConfigurationElement, no matter where we are.
+// maybe make ConfigurationElement remember parent, and go up the tree?
+
+/**
+ * a single element from a configuration
+ *
+ * @param node object DOMNode
+ * @param config object Configuration-object this node belongs to
+ */
+var ConfigurationElement = function (node, config) {
+ var _element = this;
+
+ /**
+ * extract the attributes for this element
+ *
+ * @return object list of set attributes
+ */
+ var getAttributes = function () {
+ var attributes = {};
+
+ // get the node, and use plain-old-javascript to get a list of its attributes
+ // there is jQuery-equivalent to this (as of 2012-10-10, that is)
+ var node = $n.get(0);
+
+ if (typeof node.attributes != 'undefined') {
+ $.each(node.attributes, function (index, e) {
+ // attributes with a colon are ignored, we expect them to be of xsd-nature.
+ if (!e.name.match(/:/)) {
+ attributes[e.name] = e.value;
+ }
+ });
+ }
+
+ return attributes;
+ }
+
+ /**
+ * extract the children for this element.
+ * creates new ConfigurationElement-objects for each child, so this goes recursive.
+ *
+ * @return array list of set children
+ */
+ var getChildren = function () {
+ var children = [];
+
+ $n.children().each(function () {
+ var child = new ConfigurationElement(this, _config);
+
+ children.push(child);
+ });
+
+ return children;
+ }
+
+ /**
+ * get the immediate text of this element
+ *
+ * @return string the value of this element
+ */
+ var getValue = function () {
+ return $n.clone().find("*").remove().end().text();
+ }
+
+ /**
+ * set the SchemaElement for this ConfigurationElement.
+ * Goes recursive.
+ *
+ * @param schemaElement object SchemaElement for this ConfigurationElement
+ */
+ _element.setSchemaElement = function (schemaElement) {
+ // set our own schemaElement
+ _schemaElement = schemaElement;
+
+ // if we have children, check them, too.
+ if (_element.children.length > 0) {
+
+ // go over all of our children, and set their SchemaElement
+ $.each(_element.children, function (i, child) {
+ var childName = child.name;
+ // find the SchemaElement for this child
+ var childSchemaElement = schemaElement.getSchemaElementForElementName(childName);
+
+ if (childSchemaElement == undefined) {
+ // the xsd does not match, we are invalid
+ throw 'xsd does not match this configuration, or configuration is not valid';
+ }
+
+ // and set it.
+ child.setSchemaElement(childSchemaElement);
+ });
+ }
+ }
+
+ /**
+ * get the current SchemaElement
+ *
+ * @return SchemaElement
+ */
+ _element.getSchemaElement = function () {
+ return _schemaElement;
+ }
+
+ /**
+ * check if this element, its attributes and its children are valid, when compared to a given schemaElement
+ *
+ * @return boolean valid?
+ */
+ _element.isValid = function () {
+
+ var isValid = true;
+
+ // first, check if our attributes are good
+ $.each(_element.attributes, function (name, value) {
+ // check if this attribute is allowed, at all
+ if (typeof _schemaElement.allowedAttributes[name] == 'undefined') {
+ isValid = false;
+ return;
+ }
+
+ // if we are good, check if the value is valid
+ isValid = isValid && _schemaElement.allowedAttributes[name].isValueValid(value);
+ });
+
+ if (false === isValid) {
+ // bail out, if we already know that we failed
+ return false;
+ }
+
+ // if we have children, check them, too.
+ if (_element.children.length > 0) {
+
+ // list of all of our children
+ var allSubElements = [];
+
+ // go over list of all elements, and see if some are not valid
+ // that's it
+ $.each(_element.children, function (i, child) {
+ var childName = child.name;
+ isValid = isValid && _schemaElement.isChildElementAllowed(childName);
+
+ if (isValid == false) {
+ return;
+ }
+
+ // go recursive.
+ isValid = isValid && child.isValid();
+ });
+
+ // @TODO: check bounds?
+ }
+
+ 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
+ // alas: check for validity
+
+ var value = getValue();
+
+ if (value != '') {
+ // only inspect elements with actual content. Empty nodes are deemed valid.
+ // @TODO: check if there might be nodes this does not apply for. sometime.
+ isValid = isValid && _schemaElement.isValueValid(value);
+ }
+ }
+
+ return isValid;
+ }
+
+ /**
+ * the Configuration-object this element belongs to
+ * @var object
+ */
+ var _config = config;
+
+ /**
+ * the node this element represents
+ * @var object
+ */
+ var $n = $(node);
+
+ /**
+ * our SchemaElement, if schema was set
+ * @var object
+ */
+ var _schemaElement = undefined;
+
+ /**
+ * the name of this element
+ * @var string
+ */
+ _element.name = $n.get(0).nodeName;
+
+ /**
+ * get and store a list of this elements set attributes
+ * @var object
+ */
+ _element.attributes = getAttributes();
+
+ /**
+ * get and store a list of this elements children
+ * @var array
+ */
+ _element.children = getChildren();
+
+}
\ No newline at end of file
Added: CometVisu/trunk/visu/editor/lib/DumpConfig.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/DumpConfig.js (rev 0)
+++ CometVisu/trunk/visu/editor/lib/DumpConfig.js 2012-10-21 08:28:02 UTC (rev 1067)
@@ -0,0 +1,89 @@
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Draw the configuration-tree on-screen
+ *
+ * This is most probably only an interims-file, and will be replaced by the Editor itself
+ *
+ *
+ * LICENSE: This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
+ *
+ * @category editor
+ * @package CometVisu
+ * @author Julian Makowski (julian at makowskis dot de)
+ * @copyright 2012 Julian Makowski
+ * @license GPLv3 or later, http://opensource.org/licenses/gpl-license.php
+ * @version SVN: $Id$
+ * @link http://cometvisu.de
+ * @since 2012-10-17
+ * @requires Schema.js, Configuration.js
+ */
+
+/**
+ * Class for dumping the configuration
+ *
+ * @param config object Configuration-object, fully loaded and equipped with its schema
+ */
+var DumpConfig = function (config) {
+ var _dc = this;
+
+ /**
+ * Configuration
+ * @var object
+ */
+ var _config = config;
+
+ _dc.dump = function () {
+ $('body').remove('#config');
+
+ var container = $('<ul />').attr('id', 'config');
+
+ $.each(_config.rootNodes, function (i, node) {
+ dumpElement(node, container);
+ });
+
+ $('body').append(container);
+ }
+
+ /**
+ * dump a single element
+ *
+ * @param element object ConfigurationElement to dump
+ * @param container object jQuery-object of our parent-DOMNode
+ */
+ var dumpElement = function (element, container) {
+ var elementContainer = $('<li />');
+ elementContainer.append($('<b />').html('Name: ' + element.name));
+
+ var allowed = $('<span />');
+
+ $.each(element.getSchemaElement().getAllowedElements(), function (i, item) {
+ allowed.append($('<i />').html(item.name));
+ });
+
+
+ allowed.appendTo(elementContainer);
+
+ if (element.children.length > 0) {
+ var subElementContainer = $('<ul />');
+
+ $.each(element.children, function (i, subElement) {
+ dumpElement(subElement, subElementContainer);
+ });
+ subElementContainer.appendTo(elementContainer);
+ }
+
+ elementContainer.appendTo(container);
+ }
+}
\ No newline at end of file
Added: CometVisu/trunk/visu/editor/lib/Schema.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/Schema.js (rev 0)
+++ CometVisu/trunk/visu/editor/lib/Schema.js 2012-10-21 08:28:02 UTC (rev 1067)
@@ -0,0 +1,873 @@
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Javascript-Representation of an XSD/Schema
+ *
+ * The classes in this file are able to load an XML-Schema/XSD from a give URL, and
+ * create an object-oriented representation of it. This includes the ability to check for validity of
+ * values, as well as having a tree-like structure of allowed elements and attributes.
+ *
+ *
+ * LICENSE: This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
+ *
+ * @category editor
+ * @package CometVisu
+ * @author Julian Makowski (julian at makowskis dot de)
+ * @copyright 2012 Julian Makowski
+ * @license GPLv3 or later, http://opensource.org/licenses/gpl-license.php
+ * @version SVN: $Id$
+ * @link http://cometvisu.de
+ * @since 2012-10-03
+ */
+
+
+/**
+ * starting-point for a javascript-representation of the XSD
+ *
+ * @param filename string filename of the schema, including a relative path
+ */
+var Schema = function (filename) {
+ if (filename == undefined || filename == '' || !filename.match(/\.xsd$/)) {
+ throw 'no, empty or invalid filename given, can not instantiate without one';
+ }
+
+ var _schema = this;
+ var _filename = filename;
+
+ /**
+ * jQuery-object of the schema/xsd
+ * @var object
+ */
+ var $xsd = {};
+
+ /**
+ * object of allowed root-level elements
+ * @var object
+ */
+ _schema.allowedRootElements = {};
+
+ /**
+ * load and cache the xsd from the server
+ */
+ var cacheXSD = function () {
+ $.ajax(_filename,
+ {
+ dataType: 'xml',
+ success: function (data) {
+ $xsd = $(data);
+
+ // parse the data, to have at least a list of root-level-elements
+ parseXSD();
+
+ // tell everyone that we are done!
+ $(document).trigger('schema_loaded');
+ }
+ }
+ );
+ }
+
+ /**
+ * parse the schema once
+ */
+ var parseXSD = function () {
+ // make a list of root-level elements
+ $('xsd\\:schema > xsd\\:element', $xsd).each(function () {
+ var name = $(this).attr('name');
+ _schema.allowedRootElements[name] = new SchemaElement(this, _schema);
+ });
+ }
+
+ /**
+ * 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!
+ *
+ * @param type string Type of the node (e.g. element, attributeGroup, ...)
+ * @param refName string Name as per the ref-attribute
+ * @return object jQuery-object of the ref'ed element
+ */
+ _schema.getReferencedNode = function (type, refName) {
+ var $ref = $('xsd\\:schema > xsd\\:' + type + '[name="' + refName + '"]', $xsd);
+
+ if ($ref.is('[ref]')) {
+ // do it recursively, if necessary
+ $ref = _schema.getReferencedNode(type, $ref.attr('ref'));
+ }
+
+ return $ref;
+ }
+
+ /**
+ * 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) {
+ var $type = $('xsd\\:' + type + 'Type[name="' + name + '"]', $xsd);
+
+ if ($type.length != 1) {
+ throw 'schema/xsd appears to be invalid, ' + type + 'Type "' + name + '" can not be found';
+ }
+
+ return $type;
+ }
+
+ cacheXSD();
+}
+
+/**
+ * a single SimpleType from the schema.
+ * Should be useable for SimpleContent, too.
+ * Is usable for attributes, too.
+ *
+ * @param node DOMNode the DOMNode this SimpleType is
+ * @param schema object a Schema-object of where this node comes from
+ */
+var SchemaSimpleType = function (node, schema) {
+ var _type = this;
+
+ /**
+ * parse a node, find it's data (restrictions, extensions, bases ... whatever)
+ *
+ * @param node DOMNode the node to parse
+ */
+ var fillNodeData = function (node) {
+ var $n = $(node);
+
+ if ($n.is('xsd\\:attribute[ref]')) {
+ // it's a ref, seek other element!
+ var refName = $n.attr('ref');
+ $n = _schema.getReferencedNode('attribute', refName);
+
+ if ($n.length != 1) {
+ throw 'schema/xsd appears to be invalid, can not find element ' + refName;
+ }
+ }
+
+ if ($n.is('xsd\\:attribute[type], xsd\\:element[type]')) {
+ // hacked: allow this to be used for attributes
+ var baseType = $n.attr('type');
+ nodeData.bases.push(baseType);
+ _type.baseType = baseType;
+ return;
+ }
+
+ var subNodes = $n.find('> xsd\\:restriction, > xsd\\:extension');
+
+ subNodes.each(function () {
+ var baseType = $(this).attr('base');
+ nodeData.bases.push(baseType);
+
+ if (!baseType.match(/^xsd:/)) {
+ // don't dive in for default-types, they simply can not be found
+ var subnode = _schema.getReferencedNode('simpleType', baseType)
+ fillNodeData(subnode);
+ } else {
+ _type.baseType = baseType;
+ }
+
+ });
+
+ subNodes.find('> xsd\\:pattern').each(function () {
+ var pattern = $(this).attr('value');
+ nodeData.pattern.push(pattern);
+ });
+
+
+ subNodes.find('> xsd\\:enumeration').each(function () {
+ var value = $(this).attr('value');
+ nodeData.enumerations.push(value);
+ });
+
+ }
+
+ /**
+ * check if a given value is valid for this type
+ *
+ * @param value mixed the value to check
+ * @return boolean if the value is valid
+ */
+ _type.isValueValid = function (value) {
+
+ if (-1 == _type.baseType.search(/^xsd:/)) {
+ // created our own type, will need to find and use it.
+ var typeNode = _schema.getTypeNode('simple', _type.baseType);
+ var subType = new SchemaSimpleType(typeNode, _schema);
+ return subType.isValueValid(value);
+ } else {
+ // xsd:-namespaces types, those are the originals
+ switch (_type.baseType) {
+ case 'xsd:string':
+ if (! (typeof(value) == 'string')) {
+ // it's not a string, but it should be.
+ // pretty much any input a user gives us is string, so this is pretty much moot.
+ return false;
+ }
+ break;
+ case 'xsd:decimal':
+ if (!value.match(/^[-+]?[0-9]+(\.[0-9]+)?$/)) {
+ return false;
+ }
+ break;
+ case 'xsd:integer':
+ if (!value.match(/^[-+]?[0-9]+$/)) {
+ return false;
+ }
+ break;
+ case 'xsd:float':
+ if (!value.match(/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/)) {
+ return false;
+ }
+ break;
+ case 'xsd:boolean':
+ if (!value.match(/^(true|false|0|1)$/)) {
+ return false;
+ }
+ break;
+ default:
+ throw 'not implemented baseType ' + _self.baseType;
+ }
+ }
+
+ // check if the value is in our list of valid values, if there is such a list
+ if (nodeData.enumerations.length > 0) {
+ if (-1 == $.inArray(value, nodeData.enumerations)) {
+ return false;
+ }
+ }
+
+ // check if the value matches any given pattern
+ if (nodeData.pattern.length > 0) {
+ // start with assuming it's valid
+ var boolValid = true;
+
+ $.each(nodeData.pattern, function (i, item) {
+ // create a regex from the pattern; mind ^ an $ - XSD has them implicitly (XSD Datatpes, Appenix G)
+ // so for our purpose, we need to add them
+ var mypattern = regexFromString('^' + item + '$');
+
+ if (false == mypattern.test(value)) {
+ // regular expression did not match
+ // bad bad value!
+ boolValid = false;
+ }
+ });
+
+ // if the value has been marked invalid by a regex, return invalid.
+ if (false == boolValid) {
+ return false;
+ }
+ }
+
+ // if no check said the value is invalid, then it is not invalid
+ return true;
+ }
+
+ /**
+ * this is us. well, our node.
+ * @var object jQuery-representation of our node
+ */
+ var $n = $(node);
+
+ /**
+ * the schema where this element comes from
+ * @var object Schema
+ */
+ var _schema = schema;
+ if (_schema == undefined) {
+ throw 'programming error, schema is not defined';
+ }
+
+ /**
+ * the nodes essential data
+ * @var object data
+ */
+ var nodeData = {
+ pattern: [],
+ enumerations: [],
+ bases: [],
+ };
+
+ /**
+ * the baseType of this element, which is one of the xsd-namespaced types (like 'string')
+ * @var string
+ */
+ _type.baseType = undefined;
+
+ // now load this nodes Data!
+ fillNodeData($n);
+}
+
+/**
+ * a single attribute from the schema.
+ *
+ * @param node DOMNode the DOMNode this attribute is
+ * @param schema object a Schema-object of where this node comes from
+ */
+var SchemaAttribute = function (node, schema) {
+ var _attribute = this;
+
+ /**
+ * Get the name of a schema-node
+ *
+ * @param node object node to find the name of
+ * @return string name of the node
+ * @throws if the name can not be found
+ */
+ var getAttributeName = function (node) {
+ var $n = $(node);
+
+ if ($n.is('[name]')) {
+ return $n.attr('name');
+ }
+
+ if ($n.is('[ref]')) {
+ // it's a ref, seek other element!
+ var refName = $n.attr('ref');
+ var $ref = _schema.getReferencedNode('attribute', refName);
+
+ if ($ref.length != 1) {
+ throw 'schema/xsd appears to be invalid, can not find element ' + refName;
+ }
+
+ return $ref.attr('name');
+ }
+
+ return 'unknown';
+ }
+
+ /**
+ * check if a given value is valid for this attribute
+ *
+ * @param value mixed the value to check
+ * @return boolean if the value is valid
+ */
+ this.isValueValid = function (value) {
+ return type.isValueValid(value);
+ }
+
+ /**
+ * the original node from the Schema
+ * @var object jQuery-enhanced DOM-node
+ */
+ var $node = $(node);
+
+ /**
+ * the schema where this attribute comes from
+ * @var object Schema
+ */
+ var _schema = schema;
+ if (_schema == undefined) {
+ throw 'programming error, schema is not defined';
+ }
+
+ /**
+ * is this attribute optional?
+ * @var boolean is it optional?
+ */
+ _attribute.isOptional = ($node.attr('use') == 'optional' ? true : false);
+
+ /**
+ * the name of this attribute
+ * @var string the name
+ */
+ _attribute.name = getAttributeName($node);
+
+ /**
+ * we have our own type
+ * @var object SchemaSimpleType of the attribute, for validating purposes
+ */
+ var type = new SchemaSimpleType($node, _schema);
+}
+
+/**
+ * a single element from the schema
+ *
+ * @param node DOMNode the DOMNode this element is
+ * @param schema object a Schema-object of where this node comes from
+ */
+var SchemaElement = function (node, schema) {
+ var _element = this;
+
+
+ /**
+ * Get the name of a schema-element
+ *
+ * @param element object element to find the name of
+ * @return string name of the element
+ * @throws if the name can not be found
+ */
+ var getElementName = function (element) {
+ var $e = $(element);
+
+ if ($e.is('[name]')) {
+ return $e.attr('name');
+ }
+
+ if ($e.is('[ref]')) {
+ // it's a ref, seek other element!
+ var $ref = _schema.getReferencedNode('element', $e.attr('ref'));
+
+ if ($ref.length != 1) {
+ throw 'schema/xsd appears to be invalid, can not find element ' + refName;
+ }
+
+ return $ref.attr('name');
+ }
+
+ return 'unknown';
+ }
+
+ /**
+ * find the type-node for this element
+ *
+ * @return object jQuery-fied object of the type-Node
+ */
+ var getTypeNode = function () {
+ var $type;
+
+ if ($e.is('[type]')) {
+
+ if ($e.attr('type').match(/^xsd:/)) {
+ // if it starts with xsd:, it's actually a simple type
+ // does not start with
+ $type = $e;
+ } else {
+ // otherwiese, the element is linked to a complexType
+ $type = _schema.getTypeNode('complex', $e.attr('type'));
+ }
+ } else if ($e.is('[ref]')) {
+ // the link is a reference to another element, which means it does not even have it's own name.
+ // this one is most certainly deprecated, as we do not have many root-level-elements, and only those can
+ // be ref'ed
+ } else {
+ // the element is it's own type
+ $type = $e.find('xsd\\:complexType');
+ }
+
+ return $type;
+ }
+
+ /**
+ * get a list of allowed elements for this element
+ *
+ * @return object object of SchemaElement-elements, key is the name
+ */
+ _element.getAllowedContent = function () {
+ if (true === allowedContentLoaded) {
+ // if we have parsed this already, we can simply return the 'cache'
+ return allowedContent;
+ }
+
+ // allowed sub-elements
+ // can be either choice, simpleContent or sequence (the latter is not supported)
+
+ if ($type.children('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('xsd\\:simpleContent'), _schema);
+ } else if ($type.children('xsd\\:choice').length > 0) {
+ // we have a choice. or some choices. great
+ $type.children('xsd\\:choice').each(function () {
+ allowedContent._choice.push(new SchemaChoice(this, _schema));
+ });
+ } 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('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;
+ }
+
+
+ var children = $type.find('> xsd\\:element', $e);
+ children.each(function () {
+ allowedContent[name] = new SchemaElement(this);
+ });
+
+ // remember that we parsed it already!
+ allowedContentLoaded = true;
+
+ return allowedContent;
+ }
+
+ /**
+ * get a list of all allowed elements for this element
+ *
+ * @return object list of SchemaElement-elements, key is the name
+ */
+ _element.getAllowedElements = function () {
+ var allowedContent = _element.getAllowedContent();
+
+ var allowedElements = {};
+ if (allowedContent._choice.length > 0) {
+ $.each(allowedContent._choice, function (i, subChoice) {
+ $.extend(allowedElements, subChoice.getAllowedElements(true));
+ });
+ }
+
+ return allowedElements;
+ }
+
+ /**
+ * get a list of allowed attributes for this element
+ *
+ * @return array list of SchemaAttributes
+ */
+ var getAllowedAttributes = function () {
+ var allowedAttributes = {};
+
+ // allowed attributes
+ var attributes = $type.find('> xsd\\:attribute, > xsd\\:simpleContent > xsd\\:extension > xsd\\:attribute');
+
+ // now add any attribute that comes from an attribute-group
+ var attributeGroups = $type.find('> xsd\\:attributeGroup, > xsd\\:simpleContent > xsd\\:extension > xsd\\:attributeGroup')
+ attributeGroups.each(function () {
+ // get get group itself, by reference if necessary
+ // then extract all attributes, and add them to the list of already know attributes
+
+ var $attributeGroup = {};
+ if ($(this).is('[ref]')) {
+ // we do have a reffed group
+ $attributeGroup = _schema.getReferencedNode('attributeGroup', $(this).attr('ref'));
+ } else {
+ $attributeGroup = $(this);
+ }
+
+ $attributeGroup.children('xsd\\:attribute').each(function () {
+ attributes.push(this);
+ });
+ });
+
+ // convert all allowed attributes to a more object-oriented approach
+ attributes.each(function() {
+ var attribute = new SchemaAttribute(this, _schema);
+ allowedAttributes[attribute.name] = attribute;
+ });
+
+ return allowedAttributes;
+ }
+
+ /**
+ * check if an element (specified by its name) is allowed as one of our immediate children
+ * Goes recursive if we have choices.
+ *
+ * @param child string name of the element we want to check
+ * @return boolean is this element allowed?
+ */
+ _element.isChildElementAllowed = function (child) {
+ // first, get a list of allowed content (don't worry, it's cached)
+ var allowedContent = _element.getAllowedContent();
+
+ if (allowedContent._choice == undefined || allowedContent._choice.length == 0) {
+ // when there is no choice, then there is no allowed element
+ return false;
+ }
+
+ // go over all choices, and see, if this child is allowed
+ var result = false;
+ $.each(allowedContent._choice, function (i, choice) {
+ result = result || choice.isElementAllowed(child);
+ })
+
+ return result;
+ }
+
+ /**
+ * get the SchemaElement-object for a certain element-name.
+ * May return undefined if no element is found, so you might be interested in checking isElementAllowed beforehand.
+ *
+ * @param elementName string name of the element to find the SchemaElement for
+ * @return object SchemaElement-object, or undefined if none is found
+ */
+ _element.getSchemaElementForElementName = function (elementName) {
+ // first, get a list of allowed content (don't worry, it's cached)
+ var allowedContent = _element.getAllowedContent();
+
+ if (allowedContent._choice == undefined || allowedContent._choice.length == 0) {
+ // when there is no choice, then there is no allowed element
+ return undefined;
+ }
+
+ // go over the list of sub-choices and check, if the element is allowed with them
+ var element = undefined;
+ $.each(allowedContent._choice, function (i, choice) {
+ if (element) {
+ // don't look any further if we already have our element
+ return;
+ }
+
+ if (choice.isElementAllowed(elementName)) {
+ // only look in this tree, if the element is allowed there.
+ element = choice.getSchemaElementForElementName(elementName);
+ }
+ });
+
+ return element;
+ }
+
+ /**
+ * check if a given value is valid for this element
+ *
+ * @param value string value to check
+ * @return boolean is it valid?
+ */
+ _element.isValueValid = function (value) {
+ if (_element.isMixed == true) {
+ // mixed follows no rules!
+ return true;
+ }
+
+ // first, get a list of allowed content (don't worry, it's cached)
+ var allowedContent = _element.getAllowedContent();
+
+ if (allowedContent._text == undefined || allowedContent._text == false) {
+ // if no text is allowed, the it can not be valid. period.
+ return false;
+ }
+
+ return allowedContent._text.isValueValid(value);
+ }
+
+ /**
+ * the schema where this element comes from
+ * @var object Schema
+ */
+ var _schema = schema;
+ if (_schema == undefined) {
+ throw 'programming error, schema is not defined';
+ }
+
+ /**
+ * the original node from the Schema
+ * @var object jQuery-enhanced DOM-node
+ */
+ var $e = $(node);
+
+ /**
+ * get and set the type-node for the element
+ * @var object Type-Node (most certainly a complexType)
+ */
+ var $type = getTypeNode();
+
+
+ /**
+ * get and set the name of the node
+ * @var string Name of the element
+ */
+ _element.name = getElementName($e);
+
+ /**
+ * get and set the list of allowed attributes
+ * @var array List of SchemaAttribute-objects
+ */
+ _element.allowedAttributes = getAllowedAttributes();
+
+ /**
+ * can the node have immediate content?
+ * @var object list of allowed contents
+ */
+ var allowedContent = {
+ _choice: [],
+ _text: false,
+ };
+
+
+ /**
+ * is this element of mixed nature (text and nodes as value)
+ * @var boolean
+ */
+ _element.isMixed = $type.is('[mixed=true]');
+
+ /**
+ * has allowedContent already been parsed?
+ * @var boolean has it?
+ */
+ var allowedContentLoaded = false;
+}
+
+
+/**
+ * a single choice.
+ * may be recursive
+ *
+ * @param node DOMNode the choice-node
+ * @param schema Schema the corresponding schema
+ */
+var SchemaChoice = function (node, schema) {
+ /**
+ * us
+ * @var object
+ */
+ var _choice = this;
+
+ /**
+ * parse a list of elements in a choice, even go deep down for sub-choices
+ *
+ * @param choiceNode object the node to parse
+ * @return object the data of said choide-node
+ */
+ var parseChoice = function () {
+ _choice.bounds = {
+ min: $n.attr('minOccurs'),
+ max: $n.attr('maxOccurs'),
+ };
+
+ var subElements = $n.find('> xsd\\:element');
+ subElements.each(function () {
+ var subElement = new SchemaElement(this, _schema);
+ var name = subElement.name;
+ allowedElements[name] = subElement;
+ });
+
+ var subChoices = $n.find('> xsd\\:choice');
+ subChoices.each(function () {
+ var subChoice = new SchemaChoice(this, _schema);
+ allowedSubChoices.push(subChoice);
+ })
+ }
+
+ /**
+ * is an element (specified by its name) allowed in this choice?
+ * Goes recursive.
+ * Does NOT check bounds! Does NOT check dependencies!
+ *
+ * @param element string the element we check for
+ * @return boolean is it allowed?
+ */
+ _choice.isElementAllowed = function (element) {
+ if (typeof allowedElements[element] != 'undefined') {
+ // this element is immediately allowed
+ return true;
+ }
+
+ // go over the list of sub-choices and check, if the element is allowed with them
+ var result = false;
+ $.each(allowedSubChoices, function (i, subChoice) {
+ result = result || subChoice.isElementAllowed(element);
+ });
+
+ return result;
+ }
+
+ /**
+ * get the SchemaElement-object for a certain element-name.
+ * May return undefined if no element is found, so you might be interested in checking isElementAllowed beforehand.
+ *
+ * @param elementName string name of the element to find the SchemaElement for
+ * @return object SchemaElement-object, or undefined if none is found
+ */
+ _choice.getSchemaElementForElementName = function (elementName) {
+ if (typeof allowedElements[elementName] != 'undefined') {
+ // this element is immediately allowed
+ return allowedElements[elementName];
+ }
+
+ // go over the list of sub-choices and check, if the element is allowed with them
+ var element = undefined;
+ $.each(allowedSubChoices, function (i, subChoice) {
+ if (element) {
+ // don't look any further if we already have our element
+ return;
+ }
+
+ if (subChoice.isElementAllowed(elementName)) {
+ // only look in this tree, if the element is allowed there.
+ element = subChoice.getSchemaElementForElementName(elementName);
+ }
+ });
+
+ return element;
+ }
+
+ /**
+ * get the elements allowed for this choice
+ *
+ * @param recursive boolean recursive?
+ * @return object list of allowed elements, key is the name
+ */
+ _choice.getAllowedElements = function (recursive) {
+ var myAllowedElements = allowedElements;
+
+ if (true == recursive) {
+ $.each(allowedSubChoices, function (i, subChoice) {
+ var subAllowedElements = subChoice.getAllowedElements(recursive);
+
+ $.extend(myAllowedElements, subAllowedElements);
+ });
+ }
+
+ return myAllowedElements;
+ }
+
+ /**
+ * our node
+ * @var object
+ */
+ var $n = $(node);
+
+ /**
+ * the schema we belong to
+ * @var object
+ */
+ var _schema = schema;
+ if (_schema == undefined) {
+ throw 'programming error, schema is not defined';
+ }
+
+ /**
+ * list of elements that are allowed as per our own definition
+ * @var object
+ */
+ var allowedElements = {};
+
+ /**
+ * array of sub-choices that are defined
+ * @var array
+ */
+ var allowedSubChoices = [];
+
+ /**
+ * bounds for this choice
+ * @var object
+ */
+ _choice.bounds = {
+ min: undefined,
+ max: undefined
+ };
+
+ // fill ourselves with data
+ parseChoice();
+}
+
+/**
+ * create a regex-object from a pattern
+ *
+ * For some obscure reason, this may not be inside a classes method, or else that class is not instantiateable
+ *
+ * @param input string the pattern to match (without //)
+ * @param modifiers string modifiers, if any
+ * @return object RegExp-object
+ */
+function regexFromString(input, modifiers) {
+ if (modifiers == undefined) {
+ modifiers = '';
+ }
+
+ return new RegExp(input, modifiers);
+}
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|