Diffstat (limited to 'frontend/beta/js/YUI-extensions/widgets/TemplateView.js') (more/less context) (ignore whitespace changes)
-rw-r--r-- | frontend/beta/js/YUI-extensions/widgets/TemplateView.js | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/frontend/beta/js/YUI-extensions/widgets/TemplateView.js b/frontend/beta/js/YUI-extensions/widgets/TemplateView.js new file mode 100644 index 0000000..2100205 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/widgets/TemplateView.js | |||
@@ -0,0 +1,766 @@ | |||
1 | /** | ||
2 | * @class YAHOO.ext.View | ||
3 | * @extends YAHOO.ext.util.Observable | ||
4 | * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. | ||
5 | * This class also supports single and multi selection modes. <br> | ||
6 | * Create a data model bound view: | ||
7 | <pre><code> | ||
8 | var dataModel = new YAHOO.ext.grid.XMLDataModel(...); | ||
9 | var view = new YAHOO.ext.View('my-element', | ||
10 | '<div id="{0}">{2} - {1}</div>', // auto create template | ||
11 | dataModel, { | ||
12 | singleSelect: true, | ||
13 | selectedClass: 'ydataview-selected' | ||
14 | }); | ||
15 | |||
16 | // listen for node click? | ||
17 | view.on('click', function(vw, index, node, e){ | ||
18 | alert('Node "' + node.id + '" at index: ' + index + ' was clicked.'); | ||
19 | }); | ||
20 | |||
21 | // load XML data | ||
22 | dataModel.load('foobar.xml'); | ||
23 | </code></pre> | ||
24 | For an example of creating a JSON/UpdateManager view, see {@link YAHOO.ext.JsonView}. | ||
25 | * <br><br> | ||
26 | * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to | ||
27 | * IE's limited insertion support with tables and Opera's faulty event bubbling.</b> | ||
28 | * @constructor | ||
29 | * Create a new View | ||
30 | * @param {String/HTMLElement/Element} container The container element where the view is to be rendered. | ||
31 | * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with | ||
32 | * @param {DataModel} dataModel The bound data model | ||
33 | * @param {Object} config The config object | ||
34 | */ | ||
35 | YAHOO.ext.View = function(container, tpl, dataModel, config){ | ||
36 | this.el = getEl(container, true); | ||
37 | this.nodes = this.el.dom.childNodes; | ||
38 | if(typeof tpl == 'string'){ | ||
39 | tpl = new YAHOO.ext.Template(tpl); | ||
40 | } | ||
41 | tpl.compile(); | ||
42 | /** | ||
43 | * The template used by this View | ||
44 | * @type {YAHOO.ext.DomHelper.Template} | ||
45 | */ | ||
46 | this.tpl = tpl; | ||
47 | this.setDataModel(dataModel); | ||
48 | var CE = YAHOO.util.CustomEvent; | ||
49 | /** @private */ | ||
50 | this.events = { | ||
51 | /** | ||
52 | * @event beforeclick | ||
53 | * Fires before a click is processed. Returns false to cancel the default action. | ||
54 | * @param {YAHOO.ext.View} this | ||
55 | * @param {Number} index The index of the target node | ||
56 | * @param {HTMLElement} node The target node | ||
57 | * @param {YAHOO.ext.EventObject} e The raw event object | ||
58 | */ | ||
59 | 'beforeclick' : true, | ||
60 | /** | ||
61 | * @event click | ||
62 | * Fires when a template node is clicked. | ||
63 | * @param {YAHOO.ext.View} this | ||
64 | * @param {Number} index The index of the target node | ||
65 | * @param {HTMLElement} node The target node | ||
66 | * @param {YAHOO.ext.EventObject} e The raw event object | ||
67 | */ | ||
68 | 'click' : true, | ||
69 | /** | ||
70 | * @event dblclick | ||
71 | * Fires when a template node is double clicked. | ||
72 | * @param {YAHOO.ext.View} this | ||
73 | * @param {Number} index The index of the target node | ||
74 | * @param {HTMLElement} node The target node | ||
75 | * @param {YAHOO.ext.EventObject} e The raw event object | ||
76 | */ | ||
77 | 'dblclick' : true, | ||
78 | /** | ||
79 | * @event contextmenu | ||
80 | * Fires when a template node is right clicked. | ||
81 | * @param {YAHOO.ext.View} this | ||
82 | * @param {Number} index The index of the target node | ||
83 | * @param {HTMLElement} node The target node | ||
84 | * @param {YAHOO.ext.EventObject} e The raw event object | ||
85 | */ | ||
86 | 'contextmenu' : true, | ||
87 | /** | ||
88 | * @event selectionchange | ||
89 | * Fires when the selected nodes change. | ||
90 | * @param {YAHOO.ext.View} this | ||
91 | * @param {Array} selections Array of the selected nodes | ||
92 | */ | ||
93 | 'selectionchange' : true, | ||
94 | |||
95 | /** | ||
96 | * @event beforeselect | ||
97 | * Fires before a selection is made. If any handlers return false, the selection is cancelled. | ||
98 | * @param {YAHOO.ext.View} this | ||
99 | * @param {HTMLElement} node The node to be selected | ||
100 | * @param {Array} selections Array of currently selected nodes | ||
101 | */ | ||
102 | 'beforeselect' : true | ||
103 | }; | ||
104 | this.el.mon("click", this.onClick, this, true); | ||
105 | this.el.mon("dblclick", this.onDblClick, this, true); | ||
106 | this.el.mon("contextmenu", this.onContextMenu, this, true); | ||
107 | |||
108 | /** | ||
109 | * The css class to add to selected nodes | ||
110 | * @type {YAHOO.ext.DomHelper.Template} | ||
111 | */ | ||
112 | this.selectedClass = 'ydataview-selected'; | ||
113 | |||
114 | this.emptyText = ''; | ||
115 | |||
116 | this.selections = []; | ||
117 | |||
118 | this.lastSelection = null; | ||
119 | |||
120 | /** | ||
121 | * The root property in the loaded json object that contains the data | ||
122 | * @type {String} | ||
123 | */ | ||
124 | this.jsonRoot = null; | ||
125 | YAHOO.ext.util.Config.apply(this, config); | ||
126 | if(this.renderUpdates || this.jsonRoot){ | ||
127 | var um = this.el.getUpdateManager(); | ||
128 | um.setRenderer(this); | ||
129 | } | ||
130 | }; | ||
131 | |||
132 | YAHOO.extendX(YAHOO.ext.View, YAHOO.ext.util.Observable, { | ||
133 | /** | ||
134 | * Returns the element this view is bound to. | ||
135 | * @return {YAHOO.ext.Element} | ||
136 | */ | ||
137 | getEl : function(){ | ||
138 | return this.el; | ||
139 | }, | ||
140 | |||
141 | render : function(el, response){ | ||
142 | this.clearSelections(); | ||
143 | this.el.update(''); | ||
144 | var o; | ||
145 | try{ | ||
146 | o = YAHOO.ext.util.JSON.decode(response.responseText); | ||
147 | if(this.jsonRoot){ | ||
148 | o = eval('o.' + this.jsonRoot); | ||
149 | } | ||
150 | }catch(e){} | ||
151 | /** | ||
152 | * The current json data or null | ||
153 | */ | ||
154 | this.jsonData = o; | ||
155 | this.beforeRender(); | ||
156 | this.refresh(); | ||
157 | }, | ||
158 | |||
159 | beforeRender : function(){ | ||
160 | |||
161 | }, | ||
162 | |||
163 | /** | ||
164 | * Refreshes the view. | ||
165 | */ | ||
166 | refresh : function(){ | ||
167 | this.clearSelections(); | ||
168 | this.el.update(''); | ||
169 | this.html = []; | ||
170 | if(this.renderUpdates || this.jsonRoot){ | ||
171 | var o = this.jsonData; | ||
172 | if(o){ | ||
173 | for(var i = 0, len = o.length; i < len; i++) { | ||
174 | this.renderEach(o[i]); | ||
175 | } | ||
176 | } | ||
177 | }else{ | ||
178 | this.dataModel.each(this.renderEach, this); | ||
179 | } | ||
180 | var strHtml; | ||
181 | if(this.html.length > 0){ | ||
182 | strHtml = this.html.join(''); | ||
183 | }else{ | ||
184 | strHtml = this.emptyText; | ||
185 | } | ||
186 | this.el.update(strHtml); | ||
187 | this.html = null; | ||
188 | this.nodes = this.el.dom.childNodes; | ||
189 | this.updateIndexes(0); | ||
190 | }, | ||
191 | |||
192 | /** | ||
193 | * Function to override to reformat the data that is sent to | ||
194 | * the template for each node. | ||
195 | * @param {Array/Object} data The raw data (array of colData for a data model bound view or | ||
196 | * a JSON object for an UpdateManager bound view). | ||
197 | * @param {Number} index The index of the data within the data model | ||
198 | */ | ||
199 | prepareData : function(data, index){ | ||
200 | return data; | ||
201 | }, | ||
202 | |||
203 | renderEach : function(data){ | ||
204 | this.html[this.html.length] = this.tpl.applyTemplate(this.prepareData(data)); | ||
205 | }, | ||
206 | |||
207 | /** | ||
208 | * Refresh an individual node. | ||
209 | * @param {Number} index | ||
210 | */ | ||
211 | refreshNode : function(index){ | ||
212 | this.refreshNodes(index, index); | ||
213 | }, | ||
214 | |||
215 | refreshNodes : function(dm, startIndex, endIndex){ | ||
216 | this.clearSelections(); | ||
217 | var dm = this.dataModel; | ||
218 | var ns = this.nodes; | ||
219 | for(var i = startIndex; i <= endIndex; i++){ | ||
220 | var d = this.prepareData(dm.getRow(i), i); | ||
221 | if(i < ns.length-1){ | ||
222 | var old = ns[i]; | ||
223 | this.tpl.insertBefore(old, d); | ||
224 | this.el.dom.removeChild(old); | ||
225 | }else{ | ||
226 | this.tpl.append(this.el.dom, d); | ||
227 | } | ||
228 | } | ||
229 | this.updateIndexes(startIndex, endIndex); | ||
230 | }, | ||
231 | |||
232 | deleteNodes : function(dm, startIndex, endIndex){ | ||
233 | this.clearSelections(); | ||
234 | if(startIndex == 0 && endIndex >= this.nodes.length-1){ | ||
235 | this.el.update(''); | ||
236 | }else{ | ||
237 | var el = this.el.dom; | ||
238 | for(var i = startIndex; i <= endIndex; i++){ | ||
239 | el.removeChild(this.nodes[startIndex]); | ||
240 | } | ||
241 | this.updateIndexes(startIndex); | ||
242 | } | ||
243 | }, | ||
244 | |||
245 | insertNodes : function(dm, startIndex, endIndex){ | ||
246 | if(this.nodes.length == 0){ | ||
247 | this.refresh(); | ||
248 | }else{ | ||
249 | this.clearSelections(); | ||
250 | var t = this.tpl; | ||
251 | var before = this.nodes[startIndex]; | ||
252 | var dm = this.dataModel; | ||
253 | if(before){ | ||
254 | for(var i = startIndex; i <= endIndex; i++){ | ||
255 | t.insertBefore(before, this.prepareData(dm.getRow(i), i)); | ||
256 | } | ||
257 | }else{ | ||
258 | var el = this.el.dom; | ||
259 | for(var i = startIndex; i <= endIndex; i++){ | ||
260 | t.append(el, this.prepareData(dm.getRow(i), i)); | ||
261 | } | ||
262 | } | ||
263 | this.updateIndexes(startIndex); | ||
264 | } | ||
265 | }, | ||
266 | |||
267 | updateIndexes : function(dm, startIndex, endIndex){ | ||
268 | var ns = this.nodes; | ||
269 | startIndex = startIndex || 0; | ||
270 | endIndex = endIndex || ns.length-1; | ||
271 | for(var i = startIndex; i <= endIndex; i++){ | ||
272 | ns[i].nodeIndex = i; | ||
273 | } | ||
274 | }, | ||
275 | |||
276 | /** | ||
277 | * Changes the data model this view uses and refresh the view. | ||
278 | * @param {DataModel} dataModel | ||
279 | */ | ||
280 | setDataModel : function(dm){ | ||
281 | if(!dm) return; | ||
282 | this.unplugDataModel(this.dataModel); | ||
283 | this.dataModel = dm; | ||
284 | dm.on('cellupdated', this.refreshNode, this, true); | ||
285 | dm.on('datachanged', this.refresh, this, true); | ||
286 | dm.on('rowsdeleted', this.deleteNodes, this, true); | ||
287 | dm.on('rowsinserted', this.insertNodes, this, true); | ||
288 | dm.on('rowsupdated', this.refreshNodes, this, true); | ||
289 | dm.on('rowssorted', this.refresh, this, true); | ||
290 | this.refresh(); | ||
291 | }, | ||
292 | |||
293 | /** | ||
294 | * Unplug the data model and stop updates. | ||
295 | * @param {DataModel} dataModel | ||
296 | */ | ||
297 | unplugDataModel : function(dm){ | ||
298 | if(!dm) return; | ||
299 | dm.removeListener('cellupdated', this.refreshNode, this); | ||
300 | dm.removeListener('datachanged', this.refresh, this); | ||
301 | dm.removeListener('rowsdeleted', this.deleteNodes, this); | ||
302 | dm.removeListener('rowsinserted', this.insertNodes, this); | ||
303 | dm.removeListener('rowsupdated', this.refreshNodes, this); | ||
304 | dm.removeListener('rowssorted', this.refresh, this); | ||
305 | this.dataModel = null; | ||
306 | }, | ||
307 | |||
308 | /** | ||
309 | * Returns the template node the passed child belongs to or null if it doesn't belong to one. | ||
310 | * @param {HTMLElement} node | ||
311 | * @return {HTMLElement} The template node | ||
312 | */ | ||
313 | findItemFromChild : function(node){ | ||
314 | var el = this.el.dom; | ||
315 | if(!node || node.parentNode == el){ | ||
316 | return node; | ||
317 | } | ||
318 | var p = node.parentNode; | ||
319 | while(p && p != el){ | ||
320 | if(p.parentNode == el){ | ||
321 | return p; | ||
322 | } | ||
323 | p = p.parentNode; | ||
324 | } | ||
325 | return null; | ||
326 | }, | ||
327 | |||
328 | /** @ignore */ | ||
329 | onClick : function(e){ | ||
330 | var item = this.findItemFromChild(e.getTarget()); | ||
331 | if(item){ | ||
332 | var index = this.indexOf(item); | ||
333 | if(this.onItemClick(item, index, e) !== false){ | ||
334 | this.fireEvent('click', this, index, item, e); | ||
335 | } | ||
336 | }else{ | ||
337 | this.clearSelections(); | ||
338 | } | ||
339 | }, | ||
340 | |||
341 | /** @ignore */ | ||
342 | onContextMenu : function(e){ | ||
343 | var item = this.findItemFromChild(e.getTarget()); | ||
344 | if(item){ | ||
345 | this.fireEvent('contextmenu', this, this.indexOf(item), item, e); | ||
346 | } | ||
347 | }, | ||
348 | |||
349 | /** @ignore */ | ||
350 | onDblClick : function(e){ | ||
351 | var item = this.findItemFromChild(e.getTarget()); | ||
352 | if(item){ | ||
353 | this.fireEvent('dblclick', this, this.indexOf(item), item, e); | ||
354 | } | ||
355 | }, | ||
356 | |||
357 | onItemClick : function(item, index, e){ | ||
358 | if(this.fireEvent('beforeclick', this, index, item, e) !== false){ | ||
359 | if(this.multiSelect || this.singleSelect){ | ||
360 | if(this.multiSelect && e.shiftKey && this.lastSelection){ | ||
361 | this.select(this.getNodes(this.indexOf(this.lastSelection), index), false); | ||
362 | }else{ | ||
363 | this.select(item, this.multiSelect && e.ctrlKey); | ||
364 | this.lastSelection = item; | ||
365 | } | ||
366 | e.preventDefault(); | ||
367 | } | ||
368 | return true; | ||
369 | }else{ | ||
370 | return false; | ||
371 | } | ||
372 | }, | ||
373 | |||
374 | /** | ||
375 | * Get the number of selected nodes. | ||
376 | * @return {Number} | ||
377 | */ | ||
378 | getSelectionCount : function(){ | ||
379 | return this.selections.length; | ||
380 | }, | ||
381 | |||
382 | /** | ||
383 | * Get the currently selected nodes. | ||
384 | * @return {Array} An array of HTMLElements | ||
385 | */ | ||
386 | getSelectedNodes : function(){ | ||
387 | return this.selections; | ||
388 | }, | ||
389 | |||
390 | /** | ||
391 | * Get the indexes of the selected nodes. | ||
392 | * @return {Array} | ||
393 | */ | ||
394 | getSelectedIndexes : function(){ | ||
395 | var indexes = []; | ||
396 | for(var i = 0, len = this.selections.length; i < len; i++) { | ||
397 | indexes.push(this.selections[i].nodeIndex); | ||
398 | } | ||
399 | return indexes; | ||
400 | }, | ||
401 | |||
402 | /** | ||
403 | * Clear all selections | ||
404 | * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event | ||
405 | */ | ||
406 | clearSelections : function(suppressEvent){ | ||
407 | if(this.multiSelect || this.singleSelect){ | ||
408 | YAHOO.util.Dom.removeClass(this.selections, this.selectedClass); | ||
409 | this.selections = []; | ||
410 | if(!suppressEvent){ | ||
411 | this.fireEvent('selectionchange', this, this.selections); | ||
412 | } | ||
413 | } | ||
414 | }, | ||
415 | |||
416 | /** | ||
417 | * Returns true if the passed node is selected | ||
418 | * @param {HTMLElement/Number} node The node or node index | ||
419 | * @return {Boolean} | ||
420 | */ | ||
421 | isSelected : function(node){ | ||
422 | node = this.getNode(node); | ||
423 | var s = this.selections; | ||
424 | if(s.length < 1){ | ||
425 | return false; | ||
426 | } | ||
427 | if(s.indexOf){ | ||
428 | return s.indexOf(node) !== -1; | ||
429 | }else{ | ||
430 | for(var i = 0, len = s.length; i < len; i++){ | ||
431 | if (s[i] == node){ | ||
432 | return true; | ||
433 | } | ||
434 | } | ||
435 | return false; | ||
436 | } | ||
437 | }, | ||
438 | |||
439 | /** | ||
440 | * Selects nodes. | ||
441 | * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select | ||
442 | * @param {Boolean} keepExisting (optional) true to keep existing selections | ||
443 | * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent | ||
444 | */ | ||
445 | select : function(nodeInfo, keepExisting, suppressEvent){ | ||
446 | if(!keepExisting){ | ||
447 | this.clearSelections(true); | ||
448 | } | ||
449 | if(nodeInfo instanceof Array){ | ||
450 | for(var i = 0, len = nodeInfo.length; i < len; i++) { | ||
451 | this.select(nodeInfo[i], true, true); | ||
452 | } | ||
453 | }else{ | ||
454 | var node = this.getNode(nodeInfo); | ||
455 | if(node && !this.isSelected(node)){ | ||
456 | if(this.fireEvent('beforeselect', this, node, this.selections) !== false){ | ||
457 | YAHOO.util.Dom.addClass(node, this.selectedClass); | ||
458 | this.selections.push(node); | ||
459 | } | ||
460 | } | ||
461 | } | ||
462 | if(!suppressEvent){ | ||
463 | this.fireEvent('selectionchange', this, this.selections); | ||
464 | } | ||
465 | }, | ||
466 | |||
467 | /** | ||
468 | * Gets a template node. | ||
469 | * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node | ||
470 | * @return {HTMLElement} The node or null if it wasn't found | ||
471 | */ | ||
472 | getNode : function(nodeInfo){ | ||
473 | if(typeof nodeInfo == 'object'){ | ||
474 | return nodeInfo; | ||
475 | }else if(typeof nodeInfo == 'string'){ | ||
476 | return document.getElementById(nodeInfo); | ||
477 | }else if(typeof nodeInfo == 'number'){ | ||
478 | return this.nodes[nodeInfo]; | ||
479 | } | ||
480 | return null; | ||
481 | }, | ||
482 | |||
483 | /** | ||
484 | * Gets a range template nodes. | ||
485 | * @param {Number} startIndex | ||
486 | * @param {Number} endIndex | ||
487 | * @return {Array} An array of nodes | ||
488 | */ | ||
489 | getNodes : function(start, end){ | ||
490 | var ns = this.nodes; | ||
491 | start = start || 0; | ||
492 | end = typeof end == 'undefined' ? ns.length-1 : end; | ||
493 | var nodes = []; | ||
494 | if(start <= end){ | ||
495 | for(var i = start; i <= end; i++) { | ||
496 | nodes.push(ns[i]); | ||
497 | } | ||
498 | }else{ | ||
499 | for(var i = start; i >= end; i--) { | ||
500 | nodes.push(ns[i]); | ||
501 | } | ||
502 | } | ||
503 | return nodes; | ||
504 | }, | ||
505 | |||
506 | /** | ||
507 | * Finds the index of the passed node | ||
508 | * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node | ||
509 | * @return {Number} The index of the node or -1 | ||
510 | */ | ||
511 | indexOf : function(node){ | ||
512 | node = this.getNode(node); | ||
513 | if(typeof node.nodeIndex == 'number'){ | ||
514 | return node.nodeIndex; | ||
515 | } | ||
516 | var ns = this.nodes; | ||
517 | for(var i = 0, len = ns.length; i < len; i++) { | ||
518 | if(ns[i] == node){ | ||
519 | return i; | ||
520 | } | ||
521 | } | ||
522 | return -1; | ||
523 | } | ||
524 | }); | ||
525 | |||
526 | /** | ||
527 | * @class YAHOO.ext.JsonView | ||
528 | * @extends YAHOO.ext.View | ||
529 | * Shortcut class to create a JSON + UpdateManager template view. Usage: | ||
530 | <pre><code> | ||
531 | var view = new YAHOO.ext.JsonView('my-element', | ||
532 | '<div id="{id}">{foo} - {bar}</div>', // auto create template | ||
533 | { multiSelect: true, jsonRoot: 'data' }); | ||
534 | |||
535 | // listen for node click? | ||
536 | view.on('click', function(vw, index, node, e){ | ||
537 | alert('Node "' + node.id + '" at index: ' + index + ' was clicked.'); | ||
538 | }); | ||
539 | |||
540 | // direct load of JSON data | ||
541 | view.load('foobar.php'); | ||
542 | |||
543 | |||
544 | // Example from my blog list | ||
545 | var tpl = new YAHOO.ext.Template( | ||
546 | '<div class="entry">' + | ||
547 | '<a class="entry-title" href="{link}">{title}</a>' + | ||
548 | '<h4>{date} by {author} | {comments} Comments</h4>{description}' + | ||
549 | '</div><hr />' | ||
550 | ); | ||
551 | |||
552 | var moreView = new YAHOO.ext.JsonView('entry-list', tpl, { | ||
553 | jsonRoot: 'posts' | ||
554 | }); | ||
555 | moreView.on('beforerender', this.sortEntries, this, true); | ||
556 | moreView.load({ | ||
557 | url:'/blog/get-posts.php', | ||
558 | params: 'allposts=true', | ||
559 | text:'Loading Blog Entries...' | ||
560 | }); | ||
561 | </code></pre> | ||
562 | * @constructor | ||
563 | * Create a new JsonView | ||
564 | * @param {String/HTMLElement/Element} container The container element where the view is to be rendered. | ||
565 | * @param {DomHelper.Template} tpl The rendering template | ||
566 | * @param {Object} config The config object | ||
567 | */ | ||
568 | YAHOO.ext.JsonView = function(container, tpl, config){ | ||
569 | var cfg = config || {}; | ||
570 | cfg.renderUpdates = true; | ||
571 | YAHOO.ext.JsonView.superclass.constructor.call(this, container, tpl, null, cfg); | ||
572 | /** | ||
573 | * @event beforerender | ||
574 | * Fires before rendering of the downloaded json data. | ||
575 | * @param {YAHOO.ext.View} this | ||
576 | * @param {Object} data The json data loaded | ||
577 | */ | ||
578 | this.events['beforerender'] = true; | ||
579 | /** | ||
580 | * @event load | ||
581 | * Fires when data is loaded. | ||
582 | * @param {YAHOO.ext.View} this | ||
583 | * @param {Object} data The json data loaded | ||
584 | * @param {Object} response The raw Connect response object | ||
585 | */ | ||
586 | this.events['load'] = true; | ||
587 | /** | ||
588 | * @event loadexception | ||
589 | * Fires when loading fails. | ||
590 | * @param {YAHOO.ext.View} this | ||
591 | * @param {Object} response The raw Connect response object | ||
592 | */ | ||
593 | this.events['loadexception'] = true; | ||
594 | this.el.getUpdateManager().on('update', this.onLoad, this, true); | ||
595 | this.el.getUpdateManager().on('failure', this.onLoadException, this, true); | ||
596 | }; | ||
597 | YAHOO.extendX(YAHOO.ext.JsonView, YAHOO.ext.View, { | ||
598 | /** | ||
599 | * Performs an async request, loading the JSON from the response. If params are specified it uses POST, otherwise it uses GET. | ||
600 | * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options: | ||
601 | <pre><code> | ||
602 | view.load({ | ||
603 | url: 'your-url.php',<br/> | ||
604 | params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string<br/> | ||
605 | callback: yourFunction,<br/> | ||
606 | scope: yourObject, //(optional scope) <br/> | ||
607 | discardUrl: false, <br/> | ||
608 | nocache: false,<br/> | ||
609 | text: 'Loading...',<br/> | ||
610 | timeout: 30,<br/> | ||
611 | scripts: false<br/> | ||
612 | }); | ||
613 | </code></pre> | ||
614 | * The only required property is url. The optional properties nocache, text and scripts | ||
615 | * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance. | ||
616 | * @param {<i>String/Object</i>} params (optional) The parameters to pass as either a url encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2} | ||
617 | * @param {<i>Function</i>} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess) | ||
618 | * @param {<i>Boolean</i>} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url. | ||
619 | */ | ||
620 | load : function(){ | ||
621 | var um = this.el.getUpdateManager(); | ||
622 | um.update.apply(um, arguments); | ||
623 | }, | ||
624 | |||
625 | /** | ||
626 | * Get the number of records in the current JSON dataset | ||
627 | * @return {Number} | ||
628 | */ | ||
629 | getCount : function(){ | ||
630 | return this.jsonData ? this.jsonData.length : 0; | ||
631 | }, | ||
632 | |||
633 | /** | ||
634 | * Returns the JSON object for the specified node(s) | ||
635 | * @param {HTMLElement/Array} node The node or an array of nodes | ||
636 | * @return {Object/Array} If you pass in an array, you get an array back, otherwise | ||
637 | * you get the JSON object for the node | ||
638 | */ | ||
639 | getNodeData : function(node){ | ||
640 | if(node instanceof Array){ | ||
641 | var data = []; | ||
642 | for(var i = 0, len = node.length; i < len; i++){ | ||
643 | data.push(this.getNodeData(node[i])); | ||
644 | } | ||
645 | return data; | ||
646 | } | ||
647 | return this.jsonData[this.indexOf(node)] || null; | ||
648 | }, | ||
649 | |||
650 | beforeRender : function(){ | ||
651 | this.snapshot = this.jsonData; | ||
652 | if(this.sortInfo){ | ||
653 | this.sort.apply(this, this.sortInfo); | ||
654 | } | ||
655 | this.fireEvent('beforerender', this, this.jsonData); | ||
656 | }, | ||
657 | |||
658 | onLoad : function(el, o){ | ||
659 | this.fireEvent('load', this, this.jsonData, o); | ||
660 | }, | ||
661 | |||
662 | onLoadException : function(el, o){ | ||
663 | this.fireEvent('loadexception', this, o); | ||
664 | }, | ||
665 | |||
666 | /** | ||
667 | * Filter the data by a specific property. | ||
668 | * @param {String} property A property on your JSON objects | ||
669 | * @param {String/RegExp} value Either string that the property values | ||
670 | * should start with or a RegExp to test against the property | ||
671 | */ | ||
672 | filter : function(property, value){ | ||
673 | if(this.jsonData){ | ||
674 | var data = []; | ||
675 | var ss = this.snapshot; | ||
676 | if(typeof value == 'string'){ | ||
677 | var vlen = value.length; | ||
678 | if(vlen == 0){ | ||
679 | this.clearFilter(); | ||
680 | return; | ||
681 | } | ||
682 | value = value.toLowerCase(); | ||
683 | for(var i = 0, len = ss.length; i < len; i++){ | ||
684 | var o = ss[i]; | ||
685 | if(o[property].substr(0, vlen).toLowerCase() == value){ | ||
686 | data.push(o); | ||
687 | } | ||
688 | } | ||
689 | }else if(value.exec){ // regex? | ||
690 | for(var i = 0, len = ss.length; i < len; i++){ | ||
691 | var o = ss[i]; | ||
692 | if(value.test(o[property])){ | ||
693 | data.push(o); | ||
694 | } | ||
695 | } | ||
696 | }else{ | ||
697 | return; | ||
698 | } | ||
699 | this.jsonData = data; | ||
700 | this.refresh(); | ||
701 | } | ||
702 | }, | ||
703 | |||
704 | /** | ||
705 | * Filter by a function. The passed function will be called with each | ||
706 | * object in the current dataset. If the function returns true, the value is kept | ||
707 | * otherwise it is filtered. | ||
708 | * @param {Function} fn | ||
709 | * @param {Object} scope (optional) The scope of the function (defaults to this JsonView) | ||
710 | */ | ||
711 | filterBy : function(fn, scope){ | ||
712 | if(this.jsonData){ | ||
713 | var data = []; | ||
714 | var ss = this.snapshot; | ||
715 | for(var i = 0, len = ss.length; i < len; i++){ | ||
716 | var o = ss[i]; | ||
717 | if(fn.call(scope|| this, o)){ | ||
718 | data.push(o); | ||
719 | } | ||
720 | } | ||
721 | this.jsonData = data; | ||
722 | this.refresh(); | ||
723 | } | ||
724 | }, | ||
725 | |||
726 | /** | ||
727 | * Clears the current filter. | ||
728 | */ | ||
729 | clearFilter : function(){ | ||
730 | if(this.snapshot && this.jsonData != this.snapshot){ | ||
731 | this.jsonData = this.snapshot; | ||
732 | this.refresh(); | ||
733 | } | ||
734 | }, | ||
735 | |||
736 | |||
737 | /** | ||
738 | * Sorts the data for this view and refreshes it. | ||
739 | * @param {String} property A property on your JSON objects to sort on | ||
740 | * @param {String} direction (optional) desc or asc (defaults to asc) | ||
741 | * @param {Function} sortType (optional) A function to call to convert the data to a sortable value. | ||
742 | */ | ||
743 | sort : function(property, dir, sortType){ | ||
744 | this.sortInfo = Array.prototype.slice.call(arguments, 0); | ||
745 | if(this.jsonData){ | ||
746 | var p = property; | ||
747 | var dsc = dir && dir.toLowerCase() == 'desc'; | ||
748 | var f = function(o1, o2){ | ||
749 | var v1 = sortType ? sortType(o1[p]) : o1[p]; | ||
750 | var v2 = sortType ? sortType(o2[p]) : o2[p];; | ||
751 | if(v1 < v2){ | ||
752 | return dsc ? +1 : -1; | ||
753 | }else if(v1 > v2){ | ||
754 | return dsc ? -1 : +1; | ||
755 | }else{ | ||
756 | return 0; | ||
757 | } | ||
758 | }; | ||
759 | this.jsonData.sort(f); | ||
760 | this.refresh(); | ||
761 | if(this.jsonData != this.snapshot){ | ||
762 | this.snapshot.sort(f); | ||
763 | } | ||
764 | } | ||
765 | } | ||
766 | }); | ||