HtmlTableStore.js 14 KB

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