PageRenderTime 48ms CodeModel.GetById 56ms RepoModel.GetById 32ms app.codeStats 1ms

/java/sample/authsub/web/javascript/gdata_condensed.js

http://gdata-java-client.googlecode.com/
JavaScript | 5235 lines | 3217 code | 726 blank | 1292 comment | 629 complexity | 8680b83de9fa03df2564f8ef06ae2ae5 MD5 | raw file
  1. /* Copyright (c) 2006 Google Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. /**
  16. * @fileoverview
  17. * This file contains some common helper functions
  18. */
  19. // Based on <http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/
  20. // core.html#ID-1950641247>
  21. var DOM_ELEMENT_NODE = 1;
  22. var DOM_ATTRIBUTE_NODE = 2;
  23. var DOM_TEXT_NODE = 3;
  24. var DOM_CDATA_SECTION_NODE = 4;
  25. var DOM_ENTITY_REFERENCE_NODE = 5;
  26. var DOM_ENTITY_NODE = 6;
  27. var DOM_PROCESSING_INSTRUCTION_NODE = 7;
  28. var DOM_COMMENT_NODE = 8;
  29. var DOM_DOCUMENT_NODE = 9;
  30. var DOM_DOCUMENT_TYPE_NODE = 10;
  31. var DOM_DOCUMENT_FRAGMENT_NODE = 11;
  32. var DOM_NOTATION_NODE = 12;
  33. //
  34. // regular expression for xsd date-time
  35. //
  36. var UTIL_PARSE_XMLDATE = /(\d{4})-(\d{2})-(\d{2})/;
  37. var UTIL_PARSE_XMLDATETIME =
  38. /(\d{4})-?(\d{2})-?(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))(Z)?(([+-])(\d{2}):(\d{2}))?/;
  39. function UTIL_inherits(subClass, superClass) {
  40. subClass.prototype = new superClass();
  41. subClass.prototype.Base = superClass.prototype;
  42. subClass.prototype.constructor = superClass.constructor;
  43. }
  44. /**
  45. * helper to detect if a variable is persistable
  46. * @param toTest the value to test
  47. * @return true if peristable
  48. */
  49. function UTIL_isPersistable(toTest) {
  50. if (typeof(toTest) == 'string') {
  51. if (toTest !== null && toTest.length > 0 ) {
  52. return true;
  53. }
  54. }
  55. return false;
  56. }
  57. /**
  58. * helper to convert a date to RFC3339 string presentation
  59. * @param date {Date} the date to convert
  60. * @return {string} date formatted according to RFC3339
  61. */
  62. function UTIL_dateToRFC3339(dateToConvert) {
  63. var strReturn = null;
  64. strReturn = dateToConvert.getUTCFullYear() + "-" +
  65. UTIL_pad((dateToConvert.getUTCMonth()+1).toString(), 2, "0") + "-" +
  66. UTIL_pad((dateToConvert.getUTCDate()).toString(), 2, "0");
  67. if (dateToConvert.getTime() > 0) {
  68. strReturn += "T" +
  69. UTIL_pad(dateToConvert.getUTCHours().toString(), 2, "0") + ":" +
  70. UTIL_pad(dateToConvert.getUTCMinutes().toString(), 2, "0") + ":" +
  71. UTIL_pad(dateToConvert.getUTCSeconds().toString(), 2, "0");
  72. // convert to hours
  73. var timediff = dateToConvert.getTimezoneOffset();
  74. timediff /= 60;
  75. strReturn += timediff > 0 ? "+" : "-";
  76. strReturn += UTIL_pad(Math.abs(timediff).toFixed(0), 2, "0") + ":00";
  77. }
  78. return strReturn;
  79. }
  80. /**
  81. * helper to convert a string in RFC3339 presentation to a date
  82. * @param dateString the string date value to convert
  83. * @return the date
  84. */
  85. function UTIL_parseXmlDateTime(dateString) {
  86. var d = null;
  87. var dateParts = dateString.match(UTIL_PARSE_XMLDATETIME);
  88. if (dateParts == null) {
  89. //
  90. // if dateTime didn't parse, try date
  91. //
  92. return UTIL_parseXmlDate(dateString);
  93. } else {
  94. d = new Date();
  95. var year = parseInt(dateParts[1],10);
  96. var month = parseInt(dateParts[2],10);
  97. var day = parseInt(dateParts[3],10);
  98. var hour = parseInt(dateParts[4],10);
  99. var min = parseInt(dateParts[5],10);
  100. var sec = parseInt(dateParts[6],10);
  101. if ((dateParts[8] || dateParts[9]) && !(hour==0 && min==0 && sec==0)) {
  102. // utc mode
  103. d.setUTCFullYear(year);
  104. d.setUTCMonth(month-1);
  105. d.setUTCDate(day);
  106. d.setUTCHours(hour);
  107. d.setUTCMinutes(min);
  108. d.setUTCSeconds(sec);
  109. d.setUTCMilliseconds(0);
  110. if (dateParts[8] === 'Z') {
  111. // so the time is in UTC
  112. LOG_TRACE(dateParts[8], "UTIL_parseXmlDateTime", dateString);
  113. } else {
  114. // should be an offset now
  115. var timeOffset = 0;
  116. if (dateParts[10]) {
  117. timeOffset = Number(dateParts[11]) * 60;
  118. timeOffset += Number(dateParts[12]);
  119. timeOffset *= ((dateParts[10] === '-') ? 1 : -1);
  120. }
  121. timeOffset *= 60 * 1000;
  122. d = new Date(Number(d) + timeOffset);
  123. }
  124. //
  125. // BUGBUG - apply +/- bias from part 8
  126. //
  127. } else {
  128. d.setFullYear(year);
  129. d.setMonth(month-1);
  130. d.setDate(day);
  131. d.setHours(hour);
  132. d.setMinutes(min);
  133. d.setSeconds(sec);
  134. d.setMilliseconds(0);
  135. }
  136. }
  137. LOG_TRACE(dateString + "----" + d, "UTIL_parseXmlDateTime", dateString);
  138. return d;
  139. }
  140. /**
  141. * helper to convert a string in RFC3339 presentation to a date
  142. * @param dateString the string date value to convert, no timezone
  143. * @return the date
  144. */
  145. function UTIL_parseXmlDate(dateString) {
  146. var d;
  147. var dateParts = dateString.match(UTIL_PARSE_XMLDATE);
  148. if (dateParts != null) {
  149. d = new Date();
  150. d.setHours(0);
  151. d.setMinutes(0);
  152. d.setSeconds(0);
  153. d.setMilliseconds(0);
  154. var year = parseInt(dateParts[1],10);
  155. var month = parseInt(dateParts[2],10);
  156. var day = parseInt(dateParts[3],10);
  157. d.setFullYear(year);
  158. d.setMonth(month-1);
  159. d.setDate(day);
  160. }
  161. return d;
  162. }
  163. /**
  164. * helper to pad a string with a given char
  165. * @param str the string to pad
  166. * @param length the length to pad to
  167. * @param chr the char to use for padding
  168. * @return the padded string
  169. */
  170. function UTIL_pad(str, length, chr) {
  171. while (length > str.length) {
  172. str = chr + str;
  173. }
  174. return str;
  175. }
  176. /**
  177. * helper to encode a string that is passed in to URL encoding
  178. * this is used for requests to a webserver, so do not use
  179. * encodeUriComponent here
  180. * @param sStr the stirng to encode
  181. * @return the encoded string
  182. */
  183. function UTIL_urlEncodeString(sStr) {
  184. return escape(sStr).
  185. replace(/\+/g, '%2B').
  186. replace(/\"/g,'%22').
  187. replace(/\'/g, '%27').
  188. replace(/\//g,'%2F');
  189. }
  190. /**
  191. * helper to attach an event to a target object
  192. * @param target the object to attach the event to
  193. * @param type the event type
  194. * @param callback the event callback
  195. */
  196. function UTIL_domEventHandler(target, type, callback) {
  197. if (target.addEventListener) {
  198. target.addEventListener(type, callback, false);
  199. } else {
  200. target.attachEvent("on" + type, callback);
  201. }
  202. }
  203. /**
  204. * helper to create a DIV element, currently used for debugging
  205. * @param opt_text the text in the div
  206. * @param opt_className the css class to use
  207. */
  208. function UTIL_domCreateDiv(opt_text, opt_className) {
  209. var el = document.createElement("div");
  210. if (opt_text) {
  211. UTIL_domAppendChild(el, document.createTextNode(opt_text));
  212. }
  213. if (opt_className) {
  214. el.className = opt_className;
  215. }
  216. return el;
  217. }
  218. /**
  219. * helper to append a child in the dom
  220. * @param parent the node to append to
  221. * @param child the node to append
  222. */
  223. function UTIL_domAppendChild(parent, child) {
  224. try {
  225. parent.appendChild(child);
  226. } catch (e) {
  227. LOG_ERROR("UTIL_domAppendChild: " + e);
  228. }
  229. return child;
  230. }
  231. /**
  232. * Applies the given function to each element of the array, preserving
  233. * this, and passing the index.
  234. */
  235. function UTIL_mapExec(array, func) {
  236. for (var i = 0; i < array.length; ++i) {
  237. func.call(this, array[i], i);
  238. }
  239. }
  240. /**
  241. * Returns an array that contains the return value of the given
  242. * function applied to every element of the input array.
  243. */
  244. function UTIL_mapExpr(array, func) {
  245. var ret = [];
  246. for (var i = 0; i < array.length; ++i) {
  247. ret.push(func(array[i]));
  248. }
  249. return ret;
  250. }
  251. /**
  252. * Reverses the given array in place.
  253. */
  254. function UTIL_reverseInplace(array) {
  255. for (var i = 0; i < array.length / 2; ++i) {
  256. var h = array[i];
  257. var ii = array.length - i - 1;
  258. array[i] = array[ii];
  259. array[ii] = h;
  260. }
  261. }
  262. /**
  263. * Shallow-copies an array.
  264. */
  265. function UTIL_copyArray(dst, src) {
  266. for (var i = 0; i < src.length; ++i) {
  267. dst.push(src[i]);
  268. }
  269. }
  270. /**
  271. * Returns the text value of a node; for nodes without children this
  272. * is the nodeValue, for nodes with children this is the concatenation
  273. * of the value of all children.
  274. */
  275. function UTIL_xmlValue(node) {
  276. if (!node) {
  277. return '';
  278. }
  279. var ret = '';
  280. if (node.nodeType == DOM_TEXT_NODE ||
  281. node.nodeType == DOM_CDATA_SECTION_NODE ||
  282. node.nodeType == DOM_ATTRIBUTE_NODE) {
  283. ret += node.nodeValue;
  284. } else if (node.nodeType == DOM_ELEMENT_NODE ||
  285. node.nodeType == DOM_DOCUMENT_NODE ||
  286. node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
  287. for (var i = 0; i < node.childNodes.length; ++i) {
  288. ret += arguments.callee(node.childNodes[i]);
  289. }
  290. }
  291. return ret;
  292. }
  293. /**
  294. * Returns the representation of a node as XML text.
  295. */
  296. function UTIL_xmlText(node, opt_cdata) {
  297. var buf = [];
  298. xmlTextR(node, buf, opt_cdata);
  299. return buf.join('');
  300. }
  301. /**
  302. * worker function for UTIL_xmlText()
  303. */
  304. function xmlTextR(node, buf, cdata) {
  305. if (node.nodeType == DOM_TEXT_NODE) {
  306. buf.push(xmlEscapeText(node.nodeValue));
  307. } else if (node.nodeType == DOM_CDATA_SECTION_NODE) {
  308. if (cdata) {
  309. buf.push(node.nodeValue);
  310. } else {
  311. buf.push('<![CDATA[' + node.nodeValue + ']]>');
  312. }
  313. } else if (node.nodeType == DOM_COMMENT_NODE) {
  314. buf.push('<!--' + node.nodeValue + '-->');
  315. } else if (node.nodeType == DOM_ELEMENT_NODE) {
  316. buf.push('<' + xmlFullNodeName(node));
  317. for (var i = 0; i < node.attributes.length; ++i) {
  318. var a = node.attributes[i];
  319. if (a && a.nodeName && a.nodeValue) {
  320. buf.push(' ' + xmlFullNodeName(a) + '="' +
  321. xmlEscapeAttr(a.nodeValue) + '"');
  322. }
  323. }
  324. if (node.childNodes.length === 0) {
  325. buf.push('/>');
  326. } else {
  327. buf.push('>');
  328. for (i = 0; i < node.childNodes.length; ++i) {
  329. arguments.callee(node.childNodes[i], buf, cdata);
  330. }
  331. buf.push('</' + xmlFullNodeName(node) + '>');
  332. }
  333. } else if (node.nodeType == DOM_DOCUMENT_NODE ||
  334. node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
  335. for (i = 0; i < node.childNodes.length; ++i) {
  336. arguments.callee(node.childNodes[i], buf, cdata);
  337. }
  338. }
  339. }
  340. /**
  341. * helper for xmlTextR
  342. */
  343. function xmlFullNodeName(n) {
  344. if (n.prefix && n.nodeName.indexOf(n.prefix + ':') !== 0) {
  345. return n.prefix + ':' + n.nodeName;
  346. } else {
  347. return n.nodeName;
  348. }
  349. }
  350. /**
  351. * Escape XML special markup chracters: tag delimiter < > and entity
  352. * reference start delimiter &. The escaped string can be used in XML
  353. * text portions (i.e. between tags).
  354. */
  355. function xmlEscapeText(s) {
  356. return ('' + s).replace(/&/g, '&amp;').replace(/</, '&lt;').
  357. replace(/>/, '&gt;');
  358. }
  359. /**
  360. * Escape XML special markup characters: tag delimiter < > entity
  361. * reference start delimiter & and quotes ". The escaped string can be
  362. * used in double quoted XML attribute value portions (i.e. in
  363. * attributes within start tags).
  364. */
  365. function xmlEscapeAttr(s) {
  366. return xmlEscapeText(s).replace(/\"/g, '&quot;');
  367. }
  368. /**
  369. * Escape markup in XML text, but don't touch entity references. The
  370. * escaped string can be used as XML text (i.e. between tags).
  371. */
  372. function xmlEscapeTags(s) {
  373. return s.replace(/</, '&lt;').replace(/>/g, '&gt;');
  374. }
  375. // remove this if you merge
  376. if (window.GD_Loader) {
  377. // continue loading
  378. window.GD_Loader();
  379. }
  380. // end
  381. /* Copyright (c) 2006 Google Inc.
  382. *
  383. * Licensed under the Apache License, Version 2.0 (the "License");
  384. * you may not use this file except in compliance with the License.
  385. * You may obtain a copy of the License at
  386. *
  387. * http://www.apache.org/licenses/LICENSE-2.0
  388. *
  389. * Unless required by applicable law or agreed to in writing, software
  390. * distributed under the License is distributed on an "AS IS" BASIS,
  391. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  392. * See the License for the specific language governing permissions and
  393. * limitations under the License.
  394. */
  395. /**
  396. * @fileoverview
  397. * BrowserDetector (object)
  398. *
  399. * A class for detecting version 5 browsers by the Javascript objects
  400. * they support and not their user agent strings (which can be
  401. * spoofed).
  402. *
  403. * Warning: Though slow to develop, browsers may begin to add
  404. * DOM support in later versions which might require changes to this
  405. * file.
  406. *
  407. * Typical usage:
  408. * Detect = new BrowserDetector();
  409. * if (Detect.IE()) //IE-only code...
  410. */
  411. /**
  412. * @constructor
  413. */
  414. function BrowserDetector() {
  415. //IE 4+
  416. this.IE = function() {
  417. try {
  418. return this.Run(document.all && window.ActiveXObject) !==false;
  419. } catch(e) {
  420. /* IE 5.01 doesn't support the 'contains' object and
  421. fails the first test */
  422. if (document.all) {
  423. return true;
  424. }
  425. return false;
  426. }
  427. };
  428. //IE 5.5+
  429. this.IE_5_5_newer = function(){
  430. try {
  431. return this.Run(this.IE() && Array.prototype.pop)!==false;
  432. } catch(e) {return false;}
  433. };
  434. //IE 5, Macintosh
  435. this.IE_5_Mac = function(){
  436. try {
  437. return(true === undefined);
  438. } catch(e) {
  439. return(
  440. document.all &&
  441. document.getElementById &&
  442. !document.mimeType &&
  443. !window.opera)!==false;
  444. }
  445. };
  446. //Opera 7+
  447. this.OPERA = function(){
  448. try {
  449. return this.Run(document.all && document.contains)!==false;
  450. } catch(e) {return false;}
  451. };
  452. //Gecko, actually Mozilla 1.2+
  453. this.MOZILLA = function() {
  454. try {
  455. return this.Run(
  456. document.implementation &&
  457. document.implementation.createDocument &&
  458. document.evaluate &&
  459. !document.contains )!==false;
  460. } catch(e) {return false;}
  461. };
  462. //Safari
  463. this.SAFARI = function(){
  464. try {
  465. return this.Run(
  466. document.implementation &&
  467. document.implementation.createDocument &&
  468. document.contains)!==false;
  469. } catch(e) {return false;}
  470. };
  471. //Any browser which supports the W3C DOM
  472. this.DOM = function() {
  473. return(document.getElementById);
  474. };
  475. this.Run = function(test) {
  476. if (test===undefined) {
  477. return false;
  478. } else {
  479. return test;
  480. }
  481. };
  482. this.XPathSupport = function() {
  483. return this.IE_5_5_newer() || document.implementation.hasFeature('XPath','3.0');
  484. };
  485. }
  486. var DOM = (document.getElementById);
  487. var Detect;
  488. if (DOM) {
  489. Detect = new BrowserDetector();
  490. }
  491. // remove this if you merge
  492. if (window.GD_Loader) {
  493. // continue loading
  494. window.GD_Loader();
  495. }
  496. // end
  497. /* Copyright (c) 2006 Google Inc.
  498. *
  499. * Licensed under the Apache License, Version 2.0 (the "License");
  500. * you may not use this file except in compliance with the License.
  501. * You may obtain a copy of the License at
  502. *
  503. * http://www.apache.org/licenses/LICENSE-2.0
  504. *
  505. * Unless required by applicable law or agreed to in writing, software
  506. * distributed under the License is distributed on an "AS IS" BASIS,
  507. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  508. * See the License for the specific language governing permissions and
  509. * limitations under the License.
  510. */
  511. //------------------------------------------------------------------------------
  512. function DumpError(msg) {}
  513. function DumpException(msg) {}
  514. //------------------------------------------------------------------------------
  515. // Logging
  516. //------------------------------------------------------------------------------
  517. function LOG_LEVEL() {}
  518. LOG_LEVEL.NONE = 0;
  519. LOG_LEVEL.ERROR = 1;
  520. LOG_LEVEL.DEBUG = 2;
  521. LOG_LEVEL.TRACE = 3;
  522. LOG_LEVEL.ALL = 4;
  523. function DEBUG_ENABLE_CROSSSITE() {}
  524. function DEBUG_CROSSSITE() { return false;}
  525. //------------------------------------------------------------------------------
  526. // Inline log functions
  527. //------------------------------------------------------------------------------
  528. function LOG_CLASS(classDef, name) {}
  529. function LOG(msg, opt_level) {}
  530. function LOG_DEBUG(msg, opt_show) {}
  531. function LOG_ERROR(msg) {}
  532. function LOG_EVENT(msg) {}
  533. function LOG_TRACE(obj, fn, args) {}
  534. function LOG_SEPARATOR(opt_text) {}
  535. function LOG_TIMER_START(name) {}
  536. function LOG_TIMER_STOP(name) {}
  537. function LOG_XML_PRINT(doc, opt_level) {}
  538. function LOG_DUMP_FEED(feed) {}
  539. function VARJ_INC(name) {}
  540. //------------------------------------------------------------------------------
  541. // Inline debug functions
  542. //------------------------------------------------------------------------------
  543. function DBG_ALERT(msg, opt_prefix) {}
  544. function DBG_ASSERT(cond, opt_msg) {}
  545. function DBG_ABSTRACT(obj, fn, args) {}
  546. function DBG_NOT_IMPLEMENTED(str) {}
  547. // remove this if you merge
  548. if (window.GD_Loader) {
  549. // continue loading
  550. window.GD_Loader();
  551. }
  552. // end
  553. /* Copyright (c) 2006 Google Inc.
  554. *
  555. * Licensed under the Apache License, Version 2.0 (the "License");
  556. * you may not use this file except in compliance with the License.
  557. * You may obtain a copy of the License at
  558. *
  559. * http://www.apache.org/licenses/LICENSE-2.0
  560. *
  561. * Unless required by applicable law or agreed to in writing, software
  562. * distributed under the License is distributed on an "AS IS" BASIS,
  563. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  564. * See the License for the specific language governing permissions and
  565. * limitations under the License.
  566. */
  567. /** @filecomment
  568. * transport.js provides the basic server related operations:
  569. * get a feed, create an item, update & delete an item
  570. * we abstract this out, so that a unit test framework based on FILE transport
  571. * can be developed later
  572. */
  573. /** returns the appropriate transportobject based on
  574. * the passed in type
  575. */
  576. function GD_getTransport(transportType) {
  577. switch (transportType) {
  578. case GD_Transport.TRANSPORT_FILE:
  579. // File Transport object
  580. DBG_ALERT("File transport NYI");
  581. return null;
  582. case GD_Transport.TRANSPORT_HTTP:
  583. return new GD_HttpTransport();
  584. case GD_Transport.TRANSPORT_GOOGLECALENDAR:
  585. return new GD_GaiaTransport("cl");
  586. default:
  587. return new GD_GaiaTransport("cl");
  588. }
  589. }
  590. /**
  591. * base transport, for the common things in life
  592. */
  593. function GD_Transport() {
  594. this.userName_ = null;
  595. this.pwd_ = null;
  596. }
  597. GD_Transport.TRANSPORT_FILE = 1;
  598. GD_Transport.TRANSPORT_HTTP = 2;
  599. GD_Transport.TRANSPORT_GOOGLECALENDAR = 3;
  600. GD_Transport.AUTH_HANDLER = "https://www.google.com/accounts/ClientLogin";
  601. GD_Transport.AUTH_HEADER = "Authorization";
  602. GD_Transport.AUTH_HEADERVALUE = "GoogleLogin auth=";
  603. GD_Transport.AUTH_TOKEN = "Auth";
  604. GD_Transport.AUTH_SERVICE = "service=";
  605. GD_Transport.AUTH_SOURCE = "source=desktop_Feed_reader";
  606. GD_Transport.AUTH_USER = "Email=";
  607. GD_Transport.AUTH_PWD = "Passwd=";
  608. GD_Transport.METHOD_OVERRIDE = "X-HTTP-Method-Override";
  609. GD_Transport.NOREDIRECT_HEADER = "X-If-No-Redirect";
  610. GD_Transport.SETCOOKIE_HEADER = "Set-Cookie";
  611. GD_Transport.COOKIE_HEADER = "Cookie";
  612. /**
  613. * set method GD_Transport.userName_
  614. */
  615. GD_Transport.prototype.setUserName = function(stringValue) {
  616. this.userName_ = stringValue;
  617. };
  618. /**
  619. * get method GD_Transport.userName_
  620. */
  621. GD_Transport.prototype.getUserName = function() {
  622. return this.userName_;
  623. };
  624. /**
  625. * set method GD_Transport.pwd_
  626. */
  627. GD_Transport.prototype.setPassword = function(stringValue) {
  628. this.pwd_ = stringValue;
  629. };
  630. /**
  631. * get method GD_Transport.pwd_
  632. */
  633. GD_Transport.prototype.getPassword = function() {
  634. return this.pwd_;
  635. };
  636. /**
  637. * prototype for the GetDocument method
  638. * @param docUri {string} the URI of the XML document to load
  639. * @param opt_callback {function} takes one argument,
  640. * the loaded XMLDocument for the documentUri
  641. */
  642. GD_Transport.prototype.GetDomDocument = function(docUri, opt_callback) {};
  643. /**
  644. * prototype for the InsertEntry method
  645. */
  646. GD_Transport.prototype.InsertEntry = function(feed, entry) {
  647. };
  648. /**
  649. * prototype for the UpdateEntry method
  650. */
  651. GD_Transport.prototype.UpdateEntry = function(entry) {
  652. };
  653. /**
  654. * prototype for the DeleteEntry method
  655. */
  656. GD_Transport.prototype.DeleteEntry = function(entry) {
  657. };
  658. /**
  659. * file transport section, prototype only
  660. */
  661. function GD_FileTransport() {
  662. GD_Transport.call(this);
  663. }
  664. UTIL_inherits(GD_FileTransport, GD_Transport);
  665. GD_FileTransport.prototype.toString = function(opt_verbose) {
  666. return "GD_FileTransport()";
  667. };
  668. /**
  669. * http transport
  670. */
  671. function GD_HttpTransport() {
  672. GD_Transport.call(this);
  673. this.shardingCookie_ = null;
  674. }
  675. UTIL_inherits(GD_HttpTransport, GD_Transport);
  676. GD_HttpTransport.prototype.toString = function(opt_verbose) {
  677. return "GD_HttpTransport()";
  678. };
  679. /**
  680. * set method GD_HttpTransport.shardingCookie_
  681. */
  682. GD_HttpTransport.prototype.setCookie = function(str) {
  683. this.shardingCookie_ = str;
  684. };
  685. /**
  686. * get method GD_HttpTransport.shardingCookie_
  687. */
  688. GD_HttpTransport.prototype.getCookie = function() {
  689. return this.shardingCookie_;
  690. };
  691. /**
  692. * gets the DomDocument for the HTTP transport
  693. * @param docUri {string} the URI of the XML document to load
  694. * @param opt_callback {function} takes one argument,
  695. * the loaded XMLDocument for the documentUri
  696. */
  697. GD_HttpTransport.prototype.GetDomDocument = function(docUri, opt_callback) {
  698. LOG_TRACE(docUri, "GD_HttpTransport => GetDomDocument", null);
  699. var callback = (opt_callback)
  700. ? function(request) { opt_callback.call(null, request.responseXML); }
  701. : undefined;
  702. var params = new XmlHttpParameters("GET", docUri, 200, null, callback);
  703. this.CallXmlHttp(params);
  704. };
  705. /**
  706. * DeleteEntry for the HTTP transport,
  707. * gets the target URI from the passed in entry
  708. */
  709. GD_HttpTransport.prototype.DeleteEntry = function(gdEntry, opt_callBack) {
  710. var deleteUri = gdEntry.getEditUri();
  711. LOG_TRACE(deleteUri, "GD_HttpTransport => DeleteEntry", null);
  712. var params = new XmlHttpParameters("DELETE", deleteUri, 204,
  713. null, opt_callBack);
  714. this.CallXmlHttp(params);
  715. };
  716. /**
  717. * UpdateEntry for the HTTP transport,
  718. * gets the target URI from the passed in entry
  719. */
  720. GD_HttpTransport.prototype.UpdateEntry = function(gdEntry, opt_callBack) {
  721. var updateUri = gdEntry.getEditUri();
  722. LOG_TRACE(updateUri, "GD_HttpTransport => UpdateEntry", null);
  723. var params = new XmlHttpParameters("PUT", updateUri, -1,
  724. gdEntry.save(), opt_callBack);
  725. this.CallXmlHttp(params);
  726. };
  727. /**
  728. * InsertEntry for the HTTP transport,
  729. * sets the target URI from the passed in feed
  730. */
  731. GD_HttpTransport.prototype.InsertEntry = function(ofFeed, gdEntry,
  732. opt_callBack) {
  733. var insertUri = ofFeed.getPostUri();
  734. if (insertUri != null) {
  735. LOG_TRACE(insertUri, "GD_HttpTransport => InsertEntry", null);
  736. var params = new XmlHttpParameters("POST", insertUri, -1,
  737. gdEntry.save(), opt_callBack);
  738. this.CallXmlHttp(params);
  739. }
  740. else {
  741. throw "this is a read only feed";
  742. }
  743. };
  744. /**
  745. * SetHeaders for the HTTP transport,
  746. * just sets the accept content type here
  747. */
  748. GD_HttpTransport.prototype.SetHeaders = function(xmlHttpRequestObject) {
  749. LOG_TRACE("Entering...", "HTTP.SetHeaders", null);
  750. xmlHttpRequestObject.setRequestHeader("Accept", "text/xml");
  751. xmlHttpRequestObject.setRequestHeader("content-type", "application/atom+xml");
  752. xmlHttpRequestObject.setRequestHeader("Cache-Control", "no-cache");
  753. };
  754. /**
  755. * @param params {XmlHttpParameters}
  756. */
  757. GD_HttpTransport.prototype.PrepareAuthentication = function(params) {
  758. this.DoRequest(params);
  759. };
  760. /**
  761. * Bundle a collection of parameters that are used when making an
  762. * XMLHttpRequest. The state of a transaction should be stored in
  763. * the XmlHttpParameters rather than the GD_HttpTransport, as the
  764. * GD_HttpTransport is likely a singleton, so requests it facilitates
  765. * should not share state.
  766. *
  767. * @param verb {string} must be "GET" or "DELETE" or "PUT" or "POST"
  768. * @param uri {string}
  769. * @param allowedStatus {int}
  770. * @param payload {string?}
  771. * @param opt_callback {function} will receive one argument,
  772. * the loaded XMLHttpRequest, and its return value will be ignored
  773. * @param opt_retrying {boolean} indicates whether this is a retry attempt;
  774. * defaults to false
  775. */
  776. function XmlHttpParameters(verb, uri, allowedStatus, payload,
  777. opt_callback, opt_retrying) {
  778. this.verb = verb;
  779. this.uri = uri;
  780. this.allowedStatus = allowedStatus;
  781. this.payload = payload;
  782. this.callback = opt_callback;
  783. this.retrying = !!opt_retrying;
  784. }
  785. /**
  786. * method do create the actual request. This one just calls PrepareAuth
  787. * which will continue calling DoRequest after auth is done
  788. * @param params {XmlHttpParameters}
  789. */
  790. GD_HttpTransport.prototype.CallXmlHttp = function(params) {
  791. try {
  792. LOG_TRACE("Entering...", "CallXmlHttp", null);
  793. this.PrepareAuthentication(params);
  794. } catch (e) {
  795. DBG_ALERT(e.toString());
  796. DBG_ALERT('The exception happened in CallXmlHttp for ' + params.verb +
  797. ' at URI: ' + params.uri);
  798. throw e;
  799. }
  800. LOG_TRACE("Exiting...", "CallXmlHttp", null);
  801. };
  802. /**
  803. * @param params {XmlHttpParameters}
  804. */
  805. GD_HttpTransport.prototype.DoRequest = function(params) {
  806. try {
  807. LOG_TRACE("Entering...", "CallXmlHttp", null);
  808. var xmlRequest = XMLX_getHttpRequest();
  809. var fAsync = false;
  810. if (params.callback) {
  811. xmlRequest.onreadystatechange =
  812. function() { GD_HandleAsyncData(xmlRequest, params); };
  813. fAsync = true;
  814. }
  815. if (DEBUG_CROSSSITE()) {
  816. netscape.security.PrivilegeManager.
  817. enablePrivilege("UniversalBrowserRead");
  818. netscape.security.PrivilegeManager.
  819. enablePrivilege("UniversalBrowserWrite");
  820. }
  821. LOG_TRACE("Using default XlmRequest open", "CallXmlHttp", null);
  822. xmlRequest.open(params.verb, params.uri, fAsync);
  823. // thwart the cache
  824. if (params.verb == "GET") {
  825. xmlRequest.setRequestHeader("If-Modified-Since",
  826. "Sat, 1 Jan 2000 00:00:00 GMT");
  827. }
  828. this.SetHeaders(xmlRequest);
  829. if (xmlRequest.overrideMimeType) {
  830. //only need to do this for mozilla based browsers. IE would throw
  831. xmlRequest.overrideMimeType('text/xml');
  832. }
  833. LOG_TRACE("Sending..." + xmlRequest, "CallXmlHttp", null);
  834. xmlRequest.send(params.payload);
  835. LOG_TRACE("Returned, readyState:" + xmlRequest.readyState,
  836. "CallXmlHttp", null);
  837. if (fAsync === false) {
  838. this.HandleOnXmlRequestFinished(xmlRequest, params);
  839. }
  840. } catch (e) {
  841. DBG_ALERT(e.toString());
  842. DBG_ALERT('The exception happened in CallXmlHttp for ' + params.verb_ +
  843. ' at URI: ' + params.uri_);
  844. throw e;
  845. }
  846. LOG_TRACE("Exiting...", "CallXmlHttp", null);
  847. };
  848. /**
  849. * async callback method
  850. */
  851. function GD_HandleAsyncData(request, params) {
  852. if (request.readyState != 4) {
  853. return;
  854. }
  855. LOG_TRACE("Callback done...", "GD_HandleAsyncData", null);
  856. GoogleDataFactory.getTransport().
  857. HandleOnXmlRequestFinished(request, params);
  858. }
  859. /**
  860. * handling of errors/results of the xmlhttprequest
  861. */
  862. GD_HttpTransport.prototype.HandleOnXmlRequestFinished =
  863. function(xmlRequest, params) {
  864. switch (xmlRequest.readyState) {
  865. case 1:
  866. case 2:
  867. case 3:
  868. DBG_ALERT('Bad Ready State: ' + xmlRequest.status);
  869. return false;
  870. case 4:
  871. if (xmlRequest.status > 299 ||
  872. (params.allowedStatus !== -1 &&
  873. xmlRequest.status != params.allowedStatus)) {
  874. if (params.retrying !== true) {
  875. /*
  876. * this might be a redirect/sharding issue, redo the whole thing
  877. * in javascript you can not handle the 302 return yourself,
  878. * so what happens is we get our auth token, we try to go to the
  879. * server, we get a redirect back now the redirect does not redo
  880. * the "postdata" nor the custom auth header, so it fails with
  881. * 401. Hence, redo it. But only once.
  882. */
  883. if (xmlRequest.status === 401) {
  884. params = true;
  885. this.CallXmlHttp(params);
  886. } else if (xmlRequest.status === 412) {
  887. // the server want's to redirect us to a shard !!
  888. // get the cookie and save it, then retry
  889. var cookie = xmlRequest.getResponseHeader(
  890. GD_Transport.SETCOOKIE_HEADER);
  891. DBG_ASSERT(cookie != null,
  892. "we got no cookie back from the redirection server");
  893. if (cookie != null) {
  894. this.setCookie(cookie);
  895. }
  896. // now retry
  897. params.retrying = true;
  898. this.CallXmlHttp(params);
  899. }
  900. }
  901. DBG_ALERT('Request resulted in a bad status code for the operation: '
  902. + xmlRequest.status + " " + params.allowedStatus);
  903. }
  904. break;
  905. }
  906. if (params.callback) {
  907. LOG_TRACE("calling callback to feed code", "HandleOnXmlRequestFinished",
  908. null);
  909. params.callback.call(null, xmlRequest);
  910. }
  911. }
  912. /**
  913. * Gaia authentication transport.
  914. */
  915. function GD_GaiaTransport(serviceName) {
  916. GD_HttpTransport.call(this);
  917. this.serviceName_ = serviceName;
  918. this.gaiaToken_ = null;
  919. }
  920. UTIL_inherits(GD_GaiaTransport, GD_HttpTransport);
  921. GD_GaiaTransport.prototype.toString = function(opt_verbose) {
  922. return "GD_GaiaTransport() for " + this.serviceName_;
  923. };
  924. /**
  925. * set method GD_GaiaTransport.gaiaToken_
  926. */
  927. GD_GaiaTransport.prototype.setToken = function(string) {
  928. this.gaiaToken_ = string;
  929. };
  930. /**
  931. * get method GD_GaiaTransport.gaiaToken_
  932. */
  933. GD_GaiaTransport.prototype.getToken = function() {
  934. if (/\r|\n/.test(this.gaiaToken_) === true) {
  935. alert("potential attack pattern");
  936. }
  937. return this.gaiaToken_;
  938. };
  939. /**
  940. * function to get an authtoken from the gaia servers
  941. * @param params {XmlHttpParameters}
  942. */
  943. GD_GaiaTransport.prototype.QueryAuthToken = function(params) {
  944. // Create a new request to the authentication URL.
  945. LOG_TRACE("Entering...", "QueryAuthToken()", null);
  946. var token = null;
  947. try {
  948. var xmlRequest = XMLX_getHttpRequest();
  949. var response = null;
  950. // construct the payload
  951. var postData = GD_Transport.AUTH_USER +
  952. encodeURIComponent(this.getUserName()) + "&" +
  953. GD_Transport.AUTH_PWD +
  954. encodeURIComponent(this.getPassword()) + "&" +
  955. GD_Transport.AUTH_SOURCE + "&" +
  956. GD_Transport.AUTH_SERVICE +
  957. encodeURIComponent(this.serviceName_);
  958. LOG_TRACE("postData = " + postData, "QueryAuthToken()", null);
  959. var responseData = null;
  960. var request = xmlRequest;
  961. xmlRequest.onreadystatechange = function() {
  962. GD_GaiaHandleAsyncData(request, params);
  963. }
  964. xmlRequest.open("POST", GD_Transport.AUTH_HANDLER, true);
  965. xmlRequest.setRequestHeader("content-type",
  966. "application/x-www-form-urlencoded");
  967. LOG_TRACE("sending..." + xmlRequest, "QueryAuthToken()", null);
  968. xmlRequest.send(postData);
  969. } catch (e) {
  970. DBG_ALERT(e.toString());
  971. DBG_ALERT('The exception happened in QueryAuthData');
  972. }
  973. };
  974. /**
  975. * callback for the QueryAuthToken
  976. * @param params {XmlHttpParameters}
  977. */
  978. function GD_GaiaHandleAsyncData(request, params) {
  979. LOG_TRACE("Entering callback..", "GD_GaiaHandleAsyncData", null);
  980. if (request.readyState != 4) {
  981. return;
  982. }
  983. GoogleDataFactory.getTransport().HandleOnQueryAuthRequestFinished(
  984. request, params);
  985. LOG_TRACE("QueryAuthCallback done...", "GD_GaiaHandleAsyncData", null);
  986. }
  987. /**
  988. * handling of errors/results of the xmlhttprequest
  989. * @param request {XMLHttpRequest}
  990. * @param params {XmlHttpParameters}
  991. */
  992. GD_GaiaTransport.prototype.HandleOnQueryAuthRequestFinished = function(
  993. request, params) {
  994. var response = null;
  995. var token = null;
  996. LOG_TRACE("Entering...", "HandleOnQueryAuthRequestFinished", null);
  997. if (request.status > 299) {
  998. DBG_ALERT('Bad server response during auth operation: ' +
  999. this.xmlRequest.status);
  1000. DBG_ALERT('body: ' + params.postData);
  1001. } else {
  1002. response = request.responseText;
  1003. }
  1004. DBG_ASSERT(response !== null,
  1005. "we got no data back from the authentication server");
  1006. if (response !== null) {
  1007. // now parse the result
  1008. var result = response.match(/^Auth=(.*)/m);
  1009. DBG_ASSERT(result !== null);
  1010. DBG_ASSERT(result[0] !== null);
  1011. DBG_ASSERT(result[1] !== null);
  1012. if (result !== null) {
  1013. token = result[1];
  1014. }
  1015. }
  1016. LOG_TRACE(token, "HandleOnQueryAuthRequestFinished()", null);
  1017. this.setToken(token);
  1018. // stack needs to unwind, as you can not call from XmlHttp.onreadystate
  1019. // into another xmlHttp object
  1020. window.setTimeout(function() { GD_GaiaContinue(params) }, 0);
  1021. LOG_TRACE("exiting...", "HandleOnQueryAuthRequestFinished", null);
  1022. }
  1023. /**
  1024. * callback for the QueryAuthToken
  1025. */
  1026. function GD_GaiaContinue(params) {
  1027. LOG_TRACE("GD_GaiaContinue...", "GD_GaiaContinue", null);
  1028. GoogleDataFactory.getTransport().DoRequest(params);
  1029. }
  1030. /**
  1031. * check if we need to get a token
  1032. * @returns true if we need to get a token
  1033. */
  1034. GD_GaiaTransport.prototype.NeedToken = function() {
  1035. if (UTIL_isPersistable(this.getUserName()) && this.getToken() == null) {
  1036. return true;
  1037. }
  1038. return false;
  1039. };
  1040. /**
  1041. * if a token is needed, will get one and set it
  1042. */
  1043. GD_GaiaTransport.prototype.PrepareAuthentication = function(params) {
  1044. if (this.NeedToken()) {
  1045. this.QueryAuthToken(params);
  1046. } else {
  1047. this.DoRequest(params);
  1048. }
  1049. };
  1050. /**
  1051. * sets the auth header for Gaia, if we have a token
  1052. * @param xmlHttpRequestObject object to set the header on
  1053. */
  1054. GD_GaiaTransport.prototype.SetHeaders = function(xmlHttpRequestObject) {
  1055. LOG_TRACE("Entering...", "Gaia.SetHeaders", null);
  1056. xmlHttpRequestObject.setRequestHeader(GD_Transport.NOREDIRECT_HEADER, "1");
  1057. if (this.getCookie() !== null) {
  1058. // set a previously stored shard cookie
  1059. LOG_TRACE("setting a stored cookie..." + this.getCookie(),
  1060. "SetHeaders", null);
  1061. xmlHttpRequestObject.setRequestHeader(GD_Transport.COOKIE_HEADER,
  1062. this.getCookie(), null);
  1063. }
  1064. if (this.getToken() !== null) {
  1065. xmlHttpRequestObject.setRequestHeader(GD_Transport.AUTH_HEADER,
  1066. GD_Transport.AUTH_HEADERVALUE + this.getToken());
  1067. }
  1068. this.Base.SetHeaders(xmlHttpRequestObject);
  1069. };
  1070. /**
  1071. * GD_Factory() is used to access the underlying transport layer
  1072. * one instance of the factory exists as a global
  1073. */
  1074. function GD_Factory() {
  1075. this.transPort_ = GD_getTransport(GD_Transport.TRANSPORT_GOOGLECALENDAR);
  1076. };
  1077. // set method GD_Factory.transPort_
  1078. GD_Factory.prototype.setTransport = function(value) {
  1079. this.transPort_ = value;
  1080. };
  1081. // get method GD_Factory.transPort_
  1082. GD_Factory.prototype.getTransport = function() {
  1083. return this.transPort_;
  1084. };
  1085. /**
  1086. * declaration of our global GoogleDataFactory, so that the rest
  1087. * of the code can share this...
  1088. */
  1089. var GoogleDataFactory = new GD_Factory();
  1090. // remove this if you merge
  1091. if (window.GD_Loader) {
  1092. // continue loading
  1093. window.GD_Loader();
  1094. }
  1095. // end
  1096. /* Copyright (c) 2006 Google Inc.
  1097. *
  1098. * Licensed under the Apache License, Version 2.0 (the "License");
  1099. * you may not use this file except in compliance with the License.
  1100. * You may obtain a copy of the License at
  1101. *
  1102. * http://www.apache.org/licenses/LICENSE-2.0
  1103. *
  1104. * Unless required by applicable law or agreed to in writing, software
  1105. * distributed under the License is distributed on an "AS IS" BASIS,
  1106. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1107. * See the License for the specific language governing permissions and
  1108. * limitations under the License.
  1109. */
  1110. /**
  1111. * @fileoverview
  1112. * this file relies on detect.js to do the browser detection
  1113. * it abstracts a minimal set of xpath methods so that
  1114. * you can do cross browser xpath
  1115. * the code seems to work fine in IE6+, Mozilla, Opera8.5+ and Safari2.0+
  1116. */
  1117. var XMLX_ieProgId_ = undefined;
  1118. /** initialize XMLX_ieProgId_ */
  1119. (function () {
  1120. // Nobody (on the web) is really sure which of the progid's listed is totally
  1121. // necessary. It is known, for instance, that certain installations of IE
  1122. // will not work with only Microsoft.XMLHTTP, as well as with MSXML2.XMLHTTP.
  1123. // Safest course seems to be to do this -- include all known progids for
  1124. // XmlHttp.
  1125. if (typeof XMLHttpRequest == 'undefined' &&
  1126. typeof ActiveXObject != 'undefined') {
  1127. var activeXIdents = [
  1128. "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0",
  1129. "MSXML2.XMLHTTP", "MICROSOFT.XMLHTTP.1.0", "MICROSOFT.XMLHTTP.1",
  1130. "MICROSOFT.XMLHTTP" ];
  1131. for (var i = 0; i < activeXIdents.length; ++i) {
  1132. var candidate = activeXIdents[i];
  1133. try {
  1134. new ActiveXObject(candidate);
  1135. XMLX_ieProgId_ = candidate;
  1136. return;
  1137. } catch (e) {
  1138. // do nothing; try next choice
  1139. }
  1140. }
  1141. // couldn't find any matches
  1142. throw ("Could not create ActiveXObject. ActiveX might be disabled, or " +
  1143. "msxml might not be installed");
  1144. }
  1145. })();
  1146. /**
  1147. * helper function to return the correct XMLHttpRequest() object
  1148. */
  1149. function XMLX_getHttpRequest() {
  1150. if (XMLX_ieProgId_ !== undefined) {
  1151. return new ActiveXObject(XMLX_ieProgId_);
  1152. } else {
  1153. return new XMLHttpRequest();
  1154. }
  1155. };
  1156. /**
  1157. * helper to return a brand new XMLDomDocument
  1158. */
  1159. function XMLX_createDomDocument(optXml) {
  1160. var doc = null;
  1161. if (optXml) {
  1162. if (typeof(optXml) != 'string') {
  1163. optXml = XMLX_serializeNode(optXml);
  1164. }
  1165. }
  1166. if (document.implementation.createDocument) {
  1167. if (optXml) {
  1168. var domParser = new DOMParser();
  1169. doc = domParser.parseFromString(optXml, "text/xml");
  1170. } else {
  1171. doc = document.implementation.createDocument("", "", null);
  1172. }
  1173. } else {
  1174. doc = new ActiveXObject("MSXML.DOMDocument");
  1175. if (optXml) {
  1176. doc.loadXML(optXml);
  1177. }
  1178. }
  1179. DBG_ASSERT(doc !== null, "Could not create a DOM document");
  1180. return doc;
  1181. };
  1182. /**
  1183. * helper to serialize an xmlnode into text
  1184. */
  1185. function XMLX_serializeNode(xmlNode) {
  1186. var text = null;
  1187. try {
  1188. // this is for Gecko based browsers
  1189. LOG_TRACE("Trying serializer", "XMLX_serializeNode", null);
  1190. var xmlSerializer = new XMLSerializer();
  1191. // safari 2.02 seems to work fine with this.
  1192. text = xmlSerializer.serializeToString(xmlNode);
  1193. }
  1194. catch ( e ) {
  1195. try {
  1196. // Internet Explorer.
  1197. LOG_TRACE("Trying IE way to serialize", "XMLX_serializeNode", null);
  1198. text = xmlNode.xml;
  1199. }
  1200. catch (e) {
  1201. // everyone else
  1202. LOG_TRACE("Trying UTIL_xmlText(node)", "XMLX_serializeNode", null);
  1203. text = UTIL_xmlText(xmlNode);
  1204. }
  1205. }
  1206. return text;
  1207. };
  1208. /**
  1209. * encapsulates the xpath methods we are exposing
  1210. */
  1211. function XMLX_getNodes(xmlNode, xpath) {
  1212. DBG_ASSERT(xmlNode, "XML_getNodes: " + xpath);
  1213. LOG_DEBUG("XML_getNodes: " + xpath);
  1214. try {
  1215. var document;
  1216. if (xmlNode.ownerDocument !== null) {
  1217. document = xmlNode.ownerDocument;
  1218. } else {
  1219. document = xmlNode;
  1220. }
  1221. if (Detect.XPathSupport()) {
  1222. if (Detect.IE_5_5_newer()) {
  1223. LOG_DEBUG("XML_getNodes: Inside IE5.5 path");
  1224. document.setProperty("SelectionLanguage", "XPath");
  1225. document.setProperty("SelectionNamespaces", XML.NAMESPACES);
  1226. return xmlNode.selectNodes(xpath);
  1227. } else {
  1228. LOG_DEBUG("XML_getNodes: Inside MOZILLA path");
  1229. var nsResolver = function(prefix){
  1230. var s = XML.NAMESPACE_MAP[prefix];
  1231. if (s) {
  1232. return s;
  1233. } else {
  1234. throw "Unknown prefix: '" + prefix+"'";
  1235. }
  1236. };
  1237. var tempResult = document.evaluate(xpath, xmlNode, nsResolver,
  1238. XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  1239. // now make it an array
  1240. var nodeList = new Array(tempResult.snapshotLength);
  1241. for (var i = 0; i < nodeList.length; ++i) {
  1242. nodeList[i] = tempResult.snapshotItem(i);
  1243. }
  1244. return nodeList;
  1245. }
  1246. } else {
  1247. return XMLX_parse(xmlNode, xpath);
  1248. }
  1249. } catch (e) {
  1250. // LOG_XML_PRINT(xmlNode);
  1251. // this is everyone else
  1252. LOG_DEBUG("XML_getNodes: catch path - " + e.toString());
  1253. return XMLX_parse(xmlNode, xpath);
  1254. }
  1255. };
  1256. /**
  1257. * helper method to call the javascript parser
  1258. */
  1259. function XMLX_parse(xmlNode, xpath) {
  1260. LOG_DEBUG("XML_parse: no xpath support, hence i am here");
  1261. var expr = xpathParse(xpath);
  1262. var ctx = new ExprContext(xmlNode);
  1263. var ret = expr.evaluate(ctx);
  1264. return ret.nodeSetValue();
  1265. }
  1266. /**
  1267. * @param xmlNode the node to search from
  1268. * @xpath the xpath to search for
  1269. * @return the first xmlnode found
  1270. */
  1271. function XMLX_getNode(xmlNode, xpath) {
  1272. var result = XMLX_getNodes(xmlNode, xpath);
  1273. if (result) {
  1274. return result[0];
  1275. }
  1276. return null;
  1277. }
  1278. // remove this if you merge
  1279. if (window.GD_Loader) {
  1280. // continue loading
  1281. window.GD_Loader();
  1282. }
  1283. // end
  1284. /* Copyright (c) 2006 Google Inc.
  1285. *
  1286. * Licensed under the Apache License, Version 2.0 (the "License");
  1287. * you may not use this file except in compliance with the License.
  1288. * You may obtain a copy of the License at
  1289. *
  1290. * http://www.apache.org/licenses/LICENSE-2.0
  1291. *
  1292. * Unless required by applicable law or agreed to in writing, software
  1293. * distributed under the License is distributed on an "AS IS" BASIS,
  1294. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1295. * See the License for the specific language governing permissions and
  1296. * limitations under the License.
  1297. */
  1298. // All Rights Reserved
  1299. //
  1300. // An XPath parser and evaluator written in JavaScript. The
  1301. // implementation is complete except for functions handling
  1302. // namespaces.
  1303. //
  1304. // Reference: [XPATH] XPath Specification
  1305. // <http://www.w3.org/TR/1999/REC-xpath-19991116>.
  1306. //
  1307. //
  1308. // The API of the parser has several parts:
  1309. //
  1310. // 1. The parser function xpathParse() that takes a string and returns
  1311. // an expession object.
  1312. //
  1313. // 2. The expression object that has an evaluate() method to evaluate the
  1314. // XPath expression it represents. (It is actually a hierarchy of
  1315. // objects that resembles the parse tree, but an application will call
  1316. // evaluate() only on the top node of this hierarchy.)
  1317. //
  1318. // 3. The context object that is passed as an argument to the evaluate()
  1319. // method, which represents the DOM context in which the expression is
  1320. // evaluated.
  1321. //
  1322. // 4. The value object that is returned from evaluate() and represents
  1323. // values of the different types that are defined by XPath (number,
  1324. // string, boolean, and node-set), and allows to convert between them.
  1325. //
  1326. // These parts are near the top of the file, the functions and data
  1327. // that are used internally follow after them.
  1328. //
  1329. function assert(condition) { DBG_ASSERT(condition); }
  1330. // The entry point for the parser.
  1331. //
  1332. // @param expr a string that contains an XPath expression.
  1333. // @return an expression object that can be evaluated with an
  1334. // expression context.
  1335. // right now, only used for safari
  1336. function xpathParse(expr) {
  1337. LOG_DEBUG('XPath parse ' + expr);
  1338. xpathParseInit();
  1339. var cached = xpathCacheLookup(expr);
  1340. if (cached) {
  1341. LOG_DEBUG('XPath ....cached ');
  1342. return cached;
  1343. }
  1344. // Optimize for a few common cases: simple attribute node tests
  1345. // (@id), simple element node tests (page), variable references
  1346. // ($address), numbers (4), multi-step path expressions where each
  1347. // step is a plain element node test
  1348. // (page/overlay/locations/location).
  1349. if (expr.match(/^(\$|@)?\w+$/i)) {
  1350. var ret = makeSimpleExpr(expr);
  1351. xpathParseCache[expr] = ret;
  1352. LOG_DEBUG('XPath ... simple');
  1353. return ret;
  1354. }
  1355. if (expr.match(/^\w+(\/\w+)*$/i)) {
  1356. ret = makeSimpleExpr2(expr);
  1357. xpathParseCache[expr] = ret;
  1358. LOG_DEBUG('XPath ... simple 2 ');
  1359. return ret;
  1360. }
  1361. var cachekey = expr; // expr is modified during parse
  1362. var stack = [];
  1363. var ahead = null;
  1364. var previous = null;
  1365. var done = false;
  1366. var parse_count = 0;
  1367. var lexer_count = 0;
  1368. var reduce_count = 0;
  1369. while (!done) {
  1370. parse_count+=1;
  1371. expr = expr.replace(/^\s*/, '');
  1372. previous = ahead;
  1373. ahead = null;
  1374. var rule = null;
  1375. var match = '';
  1376. for (var i = 0; i < xpathTokenRules.length; ++i) {
  1377. var result = xpathTokenRules[i].re.exec(expr);
  1378. lexer_count+=1;
  1379. if (result && result.length > 0 && result[0].length > match.length) {
  1380. rule = xpathTokenRules[i];
  1381. match = result[0];
  1382. break;
  1383. }
  1384. }
  1385. // Special case: allow operator keywords to be element and
  1386. // variable names.
  1387. // NOTE: The parser resolves conflicts by looking ahead,
  1388. // and this is the only case where we look back to
  1389. // disambiguate. So this is indeed something different, and
  1390. // looking back is usually done in the lexer (via states in the
  1391. // general case, called "start conditions" in flex(1)). Also,the
  1392. // conflict resolution in the parser is not as robust as it could
  1393. // be, so I'd like to keep as much off the parser as possible (all
  1394. // these precedence values should be computed from the grammar
  1395. // rules and possibly associativity declarations, as in bison(1),
  1396. // and not explicitly set.
  1397. if (rule &&
  1398. (rule == TOK_DIV ||
  1399. rule == TOK_MOD ||
  1400. rule == TOK_AND ||
  1401. rule == TOK_OR) &&
  1402. (!previous ||
  1403. previous.tag == TOK_AT ||
  1404. previous.tag == TOK_DSLASH ||
  1405. previous.tag == TOK_SLASH ||
  1406. previous.tag == TOK_AXIS ||
  1407. previous.tag == TOK_DOLLAR)) {
  1408. rule = TOK_QNAME;
  1409. }
  1410. if (rule) {
  1411. expr = expr.substr(match.length);
  1412. LOG_DEBUG('XPath token: parse ' + match + ' -- ' + rule.label);
  1413. ahead = {
  1414. tag: rule,
  1415. match: match,
  1416. prec: rule.prec ? rule.prec : 0, // || 0 is removed by the compiler
  1417. expr: makeTokenExpr(match) };
  1418. } else {
  1419. LOG_DEBUG('XPath DONE');
  1420. done = true;
  1421. }
  1422. while (xpathReduce(stack, ahead)) {
  1423. reduce_count+=1;
  1424. LOG_DEBUG('XPATH stack: ' + stackToString(stack));
  1425. }
  1426. }
  1427. LOG_DEBUG('XPATH stack:' + stackToString(stack));
  1428. if (stack.length != 1) {
  1429. throw 'XPath parse error ' + cachekey + ':\n' + stackToString(stack);
  1430. }
  1431. result = stack[0].expr;
  1432. xpathParseCache[cachekey] = result;
  1433. LOG_DEBUG('XPath parse: ' + parse_count + ' / ' +
  1434. lexer_count + ' / ' + reduce_count);
  1435. return result;
  1436. }
  1437. var xpathParseCache = {};
  1438. function xpathCacheLookup(expr) {
  1439. return xpathParseCache[expr];
  1440. }
  1441. function xpathReduce(stack, ahead) {
  1442. var cand = null;
  1443. if (stack.length > 0) {
  1444. var top = stack[stack.length-1];
  1445. var ruleset = xpathRules[top.tag.key];
  1446. if (ruleset) {
  1447. for (var i = 0; i < ruleset.length; ++i) {
  1448. var rule = ruleset[i];
  1449. var match = xpathMatchStack(stack, rule[1]);
  1450. if (match.length) {
  1451. cand = {
  1452. tag: rule[0],
  1453. rule: rule,
  1454. match: match };
  1455. cand.prec = xpathGrammarPrecedence(cand);
  1456. break;
  1457. }
  1458. }
  1459. }
  1460. }
  1461. var ret;
  1462. if (cand && (!ahead || cand.prec > ahead.prec ||
  1463. (ahead.tag.left && cand.prec >= ahead.prec))) {
  1464. for (i = 0; i < cand.match.matchlength; ++i) {
  1465. stack.pop();
  1466. }
  1467. LOG_DEBUG('reduce ' + cand.tag.label + ' ' + cand.prec +
  1468. ' ahead ' + (ahead ? ahead.tag.label + ' ' + ahead.prec +
  1469. (ahead.tag.left ? ' left' : '') : ' none '));
  1470. var matchexpr = UTIL_mapExpr(cand.match, function(m) { return m.expr; });
  1471. cand.expr = cand.rule[3].apply(null, matchexpr);
  1472. stack.push(cand);
  1473. ret = true;
  1474. } else {
  1475. if (ahead) {
  1476. LOG_DEBUG('shift ' + ahead.tag.label + ' ' + ahead.prec +
  1477. (ahead.tag.left ? ' left' : '') +
  1478. ' over ' + (cand ? cand.tag.label + ' ' +
  1479. cand.prec : ' none'));
  1480. stack.push(ahead);
  1481. }
  1482. ret = false;
  1483. }
  1484. return ret;
  1485. }
  1486. function xpathMatchStack(stack, pattern) {
  1487. // NOTE: The stack matches for variable cardinality are
  1488. // greedy but don't do backtracking. This would be an issue only
  1489. // with rules of the form A* A, i.e. with an element with variable
  1490. // cardinality followed by the same element. Since that doesn't
  1491. // occur in the grammar at hand, all matches on the stack are
  1492. // unambiguous.
  1493. var S = stack.length;
  1494. var P = pattern.length;
  1495. var p, s;
  1496. var match = [];
  1497. match.matchlength = 0;
  1498. var ds = 0;
  1499. for (p = P - 1, s = S - 1; p >= 0 && s >= 0; --p, s -= ds) {
  1500. ds = 0;
  1501. var qmatch = [];
  1502. if (pattern[p] == Q_MM) {
  1503. p -= 1;
  1504. match.push(qmatch);
  1505. while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
  1506. qmatch.push(stack[s - ds]);
  1507. ds += 1;
  1508. match.matchlength += 1;
  1509. }
  1510. } else if (pattern[p] == Q_01) {
  1511. p -= 1;
  1512. match.push(qmatch);
  1513. while (s - ds >= 0 && ds < 2 && stack[s - ds].tag == pattern[p]) {
  1514. qmatch.push(stack[s - ds]);
  1515. ds += 1;
  1516. match.matchlength += 1;
  1517. }
  1518. } else if (pattern[p] == Q_1M) {
  1519. p -= 1;
  1520. match.push(qmatch);
  1521. if (stack[s].tag == pattern[p]) {
  1522. while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
  1523. qmatch.push(stack[s - ds]);
  1524. ds += 1;
  1525. match.matchlength += 1;
  1526. }
  1527. } else {
  1528. return [];
  1529. }
  1530. } else if (stack[s].tag == pattern[p]) {
  1531. match.push(stack[s]);
  1532. ds += 1;
  1533. match.matchlength += 1;
  1534. } else {
  1535. return [];
  1536. }
  1537. UTIL_reverseInplace(qmatch);
  1538. qmatch.expr = UTIL_mapExpr(qmatch, function(m) { return m.expr; });
  1539. }
  1540. UTIL_reverseInplace(match);
  1541. if (p == -1) {
  1542. return match;
  1543. } else {
  1544. return [];
  1545. }
  1546. }
  1547. function xpathTokenPrecedence(tag) {
  1548. return tag.prec || 2;
  1549. }
  1550. function xpathGrammarPrecedence(frame) {
  1551. var ret = 0;
  1552. if (frame.rule) { /* normal reduce */
  1553. if (frame.rule.length >= 3 && frame.rule[2] >= 0) {
  1554. ret = frame.rule[2];
  1555. } else {
  1556. for (var i = 0; i < frame.rule[1].length; ++i) {
  1557. var p = xpathTokenPrecedence(frame.rule[1][i]);
  1558. ret = Math.max(ret, p);
  1559. }
  1560. }
  1561. } else if (frame.tag) { /* TOKEN match */
  1562. ret = xpathTokenPrecedence(frame.tag);
  1563. } else if (frame.length) { /* Q_ match */
  1564. for (var j = 0; j < frame.length; ++j) {
  1565. var p = xpathGrammarPrecedence(frame[j]);
  1566. ret = Math.max(ret, p);
  1567. }
  1568. }
  1569. return ret;
  1570. }
  1571. function stackToString(stack) {
  1572. var ret = '';
  1573. for (var i = 0; i < stack.length; ++i) {
  1574. if (ret) {
  1575. ret += '\n';
  1576. }
  1577. ret += stack[i].tag.label;
  1578. }
  1579. return ret;
  1580. }
  1581. // XPath expression evaluation context. An XPath context consists of a
  1582. // DOM node, a list of DOM nodes that contains this node, a number
  1583. // that represents the position of the single node in the list, and a
  1584. // current set of variable bindings. (See XPath spec.)
  1585. //
  1586. // The interface of the expression context:
  1587. //
  1588. // Constructor -- gets the node, its position, the node set it
  1589. // belongs to, and a parent context as arguments. The parent context
  1590. // is used to implement scoping rules for variables: if a variable
  1591. // is not found in the current context, it is looked for in the
  1592. // parent context, recursively. Except for node, all arguments have
  1593. // default values: default position is 0, default node set is the
  1594. // set that contains only the node, and the default parent is null.
  1595. //
  1596. // Notice that position starts at 0 at the outside interface;
  1597. // inside XPath expressions this shows up as position()=1.
  1598. //
  1599. // clone() -- creates a new context with the current context as
  1600. // parent. If passed as argument to clone(), the new context has a
  1601. // different node, position, or node set. What is not passed is
  1602. // inherited from the cloned context.
  1603. //
  1604. // setVariable(name, expr) -- binds given XPath expression to the
  1605. // name.
  1606. //
  1607. // getVariable(name) -- what the name says.
  1608. //
  1609. // setNode(node, position) -- sets the context to the new node and
  1610. // its corresponding position. Needed to implement scoping rules for
  1611. // variables in XPath. (A variable is visible to all subsequent
  1612. // siblings, not only to its children.)
  1613. function ExprContext(node, position, nodelist, parent) {
  1614. this.node = node;
  1615. this.position = position || 0;
  1616. this.nodelist = nodelist || [ node ];
  1617. this.variables = {};
  1618. this.parent = parent || null;
  1619. if (parent) {
  1620. this.root = parent.root;
  1621. } else if (this.node.nodeType == DOM_DOCUMENT_NODE) {
  1622. // NOTE: DOM Spec stipulates that the ownerDocument of a
  1623. // document is null. Our root, however is the document that we are
  1624. // processing, so the initial context is created from its document
  1625. // node, which case we must handle here explcitly.
  1626. this.root = node;
  1627. } else {
  1628. this.root = node.ownerDocument;
  1629. }
  1630. }
  1631. ExprContext.prototype.clone = function(node, position, nodelist) {
  1632. return new ExprContext(
  1633. node || this.node,
  1634. typeof position != 'undefined' ? position : this.position,
  1635. nodelist || this.nodelist, this);
  1636. };
  1637. ExprContext.prototype.setVariable = function(name, value) {
  1638. this.variables[name] = value;
  1639. };
  1640. ExprContext.prototype.getVariable = function(name) {
  1641. if (typeof this.variables[name] != 'undefined') {
  1642. return this.variables[name];
  1643. } else if (this.parent) {
  1644. return this.parent.getVariable(name);
  1645. } else {
  1646. return null;
  1647. }
  1648. }
  1649. ExprContext.prototype.setNode = function(node, position) {
  1650. this.node = node;
  1651. this.position = position;
  1652. }
  1653. // XPath expression values. They are what XPath expressions evaluate
  1654. // to. Strangely, the different value types are not specified in the
  1655. // XPath syntax, but only in the semantics, so they don't show up as
  1656. // nonterminals in the grammar. Yet, some expressions are required to
  1657. // evaluate to particular types, and not every type can be coerced
  1658. // into every other type. Although the types of XPath values are
  1659. // similar to the types present in JavaScript, the type coercion rules
  1660. // are a bit peculiar, so we explicitly model XPath types instead of
  1661. // mapping them onto JavaScript types. (See XPath spec.)
  1662. //
  1663. // The four types are:
  1664. //
  1665. // StringValue
  1666. //
  1667. // NumberValue
  1668. //
  1669. // BooleanValue
  1670. //
  1671. // NodeSetValue
  1672. //
  1673. // The common interface of the value classes consists of methods that
  1674. // implement the XPath type coercion rules:
  1675. //
  1676. // stringValue() -- returns the value as a JavaScript String,
  1677. //
  1678. // numberValue() -- returns the value as a JavaScript Number,
  1679. //
  1680. // booleanValue() -- returns the value as a JavaScript Boolean,
  1681. //
  1682. // nodeSetValue() -- returns the value as a JavaScript Array of DOM
  1683. // Node objects.
  1684. //
  1685. function StringValue(value) {
  1686. this.value = value;
  1687. this.type = 'string';
  1688. }
  1689. StringValue.prototype.stringValue = function() {
  1690. return this.value;
  1691. }
  1692. StringValue.prototype.booleanValue = function() {
  1693. return this.value.length > 0;
  1694. }
  1695. StringValue.prototype.numberValue = function() {
  1696. return this.value - 0;
  1697. }
  1698. StringValue.prototype.nodeSetValue = function() {
  1699. throw this;
  1700. }
  1701. function BooleanValue(value) {
  1702. this.value = value;
  1703. this.type = 'boolean';
  1704. }
  1705. BooleanValue.prototype.stringValue = function() {
  1706. return '' + this.value;
  1707. }
  1708. BooleanValue.prototype.booleanValue = function() {
  1709. return this.value;
  1710. }
  1711. BooleanValue.prototype.numberValue = function() {
  1712. return this.value ? 1 : 0;
  1713. }
  1714. BooleanValue.prototype.nodeSetValue = function() {
  1715. throw this;
  1716. }
  1717. function NumberValue(value) {
  1718. this.value = value;
  1719. this.type = 'number';
  1720. }
  1721. NumberValue.prototype.stringValue = function() {
  1722. return '' + this.value;
  1723. }
  1724. NumberValue.prototype.booleanValue = function() {
  1725. return !!this.value;
  1726. }
  1727. NumberValue.prototype.numberValue = function() {
  1728. return this.value - 0;
  1729. }
  1730. NumberValue.prototype.nodeSetValue = function() {
  1731. throw this;
  1732. }
  1733. function NodeSetValue(value) {
  1734. this.value = value;
  1735. this.type = 'node-set';
  1736. }
  1737. NodeSetValue.prototype.stringValue = function() {
  1738. if (this.value.length == 0) {
  1739. return '';
  1740. } else {
  1741. return UTIL_xmlValue(this.value[0]);
  1742. }
  1743. }
  1744. NodeSetValue.prototype.booleanValue = function() {
  1745. return this.value.length > 0;
  1746. }
  1747. NodeSetValue.prototype.numberValue = function() {
  1748. return this.stringValue() - 0;
  1749. }
  1750. NodeSetValue.prototype.nodeSetValue = function() {
  1751. return this.value;
  1752. };
  1753. // XPath expressions. They are used as nodes in the parse tree and
  1754. // possess an evaluate() method to compute an XPath value given an XPath
  1755. // context. Expressions are returned from the parser. Teh set of
  1756. // expression classes closely mirrors the set of non terminal symbols
  1757. // in the grammar. Every non trivial nonterminal symbol has a
  1758. // corresponding expression class.
  1759. //
  1760. // The common expression interface consists of the following methods:
  1761. //
  1762. // evaluate(context) -- evaluates the expression, returns a value.
  1763. //
  1764. // toString() -- returns the XPath text representation of the
  1765. // expression (defined in xsltdebug.js).
  1766. //
  1767. // parseTree(indent) -- returns a parse tree representation of the
  1768. // expression (defined in xsltdebug.js).
  1769. function TokenExpr(m) {
  1770. this.value = m;
  1771. }
  1772. TokenExpr.prototype.evaluate = function() {
  1773. return new StringValue(this.value);
  1774. };
  1775. function LocationExpr() {
  1776. this.absolute = false;
  1777. this.steps = [];
  1778. }
  1779. LocationExpr.prototype.appendStep = function(s) {
  1780. this.steps.push(s);
  1781. }
  1782. LocationExpr.prototype.prependStep = function(s) {
  1783. var steps0 = this.steps;
  1784. this.steps = [ s ];
  1785. for (var i = 0; i < steps0.length; ++i) {
  1786. this.steps.push(steps0[i]);
  1787. }
  1788. };
  1789. LocationExpr.prototype.evaluate = function(ctx) {
  1790. var start;
  1791. if (this.absolute) {
  1792. start = ctx.root;
  1793. } else {
  1794. start = ctx.node;
  1795. }
  1796. var nodes = [];
  1797. xPathStep(nodes, this.steps, 0, start, ctx);
  1798. return new NodeSetValue(nodes);
  1799. };
  1800. function xPathStep(nodes, steps, step, input, ctx) {
  1801. var s = steps[step];
  1802. var ctx2 = ctx.clone(input);
  1803. var nodelist = s.evaluate(ctx2).nodeSetValue();
  1804. for (var i = 0; i < nodelist.length; ++i) {
  1805. if (step == steps.length - 1) {
  1806. nodes.push(nodelist[i]);
  1807. } else {
  1808. xPathStep(nodes, steps, step + 1, nodelist[i], ctx);
  1809. }
  1810. }
  1811. }
  1812. function StepExpr(axis, nodetest, predicate) {
  1813. this.axis = axis;
  1814. this.nodetest = nodetest;
  1815. this.predicate = predicate || [];
  1816. }
  1817. StepExpr.prototype.appendPredicate = function(p) {
  1818. this.predicate.push(p);
  1819. }
  1820. StepExpr.prototype.evaluate = function(ctx) {
  1821. var input = ctx.node;
  1822. var nodelist = [];
  1823. // NOTE: When this was a switch() statement, it didn't work
  1824. // in Safari/2.0. Not sure why though; it resulted in the JavaScript
  1825. // console output "undefined" (without any line number or so).
  1826. if (this.axis == xpathAxis.ANCESTOR_OR_SELF) {
  1827. nodelist.push(input);
  1828. for (var n = input.parentNode; n; n = input.parentNode) {
  1829. nodelist.push(n);
  1830. }
  1831. } else if (this.axis == xpathAxis.ANCESTOR) {
  1832. for (var n = input.parentNode; n; n = input.parentNode) {
  1833. nodelist.push(n);
  1834. }
  1835. } else if (this.axis == xpathAxis.ATTRIBUTE) {
  1836. UTIL_copyArray(nodelist, input.attributes);
  1837. } else if (this.axis == xpathAxis.CHILD) {
  1838. UTIL_copyArray(nodelist, input.childNodes);
  1839. } else if (this.axis == xpathAxis.DESCENDANT_OR_SELF) {
  1840. nodelist.push(input);
  1841. xpathCollectDescendants(nodelist, input);
  1842. } else if (this.axis == xpathAxis.DESCENDANT) {
  1843. xpathCollectDescendants(nodelist, input);
  1844. } else if (this.axis == xpathAxis.FOLLOWING) {
  1845. for (var n = input.parentNode; n; n = n.parentNode) {
  1846. for (var nn = n.nextSibling; nn; nn = nn.nextSibling) {
  1847. nodelist.push(nn);
  1848. xpathCollectDescendants(nodelist, nn);
  1849. }
  1850. }
  1851. } else if (this.axis == xpathAxis.FOLLOWING_SIBLING) {
  1852. for (var n = input.nextSibling; n; n = input.nextSibling) {
  1853. nodelist.push(n);
  1854. }
  1855. } else if (this.axis == xpathAxis.NAMESPACE) {
  1856. DBG_ALERT('not implemented: axis namespace');
  1857. } else if (this.axis == xpathAxis.PARENT) {
  1858. if (input.parentNode) {
  1859. nodelist.push(input.parentNode);
  1860. }
  1861. } else if (this.axis == xpathAxis.PRECEDING) {
  1862. for (var n = input.parentNode; n; n = n.parentNode) {
  1863. for (var nn = n.previousSibling; nn; nn = nn.previousSibling) {
  1864. nodelist.push(nn);
  1865. xpathCollectDescendantsReverse(nodelist, nn);
  1866. }
  1867. }
  1868. } else if (this.axis == xpathAxis.PRECEDING_SIBLING) {
  1869. for (var n = input.previousSibling; n; n = input.previousSibling) {
  1870. nodelist.push(n);
  1871. }
  1872. } else if (this.axis == xpathAxis.SELF) {
  1873. nodelist.push(input);
  1874. } else {
  1875. throw 'ERROR -- NO SUCH AXIS: ' + this.axis;
  1876. }
  1877. // process node test
  1878. var nodelist0 = nodelist;
  1879. nodelist = [];
  1880. for (var i = 0; i < nodelist0.length; ++i) {
  1881. var n = nodelist0[i];
  1882. if (this.nodetest.evaluate(ctx.clone(n, i, nodelist0)).booleanValue()) {
  1883. nodelist.push(n);
  1884. }
  1885. }
  1886. // process predicates
  1887. for (var i = 0; i < this.predicate.length; ++i) {
  1888. var nodelist0 = nodelist;
  1889. nodelist = [];
  1890. for (var ii = 0; ii < nodelist0.length; ++ii) {
  1891. var n = nodelist0[ii];
  1892. if (this.predicate[i].evaluate(ctx.clone(n, ii, nodelist0)).booleanValue()) {
  1893. nodelist.push(n);
  1894. }
  1895. }
  1896. }
  1897. return new NodeSetValue(nodelist);
  1898. };
  1899. function NodeTestAny() {
  1900. this.value = new BooleanValue(true);
  1901. }
  1902. NodeTestAny.prototype.evaluate = function(ctx) {
  1903. return this.value;
  1904. };
  1905. function NodeTestElement() {}
  1906. NodeTestElement.prototype.evaluate = function(ctx) {
  1907. return new BooleanValue(ctx.node.nodeType == DOM_ELEMENT_NODE);
  1908. }
  1909. function NodeTestText() {}
  1910. NodeTestText.prototype.evaluate = function(ctx) {
  1911. return new BooleanValue(ctx.node.nodeType == DOM_TEXT_NODE);
  1912. }
  1913. function NodeTestComment() {}
  1914. NodeTestComment.prototype.evaluate = function(ctx) {
  1915. return new BooleanValue(ctx.node.nodeType == DOM_COMMENT_NODE);
  1916. }
  1917. function NodeTestPI(target) {
  1918. this.target = target;
  1919. }
  1920. NodeTestPI.prototype.evaluate = function(ctx) {
  1921. return new
  1922. BooleanValue(ctx.node.nodeType == DOM_PROCESSING_INSTRUCTION_NODE &&
  1923. (!this.target || ctx.node.nodeName == this.target));
  1924. }
  1925. function NodeTestNC(nsprefix) {
  1926. this.regex = new RegExp("^" + nsprefix + ":");
  1927. this.nsprefix = nsprefix;
  1928. }
  1929. NodeTestNC.prototype.evaluate = function(ctx) {
  1930. var n = ctx.node;
  1931. return new BooleanValue(this.regex.match(n.nodeName));
  1932. }
  1933. function NodeTestName(name) {
  1934. this.name = name;
  1935. }
  1936. NodeTestName.prototype.evaluate = function(ctx) {
  1937. var n = ctx.node;
  1938. return new BooleanValue(n.nodeName == this.name);
  1939. }
  1940. function PredicateExpr(expr) {
  1941. this.expr = expr;
  1942. }
  1943. PredicateExpr.prototype.evaluate = function(ctx) {
  1944. var v = this.expr.evaluate(ctx);
  1945. if (v.type == 'number') {
  1946. // NOTE: Internally, position is represented starting with
  1947. // 0, however in XPath position starts with 1. See functions
  1948. // position() and last().
  1949. return new BooleanValue(ctx.position == v.numberValue() - 1);
  1950. } else {
  1951. return new BooleanValue(v.booleanValue());
  1952. }
  1953. };
  1954. function FunctionCallExpr(name) {
  1955. this.name = name;
  1956. this.args = [];
  1957. }
  1958. FunctionCallExpr.prototype.appendArg = function(arg) {
  1959. this.args.push(arg);
  1960. };
  1961. FunctionCallExpr.prototype.evaluate = function(ctx) {
  1962. var fn = '' + this.name.value;
  1963. var f = this.xpathfunctions[fn];
  1964. if (f) {
  1965. return f.call(this, ctx);
  1966. } else {
  1967. LOG_DEBUG('XPath NO SUCH FUNCTION ' + fn);
  1968. return new BooleanValue(false);
  1969. }
  1970. };
  1971. FunctionCallExpr.prototype.xpathfunctions = {
  1972. 'last': function(ctx) {
  1973. assert(this.args.length == 0);
  1974. // NOTE: XPath position starts at 1.
  1975. return new NumberValue(ctx.nodelist.length);
  1976. },
  1977. 'position': function(ctx) {
  1978. assert(this.args.length == 0);
  1979. // NOTE: XPath position starts at 1.
  1980. return new NumberValue(ctx.position + 1);
  1981. },
  1982. 'count': function(ctx) {
  1983. assert(this.args.length == 1);
  1984. var v = this.args[0].evaluate(ctx);
  1985. return new NumberValue(v.nodeSetValue().length);
  1986. },
  1987. 'id': function(ctx) {
  1988. assert(this.args.length == 1);
  1989. var e = this.args.evaluate(ctx);
  1990. var ret = [];
  1991. var ids;
  1992. if (e.type == 'node-set') {
  1993. ids = [];
  1994. for (var i = 0; i < e.length; ++i) {
  1995. var v = UTIL_xmlValue(e[i]).split(/\s+/);
  1996. for (var ii = 0; ii < v.length; ++ii) {
  1997. ids.push(v[ii]);
  1998. }
  1999. }
  2000. } else {
  2001. ids = e.split(/\s+/);
  2002. }
  2003. var d = ctx.node.ownerDocument;
  2004. for (var i = 0; i < ids.length; ++i) {
  2005. var n = d.getElementById(ids[i]);
  2006. if (n) {
  2007. ret.push(n);
  2008. }
  2009. }
  2010. return new NodeSetValue(ret);
  2011. },
  2012. 'local-name': function(ctx) {
  2013. DBG_ALERT('not implmented yet: XPath function local-name()');
  2014. },
  2015. 'namespace-uri': function(ctx) {
  2016. DBG_ALERT('not implmented yet: XPath function namespace-uri()');
  2017. },
  2018. 'name': function(ctx) {
  2019. assert(this.args.length == 1 || this.args.length == 0);
  2020. var n;
  2021. if (this.args.length == 0) {
  2022. n = [ ctx.node ];
  2023. } else {
  2024. n = this.args[0].evaluate(ctx).nodeSetValue();
  2025. }
  2026. if (n.length == 0) {
  2027. return new StringValue('');
  2028. } else {
  2029. return new StringValue(n[0].nodeName);
  2030. }
  2031. },
  2032. 'string': function(ctx) {
  2033. assert(this.args.length == 1 || this.args.length == 0);
  2034. if (this.args.length == 0) {
  2035. return new StringValue(new NodeSetValue([ ctx.node ]).stringValue());
  2036. } else {
  2037. return new StringValue(this.args[0].evaluate(ctx).stringValue());
  2038. }
  2039. },
  2040. 'concat': function(ctx) {
  2041. var ret = '';
  2042. for (var i = 0; i < this.args.length; ++i) {
  2043. ret += this.args[i].evaluate(ctx).stringValue();
  2044. }
  2045. return new StringValue(ret);
  2046. },
  2047. 'starts-with': function(ctx) {
  2048. assert(this.args.length == 2);
  2049. var s0 = this.args[0].evaluate(ctx).stringValue();
  2050. var s1 = this.args[1].evaluate(ctx).stringValue();
  2051. return new BooleanValue(s0.indexOf(s1) == 0);
  2052. },
  2053. 'contains': function(ctx) {
  2054. assert(this.args.length == 2);
  2055. var s0 = this.args[0].evaluate(ctx).stringValue();
  2056. var s1 = this.args[1].evaluate(ctx).stringValue();
  2057. return new BooleanValue(s0.indexOf(s1) != -1);
  2058. },
  2059. 'substring-before': function(ctx) {
  2060. assert(this.args.length == 2);
  2061. var s0 = this.args[0].evaluate(ctx).stringValue();
  2062. var s1 = this.args[1].evaluate(ctx).stringValue();
  2063. var i = s0.indexOf(s1);
  2064. var ret;
  2065. if (i == -1) {
  2066. ret = '';
  2067. } else {
  2068. ret = s0.substr(0,i);
  2069. }
  2070. return new StringValue(ret);
  2071. },
  2072. 'substring-after': function(ctx) {
  2073. assert(this.args.length == 2);
  2074. var s0 = this.args[0].evaluate(ctx).stringValue();
  2075. var s1 = this.args[1].evaluate(ctx).stringValue();
  2076. var i = s0.indexOf(s1);
  2077. var ret;
  2078. if (i == -1) {
  2079. ret = '';
  2080. } else {
  2081. ret = s0.substr(i + s1.length);
  2082. }
  2083. return new StringValue(ret);
  2084. },
  2085. 'substring': function(ctx) {
  2086. // NOTE: XPath defines the position of the first character in a
  2087. // string to be 1, in JavaScript this is 0 ([XPATH] Section 4.2).
  2088. assert(this.args.length == 2 || this.args.length == 3);
  2089. var s0 = this.args[0].evaluate(ctx).stringValue();
  2090. var s1 = this.args[1].evaluate(ctx).numberValue();
  2091. var ret;
  2092. if (this.args.length == 2) {
  2093. var i1 = Math.max(0, Math.round(s1) - 1);
  2094. ret = s0.substr(i1);
  2095. } else {
  2096. var s2 = this.args[2].evaluate(ctx).numberValue();
  2097. var i0 = Math.round(s1) - 1;
  2098. var i1 = Math.max(0, i0);
  2099. var i2 = Math.round(s2) - Math.max(0, -i0);
  2100. ret = s0.substr(i1, i2);
  2101. }
  2102. return new StringValue(ret);
  2103. },
  2104. 'string-length': function(ctx) {
  2105. var s;
  2106. if (this.args.length > 0) {
  2107. s = this.args[0].evaluate(ctx).stringValue();
  2108. } else {
  2109. s = new NodeSetValue([ ctx.node ]).stringValue();
  2110. }
  2111. return new NumberValue(s.length);
  2112. },
  2113. 'normalize-space': function(ctx) {
  2114. var s;
  2115. if (this.args.length > 0) {
  2116. s = this.args[0].evaluate(ctx).stringValue();
  2117. } else {
  2118. s = new NodeSetValue([ ctx.node ]).stringValue();
  2119. }
  2120. s = s.replace(/^\s*/,'').replace(/\s*$/,'').replace(/\s+/g, ' ');
  2121. return new StringValue(s);
  2122. },
  2123. 'translate': function(ctx) {
  2124. assert(this.args.length == 3);
  2125. var s0 = this.args[0].evaluate(ctx).stringValue();
  2126. var s1 = this.args[1].evaluate(ctx).stringValue();
  2127. var s2 = this.args[2].evaluate(ctx).stringValue();
  2128. for (var i = 0; i < s1.length; ++i) {
  2129. s0 = s0.replace(new RegExp(s1.charAt(i), 'g'), s2.charAt(i));
  2130. }
  2131. return new StringValue(s0);
  2132. },
  2133. 'boolean': function(ctx) {
  2134. assert(this.args.length == 1);
  2135. return new BooleanValue(this.args[0].evaluate(ctx).booleanValue());
  2136. },
  2137. 'not': function(ctx) {
  2138. assert(this.args.length == 1);
  2139. var ret = !this.args[0].evaluate(ctx).booleanValue();
  2140. return new BooleanValue(ret);
  2141. },
  2142. 'true': function(ctx) {
  2143. assert(this.args.length == 0);
  2144. return new BooleanValue(true);
  2145. },
  2146. 'false': function(ctx) {
  2147. assert(this.args.length == 0);
  2148. return new BooleanValue(false);
  2149. },
  2150. 'lang': function(ctx) {
  2151. assert(this.args.length == 1);
  2152. var lang = this.args[0].evaluate(ctx).stringValue();
  2153. var xmllang;
  2154. var n = ctx.node;
  2155. while (n && n != n.parentNode /* just in case ... */) {
  2156. xmllang = n.getAttribute('xml:lang');
  2157. if (xmllang) {
  2158. break;
  2159. }
  2160. n = n.parentNode;
  2161. }
  2162. if (!xmllang) {
  2163. return new BooleanValue(false);
  2164. } else {
  2165. var re = new RegExp('^' + lang + '$', 'i');
  2166. return new BooleanValue(xmllang.match(re) ||
  2167. xmllang.replace(/_.*$/,'').match(re));
  2168. }
  2169. },
  2170. 'number': function(ctx) {
  2171. assert(this.args.length == 1 || this.args.length == 0);
  2172. if (this.args.length == 1) {
  2173. return new NumberValue(this.args[0].evaluate(ctx).numberValue());
  2174. } else {
  2175. return new NumberValue(new NodeSetValue([ ctx.node ]).numberValue());
  2176. }
  2177. },
  2178. 'sum': function(ctx) {
  2179. assert(this.args.length == 1);
  2180. var n = this.args[0].evaluate(ctx).nodeSetValue();
  2181. var sum = 0;
  2182. for (var i = 0; i < n.length; ++i) {
  2183. sum += UTIL_xmlValue(n[i]) - 0;
  2184. }
  2185. return new NumberValue(sum);
  2186. },
  2187. 'floor': function(ctx) {
  2188. assert(this.args.length == 1);
  2189. var num = this.args[0].evaluate(ctx).numberValue();
  2190. return new NumberValue(Math.floor(num));
  2191. },
  2192. 'ceiling': function(ctx) {
  2193. assert(this.args.length == 1);
  2194. var num = this.args[0].evaluate(ctx).numberValue();
  2195. return new NumberValue(Math.ceil(num));
  2196. },
  2197. 'round': function(ctx) {
  2198. assert(this.args.length == 1);
  2199. var num = this.args[0].evaluate(ctx).numberValue();
  2200. return new NumberValue(Math.round(num));
  2201. },
  2202. // TODO: The following functions are custom. There is a
  2203. // standard that defines how to add functions, which should be
  2204. // applied here.
  2205. 'ext-join': function(ctx) {
  2206. assert(this.args.length == 2);
  2207. var nodes = this.args[0].evaluate(ctx).nodeSetValue();
  2208. var delim = this.args[1].evaluate(ctx).stringValue();
  2209. var ret = '';
  2210. for (var i = 0; i < nodes.length; ++i) {
  2211. if (ret) {
  2212. ret += delim;
  2213. }
  2214. ret += UTIL_xmlValue(nodes[i]);
  2215. }
  2216. return new StringValue(ret);
  2217. },
  2218. // ext-if() evaluates and returns its second argument, if the
  2219. // boolean value of its first argument is true, otherwise it
  2220. // evaluates and returns its third argument.
  2221. 'ext-if': function(ctx) {
  2222. assert(this.args.length == 3);
  2223. if (this.args[0].evaluate(ctx).booleanValue()) {
  2224. return this.args[1].evaluate(ctx);
  2225. } else {
  2226. return this.args[2].evaluate(ctx);
  2227. }
  2228. },
  2229. // ext-cardinal() evaluates its single argument as a number, and
  2230. // returns the current node that many times. It can be used in the
  2231. // select attribute to iterate over an integer range.
  2232. 'ext-cardinal': function(ctx) {
  2233. assert(this.args.length >= 1);
  2234. var c = this.args[0].evaluate(ctx).numberValue();
  2235. var ret = [];
  2236. for (var i = 0; i < c; ++i) {
  2237. ret.push(ctx.node);
  2238. }
  2239. return new NodeSetValue(ret);
  2240. }
  2241. };
  2242. function UnionExpr(expr1, expr2) {
  2243. this.expr1 = expr1;
  2244. this.expr2 = expr2;
  2245. }
  2246. UnionExpr.prototype.evaluate = function(ctx) {
  2247. var nodes1 = this.expr1.evaluate(ctx).nodeSetValue();
  2248. var nodes2 = this.expr2.evaluate(ctx).nodeSetValue();
  2249. var I1 = nodes1.length;
  2250. for (var i2 = 0; i2 < nodes2.length; ++i2) {
  2251. for (var i1 = 0; i1 < I1; ++i1) {
  2252. if (nodes1[i1] == nodes2[i2]) {
  2253. // break inner loop and continue outer loop, labels confuse
  2254. // the js compiler, so we don't use them here.
  2255. i1 = I1;
  2256. }
  2257. }
  2258. nodes1.push(nodes2[i2]);
  2259. }
  2260. return new NodeSetValue(nodes2);
  2261. };
  2262. function PathExpr(filter, rel) {
  2263. this.filter = filter;
  2264. this.rel = rel;
  2265. }
  2266. PathExpr.prototype.evaluate = function(ctx) {
  2267. var nodes = this.filter.evaluate(ctx).nodeSetValue();
  2268. var nodes1 = [];
  2269. for (var i = 0; i < nodes.length; ++i) {
  2270. var nodes0 = this.rel.evaluate(ctx.clone(nodes[i], i, nodes)).nodeSetValue();
  2271. for (var ii = 0; ii < nodes0.length; ++ii) {
  2272. nodes1.push(nodes0[ii]);
  2273. }
  2274. }
  2275. return new NodeSetValue(nodes1);
  2276. };
  2277. function FilterExpr(expr, predicate) {
  2278. this.expr = expr;
  2279. this.predicate = predicate;
  2280. }
  2281. FilterExpr.prototype.evaluate = function(ctx) {
  2282. var nodes = this.expr.evaluate(ctx).nodeSetValue();
  2283. for (var i = 0; i < this.predicate.length; ++i) {
  2284. var nodes0 = nodes;
  2285. nodes = [];
  2286. for (var j = 0; j < nodes0.length; ++j) {
  2287. var n = nodes0[j];
  2288. if (this.predicate[i].evaluate(ctx.clone(n, j, nodes0)).booleanValue()) {
  2289. nodes.push(n);
  2290. }
  2291. }
  2292. }
  2293. return new NodeSetValue(nodes);
  2294. }
  2295. function UnaryMinusExpr(expr) {
  2296. this.expr = expr;
  2297. }
  2298. UnaryMinusExpr.prototype.evaluate = function(ctx) {
  2299. return new NumberValue(-this.expr.evaluate(ctx).numberValue());
  2300. };
  2301. function BinaryExpr(expr1, op, expr2) {
  2302. this.expr1 = expr1;
  2303. this.expr2 = expr2;
  2304. this.op = op;
  2305. }
  2306. BinaryExpr.prototype.evaluate = function(ctx) {
  2307. var ret;
  2308. switch (this.op.value) {
  2309. case 'or':
  2310. ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() ||
  2311. this.expr2.evaluate(ctx).booleanValue());
  2312. break;
  2313. case 'and':
  2314. ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() &&
  2315. this.expr2.evaluate(ctx).booleanValue());
  2316. break;
  2317. case '+':
  2318. ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() +
  2319. this.expr2.evaluate(ctx).numberValue());
  2320. break;
  2321. case '-':
  2322. ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() -
  2323. this.expr2.evaluate(ctx).numberValue());
  2324. break;
  2325. case '*':
  2326. ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() *
  2327. this.expr2.evaluate(ctx).numberValue());
  2328. break;
  2329. case 'mod':
  2330. ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() %
  2331. this.expr2.evaluate(ctx).numberValue());
  2332. break;
  2333. case 'div':
  2334. ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() /
  2335. this.expr2.evaluate(ctx).numberValue());
  2336. break;
  2337. case '=':
  2338. ret = this.compare(ctx, function(x1, x2) { return x1 == x2; });
  2339. break;
  2340. case '!=':
  2341. ret = this.compare(ctx, function(x1, x2) { return x1 != x2; });
  2342. break;
  2343. case '<':
  2344. ret = this.compare(ctx, function(x1, x2) { return x1 < x2; });
  2345. break;
  2346. case '<=':
  2347. ret = this.compare(ctx, function(x1, x2) { return x1 <= x2; });
  2348. break;
  2349. case '>':
  2350. ret = this.compare(ctx, function(x1, x2) { return x1 > x2; });
  2351. break;
  2352. case '>=':
  2353. ret = this.compare(ctx, function(x1, x2) { return x1 >= x2; });
  2354. break;
  2355. default:
  2356. DBG_ALERT('BinaryExpr.evaluate: ' + this.op.value);
  2357. }
  2358. return ret;
  2359. };
  2360. BinaryExpr.prototype.compare = function(ctx, cmp) {
  2361. var v1 = this.expr1.evaluate(ctx);
  2362. var v2 = this.expr2.evaluate(ctx);
  2363. var ret;
  2364. if (v1.type == 'node-set' && v2.type == 'node-set') {
  2365. var n1 = v1.nodeSetValue();
  2366. var n2 = v2.nodeSetValue();
  2367. ret = false;
  2368. for (var i1 = 0; i1 < n1.length; ++i1) {
  2369. for (var i2 = 0; i2 < n2.length; ++i2) {
  2370. if (cmp(UTIL_xmlValue(n1[i1]), UTIL_xmlValue(n2[i2]))) {
  2371. ret = true;
  2372. // Break outer loop. Labels confuse the jscompiler and we
  2373. // don't use them.
  2374. i2 = n2.length;
  2375. i1 = n1.length;
  2376. }
  2377. }
  2378. }
  2379. } else if (v1.type == 'node-set' || v2.type == 'node-set') {
  2380. if (v1.type == 'number') {
  2381. var s = v1.numberValue();
  2382. var n = v2.nodeSetValue();
  2383. ret = false;
  2384. for (var i = 0; i < n.length; ++i) {
  2385. var nn = UTIL_xmlValue(n[i]) - 0;
  2386. if (cmp(s, nn)) {
  2387. ret = true;
  2388. break;
  2389. }
  2390. }
  2391. } else if (v2.type == 'number') {
  2392. var n = v1.nodeSetValue();
  2393. var s = v2.numberValue();
  2394. ret = false;
  2395. for (var i = 0; i < n.length; ++i) {
  2396. var nn = UTIL_xmlValue(n[i]) - 0;
  2397. if (cmp(nn, s)) {
  2398. ret = true;
  2399. break;
  2400. }
  2401. }
  2402. } else if (v1.type == 'string') {
  2403. var s = v1.stringValue();
  2404. var n = v2.nodeSetValue();
  2405. ret = false;
  2406. for (var i = 0; i < n.length; ++i) {
  2407. var nn = UTIL_xmlValue(n[i]);
  2408. if (cmp(s, nn)) {
  2409. ret = true;
  2410. break;
  2411. }
  2412. }
  2413. } else if (v2.type == 'string') {
  2414. var n = v1.nodeSetValue();
  2415. var s = v2.stringValue();
  2416. ret = false;
  2417. for (var i = 0; i < n.length; ++i) {
  2418. var nn = UTIL_xmlValue(n[i]);
  2419. if (cmp(nn, s)) {
  2420. ret = true;
  2421. break;
  2422. }
  2423. }
  2424. } else {
  2425. ret = cmp(v1.booleanValue(), v2.booleanValue());
  2426. }
  2427. } else if (v1.type == 'boolean' || v2.type == 'boolean') {
  2428. ret = cmp(v1.booleanValue(), v2.booleanValue());
  2429. } else if (v1.type == 'number' || v2.type == 'number') {
  2430. ret = cmp(v1.numberValue(), v2.numberValue());
  2431. } else {
  2432. ret = cmp(v1.stringValue(), v2.stringValue());
  2433. }
  2434. return new BooleanValue(ret);
  2435. }
  2436. function LiteralExpr(value) {
  2437. this.value = value;
  2438. }
  2439. LiteralExpr.prototype.evaluate = function(ctx) {
  2440. return new StringValue(this.value);
  2441. };
  2442. function NumberExpr(value) {
  2443. this.value = value;
  2444. }
  2445. NumberExpr.prototype.evaluate = function(ctx) {
  2446. return new NumberValue(this.value);
  2447. };
  2448. function VariableExpr(name) {
  2449. this.name = name;
  2450. }
  2451. VariableExpr.prototype.evaluate = function(ctx) {
  2452. return ctx.getVariable(this.name);
  2453. }
  2454. // Factory functions for semantic values (i.e. Expressions) of the
  2455. // productions in the grammar. When a production is matched to reduce
  2456. // the current parse state stack, the function is called with the
  2457. // semantic values of the matched elements as arguments, and returns
  2458. // another semantic value. The semantic value is a node of the parse
  2459. // tree, an expression object with an evaluate() method that evaluates the
  2460. // expression in an actual context. These factory functions are used
  2461. // in the specification of the grammar rules, below.
  2462. function makeTokenExpr(m) {
  2463. return new TokenExpr(m);
  2464. }
  2465. function passExpr(e) {
  2466. return e;
  2467. }
  2468. function makeLocationExpr1(slash, rel) {
  2469. rel.absolute = true;
  2470. return rel;
  2471. }
  2472. function makeLocationExpr2(dslash, rel) {
  2473. rel.absolute = true;
  2474. rel.prependStep(makeAbbrevStep(dslash.value));
  2475. return rel;
  2476. }
  2477. function makeLocationExpr3(slash) {
  2478. var ret = new LocationExpr();
  2479. ret.appendStep(makeAbbrevStep('.'));
  2480. ret.absolute = true;
  2481. return ret;
  2482. }
  2483. function makeLocationExpr4(dslash) {
  2484. var ret = new LocationExpr();
  2485. ret.absolute = true;
  2486. ret.appendStep(makeAbbrevStep(dslash.value));
  2487. return ret;
  2488. }
  2489. function makeLocationExpr5(step) {
  2490. var ret = new LocationExpr();
  2491. ret.appendStep(step);
  2492. return ret;
  2493. }
  2494. function makeLocationExpr6(rel, slash, step) {
  2495. rel.appendStep(step);
  2496. return rel;
  2497. }
  2498. function makeLocationExpr7(rel, dslash, step) {
  2499. rel.appendStep(makeAbbrevStep(dslash.value));
  2500. return rel;
  2501. }
  2502. function makeStepExpr1(dot) {
  2503. return makeAbbrevStep(dot.value);
  2504. }
  2505. function makeStepExpr2(ddot) {
  2506. return makeAbbrevStep(ddot.value);
  2507. }
  2508. function makeStepExpr3(axisname, axis, nodetest) {
  2509. return new StepExpr(axisname.value, nodetest);
  2510. }
  2511. function makeStepExpr4(at, nodetest) {
  2512. return new StepExpr('attribute', nodetest);
  2513. }
  2514. function makeStepExpr5(nodetest) {
  2515. return new StepExpr('child', nodetest);
  2516. }
  2517. function makeStepExpr6(step, predicate) {
  2518. step.appendPredicate(predicate);
  2519. return step;
  2520. }
  2521. function makeAbbrevStep(abbrev) {
  2522. switch (abbrev) {
  2523. case '//':
  2524. return new StepExpr('descendant-or-self', new NodeTestAny);
  2525. case '.':
  2526. return new StepExpr('self', new NodeTestAny);
  2527. case '..':
  2528. return new StepExpr('parent', new NodeTestAny);
  2529. }
  2530. }
  2531. function makeNodeTestExpr1(asterisk) {
  2532. return new NodeTestElement;
  2533. }
  2534. function makeNodeTestExpr2(ncname, colon, asterisk) {
  2535. return new NodeTestNC(ncname.value);
  2536. }
  2537. function makeNodeTestExpr3(qname) {
  2538. return new NodeTestName(qname.value);
  2539. }
  2540. function makeNodeTestExpr4(typeo, parenc) {
  2541. var type = typeo.value.replace(/\s*\($/, '');
  2542. switch(type) {
  2543. case 'node':
  2544. return new NodeTestAny;
  2545. case 'text':
  2546. return new NodeTestText;
  2547. case 'comment':
  2548. return new NodeTestComment;
  2549. case 'processing-instruction':
  2550. return new NodeTestPI;
  2551. }
  2552. }
  2553. function makeNodeTestExpr5(typeo, target, parenc) {
  2554. var type = typeo.replace(/\s*\($/, '');
  2555. if (type != 'processing-instruction') {
  2556. throw type;
  2557. }
  2558. return new NodeTestPI(target.value);
  2559. }
  2560. function makePredicateExpr(pareno, expr, parenc) {
  2561. return new PredicateExpr(expr);
  2562. }
  2563. function makePrimaryExpr(pareno, expr, parenc) {
  2564. return expr;
  2565. }
  2566. function makeFunctionCallExpr1(name, pareno, parenc) {
  2567. return new FunctionCallExpr(name);
  2568. }
  2569. function makeFunctionCallExpr2(name, pareno, arg1, args, parenc) {
  2570. var ret = new FunctionCallExpr(name);
  2571. ret.appendArg(arg1);
  2572. for (var i = 0; i < args.length; ++i) {
  2573. ret.appendArg(args[i]);
  2574. }
  2575. return ret;
  2576. }
  2577. function makeArgumentExpr(comma, expr) {
  2578. return expr;
  2579. }
  2580. function makeUnionExpr(expr1, pipe, expr2) {
  2581. return new UnionExpr(expr1, expr2);
  2582. }
  2583. function makePathExpr1(filter, slash, rel) {
  2584. return new PathExpr(filter, rel);
  2585. }
  2586. function makePathExpr2(filter, dslash, rel) {
  2587. rel.prependStep(makeAbbrevStep(dslash.value));
  2588. return new PathExpr(filter, rel);
  2589. }
  2590. function makeFilterExpr(expr, predicates) {
  2591. if (predicates.length > 0) {
  2592. return new FilterExpr(expr, predicates);
  2593. } else {
  2594. return expr;
  2595. }
  2596. }
  2597. function makeUnaryMinusExpr(minus, expr) {
  2598. return new UnaryMinusExpr(expr);
  2599. }
  2600. function makeBinaryExpr(expr1, op, expr2) {
  2601. return new BinaryExpr(expr1, op, expr2);
  2602. }
  2603. function makeLiteralExpr(token) {
  2604. // remove quotes from the parsed value:
  2605. var value = token.value.substring(1, token.value.length - 1);
  2606. return new LiteralExpr(value);
  2607. }
  2608. function makeNumberExpr(token) {
  2609. return new NumberExpr(token.value);
  2610. }
  2611. function makeVariableReference(dollar, name) {
  2612. return new VariableExpr(name.value);
  2613. }
  2614. // Used before parsing for optimization of common simple cases. See
  2615. // the begin of xpathParse() for which they are.
  2616. function makeSimpleExpr(expr) {
  2617. if (expr.charAt(0) == '$') {
  2618. return new VariableExpr(expr.substr(1));
  2619. } else if (expr.charAt(0) == '@') {
  2620. var a = new NodeTestName(expr.substr(1));
  2621. var b = new StepExpr('attribute', a);
  2622. var c = new LocationExpr();
  2623. c.appendStep(b);
  2624. return c;
  2625. } else if (expr.match(/^[0-9]+$/)) {
  2626. return new NumberExpr(expr);
  2627. } else {
  2628. var a = new NodeTestName(expr);
  2629. var b = new StepExpr('child', a);
  2630. var c = new LocationExpr();
  2631. c.appendStep(b);
  2632. return c;
  2633. }
  2634. }
  2635. function makeSimpleExpr2(expr) {
  2636. var steps = expr.split('/');
  2637. var c = new LocationExpr();
  2638. for (var i in steps) {
  2639. var a = new NodeTestName(steps[i]);
  2640. var b = new StepExpr('child', a);
  2641. c.appendStep(b);
  2642. }
  2643. return c;
  2644. }
  2645. // The axes of XPath expressions.
  2646. var xpathAxis = {
  2647. ANCESTOR_OR_SELF: 'ancestor-or-self',
  2648. ANCESTOR: 'ancestor',
  2649. ATTRIBUTE: 'attribute',
  2650. CHILD: 'child',
  2651. DESCENDANT_OR_SELF: 'descendant-or-self',
  2652. DESCENDANT: 'descendant',
  2653. FOLLOWING_SIBLING: 'following-sibling',
  2654. FOLLOWING: 'following',
  2655. NAMESPACE: 'namespace',
  2656. PARENT: 'parent',
  2657. PRECEDING_SIBLING: 'preceding-sibling',
  2658. PRECEDING: 'preceding',
  2659. SELF: 'self'
  2660. };
  2661. var xpathAxesRe = [
  2662. xpathAxis.ANCESTOR_OR_SELF,
  2663. xpathAxis.ANCESTOR,
  2664. xpathAxis.ATTRIBUTE,
  2665. xpathAxis.CHILD,
  2666. xpathAxis.DESCENDANT_OR_SELF,
  2667. xpathAxis.DESCENDANT,
  2668. xpathAxis.FOLLOWING_SIBLING,
  2669. xpathAxis.FOLLOWING,
  2670. xpathAxis.NAMESPACE,
  2671. xpathAxis.PARENT,
  2672. xpathAxis.PRECEDING_SIBLING,
  2673. xpathAxis.PRECEDING,
  2674. xpathAxis.SELF
  2675. ].join('|');
  2676. // The tokens of the language. The label property is just used for
  2677. // generating debug output. The prec property is the precedence used
  2678. // for shift/reduce resolution. Default precedence is 0 as a lookahead
  2679. // token and 2 on the stack. TODO: this is certainly not
  2680. // necessary and too complicated. Simplify this!
  2681. // NOTE: tabular formatting is the big exception, but here it should
  2682. // be OK.
  2683. var TOK_PIPE = { label: "|", prec: 17, re: new RegExp("^\\|") };
  2684. var TOK_DSLASH = { label: "//", prec: 19, re: new RegExp("^//") };
  2685. var TOK_SLASH = { label: "/", prec: 30, re: new RegExp("^/") };
  2686. var TOK_AXIS = { label: "::", prec: 20, re: new RegExp("^::") };
  2687. var TOK_COLON = { label: ":", prec: 1000, re: new RegExp("^:") };
  2688. var TOK_AXISNAME = { label: "[axis]", re: new RegExp('^(' + xpathAxesRe + ')') };
  2689. var TOK_PARENO = { label: "(", prec: 34, re: new RegExp("^\\(") };
  2690. var TOK_PARENC = { label: ")", re: new RegExp("^\\)") };
  2691. var TOK_DDOT = { label: "..", prec: 34, re: new RegExp("^\\.\\.") };
  2692. var TOK_DOT = { label: ".", prec: 34, re: new RegExp("^\\.") };
  2693. var TOK_AT = { label: "@", prec: 34, re: new RegExp("^@") };
  2694. var TOK_COMMA = { label: ",", re: new RegExp("^,") };
  2695. var TOK_OR = { label: "or", prec: 10, re: new RegExp("^or\\b") };
  2696. var TOK_AND = { label: "and", prec: 11, re: new RegExp("^and\\b") };
  2697. var TOK_EQ = { label: "=", prec: 12, re: new RegExp("^=") };
  2698. var TOK_NEQ = { label: "!=", prec: 12, re: new RegExp("^!=") };
  2699. var TOK_GE = { label: ">=", prec: 13, re: new RegExp("^>=") };
  2700. var TOK_GT = { label: ">", prec: 13, re: new RegExp("^>") };
  2701. var TOK_LE = { label: "<=", prec: 13, re: new RegExp("^<=") };
  2702. var TOK_LT = { label: "<", prec: 13, re: new RegExp("^<") };
  2703. var TOK_PLUS = { label: "+", prec: 14, re: new RegExp("^\\+"), left: true };
  2704. var TOK_MINUS = { label: "-", prec: 14, re: new RegExp("^\\-"), left: true };
  2705. var TOK_DIV = { label: "div", prec: 15, re: new RegExp("^div\\b"), left: true };
  2706. var TOK_MOD = { label: "mod", prec: 15, re: new RegExp("^mod\\b"), left: true };
  2707. var TOK_BRACKO = { label: "[", prec: 32, re: new RegExp("^\\[") };
  2708. var TOK_BRACKC = { label: "]", re: new RegExp("^\\]") };
  2709. var TOK_DOLLAR = { label: "$", re: new RegExp("^\\$") };
  2710. var TOK_NCNAME = { label: "[ncname]", re: new RegExp('^[a-z][-\\w]*','i') };
  2711. var TOK_ASTERISK = { label: "*", prec: 15, re: new RegExp("^\\*"), left: true };
  2712. var TOK_LITERALQ = { label: "[litq]", prec: 20, re: new RegExp("^'[^\\']*'") };
  2713. var TOK_LITERALQQ = {
  2714. label: "[litqq]",
  2715. prec: 20,
  2716. re: new RegExp('^"[^\\"]*"')
  2717. };
  2718. var TOK_NUMBER = {
  2719. label: "[number]",
  2720. prec: 35,
  2721. re: new RegExp('^\\d+(\\.\\d*)?') };
  2722. var TOK_QNAME = {
  2723. label: "[qname]",
  2724. re: new RegExp('^([a-z][-\\w]*:)?[a-z][-\\w]*','i')
  2725. };
  2726. var TOK_NODEO = {
  2727. label: "[nodetest-start]",
  2728. re: new RegExp('^(processing-instruction|comment|text|node)\\(')
  2729. };
  2730. // The table of the tokens of our grammar, used by the lexer: first
  2731. // column the tag, second column a regexp to recognize it in the
  2732. // input, third column the precedence of the token, fourth column a
  2733. // factory function for the semantic value of the token.
  2734. //
  2735. // NOTE: order of this list is important, because the first match
  2736. // counts. Cf. DDOT and DOT, and AXIS and COLON.
  2737. var xpathTokenRules = [
  2738. TOK_DSLASH,
  2739. TOK_SLASH,
  2740. TOK_DDOT,
  2741. TOK_DOT,
  2742. TOK_AXIS,
  2743. TOK_COLON,
  2744. TOK_AXISNAME,
  2745. TOK_NODEO,
  2746. TOK_PARENO,
  2747. TOK_PARENC,
  2748. TOK_BRACKO,
  2749. TOK_BRACKC,
  2750. TOK_AT,
  2751. TOK_COMMA,
  2752. TOK_OR,
  2753. TOK_AND,
  2754. TOK_NEQ,
  2755. TOK_EQ,
  2756. TOK_GE,
  2757. TOK_GT,
  2758. TOK_LE,
  2759. TOK_LT,
  2760. TOK_PLUS,
  2761. TOK_MINUS,
  2762. TOK_ASTERISK,
  2763. TOK_PIPE,
  2764. TOK_MOD,
  2765. TOK_DIV,
  2766. TOK_LITERALQ,
  2767. TOK_LITERALQQ,
  2768. TOK_NUMBER,
  2769. TOK_QNAME,
  2770. TOK_NCNAME,
  2771. TOK_DOLLAR
  2772. ];
  2773. // All the nonterminals of the grammar. The nonterminal objects are
  2774. // identified by object identity; the labels are used in the debug
  2775. // output only.
  2776. var XPathLocationPath = { label: "LocationPath" };
  2777. var XPathRelativeLocationPath = { label: "RelativeLocationPath" };
  2778. var XPathAbsoluteLocationPath = { label: "AbsoluteLocationPath" };
  2779. var XPathStep = { label: "Step" };
  2780. var XPathNodeTest = { label: "NodeTest" };
  2781. var XPathPredicate = { label: "Predicate" };
  2782. var XPathLiteral = { label: "Literal" };
  2783. var XPathExpr = { label: "Expr" };
  2784. var XPathPrimaryExpr = { label: "PrimaryExpr" };
  2785. var XPathVariableReference = { label: "Variablereference" };
  2786. var XPathNumber = { label: "Number" };
  2787. var XPathFunctionCall = { label: "FunctionCall" };
  2788. var XPathArgumentRemainder = { label: "ArgumentRemainder" };
  2789. var XPathPathExpr = { label: "PathExpr" };
  2790. var XPathUnionExpr = { label: "UnionExpr" };
  2791. var XPathFilterExpr = { label: "FilterExpr" };
  2792. var XPathDigits = { label: "Digits" };
  2793. var xpathNonTerminals = [
  2794. XPathLocationPath,
  2795. XPathRelativeLocationPath,
  2796. XPathAbsoluteLocationPath,
  2797. XPathStep,
  2798. XPathNodeTest,
  2799. XPathPredicate,
  2800. XPathLiteral,
  2801. XPathExpr,
  2802. XPathPrimaryExpr,
  2803. XPathVariableReference,
  2804. XPathNumber,
  2805. XPathFunctionCall,
  2806. XPathArgumentRemainder,
  2807. XPathPathExpr,
  2808. XPathUnionExpr,
  2809. XPathFilterExpr,
  2810. XPathDigits
  2811. ];
  2812. // Quantifiers that are used in the productions of the grammar.
  2813. var Q_01 = { label: "?" };
  2814. var Q_MM = { label: "*" };
  2815. var Q_1M = { label: "+" };
  2816. // Tag for left associativity (right assoc is implied by undefined).
  2817. var ASSOC_LEFT = true;
  2818. // The productions of the grammar. Columns of the table:
  2819. //
  2820. // - target nonterminal,
  2821. // - pattern,
  2822. // - precedence,
  2823. // - semantic value factory
  2824. //
  2825. // The semantic value factory is a function that receives parse tree
  2826. // nodes from the stack frames of the matched symbols as arguments and
  2827. // returns an a node of the parse tree. The node is stored in the top
  2828. // stack frame along with the target object of the rule. The node in
  2829. // the parse tree is an expression object that has an evaluate() method
  2830. // and thus evaluates XPath expressions.
  2831. //
  2832. // The precedence is used to decide between reducing and shifting by
  2833. // comparing the precendence of the rule that is candidate for
  2834. // reducing with the precedence of the look ahead token. Precedence of
  2835. // -1 means that the precedence of the tokens in the pattern is used
  2836. // instead. TODO: It shouldn't be necessary to explicitly assign
  2837. // precedences to rules.
  2838. var xpathGrammarRules =
  2839. [
  2840. [ XPathLocationPath, [ XPathRelativeLocationPath ], 18,
  2841. passExpr ],
  2842. [ XPathLocationPath, [ XPathAbsoluteLocationPath ], 18,
  2843. passExpr ],
  2844. [ XPathAbsoluteLocationPath, [ TOK_SLASH, XPathRelativeLocationPath ], 18,
  2845. makeLocationExpr1 ],
  2846. [ XPathAbsoluteLocationPath, [ TOK_DSLASH, XPathRelativeLocationPath ], 18,
  2847. makeLocationExpr2 ],
  2848. [ XPathAbsoluteLocationPath, [ TOK_SLASH ], 0,
  2849. makeLocationExpr3 ],
  2850. [ XPathAbsoluteLocationPath, [ TOK_DSLASH ], 0,
  2851. makeLocationExpr4 ],
  2852. [ XPathRelativeLocationPath, [ XPathStep ], 31,
  2853. makeLocationExpr5 ],
  2854. [ XPathRelativeLocationPath,
  2855. [ XPathRelativeLocationPath, TOK_SLASH, XPathStep ], 31,
  2856. makeLocationExpr6 ],
  2857. [ XPathRelativeLocationPath,
  2858. [ XPathRelativeLocationPath, TOK_DSLASH, XPathStep ], 31,
  2859. makeLocationExpr7 ],
  2860. [ XPathStep, [ TOK_DOT ], 33,
  2861. makeStepExpr1 ],
  2862. [ XPathStep, [ TOK_DDOT ], 33,
  2863. makeStepExpr2 ],
  2864. [ XPathStep,
  2865. [ TOK_AXISNAME, TOK_AXIS, XPathNodeTest ], 33,
  2866. makeStepExpr3 ],
  2867. [ XPathStep, [ TOK_AT, XPathNodeTest ], 33,
  2868. makeStepExpr4 ],
  2869. [ XPathStep, [ XPathNodeTest ], 33,
  2870. makeStepExpr5 ],
  2871. [ XPathStep, [ XPathStep, XPathPredicate ], 33,
  2872. makeStepExpr6 ],
  2873. [ XPathNodeTest, [ TOK_ASTERISK ], 33,
  2874. makeNodeTestExpr1 ],
  2875. [ XPathNodeTest, [ TOK_NCNAME, TOK_COLON, TOK_ASTERISK ], 33,
  2876. makeNodeTestExpr2 ],
  2877. [ XPathNodeTest, [ TOK_QNAME ], 33,
  2878. makeNodeTestExpr3 ],
  2879. [ XPathNodeTest, [ TOK_NODEO, TOK_PARENC ], 33,
  2880. makeNodeTestExpr4 ],
  2881. [ XPathNodeTest, [ TOK_NODEO, XPathLiteral, TOK_PARENC ], 33,
  2882. makeNodeTestExpr5 ],
  2883. [ XPathPredicate, [ TOK_BRACKO, XPathExpr, TOK_BRACKC ], 33,
  2884. makePredicateExpr ],
  2885. [ XPathPrimaryExpr, [ XPathVariableReference ], 33,
  2886. passExpr ],
  2887. [ XPathPrimaryExpr, [ TOK_PARENO, XPathExpr, TOK_PARENC ], 33,
  2888. makePrimaryExpr ],
  2889. [ XPathPrimaryExpr, [ XPathLiteral ], 30,
  2890. passExpr ],
  2891. [ XPathPrimaryExpr, [ XPathNumber ], 30,
  2892. passExpr ],
  2893. [ XPathPrimaryExpr, [ XPathFunctionCall ], 30,
  2894. passExpr ],
  2895. [ XPathFunctionCall, [ TOK_QNAME, TOK_PARENO, TOK_PARENC ], -1,
  2896. makeFunctionCallExpr1 ],
  2897. [ XPathFunctionCall,
  2898. [ TOK_QNAME, TOK_PARENO, XPathExpr, XPathArgumentRemainder, Q_MM,
  2899. TOK_PARENC ], -1,
  2900. makeFunctionCallExpr2 ],
  2901. [ XPathArgumentRemainder, [ TOK_COMMA, XPathExpr ], -1,
  2902. makeArgumentExpr ],
  2903. [ XPathUnionExpr, [ XPathPathExpr ], 20,
  2904. passExpr ],
  2905. [ XPathUnionExpr, [ XPathUnionExpr, TOK_PIPE, XPathPathExpr ], 20,
  2906. makeUnionExpr ],
  2907. [ XPathPathExpr, [ XPathLocationPath ], 20,
  2908. passExpr ],
  2909. [ XPathPathExpr, [ XPathFilterExpr ], 19,
  2910. passExpr ],
  2911. [ XPathPathExpr,
  2912. [ XPathFilterExpr, TOK_SLASH, XPathRelativeLocationPath ], 20,
  2913. makePathExpr1 ],
  2914. [ XPathPathExpr,
  2915. [ XPathFilterExpr, TOK_DSLASH, XPathRelativeLocationPath ], 20,
  2916. makePathExpr2 ],
  2917. [ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 20,
  2918. makeFilterExpr ],
  2919. [ XPathExpr, [ XPathPrimaryExpr ], 16,
  2920. passExpr ],
  2921. [ XPathExpr, [ XPathUnionExpr ], 16,
  2922. passExpr ],
  2923. [ XPathExpr, [ TOK_MINUS, XPathExpr ], -1,
  2924. makeUnaryMinusExpr ],
  2925. [ XPathExpr, [ XPathExpr, TOK_OR, XPathExpr ], -1,
  2926. makeBinaryExpr ],
  2927. [ XPathExpr, [ XPathExpr, TOK_AND, XPathExpr ], -1,
  2928. makeBinaryExpr ],
  2929. [ XPathExpr, [ XPathExpr, TOK_EQ, XPathExpr ], -1,
  2930. makeBinaryExpr ],
  2931. [ XPathExpr, [ XPathExpr, TOK_NEQ, XPathExpr ], -1,
  2932. makeBinaryExpr ],
  2933. [ XPathExpr, [ XPathExpr, TOK_LT, XPathExpr ], -1,
  2934. makeBinaryExpr ],
  2935. [ XPathExpr, [ XPathExpr, TOK_LE, XPathExpr ], -1,
  2936. makeBinaryExpr ],
  2937. [ XPathExpr, [ XPathExpr, TOK_GT, XPathExpr ], -1,
  2938. makeBinaryExpr ],
  2939. [ XPathExpr, [ XPathExpr, TOK_GE, XPathExpr ], -1,
  2940. makeBinaryExpr ],
  2941. [ XPathExpr, [ XPathExpr, TOK_PLUS, XPathExpr ], -1,
  2942. makeBinaryExpr, ASSOC_LEFT ],
  2943. [ XPathExpr, [ XPathExpr, TOK_MINUS, XPathExpr ], -1,
  2944. makeBinaryExpr, ASSOC_LEFT ],
  2945. [ XPathExpr, [ XPathExpr, TOK_ASTERISK, XPathExpr ], -1,
  2946. makeBinaryExpr, ASSOC_LEFT ],
  2947. [ XPathExpr, [ XPathExpr, TOK_DIV, XPathExpr ], -1,
  2948. makeBinaryExpr, ASSOC_LEFT ],
  2949. [ XPathExpr, [ XPathExpr, TOK_MOD, XPathExpr ], -1,
  2950. makeBinaryExpr, ASSOC_LEFT ],
  2951. [ XPathLiteral, [ TOK_LITERALQ ], -1,
  2952. makeLiteralExpr ],
  2953. [ XPathLiteral, [ TOK_LITERALQQ ], -1,
  2954. makeLiteralExpr ],
  2955. [ XPathNumber, [ TOK_NUMBER ], -1,
  2956. makeNumberExpr ],
  2957. [ XPathVariableReference, [ TOK_DOLLAR, TOK_QNAME ], 200,
  2958. makeVariableReference ]
  2959. ];
  2960. // That function computes some optimizations of the above data
  2961. // structures and will be called right here. It merely takes the
  2962. // counter variables out of the global scope.
  2963. var xpathRules = [];
  2964. function xpathParseInit() {
  2965. if (xpathRules.length) {
  2966. return;
  2967. }
  2968. // Some simple optimizations for the xpath expression parser: sort
  2969. // grammar rules descending by length, so that the longest match is
  2970. // first found.
  2971. xpathGrammarRules.sort(function(a,b) {
  2972. var la = a[1].length;
  2973. var lb = b[1].length;
  2974. if (la < lb) {
  2975. return 1;
  2976. } else if (la > lb) {
  2977. return -1;
  2978. } else {
  2979. return 0;
  2980. }
  2981. });
  2982. var k = 1;
  2983. for (var i = 0; i < xpathNonTerminals.length; ++i) {
  2984. xpathNonTerminals[i].key = k++;
  2985. }
  2986. for (i = 0; i < xpathTokenRules.length; ++i) {
  2987. xpathTokenRules[i].key = k++;
  2988. }
  2989. LOG_DEBUG('XPath parse INIT: ' + k + ' rules');
  2990. // Another slight optimization: sort the rules into bins according
  2991. // to the last element (observing quantifiers), so we can restrict
  2992. // the match against the stack to the subest of rules that match the
  2993. // top of the stack.
  2994. //
  2995. // TODO: What we actually want is to compute states as in
  2996. // bison, so that we don't have to do any explicit and iterated
  2997. // match against the stack.
  2998. function push_(array, position, element) {
  2999. if (!array[position]) {
  3000. array[position] = [];
  3001. }
  3002. array[position].push(element);
  3003. }
  3004. for (i = 0; i < xpathGrammarRules.length; ++i) {
  3005. var rule = xpathGrammarRules[i];
  3006. var pattern = rule[1];
  3007. for (var j = pattern.length - 1; j >= 0; --j) {
  3008. if (pattern[j] == Q_1M) {
  3009. push_(xpathRules, pattern[j-1].key, rule);
  3010. break;
  3011. } else if (pattern[j] == Q_MM || pattern[j] == Q_01) {
  3012. push_(xpathRules, pattern[j-1].key, rule);
  3013. --j;
  3014. } else {
  3015. push_(xpathRules, pattern[j].key, rule);
  3016. break;
  3017. }
  3018. }
  3019. }
  3020. LOG_DEBUG('XPath parse INIT: ' + xpathRules.length + ' rule bins');
  3021. var sum = 0;
  3022. UTIL_mapExec(xpathRules, function(i) {
  3023. if (i) {
  3024. sum += i.length;
  3025. }
  3026. });
  3027. LOG_DEBUG('XPath parse INIT: ' + (sum / xpathRules.length) + ' average bin size');
  3028. }
  3029. // Local utility functions that are used by the lexer or parser.
  3030. function xpathCollectDescendants(nodelist, node) {
  3031. for (var n = node.firstChild; n; n = n.nextSibling) {
  3032. nodelist.push(n);
  3033. arguments.callee(nodelist, n);
  3034. }
  3035. }
  3036. function xpathCollectDescendantsReverse(nodelist, node) {
  3037. for (var n = node.lastChild; n; n = n.previousSibling) {
  3038. nodelist.push(n);
  3039. arguments.callee(nodelist, n);
  3040. }
  3041. }
  3042. // The entry point for the library: match an expression against a DOM
  3043. // node. Returns an XPath value.
  3044. function xpathDomEval(expr, node) {
  3045. var expr1 = xpathParse(expr);
  3046. var ret = expr1.evaluate(new ExprContext(node));
  3047. return ret;
  3048. }
  3049. // Utility function to sort a list of nodes. Used by xsltSort() and
  3050. // nxslSelect().
  3051. function xpathSort(input, sort) {
  3052. if (sort.length == 0) {
  3053. return;
  3054. }
  3055. var sortlist = [];
  3056. for (var i = 0; i < input.nodelist.length; ++i) {
  3057. var node = input.nodelist[i];
  3058. var sortitem = { node: node, key: [] };
  3059. var context = input.clone(node, 0, [ node ]);
  3060. for (var j = 0; j < sort.length; ++j) {
  3061. var s = sort[j];
  3062. var value = s.expr.evaluate(context);
  3063. var evalue;
  3064. if (s.type == 'text') {
  3065. evalue = value.stringValue();
  3066. } else if (s.type == 'number') {
  3067. evalue = value.numberValue();
  3068. }
  3069. sortitem.key.push({ value: evalue, order: s.order });
  3070. }
  3071. // Make the sort stable by adding a lowest priority sort by
  3072. // id. This is very convenient and furthermore required by the
  3073. // spec ([XSLT] - Section 10 Sorting).
  3074. sortitem.key.push({ value: i, order: 'ascending' });
  3075. sortlist.push(sortitem);
  3076. }
  3077. sortlist.sort(xpathSortByKey);
  3078. var nodes = [];
  3079. for (var i = 0; i < sortlist.length; ++i) {
  3080. nodes.push(sortlist[i].node);
  3081. }
  3082. input.nodelist = nodes;
  3083. input.setNode(nodes[0], 0);
  3084. }
  3085. // Sorts by all order criteria defined. According to the JavaScript
  3086. // spec ([ECMA] Section 11.8.5), the compare operators compare strings
  3087. // as strings and numbers as numbers.
  3088. //
  3089. // NOTE: In browsers which do not follow the spec, this breaks only in
  3090. // the case that numbers should be sorted as strings, which is very
  3091. // uncommon.
  3092. function xpathSortByKey(v1, v2) {
  3093. // NOTE: Sort key vectors of different length never occur in
  3094. // xsltSort.
  3095. for (var i = 0; i < v1.key.length; ++i) {
  3096. var o = v1.key[i].order == 'descending' ? -1 : 1;
  3097. if (v1.key[i].value > v2.key[i].value) {
  3098. return +1 * o;
  3099. } else if (v1.key[i].value < v2.key[i].value) {
  3100. return -1 * o;
  3101. }
  3102. }
  3103. return 0;
  3104. }
  3105. // remove this if you merge
  3106. if (window.GD_Loader) {
  3107. // continue loading
  3108. window.GD_Loader();
  3109. }
  3110. // end
  3111. /* Copyright (c) 2006 Google Inc.
  3112. *
  3113. * Licensed under the Apache License, Version 2.0 (the "License");
  3114. * you may not use this file except in compliance with the License.
  3115. * You may obtain a copy of the License at
  3116. *
  3117. * http://www.apache.org/licenses/LICENSE-2.0
  3118. *
  3119. * Unless required by applicable law or agreed to in writing, software
  3120. * distributed under the License is distributed on an "AS IS" BASIS,
  3121. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3122. * See the License for the specific language governing permissions and
  3123. * limitations under the License.
  3124. */
  3125. /**
  3126. * @fileoverview
  3127. * XML Support for Mozilla/IE equalization
  3128. * also some base XML support classes for convienience, like the binding
  3129. * system
  3130. * XML Binding
  3131. * - binders are utility classes that are used to read and write values from
  3132. * and to an XML document; they are configured using xpath expressions.
  3133. * - there are scalar binders for text nodes and attributes, and a list
  3134. * binder for repeated node elements; more elaborate binders can be hand built.
  3135. * - binders are stateless so they are reusable across XML documents.
  3136. * - controls are constructed with one or more binders.
  3137. * - controls paint into and extract (save) values from DOM elements.
  3138. * - type handlers coordinate the binding of controls to XML documents.
  3139. */
  3140. var XML = {};
  3141. XML.FEED_HEADER = '<?xml version="1.0"?>';
  3142. XML.DEFAULT_NAMESPACE = "atom";
  3143. XML.NAMESPACE_MAP = {
  3144. "atom" : 'http://www.w3.org/2005/Atom',
  3145. "fs" : 'http://www.google.com/schema/forseti/2005-04-01',
  3146. "gd" : 'http://schemas.google.com/g/2005',
  3147. "gCal" : 'http://schemas.google.com/gCal/2005',
  3148. "xsl" : 'http://www.w3.org/1999/XSL/Transform',
  3149. "dc" : 'http://purl.org/dc/elements/1.1/',
  3150. "content" : 'http://purl.org/rss/1.0/modules/content/',
  3151. "xhtml" : 'http://www.w3.org/1999/xhtml'};
  3152. XML.KIND_SCHEME = "http://schemas.google.com/g/2005#kind";
  3153. XML.NAMESPACES = "";
  3154. new function() {
  3155. var i = 0;
  3156. for (var abbr in XML.NAMESPACE_MAP) {
  3157. if (abbr) {
  3158. ++i;
  3159. if (i > 0) {
  3160. XML.NAMESPACES += " ";
  3161. }
  3162. XML.NAMESPACES += "xmlns:" + abbr + "='" + XML.NAMESPACE_MAP[abbr] + "'";
  3163. }
  3164. }
  3165. };
  3166. /**
  3167. * @param abbr {String} a string indicating the prefix for the namespace
  3168. * @return the full namespace
  3169. */
  3170. function XML_getNameSpace(abbr) {
  3171. var ns = XML.NAMESPACE_MAP[abbr];
  3172. DBG_ASSERT(ns, "XML.GET_NAMESPACE: " + abbr);
  3173. return ns;
  3174. }
  3175. /**
  3176. * helper to check a loaded dom document
  3177. * @param doc the dom document object
  3178. * @return the xml error parser value
  3179. */
  3180. function XML_ValidateDocument(doc) {
  3181. var err = null;
  3182. if (!doc) {
  3183. DBG_ALERT("no valid XML document passed to XML_ValidateDocument");
  3184. } else {
  3185. if (doc.parseError !== 0) {
  3186. err = "Could not obtain document; possibly a connection failure or a permission problem." + doc.parseError;
  3187. } else {
  3188. LOG_XML_PRINT(doc);
  3189. }
  3190. }
  3191. return err;
  3192. }
  3193. /**
  3194. * @param doc {Object} a document object
  3195. * @return a string representing the persisted document, including feed header
  3196. */
  3197. function XML_serializeDocument(doc) {
  3198. return XML.FEED_HEADER + XMLX_serializeNode(doc);
  3199. }
  3200. /**
  3201. * @param opt_xml xml to create a document from
  3202. * @return an xml document object
  3203. */
  3204. function XML_createDocument(opt_xml) {
  3205. LOG_DEBUG("XML_createDocument: " + opt_xml);
  3206. var doc = XMLX_createDomDocument(opt_xml);
  3207. return doc;
  3208. }
  3209. /**
  3210. * @param xmlDoc xmlDoc to create a node in
  3211. * @param parent the parent node
  3212. * @param elementName the name of the new element node
  3213. * @opt_namespace the optional namespace of the element
  3214. * @opt_text the optional text for the element
  3215. * @opt_head indicates if the new node should be appended or inserted before
  3216. * @return the new node
  3217. */
  3218. function XML_createNode(xmlDoc, parent, elementName, opt_namespace, opt_text, opt_head) {
  3219. DBG_ASSERT(xmlDoc, "XML_createNode: " + [xmlDoc, parent, elementName, opt_namespace, opt_text]);
  3220. if (!opt_namespace) {
  3221. opt_namespace = XML.DEFAULT_NAMESPACE;
  3222. }
  3223. var ns = XML_getNameSpace(opt_namespace);
  3224. var node = xmlDoc.CreateNode(1, opt_namespace + ":" + elementName, ns);
  3225. if (opt_text) {
  3226. node.text = opt_text;
  3227. }
  3228. if (parent) {
  3229. if (opt_head) {
  3230. if (parent.childNodes.length) {
  3231. parent.insertBefore(node, parent.childNodes[0]);
  3232. } else {
  3233. parent.appendChild(node);
  3234. }
  3235. } else {
  3236. parent.appendChild(node);
  3237. }
  3238. }
  3239. return node;
  3240. }
  3241. /**
  3242. * @param tag the tag to create
  3243. * @opt_namespace the optional namespace the tag is in
  3244. * @opt_attributes the attributes of the tag
  3245. * @constructor
  3246. */
  3247. function XML_Tag(tag, opt_namespace, opt_attributes) {
  3248. this.tag_ = tag;
  3249. if (Detect.XPathSupport() || opt_namespace) {
  3250. // only do the namespace jumbo when we have support
  3251. this.namespace_ = opt_namespace ? opt_namespace : XML.DEFAULT_NAMESPACE;
  3252. this.qname_ = this.namespace_ + ":" + this.tag_;
  3253. } else {
  3254. this.namespace_ = "";
  3255. this.qname_ = this.tag_;
  3256. }
  3257. this.attributes_ = opt_attributes;
  3258. // TODO: iterate attributes for filtering (how to do multiple attribute)
  3259. if (opt_attributes) {
  3260. this.qname_ += "[";
  3261. var count = 0;
  3262. for (var attr in opt_attributes) {
  3263. if (count) {
  3264. this.qname_ += " and ";
  3265. }
  3266. this.qname_ += "@" + attr + "='" + opt_attributes[attr] + "'";
  3267. ++count;
  3268. }
  3269. this.qname_ += "]";
  3270. }
  3271. }
  3272. XML_Tag.prototype.toString = function() {
  3273. return "XML_Tag('" + this.qname_ + "')";
  3274. };
  3275. /**
  3276. * @xmlRoot the parent node
  3277. * @opt_head if the new node should be created before or after
  3278. * @return the new node
  3279. */
  3280. XML_Tag.prototype.CreateNode = function(xmlRoot, opt_head) {
  3281. var xmlNode = XML_createNode(xmlRoot.ownerDocument, xmlRoot, this.tag_, this.namespace_, null, opt_head);
  3282. for (var attr in this.attributes_) {
  3283. if (this.attributes_[attr]) {
  3284. xmlNode.setAttribute(attr, this.attributes_[attr]);
  3285. }
  3286. }
  3287. return xmlNode;
  3288. };
  3289. /**
  3290. * Example: XML_NodeBinder(new XML_Tag("id")),
  3291. * @param tag the tag to bind to
  3292. * @constructor
  3293. */
  3294. function XML_NodeBinder(tags) {
  3295. if (tags) {
  3296. // make array (since tags can by array or single tag)
  3297. this.tags_ = tags.length ? tags : [tags];
  3298. var qname = "";
  3299. for (var i = 0; i < this.tags_.length; i+=1) {
  3300. if (i > 0) {
  3301. qname += "/";
  3302. }
  3303. qname += this.tags_[i].qname_;
  3304. }
  3305. this.qname_ = qname;
  3306. // TODO: compute parent qname
  3307. } else {
  3308. this.tags_ = null;
  3309. this.qname_ = null;
  3310. }
  3311. this.parentQname_ = null;
  3312. }
  3313. XML_NodeBinder.prototype.toString = function() {
  3314. return "XML_NodeBinder('" + this.qname_ + "')";
  3315. };
  3316. /**
  3317. * returns the node that is bound
  3318. * @param xmlRoot the node to test
  3319. * @return the bound node
  3320. */
  3321. XML_NodeBinder.prototype.GetNode = function(xmlRoot) {
  3322. return this.qname_ ? XMLX_getNode(xmlRoot, this.qname_) : xmlRoot;
  3323. };
  3324. /**
  3325. * either get's the bound node, or creates one in the parent
  3326. * @param xmlRoot the node to test
  3327. * @return the bound or newly created node
  3328. */
  3329. XML_NodeBinder.prototype.GetOrCreateNode = function(xmlRoot) {
  3330. var xmlNode = this.GetNode(xmlRoot);
  3331. if (!xmlNode) {
  3332. xmlNode = this.CreateNode(xmlRoot);
  3333. }
  3334. DBG_ASSERT(xmlNode, this + ".GetOrCreateNode: " + xmlRoot);
  3335. return xmlNode;
  3336. };
  3337. /**
  3338. * creates a new node in the parent
  3339. * @param xmlRoot the node to test
  3340. * @return the newly created node
  3341. */
  3342. XML_NodeBinder.prototype.CreateNode = function(xmlRoot, opt_head) {
  3343. DBG_ASSERT(this.tags_, "XML_NodeBinder.CreateNode: No tags");
  3344. if (this.tags_.length == 1) {
  3345. return this.tags_[0].CreateNode(xmlRoot, opt_head);
  3346. } else {
  3347. var parentNode = this.GetParentNode(xmlRoot, true);
  3348. return this.tags_[this.tags_.length-1].CreateNode(parentNode, opt_head);
  3349. }
  3350. };
  3351. /**
  3352. * deletes a node in the parent
  3353. * @param xmlRoot the node to test
  3354. * @return true if there was a node to delete
  3355. */
  3356. XML_NodeBinder.prototype.DeleteNode = function(xmlRoot) {
  3357. var xmlNode = this.GetNode(xmlRoot);
  3358. if (xmlNode) {
  3359. var parentNode = this.GetParentNode(xmlRoot);
  3360. parentNode.removeChild(xmlNode);
  3361. return true;
  3362. }
  3363. return false;
  3364. };
  3365. /**
  3366. * returns the parentNode. If that one does not exist yet, create one
  3367. * @param xmlRoot the node to test
  3368. * @param opt_create create if true and parent does not exist yet
  3369. * @return the parent node
  3370. */
  3371. XML_NodeBinder.prototype.GetParentNode = function(xmlRoot, opt_create) {
  3372. DBG_ASSERT(this.tags_, "XML_NodeBinder.GetParentNode: No tags");
  3373. // TODO: pre-compute xpath of parent node
  3374. var len = this.tags_.length;
  3375. if (len == 1) {
  3376. return xmlRoot;
  3377. }
  3378. var xmlNode = null;
  3379. if (this.parentQname_) {
  3380. xmlNode = xmlRoot.selectSingleNode(this.parentQname_);
  3381. }
  3382. if (!xmlNode) {
  3383. // create parents
  3384. for (var i = 0; i < len-1; i++) {
  3385. xmlNode = xmlRoot.selectSingleNode(this.tags_[i].qname_);
  3386. if (!xmlNode) {
  3387. if (!opt_create) {
  3388. return null;
  3389. }
  3390. xmlNode = this.tags_[i].CreateNode(xmlRoot);
  3391. }
  3392. xmlRoot = xmlNode;
  3393. }
  3394. }
  3395. return xmlNode;
  3396. };
  3397. /**
  3398. * Example: XML_NodeListBinder(new XML_Tag("id")),
  3399. * @param tag the tag to bind to
  3400. * @constructor
  3401. */
  3402. function XML_NodeListBinder(tags) {
  3403. DBG_ASSERT(tags, "XML_NodeListBinder");
  3404. XML_NodeBinder.call(this, tags);
  3405. }
  3406. UTIL_inherits(XML_NodeListBinder, XML_NodeBinder);
  3407. XML_NodeListBinder.prototype.toString = function() {
  3408. return "XML_NodeListBinder('" + this.qname_ + "')";
  3409. };
  3410. /**
  3411. * @param xmlRoot the node to query
  3412. * @return a nodelist
  3413. */
  3414. XML_NodeListBinder.prototype.GetNodes = function(xmlRoot) {
  3415. return XMLX_getNodes(xmlRoot, this.qname_);
  3416. };
  3417. /**
  3418. * @param xmlRoot the parent node
  3419. * @param xmlNode the node to delete
  3420. * @return true if successfully deleted
  3421. */
  3422. XML_NodeListBinder.prototype.DeleteNode = function(xmlRoot, xmlNode) {
  3423. var parentNode = this.GetParentNode(xmlRoot);
  3424. if (parentNode) {
  3425. parentNode.removeChild(xmlNode);
  3426. return true;
  3427. }
  3428. return false;
  3429. };
  3430. /**
  3431. * removes all child nodes
  3432. * @param xmlRoot the parent node
  3433. * @return true if successfully deleted
  3434. */
  3435. XML_NodeListBinder.prototype.DeleteNodes = function(xmlRoot) {
  3436. var parentNode = this.GetParentNode(xmlRoot);
  3437. if (parentNode) {
  3438. var nodes = this.GetNodes(xmlRoot);
  3439. for (var i = 0; i < nodes.length; i+=1) {
  3440. parentNode.removeChild(nodes[i]);
  3441. }
  3442. return true;
  3443. }
  3444. return false;
  3445. };
  3446. /**
  3447. * Example: XML_TextBinder(new XML_Tag("id")),
  3448. * @param tag the tag to bind to
  3449. * @opt_default the optional default value
  3450. * @constructor
  3451. */
  3452. function XML_TextBinder(tags, opt_default) {
  3453. DBG_ASSERT(tags, "XML_TextBinder");
  3454. XML_NodeBinder.call(this, tags);
  3455. this.defaultValue_ = opt_default;
  3456. }
  3457. UTIL_inherits(XML_TextBinder, XML_NodeBinder);
  3458. XML_TextBinder.prototype.toString = function() {
  3459. return "XML_TextBinder(" + this.qname_ + (this.defaultValue_ ? ", " + this.defaultValue_ : "") + ")";
  3460. };
  3461. /**
  3462. * get for the value
  3463. * @param xmlRoot the node to get the value from
  3464. * @return the value for the binder
  3465. */
  3466. XML_TextBinder.prototype.getValue = function(xmlRoot) {
  3467. DBG_ASSERT(xmlRoot, "XML_TextBinder.getValue");
  3468. var node = this.GetNode(xmlRoot);
  3469. return(node ? UTIL_xmlValue(node) : this.defaultValue_);
  3470. };
  3471. /**
  3472. * set for the value
  3473. * @param xmlRoot the node to set the value at
  3474. * @value the value to set
  3475. */
  3476. XML_TextBinder.prototype.setValue = function(xmlRoot, value) {
  3477. DBG_ASSERT(xmlRoot, "XML_TextBinder.setValue: " + value);
  3478. var node = this.GetOrCreateNode(xmlRoot);
  3479. if (node.text != value) {
  3480. node.text = value;
  3481. return true;
  3482. }
  3483. return false;
  3484. };
  3485. /**
  3486. * Example: new XML_AttributeBinder(new XML_Tag("when"), "startTime"),
  3487. * @param tag the tag to bind to
  3488. * @param attrName the attribute name to bind
  3489. * @opt_default the optional default value
  3490. * @constructor
  3491. */
  3492. function XML_AttributeBinder(tags, attrName, opt_default) {
  3493. DBG_ASSERT(attrName, "XML_AttributeBinder");
  3494. XML_NodeBinder.call(this, tags);
  3495. this.attrName_ = attrName;
  3496. this.defaultValue_ = opt_default;
  3497. }
  3498. UTIL_inherits(XML_AttributeBinder, XML_NodeBinder);
  3499. XML_AttributeBinder.prototype.toString = function() {
  3500. return "XML_AttributeBinder('" + this.qname_ + "', '" + this.attrName_ + "'"+ (this.defaultValue_ ? ", " + this.defaultValue_ : "") + ")";
  3501. };
  3502. /**
  3503. * get for the value
  3504. * @param xmlRoot the node to get the value from
  3505. * @return the value for the binder
  3506. */
  3507. XML_AttributeBinder.prototype.getValue = function(xmlRoot) {
  3508. DBG_ASSERT(xmlRoot, "XML_AttributeBinder.getValue");
  3509. var node = this.GetNode(xmlRoot);
  3510. return(node ? node.getAttribute(this.attrName_) : this.defaultValue_);
  3511. };
  3512. /**
  3513. * set for the value
  3514. * @param xmlRoot the node to set the value at
  3515. * @value the value to set
  3516. */
  3517. XML_AttributeBinder.prototype.setValue = function(xmlRoot, value) {
  3518. DBG_ASSERT(xmlRoot, "XML_AttributeBinder.setValue: " + value);
  3519. var node = this.GetOrCreateNode(xmlRoot);
  3520. if (node.getAttribute(this.attrName_) != value) {
  3521. node.setAttribute(this.attrName_, value);
  3522. return true;
  3523. }
  3524. return false;
  3525. };
  3526. // remove this if you merge
  3527. if (window.GD_Loader) {
  3528. // continue loading
  3529. window.GD_Loader();
  3530. }
  3531. // end
  3532. /* Copyright (c) 2006 Google Inc.
  3533. *
  3534. * Licensed under the Apache License, Version 2.0 (the "License");
  3535. * you may not use this file except in compliance with the License.
  3536. * You may obtain a copy of the License at
  3537. *
  3538. * http://www.apache.org/licenses/LICENSE-2.0
  3539. *
  3540. * Unless required by applicable law or agreed to in writing, software
  3541. * distributed under the License is distributed on an "AS IS" BASIS,
  3542. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3543. * See the License for the specific language governing permissions and
  3544. * limitations under the License.
  3545. */
  3546. /**
  3547. * @fileoverview
  3548. * This file contains GD_Query class, a helper class that can construct
  3549. * Google data API query URIs
  3550. */
  3551. /**
  3552. * atom category helper object
  3553. */
  3554. function AtomCategory(term, opt_scheme, opt_label) {
  3555. DBG_ASSERT(term, "AtomCategory: " + arguments);
  3556. this.term = term;
  3557. this.scheme = opt_scheme;
  3558. this.label = opt_label;
  3559. if (this.scheme) {
  3560. this.uri = "{" + this.scheme + "}" + term;
  3561. } else {
  3562. this.uri = term;
  3563. }
  3564. }
  3565. AtomCategory.prototype.toString = function() {
  3566. return this.uri;
  3567. } ;
  3568. /**
  3569. * GD_Query helper object
  3570. * @constructor
  3571. * @param opt_BaseUri the optional base Uri to use
  3572. */
  3573. function GD_Query(opt_BaseUri) {
  3574. this.categories_ = new Array();
  3575. this.searchString_ = null;
  3576. this.authorString_ = null;
  3577. this.startdate_ = null;
  3578. this.endDate_ = null;
  3579. this.startIndex_ = 0;
  3580. this.numToRetrieve_ = 0;
  3581. this.altFormatString_ = null;
  3582. this.baseUriString_ = opt_BaseUri;
  3583. this.updated_ = false;
  3584. }
  3585. GD_Query.FORMAT_ATOM = "atom";
  3586. GD_Query.FORMAT_RSS = "rss";
  3587. /**
  3588. * toString overload. Returns, based on opt_verbose
  3589. * either a GD_Query("categories")
  3590. * or GD_Query("<numCategeories>")
  3591. */
  3592. GD_Query.prototype.toString = function(opt_verbose) {
  3593. if (opt_verbose) {
  3594. var str = "GD_Query(";
  3595. for (var i = 0; i < this.categories.length; i+=1) {
  3596. if (i > 0) {
  3597. str += ", ";
  3598. }
  3599. str += this.categories[i];
  3600. }
  3601. str += ")";
  3602. return str;
  3603. } else {
  3604. return "GD_Query(" + this.categories_.length + ")";
  3605. }
  3606. };
  3607. /**
  3608. * add a category to the query and keep the category array sorted.
  3609. */
  3610. GD_Query.prototype.addCategory = function(category) {
  3611. var i;
  3612. for (i = this.categories_.length - 1; (i >= 0 &&
  3613. category.uri < this.categories_[i].uri); i-=1) {
  3614. this.categories_[i+1] = this.categories[i];
  3615. }
  3616. this.categories_[i+1] = category;
  3617. this.setUpdated();
  3618. };
  3619. /**
  3620. * clears the categories array of the GD_Query object
  3621. */
  3622. GD_Query.prototype.clearCategories = function() {
  3623. if (this.categories_.length > 0) {
  3624. this.categories_.clear();
  3625. this.setUpdated();
  3626. }
  3627. };
  3628. /**
  3629. * set's a category of the GD_Query object
  3630. */
  3631. GD_Query.prototype.getCategory = function(index) {
  3632. return this.categories_[index];
  3633. };
  3634. /**
  3635. * get's the category count of the GD_Query object
  3636. */
  3637. GD_Query.prototype.getCategoryCount = function() {
  3638. return this.categories_.length;
  3639. };
  3640. /**
  3641. * set's the search string of the GD_Query object
  3642. */
  3643. GD_Query.prototype.setSearchString = function(string) {
  3644. this.searchString_ = string;
  3645. this.setUpdated();
  3646. };
  3647. /**
  3648. * get's the search string of the GD_Query object
  3649. */
  3650. GD_Query.prototype.getSearchString = function() {
  3651. return this.searchString_;
  3652. };
  3653. /**
  3654. * set's the author string of the GD_Query object
  3655. */
  3656. GD_Query.prototype.setAuthorString = function(string) {
  3657. this.authorString_ = string;
  3658. this.setUpdated();
  3659. };
  3660. /**
  3661. * get's the search string of the GD_Query object
  3662. */
  3663. GD_Query.prototype.getAuthorString = function() {
  3664. return this.authorString_;
  3665. };
  3666. /**
  3667. * set's the startdate of the GD_Query object
  3668. */
  3669. GD_Query.prototype.setStartDate = function(string) {
  3670. this.startDate_ = string;
  3671. this.setUpdated();
  3672. };
  3673. /**
  3674. * get's the startDate of the GD_Query object
  3675. */
  3676. GD_Query.prototype.getStartDate = function() {
  3677. return this.startDate_;
  3678. };
  3679. /**
  3680. * set's the enddate of the GD_Query object
  3681. */
  3682. GD_Query.prototype.setEndDate = function(string) {
  3683. this.endDate_ = string;
  3684. this.setUpdated();
  3685. };
  3686. /**
  3687. * get's the endDate of the GD_Query object
  3688. */
  3689. GD_Query.prototype.getEndDate = function() {
  3690. return this.endDate_;
  3691. };
  3692. /**
  3693. * set method GD_Query.startIndex_
  3694. */
  3695. GD_Query.prototype.setStartIndex = function(string) {
  3696. this.startIndex_ = string;
  3697. this.setUpdated();
  3698. };
  3699. /**
  3700. * get method GD_Query.startIndex_
  3701. */
  3702. GD_Query.prototype.getStartIndex = function() {
  3703. return this.startIndex_;
  3704. };
  3705. /**
  3706. * set method GD_Query.numberToRetrieve_
  3707. */
  3708. GD_Query.prototype.setNumberToRetrieve = function(string) {
  3709. this.numberToRetrieve_ = string;
  3710. this.setUpdated();
  3711. };
  3712. /**
  3713. * get method GD_Query.numberToRetrieve_
  3714. */
  3715. GD_Query.prototype.getNumberToRetrieve = function() {
  3716. return this.numberToRetrieve_;
  3717. };
  3718. /**
  3719. * set method GD_Query.altFormatString_
  3720. */
  3721. GD_Query.prototype.setFormatString = function(string) {
  3722. this.altFormatString_ = string;
  3723. this.setUpdated();
  3724. };
  3725. /**
  3726. * get method GD_Query.altFormatString_
  3727. */
  3728. GD_Query.prototype.getFormatString = function() {
  3729. return this.altFormatString_;
  3730. };
  3731. /**
  3732. * set method GD_Query.baseUriString_
  3733. */
  3734. GD_Query.prototype.setBaseUriString = function(string) {
  3735. this.baseUriString_ = string;
  3736. this.setUpdated();
  3737. };
  3738. /**
  3739. * get method GD_Query.baseUriString_
  3740. */
  3741. GD_Query.prototype.getBaseUriString = function() {
  3742. return this.baseUriString_;
  3743. };
  3744. /**
  3745. * get's the isUpdated property of the GD_Query object
  3746. */
  3747. GD_Query.prototype.isUpdated = function() {
  3748. return this.updated_;
  3749. };
  3750. /**
  3751. * set's the isUpdated property of the GD_Query object
  3752. */
  3753. GD_Query.prototype.setUpdated = function() {
  3754. this.updated_ = true;
  3755. };
  3756. /**
  3757. * clone's the object
  3758. */
  3759. GD_Query.prototype.clone = function() {
  3760. var retQuery = new GD_Query();
  3761. for (var i = 0; i < this.categories_.length; i+=1) {
  3762. retQuery.addCategory(this.categories_[i]);
  3763. }
  3764. retQuery.setSearchString(this.searchString_);
  3765. retQuery.setUpToDate();
  3766. return retQuery;
  3767. };
  3768. /**
  3769. * calculates the target URI string based on the properties set
  3770. */
  3771. GD_Query.prototype.CalculateUri = function() {
  3772. var retQueryUri = this.baseUriString_;
  3773. var paraConnection = "?" ;
  3774. var firstTime = true;
  3775. if (UTIL_isPersistable(retQueryUri) &&
  3776. retQueryUri.charAt(retQueryUri.length-1)=="/") {
  3777. // remove the trailing slash
  3778. retQueryUri = retQueryUri.substr(0, retQueryUri.length -1);
  3779. }
  3780. for (var count in this.categories_) {
  3781. var category = this.categories_[count];
  3782. if (UTIL_isPersistable(category.toString())) {
  3783. if (firstTime === true) {
  3784. retQueryUri += "/-";
  3785. firstTime = false;
  3786. }
  3787. retQueryUri += "/" + UTIL_urlEncodeString(category.toString());
  3788. }
  3789. }
  3790. if (UTIL_isPersistable(this.altFormatString) &&
  3791. this.altFormatString != GD_Query.FORMAT_ATOM) {
  3792. retQueryUri += paraConnection + "alt=" +
  3793. encodeURIComponent(this.altFormatString);
  3794. paraConnection = "&";
  3795. }
  3796. if (UTIL_isPersistable(this.searchString_)) {
  3797. retQueryUri += paraConnection + "q=" +
  3798. encodeURIComponent(this.searchString_);
  3799. paraConnection = "&";
  3800. }
  3801. if (UTIL_isPersistable(this.authorString_)) {
  3802. retQueryUri += paraConnection + "author=" +
  3803. encodeURIComponent(this.authorString_);
  3804. paraConnection = "&";
  3805. }
  3806. if (this.startDate_ instanceof Date) {
  3807. retQueryUri += paraConnection + "updated-min=" +
  3808. UTIL_dateToRFC3339(this.startDate_);
  3809. paraConnection = "&";
  3810. }
  3811. if (this.endDate_ instanceof Date) {
  3812. retQueryUri += paraConnection + "updated-max=" +
  3813. UTIL_dateToRFC3339(this.endDate_);
  3814. paraConnection = "&";
  3815. }
  3816. if (this.startIndex_ !== 0) {
  3817. retQueryUri += paraConnection + "start-index=" +
  3818. encodeURIComponent(this.startIndex_);
  3819. paraConnection = "&";
  3820. }
  3821. // Currently, if the max-results parameter is not set, it defaults to 25
  3822. // rather than returning all of the results. For now, if the client does
  3823. // not specify the max-results in the JavaScript library, we set it to an
  3824. // arbitrarily large number, in this case, 5000.
  3825. var numToRetrieve = (this.numToRetrieve_) ? this.numToRetrieve_ : 5000;
  3826. retQueryUri += paraConnection + "max-results=" +
  3827. encodeURIComponent(numToRetrieve);
  3828. paraConnection = "&";
  3829. return retQueryUri;
  3830. };
  3831. // remove this if you merge
  3832. if (window.GD_Loader) {
  3833. // continue loading
  3834. window.GD_Loader();
  3835. }
  3836. // end
  3837. /* Copyright (c) 2006 Google Inc.
  3838. *
  3839. * Licensed under the Apache License, Version 2.0 (the "License");
  3840. * you may not use this file except in compliance with the License.
  3841. * You may obtain a copy of the License at
  3842. *
  3843. * http://www.apache.org/licenses/LICENSE-2.0
  3844. *
  3845. * Unless required by applicable law or agreed to in writing, software
  3846. * distributed under the License is distributed on an "AS IS" BASIS,
  3847. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3848. * See the License for the specific language governing permissions and
  3849. * limitations under the License.
  3850. */
  3851. /**
  3852. * @fileoverview
  3853. * This file contains the GD_Entry class, a representation and
  3854. * access mechanims to the atom/feed/entry xml part of the feed
  3855. * properties are exposed as needed, so feel free to add
  3856. * to them following the examples below as you need them
  3857. *
  3858. */
  3859. /**
  3860. * A GD_Entry object provides methods for persisting the feed/entry
  3861. * object, as well as some rudimentary access methods to properties
  3862. */
  3863. function GD_Entry(xmlNode, ofFeed) {
  3864. this.xmlNode_ = xmlNode;
  3865. this.ofFeed_ = ofFeed;
  3866. this.isDirty_ = false;
  3867. this.isDeleted_ = false;
  3868. this.isNew_ = false;
  3869. }
  3870. /**
  3871. * GoogleData entry commonly used properties, add at your leisure
  3872. */
  3873. GD_Entry.BINDERS = {
  3874. "id" : new XML_TextBinder(new XML_Tag("id")),
  3875. "title" : new XML_TextBinder(new XML_Tag("title")),
  3876. "updated" : new XML_TextBinder(new XML_Tag("updated")),
  3877. "published" : new XML_TextBinder(new XML_Tag("published")),
  3878. "author.name" : new XML_TextBinder([new XML_Tag("author"),
  3879. new XML_Tag("name")]),
  3880. "content" : new XML_TextBinder(new XML_Tag("content")),
  3881. "summary" : new XML_TextBinder(new XML_Tag("summary")),
  3882. // link supports
  3883. "edit" : new XML_AttributeBinder(new XML_Tag("link",
  3884. "atom",{"rel":"edit"}), "href"),
  3885. "self" : new XML_AttributeBinder(new XML_Tag("link",
  3886. "atom",{"rel":"self"}), "href"),
  3887. "alternate" : new XML_AttributeBinder(new XML_Tag("link",
  3888. "atom", {"rel":"alternate"}), "href"),
  3889. // categories
  3890. "kind" : new XML_AttributeBinder(new XML_Tag("category",
  3891. "atom",{"scheme":XML.KIND_SCHEME}), "term")
  3892. };
  3893. /**
  3894. * property get/set for the dirty flag
  3895. */
  3896. GD_Entry.prototype.setDirty = function(optFlag){
  3897. this.isDirty_ = (optFlag === false) ? false: true;
  3898. };
  3899. GD_Entry.prototype.getDirty = function() {
  3900. return this.isDirty_;
  3901. };
  3902. /**
  3903. * property get/set for the deleted property
  3904. */
  3905. GD_Entry.prototype.setDeleted = function(optFlag) {
  3906. this.isDeleted_ = (optFlag === false) ? false: true;
  3907. this.setDirty();
  3908. };
  3909. GD_Entry.prototype.getDeleted = function() {
  3910. return this.isDeleted_;
  3911. };
  3912. /**
  3913. * property get/set for the isNew_ property
  3914. */
  3915. GD_Entry.prototype.setNew = function(optFlag) {
  3916. this.isNew_ = (optFlag === false) ? false: true;
  3917. this.setDirty();
  3918. };
  3919. GD_Entry.prototype.getNew = function() {
  3920. return this.isNew_;
  3921. };
  3922. GD_Entry.prototype.toString = function() {
  3923. return "GD_Entry(" + GD_Entry.BINDERS.title.getValue(this.xmlNode_) + ")";
  3924. };
  3925. /**
  3926. * to make the entry more usable, provide direct access to some properties
  3927. */
  3928. /**
  3929. * property get/set to Atom:entry:Id
  3930. */
  3931. GD_Entry.prototype.getId = function() {
  3932. return GD_Entry.BINDERS.id.getValue(this.xmlNode_);
  3933. };
  3934. GD_Entry.prototype.setId = function(newValue) {
  3935. GD_Entry.BINDERS.id.setValue(this.xmlNode_, newValue);
  3936. this.setDirty();
  3937. };
  3938. /**
  3939. * property get/set to Atom:entry:title
  3940. */
  3941. GD_Entry.prototype.getTitle = function() {
  3942. return GD_Entry.BINDERS.title.getValue(this.xmlNode_);
  3943. };
  3944. GD_Entry.prototype.setTitle = function(newValue) {
  3945. GD_Entry.BINDERS.title.setValue(this.xmlNode_, newValue);
  3946. this.setDirty();
  3947. };
  3948. /**
  3949. * property get/set to Atom:entry:updated
  3950. */
  3951. GD_Entry.prototype.getUpdated = function() {
  3952. return GD_Entry.BINDERS.updated.getValue(this.xmlNode_);
  3953. };
  3954. GD_Entry.prototype.setUpdated = function(newValue) {
  3955. GD_Entry.BINDERS.updated.setValue(this.xmlNode_, newValue);
  3956. this.setDirty();
  3957. };
  3958. /**
  3959. * property get/set to Atom:entry:published
  3960. */
  3961. GD_Entry.prototype.getPublished = function() {
  3962. return GD_Entry.BINDERS.published.getValue(this.xmlNode_);
  3963. };
  3964. GD_Entry.prototype.setPublished = function(newValue) {
  3965. GD_Entry.BINDERS.published.setValue(this.xmlNode_, newValue);
  3966. this.setDirty();
  3967. };
  3968. /**
  3969. * property get/set to Atom:entry:author:name
  3970. */
  3971. GD_Entry.prototype.getAuthorName = function() {
  3972. return GD_Entry.BINDERS["author.name"].getValue(this.xmlNode_);
  3973. };
  3974. GD_Entry.prototype.setAuthorName = function(newValue) {
  3975. GD_Entry.BINDERS["author.name"].setValue(this.xmlNode_, newValue);
  3976. this.setDirty();
  3977. };
  3978. /**
  3979. * property get/set to Atom:entry:content
  3980. */
  3981. GD_Entry.prototype.getContent = function() {
  3982. return GD_Entry.BINDERS.content.getValue(this.xmlNode_);
  3983. };
  3984. GD_Entry.prototype.setContent = function(newValue) {
  3985. GD_Entry.BINDERS.content.setValue(this.xmlNode_, newValue);
  3986. this.setDirty();
  3987. };
  3988. /**
  3989. * property get/set to Atom:entry:summary
  3990. */
  3991. GD_Entry.prototype.getSummary = function() {
  3992. return GD_Entry.BINDERS.summary.getValue(this.xmlNode_);
  3993. };
  3994. GD_Entry.prototype.setSummary = function(newValue) {
  3995. GD_Entry.BINDERS.summary.setValue(this.xmlNode_, newValue);
  3996. this.setDirty();
  3997. };
  3998. /**
  3999. * property get/set to Atom:entry:category==kind
  4000. */
  4001. GD_Entry.prototype.getKind = function() {
  4002. return GD_Entry.BINDERS.kind.getValue(this.xmlNode_);
  4003. };
  4004. GD_Entry.prototype.setKind = function(newValue) {
  4005. GD_Entry.BINDERS.kind.setValue(this.xmlNode_, newValue);
  4006. this.setDirty();
  4007. };
  4008. /**
  4009. * property get to Atom:entry:rel==edit
  4010. */
  4011. GD_Entry.prototype.getEditUri = function() {
  4012. return GD_Entry.BINDERS.edit.getValue(this.xmlNode_);
  4013. };
  4014. /**
  4015. * property get to Atom:entry:rel==self
  4016. */
  4017. GD_Entry.prototype.getSelfUri = function() {
  4018. return GD_Entry.BINDERS.self.getValue(this.xmlNode_);
  4019. };
  4020. /**
  4021. * property get to Atom:entry:rel==alternate
  4022. */
  4023. GD_Entry.prototype.getAlternateUri = function() {
  4024. return GD_Entry.BINDERS.alternate.getValue(this.xmlNode_);
  4025. };
  4026. /**
  4027. * save the entry, return a XML document
  4028. * serialized into a string
  4029. * @returns the xml representing the entry as a string
  4030. */
  4031. GD_Entry.prototype.save = function() {
  4032. var document = XML_createDocument(this.xmlNode_);
  4033. return XML_serializeDocument(document);
  4034. };
  4035. /**
  4036. * update the entry back to the store
  4037. * @params optForceIt if this is true, the entry will be saved regardless
  4038. * of state
  4039. */
  4040. GD_Entry.prototype.flush = function(optForceIt) {
  4041. // only update if we are dirty or forced
  4042. if (this.getDirty() || optForceIt === true) {
  4043. if (this.getDeleted()) {
  4044. LOG_DEBUG("deleting entry", true);
  4045. GoogleDataFactory.getTransport().DeleteEntry(this);
  4046. } else if (this.getNew()) {
  4047. LOG_DEBUG("inserting new entry", true);
  4048. GoogleDataFactory.getTransport().InsertEntry(this.ofFeed_, this);
  4049. } else {
  4050. LOG_DEBUG("updating entry", true);
  4051. GoogleDataFactory.getTransport().UpdateEntry(this);
  4052. }
  4053. }
  4054. };
  4055. /**
  4056. * this supports our loading functionallity
  4057. */
  4058. // remove this if you merge
  4059. if (window.GD_Loader) {
  4060. // continue loading
  4061. window.GD_Loader();
  4062. }
  4063. // end
  4064. /* Copyright (c) 2006 Google Inc.
  4065. *
  4066. * Licensed under the Apache License, Version 2.0 (the "License");
  4067. * you may not use this file except in compliance with the License.
  4068. * You may obtain a copy of the License at
  4069. *
  4070. * http://www.apache.org/licenses/LICENSE-2.0
  4071. *
  4072. * Unless required by applicable law or agreed to in writing, software
  4073. * distributed under the License is distributed on an "AS IS" BASIS,
  4074. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4075. * See the License for the specific language governing permissions and
  4076. * limitations under the License.
  4077. */
  4078. /**
  4079. * @fileoverview
  4080. * GoogleData Feed object
  4081. * access mechanims to the atom/feed/ xml part of the feed
  4082. */
  4083. /**
  4084. * Create a new GoogleData Feed object.
  4085. *
  4086. * Because a GD_Feed is loaded asynchronously, the following usage will likely
  4087. * result in undesired behavior:
  4088. *
  4089. * var feed = new GD_Feed(someUrl);
  4090. * var entries = feed.getEntries();
  4091. * // start using entries...
  4092. *
  4093. * When getEntries() is invoked in the above example, the new GD_Feed has not
  4094. * loaded yet, so entries will be an empty array. Instead, getEntries() should
  4095. * be invoked from a callback:
  4096. *
  4097. * var callback = function(feed) {
  4098. * var entries = feed.getEntries();
  4099. * // start using entries...
  4100. * }
  4101. * var feed = new GD_Feed(someUrl);
  4102. *
  4103. * In this example, getEntries() is not called until after the feed is loaded,
  4104. * so it will result in the expected behavior.
  4105. *
  4106. * @param uriStringOrQuery either a URI string or a queryObject
  4107. * @param opt_callback {function} callback function that is called once
  4108. * the new GD_Feed is loaded -- it will receive the GD_Feed as
  4109. * its only argument, and its return value will be ignored.
  4110. * @constructor
  4111. */
  4112. function GD_Feed(uriStringOrQuery, opt_callback) {
  4113. // declare the variables
  4114. this.uriString_ = null;
  4115. this.xmlDom_ = null;
  4116. this.entries_ = null;
  4117. this.links_ = null;
  4118. this.isLoaded_ = false;
  4119. if (typeof(uriStringOrQuery) == "string") {
  4120. this.uriString_ = uriStringOrQuery;
  4121. } else if (uriStringOrQuery != null) {
  4122. this.uriString_ = uriStringOrQuery.CalculateUri();
  4123. }
  4124. if (this.uriString_) {
  4125. var thisFeed = this;
  4126. var callback = function(xmlDom) {
  4127. thisFeed.xmlDom_ = xmlDom;
  4128. XML_ValidateDocument(thisFeed.xmlDom_);
  4129. thisFeed.isLoaded_ = true;
  4130. LOG_XML_PRINT(thisFeed.xmlDom_);
  4131. if (opt_callback) {
  4132. opt_callback.call(null, thisFeed);
  4133. }
  4134. };
  4135. LOG_TRACE(this.uriString_, "loading from", null);
  4136. // now try to get the underlying xml document
  4137. GoogleDataFactory.getTransport().
  4138. GetDomDocument(this.uriString_, callback);
  4139. LOG_TRACE("finished reload Feed", "GD_Feed", uriStringOrQuery);
  4140. }
  4141. };
  4142. /** @return true if the feed has been loaded */
  4143. GD_Feed.prototype.isLoaded = function() {
  4144. return this.isLoaded_;
  4145. }
  4146. /**
  4147. * GoogleData entry commonly used properties
  4148. */
  4149. GD_Feed.BINDERS = {
  4150. entries : new XML_NodeListBinder(new XML_Tag("entry")),
  4151. links : new XML_NodeListBinder(new XML_Tag("link")),
  4152. postUri : new XML_AttributeBinder(new XML_Tag("link", "atom",
  4153. {rel : "http://schemas.google.com/g/2005#post"}), "href"),
  4154. nextUri : new XML_AttributeBinder(new XML_Tag("link", "atom",
  4155. {rel : "next"}), "href"),
  4156. prevUri : new XML_AttributeBinder(new XML_Tag("link", "atom",
  4157. {rel : "prev"}), "href")
  4158. };
  4159. /**
  4160. * Get the entries that are defined in this feed.
  4161. * @return GD_Entry[]
  4162. */
  4163. GD_Feed.prototype.getEntries = function() {
  4164. if (this.entries_ === null && this.xmlDom_) {
  4165. // first time. create them.
  4166. this.entries_ = [];
  4167. if (this.xmlDom_.documentElement) {
  4168. var entryNodes = GD_Feed.BINDERS.entries.GetNodes(
  4169. this.xmlDom_.documentElement);
  4170. for (var i = 0; i < entryNodes.length; ++i) {
  4171. var entryNode = entryNodes[i];
  4172. var entry = this.createEntry(entryNode);
  4173. this.entries_.push(entry);
  4174. }
  4175. }
  4176. }
  4177. // return a copy of the array since the client can mutate it
  4178. return [].concat(this.entries_);
  4179. };
  4180. /**
  4181. * this is designed for subclassing, just encapsulates what
  4182. * kind of entry to create
  4183. * @protected
  4184. */
  4185. GD_Feed.prototype.createEntry = function(xmlNode) {
  4186. return new GD_Entry(xmlNode, this);
  4187. }
  4188. /**
  4189. * get the creation URI back
  4190. */
  4191. GD_Feed.prototype.getUri = function() {
  4192. return this.uriString_;
  4193. };
  4194. /**
  4195. * get the link collection
  4196. */
  4197. GD_Feed.prototype.getLinks = function() {
  4198. if (this.links_ === null) {
  4199. // first time. create them.
  4200. this.links_ = GD_Feed.BINDERS.links.getNodes(
  4201. this.xmlDom_.documentElement);
  4202. }
  4203. return this.links_;
  4204. };
  4205. /** @return a string representation of the feed */
  4206. GD_Feed.prototype.toString = function() {
  4207. return this.uriString_;
  4208. };
  4209. /**
  4210. * get the POST Uri for new Inserts
  4211. */
  4212. GD_Feed.prototype.getPostUri = function() {
  4213. return GD_Feed.BINDERS.postUri.getValue(this.xmlDom_.documentElement);
  4214. };
  4215. /**
  4216. * get the URI for the next chunk
  4217. */
  4218. GD_Feed.prototype.getNextChunkUri = function() {
  4219. return GD_Feed.BINDERS.nextUri.getValue(this.xmlDom_.documentElement);
  4220. };
  4221. /**
  4222. * get the URI for the previous chunk
  4223. */
  4224. GD_Feed.prototype.getPrevChunkUri = function() {
  4225. return GD_Feed.BINDERS.prevUri.getValue(this.xmlDom_.documentElement);
  4226. };
  4227. /**
  4228. * Get a new GD_Feed for the next chunk
  4229. *
  4230. * @param opt_callback {function} callback function that is called once
  4231. * the new GD_Feed is loaded -- it will receive the GD_Feed as
  4232. * its only argument, and its return value will be ignored.
  4233. */
  4234. GD_Feed.prototype.nextChunk = function(opt_callback) {
  4235. return new GD_Feed(this.getNextChunkUri(), opt_callback);
  4236. };
  4237. /**
  4238. * Get a new GD_Feed for the previous chunk
  4239. *
  4240. * @param opt_callback {function} callback function that is called once
  4241. * the new GD_Feed is loaded -- it will receive the GD_Feed as
  4242. * its only argument, and its return value will be ignored.
  4243. */
  4244. GD_Feed.prototype.prevChunk = function(opt_callback) {
  4245. return new GD_Feed(this.getPrevChunkUri(), opt_callback);
  4246. };
  4247. /**
  4248. * save the feed back to the server
  4249. */
  4250. GD_Feed.prototype.flush = function() {
  4251. // as we can not save a feed in the first place,
  4252. // all we are doing here is iterating over the entries
  4253. // and call flush on them
  4254. // ensure entries are there
  4255. var entries = this.getEntries();
  4256. for (var i = 0; i < entries.length; ++i) {
  4257. entries[i].flush();
  4258. }
  4259. };
  4260. // remove this if you merge
  4261. if (window.GD_Loader) {
  4262. // continue loading
  4263. window.GD_Loader();
  4264. }
  4265. // end
  4266. /* Copyright (c) 2006 Google Inc.
  4267. *
  4268. * Licensed under the Apache License, Version 2.0 (the "License");
  4269. * you may not use this file except in compliance with the License.
  4270. * You may obtain a copy of the License at
  4271. *
  4272. * http://www.apache.org/licenses/LICENSE-2.0
  4273. *
  4274. * Unless required by applicable law or agreed to in writing, software
  4275. * distributed under the License is distributed on an "AS IS" BASIS,
  4276. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4277. * See the License for the specific language governing permissions and
  4278. * limitations under the License.
  4279. */
  4280. /**
  4281. * @fileoverview
  4282. * this file contains calendar specific subclasses of
  4283. * the entry and feed classes
  4284. */
  4285. /**
  4286. * GD_EventFeed subclass.
  4287. */
  4288. /** @see GD_Feed */
  4289. function GD_EventFeed(uriStringOrQuery, callBack) {
  4290. LOG_TRACE("Created EventFeed", "GD_EventFeed()", null);
  4291. GD_Feed.call(this, uriStringOrQuery, callBack);
  4292. }
  4293. UTIL_inherits(GD_EventFeed, GD_Feed);
  4294. GD_EventFeed.BINDERS = {
  4295. when : new XML_NodeListBinder(new XML_Tag("when", "gd")),
  4296. originalEvent : new XML_NodeListBinder(new XML_Tag("originalEvent", "gd")),
  4297. recurrence : new XML_NodeListBinder(new XML_Tag("recurrence", "gd"))
  4298. };
  4299. /**
  4300. * This is designed for subclassing, just encapsulates what
  4301. * kind of entry to create. Here we want event entries in the GD_EventFeed.
  4302. * @protected
  4303. */
  4304. GD_EventFeed.prototype.createEntry = function(xmlNode) {
  4305. return new GD_EventEntry(xmlNode, this);
  4306. }
  4307. /**
  4308. * Get the entries that are defined in this feed.
  4309. * @return GD_EventEntry[]
  4310. */
  4311. GD_EventFeed.prototype.getEntries = function() {
  4312. if (this.entries_ === null && this.xmlDom_) {
  4313. // declare some functions private to this function
  4314. /**
  4315. * Find the <gd:originalEvent> node in an <entry>, if any
  4316. *
  4317. * @param entryNode {Element}
  4318. * @return the <gd:originalEvent>, if present; otherwise, return null
  4319. */
  4320. function hasOriginalEvent_(entryNode) {
  4321. var oe = GD_EventFeed.BINDERS.originalEvent.GetNodes(entryNode);
  4322. return (oe && oe.length) ? oe[0] : null;
  4323. }
  4324. /**
  4325. * We want to sort an array of entryNodes so that nodes that are
  4326. * specialized recurrences appear first. This way, when they are
  4327. * processed, they can be added to a "processed" list, so that when
  4328. * an entryNode that represents a recurrence is processed, it can
  4329. * check each instance of the recurrence against the "processed" list
  4330. * so that it does add a duplicate entry
  4331. */
  4332. function compareEntryNode_(a, b) {
  4333. return !!hasOriginalEvent_(b) - !!hasOriginalEvent_(a);
  4334. }
  4335. /**
  4336. * Entry is part of a recurrence, so some of its methods need to be
  4337. * overridden.
  4338. */
  4339. function adaptEntry_(entry, id, whenNode) {
  4340. var start = whenNode.getAttribute('startTime');
  4341. var end = whenNode.getAttribute('endTime');
  4342. entry.getStartTime = function() { return start; }
  4343. entry.getEndTime = function() { return end; }
  4344. entry.getId = function() { return id + start + end; }
  4345. }
  4346. // first time. create them.
  4347. this.entries_ = [];
  4348. var entryNodesCollection =
  4349. GD_Feed.BINDERS.entries.GetNodes(this.xmlDom_.documentElement);
  4350. // keys in processedNodes are identifiers for specialized recurrences
  4351. // that have been processed individually instead of as part of
  4352. // a recurrence
  4353. var processedNodes = {};
  4354. var key, entry;
  4355. // any specialized events must be processed first so that
  4356. // processedNodes is populated before recurrences are evaluated
  4357. var entryNodes = [];
  4358. for (var i = 0; i < entryNodesCollection.length; ++i) {
  4359. entryNodes.push(entryNodesCollection[i]);
  4360. }
  4361. entryNodes.sort(compareEntryNode_);
  4362. for (var i = 0; i < entryNodes.length; ++i) {
  4363. var entryNode = entryNodes[i];
  4364. // see if this entry is a recurrence
  4365. if (GD_EventFeed.BINDERS.recurrence.GetNodes(entryNode).length) {
  4366. var whens = GD_EventFeed.BINDERS.when.GetNodes(entryNode);
  4367. var id = undefined;
  4368. for (var j = 0; j < whens.length; ++j) {
  4369. entry = this.createEntry(entryNode);
  4370. if (!id) id = entry.getId();
  4371. var when = whens[j];
  4372. key = id + when.getAttribute('startTime');
  4373. // a specialization of this recurrence may have already been
  4374. // processed, in which case, continue
  4375. if (key in processedNodes) continue;
  4376. adaptEntry_(entry, id, when);
  4377. this.entries_.push(entry);
  4378. }
  4379. } else {
  4380. // check if has gd:originalEvent, indictating specialization
  4381. entry = this.createEntry(entryNode);
  4382. var originalEvent = hasOriginalEvent_(entryNode);
  4383. if (originalEvent) {
  4384. // this node is also listed in a recurrence
  4385. var oeId = originalEvent.getAttribute('href');
  4386. var oeWhen = GD_EventFeed.BINDERS.when.GetNodes(originalEvent)[0];
  4387. key = oeId + entry.getStartTime();
  4388. processedNodes[key] = undefined;
  4389. }
  4390. this.entries_.push(entry);
  4391. }
  4392. }
  4393. }
  4394. return [].concat(this.entries_);
  4395. };
  4396. /**
  4397. * Get a new GD_EventFeed for the next chunk
  4398. *
  4399. * @param opt_callback {function} callback function that is called once
  4400. * the new GD_EventFeed is loaded -- it will receive the GD_EventFeed as
  4401. * its only argument, and its return value will be ignored.
  4402. */
  4403. GD_EventFeed.prototype.nextChunk = function(opt_callback) {
  4404. return new GD_EventFeed(this.getNextChunkUri(), opt_callback);
  4405. };
  4406. /**
  4407. * Get a new GD_EventFeed for the previous chunk
  4408. *
  4409. * @param opt_callback {function} callback function that is called once
  4410. * the new GD_EventFeed is loaded -- it will receive the GD_EventFeed as
  4411. * its only argument, and its return value will be ignored.
  4412. */
  4413. GD_EventFeed.prototype.prevChunk = function(opt_callback) {
  4414. return new GD_EventFeed(this.getPrevChunkUri(), opt_callback);
  4415. };
  4416. /**
  4417. * GD_EventEntry subclass.
  4418. */
  4419. function GD_EventEntry(xmlNode, ofFeed) {
  4420. LOG_TRACE("Created EventEntry", "GD_EventEntry()", null);
  4421. GD_Entry.call(this, xmlNode, ofFeed);
  4422. }
  4423. UTIL_inherits(GD_EventEntry, GD_Entry);
  4424. /** Event status enum */
  4425. GD_EventEntry.EventStatus = {
  4426. CANCELED : 0,
  4427. CONFIRMED : 1,
  4428. TENTATIVE : 2,
  4429. UNKNOWN : 3
  4430. };
  4431. /**
  4432. * Binders specific for an event entry
  4433. */
  4434. GD_EventEntry.BINDERS = {
  4435. // gd:when specific
  4436. "start" : new XML_AttributeBinder(
  4437. new XML_Tag("when", "gd"), "startTime"),
  4438. "end" : new XML_AttributeBinder(
  4439. new XML_Tag("when", "gd"), "endTime"),
  4440. // gd:eventStatus
  4441. "eventStatus" : new XML_AttributeBinder(
  4442. new XML_Tag("eventStatus", "gd"), "value")
  4443. };
  4444. // gd: specific section
  4445. /**
  4446. * property get/set to gd:when.startTime
  4447. */
  4448. GD_EventEntry.prototype.getStartTime = function() {
  4449. return GD_EventEntry.BINDERS.start.getValue(this.xmlNode_);
  4450. };
  4451. GD_EventEntry.prototype.setStartTime = function(newValue) {
  4452. GD_EventEntry.BINDERS.start.setValue(this.xmlNode_, newValue);
  4453. this.setDirty();
  4454. };
  4455. /**
  4456. * property get/set to gd:when.endTime
  4457. */
  4458. GD_EventEntry.prototype.getEndTime = function() {
  4459. return GD_EventEntry.BINDERS.end.getValue(this.xmlNode_);
  4460. };
  4461. GD_EventEntry.prototype.setEndTime = function(newValue) {
  4462. GD_EventEntry.BINDERS.end.setValue(this.xmlNode_, newValue);
  4463. this.setDirty();
  4464. };
  4465. /**
  4466. * parses the event status string, and returns an EventStatus code
  4467. */
  4468. function GD_ParseEventStatus_(value) {
  4469. var m = "";
  4470. if (value) {
  4471. m = value.match(/http:\/\/schemas.google.com\/g\/2005#event\.(.*)/);
  4472. }
  4473. switch (m[1]) {
  4474. case 'canceled' : return GD_EventEntry.EventStatus.CANCELED;
  4475. case 'confirmed' : return GD_EventEntry.EventStatus.CONFIRMED;
  4476. case 'tentative' : return GD_EventEntry.EventStatus.TENTATIVE;
  4477. default: return GD_EventEntry.EventStatus.UNKNOWN;
  4478. }
  4479. }
  4480. /**
  4481. * @return {GD_EventEntry.EventStatus}
  4482. */
  4483. GD_EventEntry.prototype.getEventStatus = function() {
  4484. var value = GD_EventEntry.BINDERS.eventStatus.getValue(this.xmlNode_);
  4485. return GD_ParseEventStatus_(value);
  4486. }
  4487. /**
  4488. * @return {string} URL for the event's event page
  4489. */
  4490. GD_EventEntry.prototype.getEventPageUrl = function() {
  4491. var url = this.getAlternateUri();
  4492. if (url) return url;
  4493. // this is probably safari -- works fine on browsers with
  4494. // proper xpath support
  4495. var children = this.xmlNode_.childNodes;
  4496. for (var i = 0; i < children.length; ++i) {
  4497. var child = children[i];
  4498. if (child.nodeName == 'link' && child.getAttribute('rel') == 'alternate') {
  4499. return child.getAttribute('href');
  4500. }
  4501. }
  4502. return null;
  4503. }
  4504. // remove this if you merge
  4505. if (window.GD_Loader) {
  4506. // continue loading
  4507. window.GD_Loader();
  4508. }
  4509. // end