dataschema.js revision 0d6d1a2d994933a68a100ec3dcdc7c7a0eeeae6c
0N/AYUI.add('dataschema-base', function(Y) {
4680N/A
0N/A/**
0N/A * The DataSchema utility provides a common configurable interface for widgets to
0N/A * apply a given schema to a variety of data.
0N/A *
2362N/A * @module dataschema
0N/A */
2362N/A
0N/A/**
0N/A * Base class for the YUI DataSchema utility.
0N/A * @class DataSchema.Base
0N/A * @static
0N/A */
0N/Avar SchemaBase = {
0N/A /**
0N/A * Returns string name.
0N/A *
0N/A * @method toString
0N/A * @return {String} String representation for this object.
2362N/A */
2362N/A toString: function() {
2362N/A return "DataSchema.Base";
0N/A },
0N/A
0N/A /**
0N/A * Overridable method returns data as-is.
0N/A *
0N/A * @method apply
0N/A * @param schema {Object} Schema to apply.
0N/A * @param data {Object} Data.
0N/A * @return {Object} Schema-parsed data.
0N/A * @static
0N/A */
0N/A apply: function(schema, data) {
16N/A return data;
0N/A }
0N/A};
0N/A
0N/AY.namespace("DataSchema").Base = SchemaBase;
0N/A
0N/A
0N/A
0N/A}, '@VERSION@' ,{requires:['base']});
0N/A
0N/AYUI.add('dataschema-json', function(Y) {
0N/A
0N/A/**
0N/A * The DataSchema utility provides a common configurable interface for widgets to
0N/A * apply a given schema to a variety of data.
0N/A *
0N/A * @module dataschema
2963N/A */
2963N/Avar LANG = Y.Lang,
2963N/A
2963N/A/**
2963N/A * JSON subclass for the YUI DataSchema utility.
2963N/A * @class DataSchema.JSON
2963N/A * @extends DataSchema.Base
2963N/A * @static
2963N/A */
2963N/ASchemaJSON = {
2963N/A
2963N/A /////////////////////////////////////////////////////////////////////////////
2963N/A //
2963N/A // DataSchema.JSON static methods
2963N/A //
2963N/A /////////////////////////////////////////////////////////////////////////////
2963N/A /**
2963N/A * Returns string name.
2963N/A *
3090N/A * @method toString
3090N/A * @return {String} String representation for this object.
3090N/A */
3090N/A toString: function() {
3090N/A return "DataSchema.JSON";
2963N/A },
2963N/A
2963N/A /**
2963N/A * Utility function converts JSON locator strings into walkable paths
2963N/A *
2963N/A * @method DataSchema.JSON.buildPath
2963N/A * @param locator {String} JSON value locator.
2963N/A * @return {String[]} Walkable path to data value.
2963N/A * @static
2963N/A */
2963N/A buildPath: function(locator) {
2963N/A var path = null,
2963N/A keys = [],
2963N/A i = 0;
2963N/A
2963N/A if (locator) {
2963N/A // Strip the ["string keys"] and [1] array indexes
2963N/A locator = locator.
2963N/A replace(/\[(['"])(.*?)\1\]/g,
2963N/A function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
2963N/A replace(/\[(\d+)\]/g,
2963N/A function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
2963N/A replace(/^\./,''); // remove leading dot
2963N/A
2963N/A // Validate against problematic characters.
2963N/A if (!/[^\w\.\$@]/.test(locator)) {
2963N/A path = locator.split('.');
2963N/A for (i=path.length-1; i >= 0; --i) {
2963N/A if (path[i].charAt(0) === '@') {
2963N/A path[i] = keys[parseInt(path[i].substr(1),10)];
2963N/A }
2963N/A }
2963N/A }
2963N/A else {
2963N/A }
2963N/A }
2963N/A return path;
2963N/A },
2963N/A
2963N/A /**
2963N/A * Utility function to walk a path and return the value located there.
2963N/A *
2963N/A * @method DataSchema.JSON.getLocationValue
2963N/A * @param path {String[]} Locator path.
2963N/A * @param data {String} Data to traverse.
2963N/A * @return {Object} Data value at location.
2963N/A * @static
2963N/A */
2963N/A getLocationValue: function (path, data) {
2963N/A var i = 0,
2963N/A len = path.length;
2963N/A for (;i<len;i++) {
2963N/A data = data[path[i]];
2963N/A }
2963N/A return data;
2963N/A },
2963N/A
2963N/A /**
2963N/A * Applies a given schema to given JSON data.
2963N/A *
2963N/A * @method apply
2963N/A * @param schema {Object} Schema to apply.
2963N/A * @param data {Object} JSON data.
2963N/A * @return {Object} Schema-parsed data.
2963N/A * @static
0N/A */
0N/A apply: function(schema, data) {
0N/A var data_in = data,
0N/A data_out = {results:[],meta:{}};
0N/A
0N/A // Convert incoming JSON strings
0N/A if(!LANG.isObject(data)) {
0N/A try {
0N/A data_in = Y.JSON.parse(data);
0N/A }
0N/A catch(e) {
0N/A data_out.error = e;
0N/A return data_out;
0N/A }
0N/A }
0N/A
0N/A if(LANG.isObject(data_in) && schema) {
0N/A // Parse results data
0N/A if(!LANG.isUndefined(schema.resultsLocator)) {
0N/A data_out = SchemaJSON._parseResults(schema, data_in, data_out);
0N/A }
0N/A
0N/A // Parse meta data
0N/A if(!LANG.isUndefined(schema.metaFields)) {
0N/A data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
0N/A }
0N/A }
0N/A else {
0N/A data_out.error = true;
2581N/A }
2581N/A
4680N/A return data_out;
4680N/A },
0N/A
0N/A /**
0N/A * Schema-parsed list of results from full data
0N/A *
0N/A * @method _parseResults
0N/A * @param schema {Object} Schema to parse against.
2581N/A * @param data_in {Object} Data to parse.
2581N/A * @param data_out {Object} In-progress parsed data to update.
2581N/A * @return {Object} Parsed data object.
0N/A * @static
0N/A * @protected
0N/A */
2581N/A _parseResults: function(schema, data_in, data_out) {
0N/A var results = [],
0N/A path,
0N/A error;
0N/A
0N/A if(schema.resultsLocator) {
517N/A path = SchemaJSON.buildPath(schema.resultsLocator);
0N/A if(path) {
0N/A results = SchemaJSON.getLocationValue(path, data_in);
0N/A if (results === undefined) {
0N/A data_out.results = [];
0N/A error = new Error(this.toString() + " Results retrieval failure");
517N/A }
0N/A if(LANG.isArray(schema.resultsFields) && LANG.isArray(results)) {
0N/A data_out = SchemaJSON._getFieldValues(schema.resultsFields, results, data_out);
0N/A }
4680N/A else {
5694N/A data_out.results = [];
4680N/A error = new Error(this.toString() + " Fields retrieval failure");
0N/A }
4680N/A }
517N/A else {
0N/A error = new Error(this.toString() + " Results locator failure");
0N/A }
2581N/A
2581N/A if (error) {
2581N/A data_out.error = error;
2581N/A }
2581N/A
2581N/A }
0N/A return data_out;
0N/A },
0N/A
517N/A /**
0N/A * Get field data values out of list of full results
0N/A *
0N/A * @method _getFieldValues
2963N/A * @param fields {Array} Fields to find.
2963N/A * @param data_in {Array} Results data to parse.
2963N/A * @param data_out {Object} In-progress parsed data to update.
2963N/A * @return {Object} Parsed data object.
2963N/A * @static
2963N/A * @protected
2963N/A */
2963N/A _getFieldValues: function(fields, data_in, data_out) {
2963N/A var results = [],
2963N/A len = fields.length,
2963N/A i, j,
2963N/A field, key, path, parser,
2963N/A simplePaths = [], complexPaths = [], fieldParsers = [],
2963N/A result, record;
2963N/A
2963N/A // First collect hashes of simple paths, complex paths, and parsers
2963N/A for (i=0; i<len; i++) {
2963N/A field = fields[i]; // A field can be a simple string or a hash
2963N/A key = field.key || field; // Find the key
2963N/A
2963N/A // Validate and store locators for later
2963N/A path = SchemaJSON.buildPath(key);
2963N/A if (path) {
2963N/A if (path.length === 1) {
0N/A simplePaths[simplePaths.length] = {key:key, path:path[0]};
0N/A } else {
1365N/A complexPaths[complexPaths.length] = {key:key, path:path};
1365N/A }
1365N/A } else {
1365N/A }
1365N/A
1365N/A // Validate and store parsers for later
1365N/A //TODO: implement shortcuts
1365N/A parser = (LANG.isFunction(field.parser)) ? field.parser : Y.DataSchema[field.parser+''];
1365N/A if (parser) {
1365N/A fieldParsers[fieldParsers.length] = {key:key, parser:parser};
1365N/A }
1365N/A }
1365N/A
1365N/A // Traverse list of data_in, creating records of simple fields,
2324N/A // complex fields, and applying parsers as necessary
2324N/A for (i=data_in.length-1; i>=0; --i) {
2324N/A record = {};
2324N/A result = data_in[i];
1365N/A if(result) {
1365N/A // Cycle through simpleLocators
1365N/A for (j=simplePaths.length-1; j>=0; --j) {
1365N/A // Bug 1777850: The result might be an array instead of object
1365N/A record[simplePaths[j].key] =
2324N/A LANG.isUndefined(result[simplePaths[j].path]) ?
2324N/A result[j] : result[simplePaths[j].path];
2324N/A }
1365N/A
1365N/A // Cycle through complexLocators
5694N/A for (j=complexPaths.length - 1; j>=0; --j) {
5694N/A record[complexPaths[j].key] = SchemaJSON.getLocationValue(complexPaths[j].path, result);
2746N/A }
2746N/A
2746N/A // Cycle through fieldParsers
1365N/A for (j=fieldParsers.length-1; j>=0; --j) {
1365N/A key = fieldParsers[j].key;
1365N/A record[key] = fieldParsers[j].parser(record[key]);
1365N/A // Safety net
1365N/A if (LANG.isUndefined(record[key])) {
1365N/A record[key] = null;
1365N/A }
1365N/A }
1365N/A }
1365N/A results[i] = record;
1365N/A }
1365N/A data_out.results = results;
1365N/A return data_out;
1365N/A },
1365N/A
1365N/A /**
1365N/A * Parses results data according to schema
0N/A *
0N/A * @method _parseMeta
0N/A * @param data_out {Object} Data to parse.
0N/A * @param data_in {Object} In-progress parsed data to update.
0N/A * @return {Object} Schema-parsed meta data.
0N/A * @static
0N/A * @protected
0N/A */
0N/A _parseMeta: function(metaFields, data_in, data_out) {
0N/A if(LANG.isObject(metaFields)) {
0N/A var key, path;
2581N/A for(key in metaFields) {
0N/A if (metaFields.hasOwnProperty(key)) {
2581N/A path = SchemaJSON.buildPath(metaFields[key]);
2581N/A if (path && data_in) {
0N/A data_out.meta[key] = SchemaJSON.getLocationValue(path, data_in);
0N/A }
0N/A }
2581N/A }
0N/A }
0N/A else {
2581N/A data_out.error = new Error(this.toString() + " Meta retrieval failure");
2581N/A }
0N/A return data_out;
0N/A }
0N/A};
0N/A
0N/AY.DataSchema.JSON = Y.mix(SchemaJSON, Y.DataSchema.Base);
2581N/A
2581N/A
0N/A
0N/A}, '@VERSION@' ,{requires:['dataschema-base']});
517N/A
0N/AYUI.add('dataschema-xml', function(Y) {
0N/A
0N/A/**
0N/A * The DataSchema utility provides a common configurable interface for widgets to
0N/A * apply a given schema to a variety of data.
0N/A *
0N/A * @module dataschema
0N/A */
0N/Avar LANG = Y.Lang,
0N/A
0N/A/**
0N/A * XML subclass for the YUI DataSchema utility.
0N/A * @class DataSchema.XML
0N/A * @extends DataSchema.Base
0N/A * @static
2581N/A */
0N/ASchemaXML = {
5694N/A
5694N/A /////////////////////////////////////////////////////////////////////////////
0N/A //
0N/A // DataSchema.XML static methods
0N/A //
0N/A /////////////////////////////////////////////////////////////////////////////
0N/A /**
0N/A * Returns string name.
0N/A *
0N/A * @method toString
0N/A * @return {String} String representation for this object.
0N/A */
0N/A toString: function() {
0N/A return "DataSchema.XML";
0N/A },
0N/A
0N/A /**
0N/A * Applies a given schema to given XML data.
0N/A *
0N/A * @method apply
0N/A * @param schema {Object} Schema to apply.
0N/A * @param data {XMLDoc} XML document.
0N/A * @return {Object} Schema-parsed data.
0N/A * @static
0N/A */
795N/A apply: function(schema, data) {
0N/A var data_in = data,
1365N/A data_out = {results:[],meta:{}};
0N/A
0N/A if(LANG.isObject(data_in) && schema) {
0N/A // Parse results data
517N/A if(!LANG.isUndefined(schema.resultsLocator)) {
0N/A data_out = SchemaXML._parseResults(schema, data_in, data_out);
0N/A }
0N/A
0N/A // Parse meta data
0N/A if(!LANG.isUndefined(schema.metaFields)) {
0N/A data_out = SchemaXML._parseMeta(schema.metaFields, data_in, data_out);
0N/A }
0N/A }
0N/A else {
517N/A data_out.error = true;
0N/A }
0N/A
0N/A return data_out;
0N/A },
0N/A
0N/A /**
0N/A * Schema-parsed list of results from full data
0N/A *
0N/A * @method _parseResults
0N/A * @param schema {Object} Schema to parse against.
0N/A * @param data_in {Object} Data to parse.
0N/A * @param data_out {Object} In-progress parsed data to update.
0N/A * @return {Object} Parsed data object.
0N/A * @static
0N/A * @protected
0N/A */
0N/A _parseResults: function(schema, data_in, data_out) {
0N/A var results = [],
0N/A nodeList,
0N/A error;
0N/A
0N/A if(schema.resultsLocator) {
0N/A nodeList = data_in.getElementsByTagName(schema.resultsLocator);
0N/A //if(xmlList) {
0N/A if(LANG.isArray(schema.resultsFields) && nodeList.length) {
0N/A data_out = SchemaXML._getFieldValues(schema.resultsFields, nodeList, data_out);
0N/A }
0N/A else {
0N/A data_out.results = [];
0N/A data_out.error = new Error(this.toString() + " Fields retrieval failure");
0N/A }
0N/A //}
0N/A //else {
0N/A //error = new Error(this.toString() + " Results locator failure");
0N/A //}
0N/A
0N/A //if (error) {
0N/A //data_out.error = error;
0N/A //}
0N/A
0N/A }
0N/A return data_out;
0N/A },
0N/A
0N/A /**
0N/A * Get field data values out of nodelist of full results
0N/A *
0N/A * @method _getFieldValues
0N/A * @param fields {Array} Fields to find.
0N/A * @param data_in {Array} Results data to parse.
0N/A * @param data_out {Object} In-progress parsed data to update.
0N/A * @return {Object} Parsed data object.
0N/A * @static
0N/A * @protected
0N/A */
0N/A _getFieldValues: function(fields, data_in, data_out) {
0N/A var results = [],
0N/A i = data_in.length-1,
0N/A j,
0N/A item, result_in, result_out, field, key, data, subnode, datapieces;
0N/A
517N/A try {
0N/A // Loop through each result node
0N/A for(; i>= 0; i--) {
0N/A result_out = {};
0N/A result_in = data_in[i];
0N/A
517N/A // Find each field value
0N/A j = fields.length-1;
0N/A for(; j>= 0; j--) {
0N/A field = fields[j];
0N/A key = (LANG.isValue(field.key)) ? field.key : field;
0N/A
517N/A // Values may be held in an attribute...
0N/A if(result_in.attributes.getNamedItem(key)) {
0N/A data = result_in.attributes.getNamedItem(key).value;
0N/A }
0N/A // ...or in a node
0N/A else {
0N/A subnode = result_in.getElementsByTagName(key);
0N/A if(subnode && subnode.item(0)) {
517N/A item = subnode.item(0);
0N/A // For IE, then DOM...
0N/A data = (item) ? ((item.text) ? item.text : (item.textContent) ? item.textContent : null) : null;
0N/A // ...then fallback, but check for multiple child nodes
0N/A if(!data) {
0N/A datapieces = [];
517N/A for(var k=0, len=item.childNodes.length; k<len; k++) {
0N/A if(item.childNodes[k].nodeValue) {
0N/A datapieces[datapieces.length] = item.childNodes[k].nodeValue;
0N/A }
0N/A }
0N/A if(datapieces.length > 0) {
0N/A data = datapieces.join("");
0N/A }
0N/A }
0N/A }
0N/A }
0N/A // Safety net
0N/A if(data === null) {
0N/A data = "";
0N/A }
0N/A
0N/A var parser = (typeof field.parser === 'function') ?
0N/A field.parser :
0N/A // TODO: implement shortcuts
0N/A null;//DS.Parser[field.parser+''];
0N/A if(parser) {
0N/A data = parser.call(this, data);
0N/A }
0N/A // Safety measure
0N/A if(data === undefined) {
0N/A data = null;
0N/A }
0N/A result_out[key] = data;
0N/A }
0N/A results[i] = result_out;
0N/A }
0N/A }
0N/A catch(e) {
0N/A data_out.error = new Error(this.toString() + " Fields retrieval failure");
0N/A }
0N/A
0N/A data_out.results = results;
0N/A return data_out;
0N/A },
0N/A
0N/A /**
0N/A * Parses results data according to schema
0N/A *
0N/A * @method _parseMeta
0N/A * @param data_out {Object} Data to parse.
0N/A * @param data_in {Object} In-progress parsed data to update.
0N/A * @return {Object} Schema-parsed meta data.
0N/A * @static
0N/A * @protected
0N/A */
0N/A _parseMeta: function(metaFields, data_in, data_out) {
5694N/A if(LANG.isObject(metaFields)) {
5694N/A var key, path, value;
5694N/A for(key in metaFields) {
5694N/A if (metaFields.hasOwnProperty(key)) {
5694N/A path = metaFields[key];
5694N/A // Look for a node
5694N/A value = data_in.getElementsByTagName(path)[0];
5694N/A
5694N/A if (value) {
5694N/A value = value.firstChild.nodeValue;
5694N/A } else {
5694N/A // Look for an attribute
5694N/A value = data_in.attributes.getNamedItem(path);
5694N/A if (value) {
5694N/A value = value.value;
5694N/A }
5694N/A }
5694N/A
5694N/A if (LANG.isValue(value)) {
5694N/A data_out.meta[key] = value;
5694N/A }
5694N/A }
5694N/A }
0N/A }
517N/A else {
0N/A data_out.error = new Error(this.toString() + " Meta retrieval failure");
0N/A }
0N/A return data_out;
0N/A }
0N/A};
0N/A
0N/AY.DataSchema.XML = Y.mix(SchemaXML, Y.DataSchema.Base);
0N/A
0N/A
0N/A
0N/A}, '@VERSION@' ,{requires:['dataschema-base']});
0N/A
0N/A
0N/A
0N/AYUI.add('dataschema', function(Y){}, '@VERSION@' ,{use:['dataschema-base','dataschema-json','dataschema-xml']});
0N/A
0N/A