HtmlTableStore.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. if (!dojo._hasResource["dojox.data.HtmlTableStore"]) { // _hasResource checks
  2. // added by build. Do
  3. // not use _hasResource
  4. // directly in your
  5. // code.
  6. dojo._hasResource["dojox.data.HtmlTableStore"] = true;
  7. dojo.provide("dojox.data.HtmlTableStore");
  8. dojo.require("dojox.data.dom");
  9. dojo.require("dojo.data.util.simpleFetch");
  10. dojo.require("dojo.data.util.filter");
  11. dojo.declare("dojox.data.HtmlTableStore", null, {
  12. constructor : function(/* Object */args) {
  13. // summary:
  14. // Initializer for the HTML table store.
  15. // description:
  16. // The HtmlTableStore can be created in one of two ways: a) by
  17. // parsing an existing
  18. // table DOM node on the current page or b) by referencing an
  19. // external url and giving
  20. // the id of the table in that page. The remote url will be parsed
  21. // as an html page.
  22. //
  23. // The HTML table should be of the following form:
  24. // <table id="myTable">
  25. // <thead>
  26. // <tr>
  27. // <th>Attribute1</th>
  28. // <th>Attribute2</th>
  29. // </tr>
  30. // </thead>
  31. // <tbody>
  32. // <tr>
  33. // <td>Value1.1</td>
  34. // <td>Value1.2</td>
  35. // </tr>
  36. // <tr>
  37. // <td>Value2.1</td>
  38. // <td>Value2.2</td>
  39. // </tr>
  40. // </tbody>
  41. // </table>
  42. //
  43. // args:
  44. // An anonymous object to initialize properties. It expects the
  45. // following values:
  46. // tableId: The id of the HTML table to use.
  47. // OR
  48. // url: The url of the remote page to load
  49. // tableId: The id of the table element in the remote page
  50. if (args.url) {
  51. if (!args.tableId)
  52. throw new Error("dojo.data.HtmlTableStore: Cannot instantiate using url without an id!");
  53. this.url = args.url;
  54. this.tableId = args.tableId;
  55. } else {
  56. if (args.tableId) {
  57. this._rootNode = dojo.byId(args.tableId);
  58. this.tableId = this._rootNode.id;
  59. } else {
  60. this._rootNode = dojo.byId(this.tableId);
  61. }
  62. this._getHeadings();
  63. for (var i = 0; i < this._rootNode.rows.length; i++) {
  64. this._rootNode.rows[i].store = this;
  65. }
  66. }
  67. },
  68. url : "", // So the parser can instantiate the store via markup.
  69. tableId : "", // So the parser can instantiate the store via markup.
  70. _getHeadings : function() {
  71. // summary:
  72. // Function to load the attribute names from the table header so
  73. // that the
  74. // attributes (cells in a row), can have a reasonable name.
  75. this._headings = [];
  76. dojo.forEach(this._rootNode.tHead.rows[0].cells, dojo.hitch(this,
  77. function(th) {
  78. this._headings.push(dojox.data.dom
  79. .textContent(th));
  80. }));
  81. },
  82. _getAllItems : function() {
  83. // summary:
  84. // Function to return all rows in the table as an array of items.
  85. var items = [];
  86. for (var i = 1; i < this._rootNode.rows.length; i++) {
  87. items.push(this._rootNode.rows[i]);
  88. }
  89. return items; // array
  90. },
  91. _assertIsItem : function(/* item */item) {
  92. // summary:
  93. // This function tests whether the item passed in is indeed an item
  94. // in the store.
  95. // item:
  96. // The item to test for being contained by the store.
  97. if (!this.isItem(item)) {
  98. throw new Error("dojo.data.HtmlTableStore: a function was passed an item argument that was not an item");
  99. }
  100. },
  101. _assertIsAttribute : function(/* String */attribute) {
  102. // summary:
  103. // This function tests whether the item passed in is indeed a valid
  104. // 'attribute' like type for the store.
  105. // attribute:
  106. // The attribute to test for being contained by the store.
  107. //
  108. // returns:
  109. // Returns the index (column) that the attribute resides in the row.
  110. if (typeof attribute !== "string") {
  111. throw new Error("dojo.data.HtmlTableStore: a function was passed an attribute argument that was not an attribute name string");
  112. return;
  113. }
  114. return dojo.indexOf(this._headings, attribute); // int
  115. },
  116. /***********************************************************************
  117. * dojo.data.api.Read API
  118. **********************************************************************/
  119. getValue : function( /* item */item,
  120. /* attribute-name-string */attribute,
  121. /* value? */defaultValue) {
  122. // summary:
  123. // See dojo.data.api.Read.getValue()
  124. var values = this.getValues(item, attribute);
  125. return (values.length > 0) ? values[0] : defaultValue; // Object ||
  126. // int ||
  127. // Boolean
  128. },
  129. getValues : function(/* item */item,
  130. /* attribute-name-string */attribute) {
  131. // summary:
  132. // See dojo.data.api.Read.getValues()
  133. this._assertIsItem(item);
  134. var index = this._assertIsAttribute(attribute);
  135. if (index > -1) {
  136. return [dojox.data.dom.textContent(item.cells[index])];
  137. }
  138. return []; // Array
  139. },
  140. getAttributes : function(/* item */item) {
  141. // summary:
  142. // See dojo.data.api.Read.getAttributes()
  143. this._assertIsItem(item);
  144. var attributes = [];
  145. for (var i = 0; i < this._headings.length; i++) {
  146. if (this.hasAttribute(item, this._headings[i]))
  147. attributes.push(this._headings[i]);
  148. }
  149. return attributes; // Array
  150. },
  151. hasAttribute : function( /* item */item,
  152. /* attribute-name-string */attribute) {
  153. // summary:
  154. // See dojo.data.api.Read.hasAttribute()
  155. return this.getValues(item, attribute).length > 0;
  156. },
  157. containsValue : function(/* item */item,
  158. /* attribute-name-string */attribute,
  159. /* anything */value) {
  160. // summary:
  161. // See dojo.data.api.Read.containsValue()
  162. var regexp = undefined;
  163. if (typeof value === "string") {
  164. regexp = dojo.data.util.filter.patternToRegExp(value, false);
  165. }
  166. return this._containsValue(item, attribute, value, regexp); // boolean.
  167. },
  168. _containsValue : function( /* item */item,
  169. /* attribute-name-string */attribute,
  170. /* anything */value,
  171. /* RegExp? */regexp) {
  172. // summary:
  173. // Internal function for looking at the values contained by the
  174. // item.
  175. // description:
  176. // Internal function for looking at the values contained by the
  177. // item. This
  178. // function allows for denoting if the comparison should be case
  179. // sensitive for
  180. // strings or not (for handling filtering cases where string case
  181. // should not matter)
  182. //
  183. // item:
  184. // The data item to examine for attribute values.
  185. // attribute:
  186. // The attribute to inspect.
  187. // value:
  188. // The value to match.
  189. // regexp:
  190. // Optional regular expression generated off value if value was of
  191. // string type to handle wildcarding.
  192. // If present and attribute values are string, then it can be used
  193. // for comparison instead of 'value'
  194. var values = this.getValues(item, attribute);
  195. for (var i = 0; i < values.length; ++i) {
  196. var possibleValue = values[i];
  197. if (typeof possibleValue === "string" && regexp) {
  198. return (possibleValue.match(regexp) !== null);
  199. } else {
  200. // Non-string matching.
  201. if (value === possibleValue) {
  202. return true; // Boolean
  203. }
  204. }
  205. }
  206. return false; // Boolean
  207. },
  208. isItem : function(/* anything */something) {
  209. // summary:
  210. // See dojo.data.api.Read.isItem()
  211. if (something && something.store && something.store === this) {
  212. return true; // boolean
  213. }
  214. return false; // boolean
  215. },
  216. isItemLoaded : function(/* anything */something) {
  217. // summary:
  218. // See dojo.data.api.Read.isItemLoaded()
  219. return this.isItem(something);
  220. },
  221. loadItem : function(/* Object */keywordArgs) {
  222. // summary:
  223. // See dojo.data.api.Read.loadItem()
  224. this._assertIsItem(keywordArgs.item);
  225. },
  226. _fetchItems : function(request, fetchHandler, errorHandler) {
  227. // summary:
  228. // Fetch items (XML elements) that match to a query
  229. // description:
  230. // If '_fetchUrl' is specified, it is used to load an XML document
  231. // with a query string.
  232. // Otherwise and if 'url' is specified, the XML document is
  233. // loaded and list XML elements that match to a query (set of
  234. // element
  235. // names and their text attribute values that the items to contain).
  236. // A wildcard, "*" can be used to query values to match all
  237. // occurrences.
  238. // If '_rootItem' is specified, it is used to fetch items.
  239. // request:
  240. // A request object
  241. // fetchHandler:
  242. // A function to call for fetched items
  243. // errorHandler:
  244. // A function to call on error
  245. if (this._rootNode) {
  246. this._finishFetchItems(request, fetchHandler, errorHandler);
  247. } else {
  248. if (!this.url) {
  249. this._rootNode = dojo.byId(this.tableId);
  250. this._getHeadings();
  251. for (var i = 0; i < this._rootNode.rows.length; i++) {
  252. this._rootNode.rows[i].store = this;
  253. }
  254. } else {
  255. var getArgs = {
  256. url : this.url,
  257. handleAs : "text"
  258. };
  259. var self = this;
  260. var getHandler = dojo.xhrGet(getArgs);
  261. getHandler.addCallback(function(data) {
  262. var findNode = function(node, id) {
  263. if (node.id == id) {
  264. return node; // object
  265. }
  266. if (node.childNodes) {
  267. for (var i = 0; i < node.childNodes.length; i++) {
  268. var returnNode = findNode(
  269. node.childNodes[i], id);
  270. if (returnNode) {
  271. return returnNode; // object
  272. }
  273. }
  274. }
  275. return null; // null
  276. }
  277. var d = document.createElement("div");
  278. d.innerHTML = data;
  279. self._rootNode = findNode(d, self.tableId);
  280. self._getHeadings.call(self);
  281. for (var i = 0; i < self._rootNode.rows.length; i++) {
  282. self._rootNode.rows[i].store = self;
  283. }
  284. self._finishFetchItems(request, fetchHandler,
  285. errorHandler);
  286. });
  287. getHandler.addErrback(function(error) {
  288. errorHandler(error, request);
  289. });
  290. }
  291. }
  292. },
  293. _finishFetchItems : function(request, fetchHandler, errorHandler) {
  294. // summary:
  295. // Internal function for processing the passed in request and
  296. // locating the requested items.
  297. var items = null;
  298. var arrayOfAllItems = this._getAllItems();
  299. if (request.query) {
  300. var ignoreCase = request.queryOptions
  301. ? request.queryOptions.ignoreCase
  302. : false;
  303. items = [];
  304. // See if there are any string values that can be regexp parsed
  305. // first to avoid multiple regexp gens on the
  306. // same value for each item examined. Much more efficient.
  307. var regexpList = {};
  308. for (var key in request.query) {
  309. var value = request.query[key] + '';
  310. if (typeof value === "string") {
  311. regexpList[key] = dojo.data.util.filter
  312. .patternToRegExp(value, ignoreCase);
  313. }
  314. }
  315. for (var i = 0; i < arrayOfAllItems.length; ++i) {
  316. var match = true;
  317. var candidateItem = arrayOfAllItems[i];
  318. for (var key in request.query) {
  319. var value = request.query[key] + '';
  320. if (!this._containsValue(candidateItem, key, value,
  321. regexpList[key])) {
  322. match = false;
  323. }
  324. }
  325. if (match) {
  326. items.push(candidateItem);
  327. }
  328. }
  329. fetchHandler(items, request);
  330. } else {
  331. // We want a copy to pass back in case the parent wishes to sort
  332. // the array. We shouldn't allow resort
  333. // of the internal list so that multiple callers can get
  334. // listsand sort without affecting each other.
  335. if (arrayOfAllItems.length > 0) {
  336. items = arrayOfAllItems.slice(0, arrayOfAllItems.length);
  337. }
  338. fetchHandler(items, request);
  339. }
  340. },
  341. getFeatures : function() {
  342. // summary:
  343. // See dojo.data.api.Read.getFeatures()
  344. return {
  345. 'dojo.data.api.Read' : true,
  346. 'dojo.data.api.Identity' : true
  347. };
  348. },
  349. close : function(
  350. /* dojo.data.api.Request || keywordArgs || null */request) {
  351. // summary:
  352. // See dojo.data.api.Read.close()
  353. // nothing to do here!
  354. },
  355. getLabel : function(/* item */item) {
  356. // summary:
  357. // See dojo.data.api.Read.getLabel()
  358. if (this.isItem(item))
  359. return "Table Row #" + this.getIdentity(item);
  360. return undefined;
  361. },
  362. getLabelAttributes : function(/* item */item) {
  363. // summary:
  364. // See dojo.data.api.Read.getLabelAttributes()
  365. return null;
  366. },
  367. /***********************************************************************
  368. * dojo.data.api.Identity API
  369. **********************************************************************/
  370. getIdentity : function(/* item */item) {
  371. // summary:
  372. // See dojo.data.api.Identity.getIdentity()
  373. this._assertIsItem(item);
  374. // Opera doesn't support the sectionRowIndex,
  375. // So, have to call the indexOf to locate it.
  376. // Blah.
  377. if (!dojo.isOpera) {
  378. return item.sectionRowIndex; // int
  379. } else {
  380. return (dojo.indexOf(this._rootNode.rows, item) - 1) // int
  381. }
  382. },
  383. getIdentityAttributes : function(/* item */item) {
  384. // summary:
  385. // See dojo.data.api.Identity.getIdentityAttributes()
  386. // Identity isn't taken from a public attribute.
  387. return null;
  388. },
  389. fetchItemByIdentity : function(keywordArgs) {
  390. // summary:
  391. // See dojo.data.api.Identity.fetchItemByIdentity()
  392. var identity = keywordArgs.identity;
  393. var self = this;
  394. var item = null
  395. if (!this._rootNode) {
  396. if (!this.url) {
  397. this._rootNode = dojo.byId(this.tableId);
  398. this._getHeadings();
  399. for (var i = 0; i < this._rootNode.rows.length; i++) {
  400. this._rootNode.rows[i].store = this;
  401. }
  402. item = this._rootNode.rows[identity + 1];
  403. if (keywordArgs.onItem) {
  404. var scope = keywordArgs.scope
  405. ? keywordArgs.scope
  406. : dojo.global;
  407. keywordArgs.onItem.call(scope, item);
  408. }
  409. } else {
  410. var getArgs = {
  411. url : this.url,
  412. handleAs : "text"
  413. };
  414. var self = this;
  415. var getHandler = dojo.xhrGet(getArgs);
  416. getHandler.addCallback(function(data) {
  417. var findNode = function(node, id) {
  418. if (node.id == id) {
  419. return node; // object
  420. }
  421. if (node.childNodes) {
  422. for (var i = 0; i < node.childNodes.length; i++) {
  423. var returnNode = findNode(
  424. node.childNodes[i], id);
  425. if (returnNode) {
  426. return returnNode; // object
  427. }
  428. }
  429. }
  430. return null; // null
  431. }
  432. var d = document.createElement("div");
  433. d.innerHTML = data;
  434. self._rootNode = findNode(d, self.tableId);
  435. self._getHeadings.call(self);
  436. for (var i = 0; i < self._rootNode.rows.length; i++) {
  437. self._rootNode.rows[i].store = self;
  438. }
  439. item = self._rootNode.rows[identity + 1];
  440. if (keywordArgs.onItem) {
  441. var scope = keywordArgs.scope
  442. ? keywordArgs.scope
  443. : dojo.global;
  444. keywordArgs.onItem.call(scope, item);
  445. }
  446. });
  447. getHandler.addErrback(function(error) {
  448. if (keywordArgs.onError) {
  449. var scope = keywordArgs.scope
  450. ? keywordArgs.scope
  451. : dojo.global;
  452. keywordArgs.onError.call(scope, error);
  453. }
  454. });
  455. }
  456. } else {
  457. if (this._rootNode.rows[identity + 1]) {
  458. item = this._rootNode.rows[identity + 1];
  459. if (keywordArgs.onItem) {
  460. var scope = keywordArgs.scope
  461. ? keywordArgs.scope
  462. : dojo.global;
  463. keywordArgs.onItem.call(scope, item);
  464. }
  465. }
  466. }
  467. }
  468. });
  469. dojo.extend(dojox.data.HtmlTableStore, dojo.data.util.simpleFetch);
  470. }