PageRenderTime 62ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/ajax/libs//1.2.6/defiant.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 846 lines | 623 code | 35 blank | 188 comment | 50 complexity | 192e5587fcb80cb556e34c89308d2702 MD5 | raw file
  1. /*
  2. * Defiant.js v1.2.6
  3. * Search JSON structures plus smart templating with XSLT and XPath.
  4. * http://defiantjs.com
  5. *
  6. * Copyright (c) 2013-2015, Hakan Bilgin <hbi@longscript.com>
  7. * Licensed under the MIT License
  8. */
  9. (function(window, undefined) {
  10. //'use strict';
  11. var x10 = {
  12. init: function() {
  13. return this;
  14. },
  15. work_handler: function(event) {
  16. var args = Array.prototype.slice.call(event.data, 1),
  17. func = event.data[0],
  18. ret = tree[func].apply(tree, args);
  19. // return process finish
  20. postMessage([func, ret]);
  21. },
  22. setup: function(tree) {
  23. var url = window.URL || window.webkitURL,
  24. script = 'var tree = {'+ this.parse(tree).join(',') +'};',
  25. blob = new Blob([script + 'self.addEventListener("message", '+ this.work_handler.toString() +', false);'],
  26. {type: 'text/javascript'}),
  27. worker = new Worker(url.createObjectURL(blob));
  28. // thread pipe
  29. worker.onmessage = function(event) {
  30. var args = Array.prototype.slice.call(event.data, 1),
  31. func = event.data[0];
  32. x10.observer.emit('x10:'+ func, args);
  33. };
  34. return worker;
  35. },
  36. call_handler: function(func, worker) {
  37. return function() {
  38. var args = Array.prototype.slice.call(arguments, 0, -1),
  39. callback = arguments[arguments.length-1];
  40. // add method name
  41. args.unshift(func);
  42. // listen for 'done'
  43. x10.observer.on('x10:'+ func, function(event) {
  44. callback(event.detail[0]);
  45. });
  46. // start worker
  47. worker.postMessage(args);
  48. };
  49. },
  50. compile: function(hash) {
  51. var worker = this.setup(typeof(hash) === 'function' ? {func: hash} : hash),
  52. obj = {},
  53. fn;
  54. // create return object
  55. if (typeof(hash) === 'function') {
  56. obj.func = this.call_handler('func', worker);
  57. return obj.func;
  58. } else {
  59. for (fn in hash) {
  60. obj[fn] = this.call_handler(fn, worker);
  61. }
  62. return obj;
  63. }
  64. },
  65. parse: function(tree, isArray) {
  66. var hash = [],
  67. key,
  68. val,
  69. v;
  70. for (key in tree) {
  71. v = tree[key];
  72. // handle null
  73. if (v === null) {
  74. hash.push(key +':null');
  75. continue;
  76. }
  77. // handle undefined
  78. if (v === undefined) {
  79. hash.push(key +':undefined');
  80. continue;
  81. }
  82. switch (v.constructor) {
  83. case Date: val = 'new Date('+ v.valueOf() +')'; break;
  84. case Object: val = '{'+ this.parse(v).join(',') +'}'; break;
  85. case Array: val = '['+ this.parse(v, true).join(',') +']'; break;
  86. case String: val = '"'+ v.replace(/"/g, '\\"') +'"'; break;
  87. case RegExp:
  88. case Function: val = v.toString(); break;
  89. default: val = v;
  90. }
  91. if (isArray) hash.push(val);
  92. else hash.push(key +':'+ val);
  93. }
  94. return hash;
  95. },
  96. // simple event emitter
  97. observer: (function() {
  98. var stack = {};
  99. return {
  100. on: function(type, fn) {
  101. if (!stack[type]) {
  102. stack[type] = [];
  103. }
  104. stack[type].unshift(fn);
  105. },
  106. off: function(type, fn) {
  107. if (!stack[type]) return;
  108. var i = stack[type].indexOf(fn);
  109. stack[type].splice(i,1);
  110. },
  111. emit: function(type, detail) {
  112. if (!stack[type]) return;
  113. var event = {
  114. type : type,
  115. detail : detail,
  116. isCanceled : false,
  117. cancelBubble : function() {
  118. this.isCanceled = true;
  119. }
  120. },
  121. len = stack[type].length;
  122. while(len--) {
  123. if (event.isCanceled) return;
  124. stack[type][len](event);
  125. }
  126. }
  127. };
  128. })()
  129. };
  130. if (typeof module === "undefined") {
  131. // publish x10
  132. window.x10 = x10.init();
  133. } else {
  134. module.exports = x10.init();
  135. }
  136. })(this);
  137. if (typeof module === "undefined") {
  138. var module = { exports: undefined };
  139. } else {
  140. // Node env adaptation goes here...
  141. }
  142. module.exports = Defiant = (function(window, undefined) {
  143. 'use strict';
  144. var Defiant = {
  145. is_ie : /msie/i.test(navigator.userAgent),
  146. is_safari : /safari/i.test(navigator.userAgent),
  147. env : 'production',
  148. xml_decl : '<?xml version="1.0" encoding="utf-8"?>',
  149. namespace : 'xmlns:d="defiant-namespace"',
  150. tabsize : 4,
  151. render: function(template, data) {
  152. var processor = new XSLTProcessor(),
  153. span = document.createElement('span'),
  154. opt = {match: '/'},
  155. tmpltXpath,
  156. scripts,
  157. temp,
  158. sorter;
  159. // handle arguments
  160. switch (typeof(template)) {
  161. case 'object':
  162. this.extend(opt, template);
  163. if (!opt.data) opt.data = data;
  164. break;
  165. case 'string':
  166. opt.template = template;
  167. opt.data = data;
  168. break;
  169. default:
  170. throw 'error';
  171. }
  172. opt.data = JSON.toXML(opt.data);
  173. tmpltXpath = '//xsl:template[@name="'+ opt.template +'"]';
  174. if (!this.xsl_template) this.gatherTemplates();
  175. if (opt.sorter) {
  176. sorter = this.node.selectSingleNode(this.xsl_template, tmpltXpath +'//xsl:for-each//xsl:sort');
  177. if (sorter) {
  178. if (opt.sorter.order) sorter.setAttribute('order', opt.sorter.order);
  179. if (opt.sorter.select) sorter.setAttribute('select', opt.sorter.select);
  180. sorter.setAttribute('data-type', opt.sorter.type || 'text');
  181. }
  182. }
  183. temp = this.node.selectSingleNode(this.xsl_template, tmpltXpath);
  184. temp.setAttribute('match', opt.match);
  185. processor.importStylesheet(this.xsl_template);
  186. span.appendChild(processor.transformToFragment(opt.data, document));
  187. temp.removeAttribute('match');
  188. if (this.is_safari) {
  189. scripts = span.getElementsByTagName('script');
  190. for (var i=0, il=scripts.length; i<il; i++) scripts[i].defer = true;
  191. }
  192. return span.innerHTML;
  193. },
  194. gatherTemplates: function() {
  195. var scripts = document.getElementsByTagName('script'),
  196. str = '',
  197. i = 0,
  198. il = scripts.length;
  199. for (; i<il; i++) {
  200. if (scripts[i].type === 'defiant/xsl-template') str += scripts[i].innerHTML;
  201. }
  202. 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>');
  203. },
  204. getSnapshot: function(data, callback) {
  205. return JSON.toXML(data, callback || true);
  206. },
  207. xmlFromString: function(str) {
  208. var parser,
  209. doc;
  210. str = str.replace(/>\s{1,}</g, '><');
  211. if (str.trim().match(/<\?xml/) === null) {
  212. str = this.xml_decl + str;
  213. }
  214. if (this.is_ie) {
  215. doc = new ActiveXObject('Msxml2.DOMDocument');
  216. doc.loadXML(str);
  217. if (str.indexOf('xsl:stylesheet') === -1) {
  218. doc.setProperty('SelectionLanguage', 'XPath');
  219. }
  220. } else {
  221. parser = new DOMParser();
  222. doc = parser.parseFromString(str, 'text/xml');
  223. }
  224. return doc;
  225. },
  226. extend: function(src, dest) {
  227. for (var content in dest) {
  228. if (!src[content] || typeof(dest[content]) !== 'object') {
  229. src[content] = dest[content];
  230. } else {
  231. this.extend(src[content], dest[content]);
  232. }
  233. }
  234. return src;
  235. },
  236. node: {}
  237. };
  238. return Defiant;
  239. })(this);
  240. if (typeof(XSLTProcessor) === 'undefined') {
  241. // emulating XSLT Processor (enough to be used in defiant)
  242. var XSLTProcessor = function() {};
  243. XSLTProcessor.prototype = {
  244. importStylesheet: function(xsldoc) {
  245. this.xsldoc = xsldoc;
  246. },
  247. transformToFragment: function(data, doc) {
  248. var str = data.transformNode(this.xsldoc),
  249. span = document.createElement('span');
  250. span.innerHTML = str;
  251. return span;
  252. }
  253. };
  254. }
  255. // extending STRING
  256. if (!String.prototype.fill) {
  257. String.prototype.fill = function(i,c) {
  258. var str = this;
  259. c = c || ' ';
  260. for (; str.length<i; str+=c){}
  261. return str;
  262. };
  263. }
  264. if (!String.prototype.trim) {
  265. String.prototype.trim = function () {
  266. return this.replace(/^\s+|\s+$/gm, '');
  267. };
  268. }
  269. if (!String.prototype.xTransform) {
  270. String.prototype.xTransform = function () {
  271. var str = this;
  272. if (this.indexOf('translate(') === -1) {
  273. str = this.replace(/contains\(([^,]+),([^\\)]+)\)/, function(c,h,n) {
  274. var a = 'abcdefghijklmnopqrstuvwxyz',
  275. q = n.trim().slice(-1);
  276. return "contains(translate("+ h +", "+ q + a.toUpperCase() + q +", "+ q + a + q +"),"+ n.toLowerCase() +")";
  277. });
  278. }
  279. return str.toString();
  280. };
  281. }
  282. if (typeof(JSON) === 'undefined') {
  283. window.JSON = {
  284. parse: function (sJSON) { return eval("(" + sJSON + ")"); },
  285. stringify: function (vContent) {
  286. if (vContent instanceof Object) {
  287. var sOutput = "";
  288. if (vContent.constructor === Array) {
  289. for (var nId = 0; nId < vContent.length; sOutput += this.stringify(vContent[nId]) + ",", nId++);
  290. return "[" + sOutput.substr(0, sOutput.length - 1) + "]";
  291. }
  292. if (vContent.toString !== Object.prototype.toString) {
  293. return "\"" + vContent.toString().replace(/"/g, "\\$&") + "\"";
  294. }
  295. for (var sProp in vContent) {
  296. sOutput += "\"" + sProp.replace(/"/g, "\\$&") + "\":" + this.stringify(vContent[sProp]) + ",";
  297. }
  298. return "{" + sOutput.substr(0, sOutput.length - 1) + "}";
  299. }
  300. return typeof vContent === "string" ? "\"" + vContent.replace(/"/g, "\\$&") + "\"" : String(vContent);
  301. }
  302. };
  303. }
  304. /* jshint ignore:end */
  305. if (!JSON.toXML) {
  306. JSON.toXML = function(tree, callback) {
  307. 'use strict';
  308. var interpreter = {
  309. map : [],
  310. rx_validate_name : /^(?!xml)[a-z_][\w\d.:]*$/i,
  311. rx_node : /<(.+?)( .*?)>/,
  312. rx_constructor : /<(.+?)( d:contr=".*?")>/,
  313. rx_namespace : / xmlns\:d="defiant\-namespace"/,
  314. rx_data : /(<.+?>)(.*?)(<\/d:data>)/i,
  315. rx_function : /function (\w+)/i,
  316. namespace : 'xmlns:d="defiant-namespace"',
  317. to_xml_str: function(tree) {
  318. return {
  319. str: this.hash_to_xml(null, tree),
  320. map: this.map
  321. };
  322. },
  323. hash_to_xml: function(name, tree, array_child) {
  324. var is_array = tree.constructor === Array,
  325. elem = [],
  326. attr = [],
  327. key,
  328. val,
  329. val_is_array,
  330. type,
  331. is_attr,
  332. cname,
  333. constr,
  334. cnName,
  335. i;
  336. for (key in tree) {
  337. val = tree[key];
  338. if (val === null || val === undefined || val.toString() === 'NaN') val = null;
  339. is_attr = key.slice(0,1) === '@';
  340. cname = array_child ? name : key;
  341. if (cname == +cname && tree.constructor !== Object) cname = 'd:item';
  342. if (val === null) {
  343. constr = null;
  344. cnName = false;
  345. } else {
  346. constr = val.constructor;
  347. cnName = constr.toString().match(this.rx_function)[1];
  348. }
  349. if (is_attr) {
  350. attr.push( cname.slice(1) +'="'+ this.escape_xml(val) +'"' );
  351. if (cnName !== 'String') attr.push( 'd:'+ cname.slice(1) +'="'+ cnName +'"' );
  352. } else if (val === null) {
  353. elem.push( this.scalar_to_xml( cname, val ) );
  354. } else {
  355. switch (constr) {
  356. case Function:
  357. // if constructor is function, then it's not a JSON structure
  358. // throw ERROR ?
  359. break;
  360. case Object:
  361. elem.push( this.hash_to_xml( cname, val ) );
  362. break;
  363. case Array:
  364. if (key === cname) {
  365. val_is_array = val.constructor === Array;
  366. if (val_is_array) {
  367. i = val.length;
  368. while (i--) {
  369. if (val[i].constructor === Array) val_is_array = true;
  370. if (!val_is_array && val[i].constructor === Object) val_is_array = true;
  371. }
  372. }
  373. elem.push( this.scalar_to_xml( cname, val, val_is_array ) );
  374. break;
  375. }
  376. /* falls through */
  377. case String:
  378. if (typeof(val) === 'string') val = val.toString().replace(/\&/g, '&amp;');
  379. if (cname === '#text') {
  380. // prepare map
  381. this.map.push(tree);
  382. attr.push('d:mi="'+ this.map.length +'"');
  383. attr.push('d:constr="'+ cnName +'"');
  384. elem.push( this.escape_xml(val) );
  385. break;
  386. }
  387. /* falls through */
  388. case Number:
  389. case Boolean:
  390. if (cname === '#text' && cnName !== 'String') {
  391. // prepare map
  392. this.map.push(tree);
  393. attr.push('d:mi="'+ this.map.length +'"');
  394. attr.push('d:constr="'+ cnName +'"');
  395. elem.push( this.escape_xml(val) );
  396. break;
  397. }
  398. elem.push( this.scalar_to_xml( cname, val ) );
  399. break;
  400. }
  401. }
  402. }
  403. if (!name) {
  404. name = 'd:data';
  405. attr.push(this.namespace);
  406. if (is_array) attr.push('d:constr="Array"');
  407. }
  408. if (name.match(this.rx_validate_name) === null) {
  409. attr.push( 'd:name="'+ name +'"' );
  410. name = 'd:name';
  411. }
  412. if (array_child) return elem.join('');
  413. // prepare map
  414. this.map.push(tree);
  415. attr.push('d:mi="'+ this.map.length +'"');
  416. return '<'+ name + (attr.length ? ' '+ attr.join(' ') : '') + (elem.length ? '>'+ elem.join('') +'</'+ name +'>' : '/>' );
  417. },
  418. scalar_to_xml: function(name, val, override) {
  419. var attr = '',
  420. text,
  421. constr,
  422. cnName;
  423. // check whether the nodename is valid
  424. if (name.match(this.rx_validate_name) === null) {
  425. attr += ' d:name="'+ name +'"';
  426. name = 'd:name';
  427. override = false;
  428. }
  429. if (val === null || val.toString() === 'NaN') val = null;
  430. if (val === null) return '<'+ name +' d:constr="null"/>';
  431. if (val.length === 1 && val[0].constructor === Object) {
  432. text = this.hash_to_xml(false, val[0]);
  433. var a1 = text.match(this.rx_node),
  434. a2 = text.match(this.rx_constructor);
  435. a1 = (a1 !== null)? a1[2]
  436. .replace(this.rx_namespace, '')
  437. .replace(/>/, '')
  438. .replace(/"\/$/, '"') : '';
  439. a2 = (a2 !== null)? a2[2] : '';
  440. text = text.match(this.rx_data);
  441. text = (text !== null)? text[2] : '';
  442. return '<'+ name + a1 +' '+ a2 +' d:type="ArrayItem">'+ text +'</'+ name +'>';
  443. } else if (val.length === 0 && val.constructor === Array) {
  444. return '<'+ name +' d:constr="Array"/>';
  445. }
  446. // else
  447. if (override) {
  448. return this.hash_to_xml( name, val, true );
  449. }
  450. constr = val.constructor;
  451. cnName = constr.toString().match(this.rx_function)[1];
  452. text = (constr === Array) ? this.hash_to_xml( 'd:item', val, true )
  453. : this.escape_xml(val);
  454. attr += ' d:constr="'+ cnName +'"';
  455. // prepare map
  456. this.map.push(val);
  457. attr += ' d:mi="'+ this.map.length +'"';
  458. return (name === '#text') ? this.escape_xml(val) : '<'+ name + attr +'>'+ text +'</'+ name +'>';
  459. },
  460. escape_xml: function(text) {
  461. return String(text) .replace(/</g, '&lt;')
  462. .replace(/>/g, '&gt;')
  463. .replace(/"/g, '&quot;')
  464. .replace(/&nbsp;/g, '&#160;');
  465. }
  466. },
  467. processed,
  468. doc,
  469. task;
  470. // depending on request
  471. switch (typeof callback) {
  472. case 'function':
  473. // compile interpreter with 'x10.js'
  474. task = x10.compile(interpreter);
  475. // parse in a dedicated thread
  476. task.to_xml_str(tree, function(processed) {
  477. // snapshot distinctly improves performance
  478. callback({
  479. doc: Defiant.xmlFromString(processed.str),
  480. src: tree,
  481. map: processed.map
  482. });
  483. });
  484. return;
  485. case 'boolean':
  486. processed = interpreter.to_xml_str.call(interpreter, tree);
  487. // return snapshot
  488. return {
  489. doc: Defiant.xmlFromString(processed.str),
  490. src: tree,
  491. map: processed.map
  492. };
  493. default:
  494. processed = interpreter.to_xml_str.call(interpreter, tree);
  495. doc = Defiant.xmlFromString(processed.str);
  496. this.search.map = processed.map;
  497. return doc;
  498. }
  499. };
  500. }
  501. if (!JSON.search) {
  502. JSON.search = function(tree, xpath, single) {
  503. 'use strict';
  504. var isSnapshot = tree.doc && tree.doc.nodeType,
  505. doc = isSnapshot ? tree.doc : JSON.toXML(tree),
  506. map = isSnapshot ? tree.map : this.search.map,
  507. src = isSnapshot ? tree.src : tree,
  508. xres = Defiant.node[ single ? 'selectSingleNode' : 'selectNodes' ](doc, xpath.xTransform()),
  509. ret = [],
  510. mapIndex,
  511. i;
  512. if (single) xres = [xres];
  513. i = xres.length;
  514. while (i--) {
  515. switch(xres[i].nodeType) {
  516. case 2:
  517. case 3:
  518. ret.unshift( xres[i].nodeValue );
  519. break;
  520. default:
  521. mapIndex = +xres[i].getAttribute('d:mi');
  522. if (map[mapIndex-1]) ret.unshift( map[mapIndex-1] );
  523. }
  524. }
  525. // if environment = development, add search tracing
  526. if (Defiant.env === 'development') {
  527. this.trace = JSON.mtrace(src, ret, xres);
  528. }
  529. return ret;
  530. };
  531. }
  532. if (!JSON.mtrace) {
  533. JSON.mtrace = function(root, hits, xres) {
  534. 'use strict';
  535. var win = window,
  536. stringify = JSON.stringify,
  537. sroot = stringify( root, null, '\t' ).replace(/\t/g, ''),
  538. trace = [],
  539. i = 0,
  540. il = xres.length,
  541. od = il ? xres[i].ownerDocument.documentElement : false,
  542. map = this.search.map,
  543. hstr,
  544. cConstr,
  545. fIndex = 0,
  546. mIndex,
  547. lStart,
  548. lEnd;
  549. for (; i<il; i++) {
  550. switch (xres[i].nodeType) {
  551. case 2:
  552. cConstr = xres[i].ownerElement ? xres[i].ownerElement.getAttribute('d:'+ xres[i].nodeName) : 'String';
  553. hstr = '"@'+ xres[i].nodeName +'": '+ win[ cConstr ]( hits[i] );
  554. mIndex = sroot.indexOf(hstr);
  555. lEnd = 0;
  556. break;
  557. case 3:
  558. cConstr = xres[i].parentNode.getAttribute('d:constr');
  559. hstr = win[ cConstr ]( hits[i] );
  560. hstr = '"'+ xres[i].parentNode.nodeName +'": '+ (hstr === 'Number' ? hstr : '"'+ hstr +'"');
  561. mIndex = sroot.indexOf(hstr);
  562. lEnd = 0;
  563. break;
  564. default:
  565. if (xres[i] === od) continue;
  566. if (xres[i].getAttribute('d:constr') === 'String') {
  567. cConstr = xres[i].getAttribute('d:constr');
  568. hstr = win[ cConstr ]( hits[i] );
  569. hstr = '"'+ xres[i].nodeName +'": '+ (hstr === 'Number' ? hstr : '"'+ hstr +'"');
  570. mIndex = sroot.indexOf(hstr, fIndex);
  571. lEnd = 0;
  572. fIndex = mIndex + 1;
  573. } else {
  574. hstr = stringify( hits[i], null, '\t' ).replace(/\t/g, '');
  575. mIndex = sroot.indexOf(hstr);
  576. lEnd = hstr.match(/\n/g).length;
  577. }
  578. }
  579. lStart = sroot.substring(0,mIndex).match(/\n/g).length+1;
  580. trace.push([lStart, lEnd]);
  581. }
  582. return trace;
  583. };
  584. }
  585. Defiant.node.selectNodes = function(XNode, XPath) {
  586. if (XNode.evaluate) {
  587. var ns = XNode.createNSResolver(XNode.documentElement),
  588. qI = XNode.evaluate(XPath, XNode, ns, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null),
  589. res = [],
  590. i = 0,
  591. il = qI.snapshotLength;
  592. for (; i<il; i++) {
  593. res.push( qI.snapshotItem(i) );
  594. }
  595. return res;
  596. } else {
  597. return XNode.selectNodes(XPath);
  598. }
  599. };
  600. Defiant.node.selectSingleNode = function(XNode, XPath) {
  601. if (XNode.evaluate) {
  602. var xI = this.selectNodes(XNode, XPath);
  603. return (xI.length > 0)? xI[0] : null;
  604. } else {
  605. return XNode.selectSingleNode(XPath);
  606. }
  607. };
  608. Defiant.node.prettyPrint = function(node) {
  609. var root = Defiant,
  610. tabs = root.tabsize,
  611. decl = root.xml_decl.toLowerCase(),
  612. ser,
  613. xstr;
  614. if (root.is_ie) {
  615. xstr = node.xml;
  616. } else {
  617. ser = new XMLSerializer();
  618. xstr = ser.serializeToString(node);
  619. }
  620. if (root.env !== 'development') {
  621. // if environment is not development, remove defiant related info
  622. xstr = xstr.replace(/ \w+\:d=".*?"| d\:\w+=".*?"/g, '');
  623. }
  624. var str = xstr.trim().replace(/(>)\s*(<)(\/*)/g, '$1\n$2$3'),
  625. lines = str.split('\n'),
  626. indent = -1,
  627. i = 0,
  628. il = lines.length,
  629. start,
  630. end;
  631. for (; i<il; i++) {
  632. if (i === 0 && lines[i].toLowerCase() === decl) continue;
  633. start = lines[i].match(/<[A-Za-z_\:]+.*?>/g) !== null;
  634. //start = lines[i].match(/<[^\/]+>/g) !== null;
  635. end = lines[i].match(/<\/[\w\:]+>/g) !== null;
  636. if (lines[i].match(/<.*?\/>/g) !== null) start = end = true;
  637. if (start) indent++;
  638. lines[i] = String().fill(indent, '\t') + lines[i];
  639. if (start && end) indent--;
  640. if (!start && end) indent--;
  641. }
  642. return lines.join('\n').replace(/\t/g, String().fill(tabs, ' '));
  643. };
  644. Defiant.node.toJSON = function(xnode, stringify) {
  645. 'use strict';
  646. var interpret = function(leaf) {
  647. var obj = {},
  648. win = window,
  649. attr,
  650. type,
  651. item,
  652. cname,
  653. cConstr,
  654. cval,
  655. text,
  656. i, il, a;
  657. switch (leaf.nodeType) {
  658. case 1:
  659. cConstr = leaf.getAttribute('d:constr');
  660. if (cConstr === 'Array') obj = [];
  661. else if (cConstr === 'String' && leaf.textContent === '') obj = '';
  662. attr = leaf.attributes;
  663. i = 0;
  664. il = attr.length;
  665. for (; i<il; i++) {
  666. a = attr.item(i);
  667. if (a.nodeName.match(/\:d|d\:/g) !== null) continue;
  668. cConstr = leaf.getAttribute('d:'+ a.nodeName);
  669. if (cConstr && cConstr !== 'undefined') {
  670. if (a.nodeValue === 'null') cval = null;
  671. else cval = win[ cConstr ]( (a.nodeValue === 'false') ? '' : a.nodeValue );
  672. } else {
  673. cval = a.nodeValue;
  674. }
  675. obj['@'+ a.nodeName] = cval;
  676. }
  677. break;
  678. case 3:
  679. type = leaf.parentNode.getAttribute('d:type');
  680. cval = (type) ? win[ type ]( leaf.nodeValue === 'false' ? '' : leaf.nodeValue ) : leaf.nodeValue;
  681. obj = cval;
  682. break;
  683. }
  684. if (leaf.hasChildNodes()) {
  685. i = 0;
  686. il = leaf.childNodes.length;
  687. for(; i<il; i++) {
  688. item = leaf.childNodes.item(i);
  689. cname = item.nodeName;
  690. attr = leaf.attributes;
  691. if (cname === 'd:name') {
  692. cname = item.getAttribute('d:name');
  693. }
  694. if (cname === '#text') {
  695. cConstr = leaf.getAttribute('d:constr');
  696. if (cConstr === 'undefined') cConstr = undefined;
  697. text = item.textContent || item.text;
  698. cval = cConstr === 'Boolean' && text === 'false' ? '' : text;
  699. if (!cConstr && !attr.length) obj = cval;
  700. else if (cConstr && il === 1) {
  701. obj = win[cConstr](cval);
  702. } else if (!leaf.hasChildNodes()) {
  703. obj[cname] = (cConstr)? win[cConstr](cval) : cval;
  704. } else {
  705. if (attr.length < 3) obj = (cConstr)? win[cConstr](cval) : cval;
  706. else obj[cname] = (cConstr)? win[cConstr](cval) : cval;
  707. }
  708. } else {
  709. if (obj[cname]) {
  710. if (obj[cname].push) obj[cname].push( interpret(item) );
  711. else obj[cname] = [obj[cname], interpret(item)];
  712. continue;
  713. }
  714. cConstr = item.getAttribute('d:constr');
  715. switch (cConstr) {
  716. case 'null':
  717. if (obj.push) obj.push(null);
  718. else obj[cname] = null;
  719. break;
  720. case 'Array':
  721. //console.log( Defiant.node.prettyPrint(item) );
  722. if (item.parentNode.firstChild === item && cConstr === 'Array' && cname !== 'd:item') {
  723. if (cname === 'd:item' || cConstr === 'Array') {
  724. cval = interpret(item);
  725. obj[cname] = cval.length ? [cval] : cval;
  726. } else {
  727. obj[cname] = interpret(item);
  728. }
  729. }
  730. else if (obj.push) obj.push( interpret(item) );
  731. else obj[cname] = interpret(item);
  732. break;
  733. case 'String':
  734. case 'Number':
  735. case 'Boolean':
  736. text = item.textContent || item.text;
  737. cval = cConstr === 'Boolean' && text === 'false' ? '' : text;
  738. if (obj.push) obj.push( win[cConstr](cval) );
  739. else obj[cname] = interpret(item);
  740. break;
  741. default:
  742. if (obj.push) obj.push( interpret( item ) );
  743. else obj[cname] = interpret( item );
  744. }
  745. }
  746. }
  747. }
  748. if (leaf.nodeType === 1 && leaf.getAttribute('d:type') === 'ArrayItem') {
  749. obj = [obj];
  750. }
  751. return obj;
  752. },
  753. node = (xnode.nodeType === 9) ? xnode.documentElement : xnode,
  754. ret = interpret(node),
  755. rn = ret[node.nodeName];
  756. // exclude root, if "this" is root node
  757. if (node === node.ownerDocument.documentElement && rn && rn.constructor === Array) {
  758. ret = rn;
  759. }
  760. if (stringify && stringify.toString() === 'true') stringify = '\t';
  761. return stringify ? JSON.stringify(ret, null, stringify) : ret;
  762. };
  763. // check if jQuery is present
  764. if (typeof(jQuery) !== 'undefined') {
  765. (function ( $ ) {
  766. 'use strict';
  767. $.fn.defiant = function(template, xpath) {
  768. this.html( Defiant.render(template, xpath) );
  769. return this;
  770. };
  771. }(jQuery));
  772. }