688 lines
19 KiB
JavaScript
688 lines
19 KiB
JavaScript
|
/*
|
||
|
* Defiant.js v1.2.5
|
||
|
* Serch JSON structures plus smart templating with XSLT and XPath.
|
||
|
* http://defiantjs.com
|
||
|
*
|
||
|
* Copyright (c) 2013-2015, Hakan Bilgin <hbi@longscript.com>
|
||
|
* Licensed under the MIT License
|
||
|
*
|
||
|
* NOTE:
|
||
|
* Robbie Antenesse edited line 165's RegExp to search global and case-insensitive in case of multiple contains() groups.
|
||
|
*/
|
||
|
|
||
|
if (typeof module === "undefined") {
|
||
|
var module = { exports: undefined };
|
||
|
} else {
|
||
|
// Node env adaptation goes here...
|
||
|
}
|
||
|
|
||
|
module.exports = Defiant = (function(window, undefined) {
|
||
|
'use strict';
|
||
|
|
||
|
var Defiant = {
|
||
|
is_ie : /msie/i.test(navigator.userAgent),
|
||
|
is_safari : /safari/i.test(navigator.userAgent),
|
||
|
env : 'production',
|
||
|
xml_decl : '<?xml version="1.0" encoding="utf-8"?>',
|
||
|
namespace : 'xmlns:d="defiant-namespace"',
|
||
|
tabsize : 4,
|
||
|
render: function(template, data) {
|
||
|
var processor = new XSLTProcessor(),
|
||
|
span = document.createElement('span'),
|
||
|
opt = {match: '/'},
|
||
|
tmpltXpath,
|
||
|
scripts,
|
||
|
temp,
|
||
|
sorter;
|
||
|
// handle arguments
|
||
|
switch (typeof(template)) {
|
||
|
case 'object':
|
||
|
this.extend(opt, template);
|
||
|
if (!opt.data) opt.data = data;
|
||
|
break;
|
||
|
case 'string':
|
||
|
opt.template = template;
|
||
|
opt.data = data;
|
||
|
break;
|
||
|
default:
|
||
|
throw 'error';
|
||
|
}
|
||
|
opt.data = JSON.toXML(opt.data);
|
||
|
tmpltXpath = '//xsl:template[@name="'+ opt.template +'"]';
|
||
|
|
||
|
if (!this.xsl_template) this.gatherTemplates();
|
||
|
|
||
|
if (opt.sorter) {
|
||
|
sorter = this.node.selectSingleNode(this.xsl_template, tmpltXpath +'//xsl:for-each//xsl:sort');
|
||
|
if (sorter) {
|
||
|
if (opt.sorter.order) sorter.setAttribute('order', opt.sorter.order);
|
||
|
if (opt.sorter.select) sorter.setAttribute('select', opt.sorter.select);
|
||
|
sorter.setAttribute('data-type', opt.sorter.type || 'text');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
temp = this.node.selectSingleNode(this.xsl_template, tmpltXpath);
|
||
|
temp.setAttribute('match', opt.match);
|
||
|
processor.importStylesheet(this.xsl_template);
|
||
|
span.appendChild(processor.transformToFragment(opt.data, document));
|
||
|
temp.removeAttribute('match');
|
||
|
|
||
|
if (this.is_safari) {
|
||
|
scripts = span.getElementsByTagName('script');
|
||
|
for (var i=0, il=scripts.length; i<il; i++) scripts[i].defer = true;
|
||
|
}
|
||
|
return span.innerHTML;
|
||
|
},
|
||
|
gatherTemplates: function() {
|
||
|
var scripts = document.getElementsByTagName('script'),
|
||
|
str = '',
|
||
|
i = 0,
|
||
|
il = scripts.length;
|
||
|
for (; i<il; i++) {
|
||
|
if (scripts[i].type === 'defiant/xsl-template') str += scripts[i].innerHTML;
|
||
|
}
|
||
|
this.xsl_template = this.xmlFromString('<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" '+ this.namespace +'>'+ str.replace(/defiant:(\w+)/g, '$1') +'</xsl:stylesheet>');
|
||
|
},
|
||
|
getSnapshot: function(data) {
|
||
|
return JSON.toXML(data, true);
|
||
|
},
|
||
|
xmlFromString: function(str) {
|
||
|
var parser,
|
||
|
doc;
|
||
|
str = str.replace(/>\s{1,}</g, '><');
|
||
|
if (str.trim().match(/<\?xml/) === null) {
|
||
|
str = this.xml_decl + str;
|
||
|
}
|
||
|
if (this.is_ie) {
|
||
|
doc = new ActiveXObject('Msxml2.DOMDocument');
|
||
|
doc.loadXML(str);
|
||
|
if (str.indexOf('xsl:stylesheet') === -1) {
|
||
|
doc.setProperty('SelectionLanguage', 'XPath');
|
||
|
}
|
||
|
} else {
|
||
|
parser = new DOMParser();
|
||
|
doc = parser.parseFromString(str, 'text/xml');
|
||
|
}
|
||
|
return doc;
|
||
|
},
|
||
|
extend: function(src, dest) {
|
||
|
for (var content in dest) {
|
||
|
if (!src[content] || typeof(dest[content]) !== 'object') {
|
||
|
src[content] = dest[content];
|
||
|
} else {
|
||
|
this.extend(src[content], dest[content]);
|
||
|
}
|
||
|
}
|
||
|
return src;
|
||
|
},
|
||
|
node: {}
|
||
|
};
|
||
|
|
||
|
return Defiant;
|
||
|
|
||
|
})(this);
|
||
|
|
||
|
|
||
|
if (typeof(XSLTProcessor) === 'undefined') {
|
||
|
|
||
|
// emulating XSLT Processor (enough to be used in defiant)
|
||
|
var XSLTProcessor = function() {};
|
||
|
XSLTProcessor.prototype = {
|
||
|
importStylesheet: function(xsldoc) {
|
||
|
this.xsldoc = xsldoc;
|
||
|
},
|
||
|
transformToFragment: function(data, doc) {
|
||
|
var str = data.transformNode(this.xsldoc),
|
||
|
span = document.createElement('span');
|
||
|
span.innerHTML = str;
|
||
|
return span;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
// extending STRING
|
||
|
if (!String.prototype.fill) {
|
||
|
String.prototype.fill = function(i,c) {
|
||
|
var str = this;
|
||
|
c = c || ' ';
|
||
|
for (; str.length<i; str+=c){}
|
||
|
return str;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (!String.prototype.trim) {
|
||
|
String.prototype.trim = function () {
|
||
|
return this.replace(/^\s+|\s+$/gm, '');
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (!String.prototype.xTransform) {
|
||
|
String.prototype.xTransform = function () {
|
||
|
var str = this;
|
||
|
if (this.indexOf('translate(') === -1) {
|
||
|
str = this.replace(/contains\(([^,]+),([^\\)]+)\)/gi, function(c,h,n) {
|
||
|
var a = 'abcdefghijklmnopqrstuvwxyz',
|
||
|
q = n.trim().slice(-1);
|
||
|
return "contains(translate("+ h +", "+ q + a.toUpperCase() + q +", "+ q + a + q +"),"+ n.toLowerCase() +")";
|
||
|
});
|
||
|
}
|
||
|
return str.toString();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (typeof(JSON) === 'undefined') {
|
||
|
window.JSON = {
|
||
|
parse: function (sJSON) { return eval("(" + sJSON + ")"); },
|
||
|
stringify: function (vContent) {
|
||
|
if (vContent instanceof Object) {
|
||
|
var sOutput = "";
|
||
|
if (vContent.constructor === Array) {
|
||
|
for (var nId = 0; nId < vContent.length; sOutput += this.stringify(vContent[nId]) + ",", nId++);
|
||
|
return "[" + sOutput.substr(0, sOutput.length - 1) + "]";
|
||
|
}
|
||
|
if (vContent.toString !== Object.prototype.toString) {
|
||
|
return "\"" + vContent.toString().replace(/"/g, "\\$&") + "\"";
|
||
|
}
|
||
|
for (var sProp in vContent) {
|
||
|
sOutput += "\"" + sProp.replace(/"/g, "\\$&") + "\":" + this.stringify(vContent[sProp]) + ",";
|
||
|
}
|
||
|
return "{" + sOutput.substr(0, sOutput.length - 1) + "}";
|
||
|
}
|
||
|
return typeof vContent === "string" ? "\"" + vContent.replace(/"/g, "\\$&") + "\"" : String(vContent);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
/* jshint ignore:end */
|
||
|
|
||
|
if (!JSON.toXML) {
|
||
|
JSON.toXML = function(tree, snapshot) {
|
||
|
'use strict';
|
||
|
|
||
|
var interpreter = {
|
||
|
map : [],
|
||
|
rx_validate_name : /^(?!xml)[a-z_][\w\d.:]*$/i,
|
||
|
rx_node : /<(.+?)( .*?)>/,
|
||
|
rx_constructor : /<(.+?)( d:contr=".*?")>/,
|
||
|
rx_namespace : / xmlns\:d="defiant\-namespace"/,
|
||
|
rx_data : /(<.+?>)(.*?)(<\/d:data>)/i,
|
||
|
rx_function : /function (\w+)/i,
|
||
|
to_xml: function(tree) {
|
||
|
var str = this.hash_to_xml(null, tree);
|
||
|
return Defiant.xmlFromString(str);
|
||
|
},
|
||
|
hash_to_xml: function(name, tree, array_child) {
|
||
|
var is_array = tree.constructor === Array,
|
||
|
elem = [],
|
||
|
attr = [],
|
||
|
key,
|
||
|
val,
|
||
|
val_is_array,
|
||
|
type,
|
||
|
is_attr,
|
||
|
cname,
|
||
|
constr,
|
||
|
cnName,
|
||
|
i;
|
||
|
|
||
|
for (key in tree) {
|
||
|
val = tree[key];
|
||
|
if (val === null || val === undefined || val.toString() === 'NaN') val = null;
|
||
|
|
||
|
is_attr = key.slice(0,1) === '@';
|
||
|
cname = array_child ? name : key;
|
||
|
if (cname == +cname && tree.constructor !== Object) cname = 'd:item';
|
||
|
if (val === null) {
|
||
|
constr = null;
|
||
|
cnName = false;
|
||
|
} else {
|
||
|
constr = val.constructor;
|
||
|
cnName = constr.toString().match(this.rx_function)[1];
|
||
|
}
|
||
|
|
||
|
if (is_attr) {
|
||
|
attr.push( cname.slice(1) +'="'+ this.escape_xml(val) +'"' );
|
||
|
if (cnName !== 'String') attr.push( 'd:'+ cname.slice(1) +'="'+ cnName +'"' );
|
||
|
} else if (val === null) {
|
||
|
elem.push( this.scalar_to_xml( cname, val ) );
|
||
|
} else {
|
||
|
switch (constr) {
|
||
|
case Function:
|
||
|
// if constructor is function, then it's not a JSON structure
|
||
|
// throw ERROR ?
|
||
|
break;
|
||
|
case Object:
|
||
|
elem.push( this.hash_to_xml( cname, val ) );
|
||
|
break;
|
||
|
case Array:
|
||
|
if (key === cname) {
|
||
|
val_is_array = val.constructor === Array;
|
||
|
if (val_is_array) {
|
||
|
i = val.length;
|
||
|
while (i--) {
|
||
|
if (val[i].constructor === Array) val_is_array = true;
|
||
|
if (!val_is_array && val[i].constructor === Object) val_is_array = true;
|
||
|
}
|
||
|
}
|
||
|
elem.push( this.scalar_to_xml( cname, val, val_is_array ) );
|
||
|
break;
|
||
|
}
|
||
|
/* falls through */
|
||
|
case String:
|
||
|
if (typeof(val) === 'string') {
|
||
|
val = val.toString().replace(/\&/g, '&')
|
||
|
.replace(/\r|\n/g, ' ');
|
||
|
}
|
||
|
if (cname === '#text') {
|
||
|
// prepare map
|
||
|
this.map.push(tree);
|
||
|
attr.push('d:mi="'+ this.map.length +'"');
|
||
|
attr.push('d:constr="'+ cnName +'"');
|
||
|
elem.push( this.escape_xml(val) );
|
||
|
break;
|
||
|
}
|
||
|
/* falls through */
|
||
|
case Number:
|
||
|
case Boolean:
|
||
|
if (cname === '#text' && cnName !== 'String') {
|
||
|
// prepare map
|
||
|
this.map.push(tree);
|
||
|
attr.push('d:mi="'+ this.map.length +'"');
|
||
|
attr.push('d:constr="'+ cnName +'"');
|
||
|
elem.push( this.escape_xml(val) );
|
||
|
break;
|
||
|
}
|
||
|
elem.push( this.scalar_to_xml( cname, val ) );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!name) {
|
||
|
name = 'd:data';
|
||
|
attr.push(Defiant.namespace);
|
||
|
if (is_array) attr.push('d:constr="Array"');
|
||
|
}
|
||
|
if (name.match(this.rx_validate_name) === null) {
|
||
|
attr.push( 'd:name="'+ name +'"' );
|
||
|
name = 'd:name';
|
||
|
}
|
||
|
if (array_child) return elem.join('');
|
||
|
// prepare map
|
||
|
this.map.push(tree);
|
||
|
attr.push('d:mi="'+ this.map.length +'"');
|
||
|
|
||
|
return '<'+ name + (attr.length ? ' '+ attr.join(' ') : '') + (elem.length ? '>'+ elem.join('') +'</'+ name +'>' : '/>' );
|
||
|
},
|
||
|
scalar_to_xml: function(name, val, override) {
|
||
|
var attr = '',
|
||
|
text,
|
||
|
constr,
|
||
|
cnName;
|
||
|
|
||
|
// check whether the nodename is valid
|
||
|
if (name.match(this.rx_validate_name) === null) {
|
||
|
attr += ' d:name="'+ name +'"';
|
||
|
name = 'd:name';
|
||
|
override = false;
|
||
|
}
|
||
|
if (val === null || val.toString() === 'NaN') val = null;
|
||
|
if (val === null) return '<'+ name +' d:constr="null"/>';
|
||
|
if (val.length === 1 && val[0].constructor === Object) {
|
||
|
|
||
|
text = this.hash_to_xml(false, val[0]);
|
||
|
|
||
|
var a1 = text.match(this.rx_node),
|
||
|
a2 = text.match(this.rx_constructor);
|
||
|
a1 = (a1 !== null)? a1[2]
|
||
|
.replace(this.rx_namespace, '')
|
||
|
.replace(/>/, '')
|
||
|
.replace(/"\/$/, '"') : '';
|
||
|
a2 = (a2 !== null)? a2[2] : '';
|
||
|
|
||
|
text = text.match(this.rx_data);
|
||
|
text = (text !== null)? text[2] : '';
|
||
|
|
||
|
return '<'+ name + a1 +' '+ a2 +' d:type="ArrayItem">'+ text +'</'+ name +'>';
|
||
|
} else if (val.length === 0 && val.constructor === Array) {
|
||
|
return '<'+ name +' d:constr="Array"/>';
|
||
|
}
|
||
|
// else
|
||
|
if (override) {
|
||
|
return this.hash_to_xml( name, val, true );
|
||
|
}
|
||
|
|
||
|
constr = val.constructor;
|
||
|
cnName = constr.toString().match(this.rx_function)[1];
|
||
|
text = (constr === Array) ? this.hash_to_xml( 'd:item', val, true )
|
||
|
: this.escape_xml(val);
|
||
|
|
||
|
attr += ' d:constr="'+ cnName +'"';
|
||
|
// prepare map
|
||
|
this.map.push(val);
|
||
|
attr += ' d:mi="'+ this.map.length +'"';
|
||
|
|
||
|
return (name === '#text') ? this.escape_xml(val) : '<'+ name + attr +'>'+ text +'</'+ name +'>';
|
||
|
},
|
||
|
escape_xml: function(text) {
|
||
|
return String(text) .replace(/</g, '<')
|
||
|
.replace(/>/g, '>')
|
||
|
.replace(/"/g, '"')
|
||
|
.replace(/ /g, ' ');
|
||
|
}
|
||
|
},
|
||
|
doc = interpreter.to_xml.call(interpreter, tree);
|
||
|
|
||
|
// snapshot distinctly improves performance
|
||
|
if (snapshot) {
|
||
|
return {
|
||
|
doc: doc,
|
||
|
src: tree,
|
||
|
map: interpreter.map
|
||
|
};
|
||
|
}
|
||
|
|
||
|
this.search.map = interpreter.map;
|
||
|
return doc;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (!JSON.search) {
|
||
|
JSON.search = function(tree, xpath, single) {
|
||
|
'use strict';
|
||
|
|
||
|
var isSnapshot = tree.doc && tree.doc.nodeType,
|
||
|
doc = isSnapshot ? tree.doc : JSON.toXML(tree),
|
||
|
map = isSnapshot ? tree.map : this.search.map,
|
||
|
src = isSnapshot ? tree.src : tree,
|
||
|
xres = Defiant.node[ single ? 'selectSingleNode' : 'selectNodes' ](doc, xpath.xTransform()),
|
||
|
ret = [],
|
||
|
mapIndex,
|
||
|
i;
|
||
|
|
||
|
if (single) xres = [xres];
|
||
|
i = xres.length;
|
||
|
|
||
|
while (i--) {
|
||
|
switch(xres[i].nodeType) {
|
||
|
case 2:
|
||
|
case 3:
|
||
|
ret.unshift( xres[i].nodeValue );
|
||
|
break;
|
||
|
default:
|
||
|
mapIndex = +xres[i].getAttribute('d:mi');
|
||
|
if (map[mapIndex-1]) ret.unshift( map[mapIndex-1] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if environment = development, add search tracing
|
||
|
if (Defiant.env === 'development') {
|
||
|
this.trace = JSON.mtrace(src, ret, xres);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (!JSON.mtrace) {
|
||
|
JSON.mtrace = function(root, hits, xres) {
|
||
|
'use strict';
|
||
|
|
||
|
var win = window,
|
||
|
stringify = JSON.stringify,
|
||
|
sroot = stringify( root, null, '\t' ).replace(/\t/g, ''),
|
||
|
trace = [],
|
||
|
i = 0,
|
||
|
il = xres.length,
|
||
|
od = il ? xres[i].ownerDocument.documentElement : false,
|
||
|
map = this.search.map,
|
||
|
hstr,
|
||
|
cConstr,
|
||
|
fIndex = 0,
|
||
|
mIndex,
|
||
|
lStart,
|
||
|
lEnd;
|
||
|
|
||
|
for (; i<il; i++) {
|
||
|
switch (xres[i].nodeType) {
|
||
|
case 2:
|
||
|
cConstr = xres[i].ownerElement ? xres[i].ownerElement.getAttribute('d:'+ xres[i].nodeName) : 'String';
|
||
|
hstr = '"@'+ xres[i].nodeName +'": '+ win[ cConstr ]( hits[i] );
|
||
|
mIndex = sroot.indexOf(hstr);
|
||
|
lEnd = 0;
|
||
|
break;
|
||
|
case 3:
|
||
|
cConstr = xres[i].parentNode.getAttribute('d:constr');
|
||
|
hstr = win[ cConstr ]( hits[i] );
|
||
|
hstr = '"'+ xres[i].parentNode.nodeName +'": '+ (hstr === 'Number' ? hstr : '"'+ hstr +'"');
|
||
|
mIndex = sroot.indexOf(hstr);
|
||
|
lEnd = 0;
|
||
|
break;
|
||
|
default:
|
||
|
if (xres[i] === od) continue;
|
||
|
if (xres[i].getAttribute('d:constr') === 'String') {
|
||
|
cConstr = xres[i].getAttribute('d:constr');
|
||
|
hstr = win[ cConstr ]( hits[i] );
|
||
|
hstr = '"'+ xres[i].nodeName +'": '+ (hstr === 'Number' ? hstr : '"'+ hstr +'"');
|
||
|
mIndex = sroot.indexOf(hstr, fIndex);
|
||
|
lEnd = 0;
|
||
|
fIndex = mIndex + 1;
|
||
|
} else {
|
||
|
hstr = stringify( hits[i], null, '\t' ).replace(/\t/g, '');
|
||
|
mIndex = sroot.indexOf(hstr);
|
||
|
lEnd = hstr.match(/\n/g).length;
|
||
|
}
|
||
|
}
|
||
|
lStart = sroot.substring(0,mIndex).match(/\n/g).length+1;
|
||
|
trace.push([lStart, lEnd]);
|
||
|
}
|
||
|
|
||
|
return trace;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
Defiant.node.selectNodes = function(XNode, XPath) {
|
||
|
if (XNode.evaluate) {
|
||
|
var ns = XNode.createNSResolver(XNode.documentElement),
|
||
|
qI = XNode.evaluate(XPath, XNode, ns, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null),
|
||
|
res = [],
|
||
|
i = 0,
|
||
|
il = qI.snapshotLength;
|
||
|
for (; i<il; i++) {
|
||
|
res.push( qI.snapshotItem(i) );
|
||
|
}
|
||
|
return res;
|
||
|
} else {
|
||
|
return XNode.selectNodes(XPath);
|
||
|
}
|
||
|
};
|
||
|
Defiant.node.selectSingleNode = function(XNode, XPath) {
|
||
|
if (XNode.evaluate) {
|
||
|
var xI = this.selectNodes(XNode, XPath);
|
||
|
return (xI.length > 0)? xI[0] : null;
|
||
|
} else {
|
||
|
return XNode.selectSingleNode(XPath);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
Defiant.node.prettyPrint = function(node) {
|
||
|
var root = Defiant,
|
||
|
tabs = root.tabsize,
|
||
|
decl = root.xml_decl.toLowerCase(),
|
||
|
ser,
|
||
|
xstr;
|
||
|
if (root.is_ie) {
|
||
|
xstr = node.xml;
|
||
|
} else {
|
||
|
ser = new XMLSerializer();
|
||
|
xstr = ser.serializeToString(node);
|
||
|
}
|
||
|
if (root.env !== 'development') {
|
||
|
// if environment is not development, remove defiant related info
|
||
|
xstr = xstr.replace(/ \w+\:d=".*?"| d\:\w+=".*?"/g, '');
|
||
|
}
|
||
|
var str = xstr.trim().replace(/(>)\s*(<)(\/*)/g, '$1\n$2$3'),
|
||
|
lines = str.split('\n'),
|
||
|
indent = -1,
|
||
|
i = 0,
|
||
|
il = lines.length,
|
||
|
start,
|
||
|
end;
|
||
|
for (; i<il; i++) {
|
||
|
if (i === 0 && lines[i].toLowerCase() === decl) continue;
|
||
|
start = lines[i].match(/<[A-Za-z_\:]+.*?>/g) !== null;
|
||
|
//start = lines[i].match(/<[^\/]+>/g) !== null;
|
||
|
end = lines[i].match(/<\/[\w\:]+>/g) !== null;
|
||
|
if (lines[i].match(/<.*?\/>/g) !== null) start = end = true;
|
||
|
if (start) indent++;
|
||
|
lines[i] = String().fill(indent, '\t') + lines[i];
|
||
|
if (start && end) indent--;
|
||
|
if (!start && end) indent--;
|
||
|
}
|
||
|
return lines.join('\n').replace(/\t/g, String().fill(tabs, ' '));
|
||
|
};
|
||
|
|
||
|
|
||
|
Defiant.node.toJSON = function(xnode, stringify) {
|
||
|
'use strict';
|
||
|
|
||
|
var interpret = function(leaf) {
|
||
|
var obj = {},
|
||
|
win = window,
|
||
|
attr,
|
||
|
type,
|
||
|
item,
|
||
|
cname,
|
||
|
cConstr,
|
||
|
cval,
|
||
|
text,
|
||
|
i, il, a;
|
||
|
|
||
|
switch (leaf.nodeType) {
|
||
|
case 1:
|
||
|
cConstr = leaf.getAttribute('d:constr');
|
||
|
if (cConstr === 'Array') obj = [];
|
||
|
else if (cConstr === 'String' && leaf.textContent === '') obj = '';
|
||
|
|
||
|
attr = leaf.attributes;
|
||
|
i = 0;
|
||
|
il = attr.length;
|
||
|
for (; i<il; i++) {
|
||
|
a = attr.item(i);
|
||
|
if (a.nodeName.match(/\:d|d\:/g) !== null) continue;
|
||
|
|
||
|
cConstr = leaf.getAttribute('d:'+ a.nodeName);
|
||
|
if (cConstr && cConstr !== 'undefined') {
|
||
|
if (a.nodeValue === 'null') cval = null;
|
||
|
else cval = win[ cConstr ]( (a.nodeValue === 'false') ? '' : a.nodeValue );
|
||
|
} else {
|
||
|
cval = a.nodeValue;
|
||
|
}
|
||
|
obj['@'+ a.nodeName] = cval;
|
||
|
}
|
||
|
break;
|
||
|
case 3:
|
||
|
type = leaf.parentNode.getAttribute('d:type');
|
||
|
cval = (type) ? win[ type ]( leaf.nodeValue === 'false' ? '' : leaf.nodeValue ) : leaf.nodeValue;
|
||
|
obj = cval;
|
||
|
break;
|
||
|
}
|
||
|
if (leaf.hasChildNodes()) {
|
||
|
i = 0;
|
||
|
il = leaf.childNodes.length;
|
||
|
for(; i<il; i++) {
|
||
|
item = leaf.childNodes.item(i);
|
||
|
cname = item.nodeName;
|
||
|
attr = leaf.attributes;
|
||
|
|
||
|
if (cname === 'd:name') {
|
||
|
cname = item.getAttribute('d:name');
|
||
|
}
|
||
|
if (cname === '#text') {
|
||
|
cConstr = leaf.getAttribute('d:constr');
|
||
|
if (cConstr === 'undefined') cConstr = undefined;
|
||
|
text = item.textContent || item.text;
|
||
|
cval = cConstr === 'Boolean' && text === 'false' ? '' : text;
|
||
|
|
||
|
if (!cConstr && !attr.length) obj = cval;
|
||
|
else if (cConstr && il === 1) {
|
||
|
obj = win[cConstr](cval);
|
||
|
} else if (!leaf.hasChildNodes()) {
|
||
|
obj[cname] = (cConstr)? win[cConstr](cval) : cval;
|
||
|
} else {
|
||
|
if (attr.length < 3) obj = (cConstr)? win[cConstr](cval) : cval;
|
||
|
else obj[cname] = (cConstr)? win[cConstr](cval) : cval;
|
||
|
}
|
||
|
} else {
|
||
|
if (obj[cname]) {
|
||
|
if (obj[cname].push) obj[cname].push( interpret(item) );
|
||
|
else obj[cname] = [obj[cname], interpret(item)];
|
||
|
continue;
|
||
|
}
|
||
|
cConstr = item.getAttribute('d:constr');
|
||
|
switch (cConstr) {
|
||
|
case 'null':
|
||
|
if (obj.push) obj.push(null);
|
||
|
else obj[cname] = null;
|
||
|
break;
|
||
|
case 'Array':
|
||
|
//console.log( Defiant.node.prettyPrint(item) );
|
||
|
if (item.parentNode.firstChild === item && cConstr === 'Array' && cname !== 'd:item') {
|
||
|
if (cname === 'd:item' || cConstr === 'Array') {
|
||
|
cval = interpret(item);
|
||
|
obj[cname] = cval.length ? [cval] : cval;
|
||
|
} else {
|
||
|
obj[cname] = interpret(item);
|
||
|
}
|
||
|
}
|
||
|
else if (obj.push) obj.push( interpret(item) );
|
||
|
else obj[cname] = interpret(item);
|
||
|
break;
|
||
|
case 'String':
|
||
|
case 'Number':
|
||
|
case 'Boolean':
|
||
|
text = item.textContent || item.text;
|
||
|
cval = cConstr === 'Boolean' && text === 'false' ? '' : text;
|
||
|
|
||
|
if (obj.push) obj.push( win[cConstr](cval) );
|
||
|
else obj[cname] = interpret(item);
|
||
|
break;
|
||
|
default:
|
||
|
if (obj.push) obj.push( interpret( item ) );
|
||
|
else obj[cname] = interpret( item );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (leaf.nodeType === 1 && leaf.getAttribute('d:type') === 'ArrayItem') {
|
||
|
obj = [obj];
|
||
|
}
|
||
|
return obj;
|
||
|
},
|
||
|
node = (xnode.nodeType === 9) ? xnode.documentElement : xnode,
|
||
|
ret = interpret(node),
|
||
|
rn = ret[node.nodeName];
|
||
|
|
||
|
// exclude root, if "this" is root node
|
||
|
if (node === node.ownerDocument.documentElement && rn && rn.constructor === Array) {
|
||
|
ret = rn;
|
||
|
}
|
||
|
if (stringify && stringify.toString() === 'true') stringify = '\t';
|
||
|
return stringify ? JSON.stringify(ret, null, stringify) : ret;
|
||
|
};
|
||
|
|
||
|
|
||
|
// check if jQuery is present
|
||
|
if (typeof(jQuery) !== 'undefined') {
|
||
|
(function ( $ ) {
|
||
|
'use strict';
|
||
|
|
||
|
$.fn.defiant = function(template, xpath) {
|
||
|
this.html( Defiant.render(template, xpath) );
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
}(jQuery));
|
||
|
}
|