HtmlTableStore.js 14 KB

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