summaryrefslogtreecommitdiff
path: root/frontend/beta/js/YUI-extensions/widgets/TemplateView.js
authorGiulio Cesare Solaroli <giulio.cesare@clipperz.com>2011-10-02 23:56:18 (UTC)
committer Giulio Cesare Solaroli <giulio.cesare@clipperz.com>2011-10-02 23:56:18 (UTC)
commitef68436ac04da078ffdcacd7e1f785473a303d45 (patch) (unidiff)
treec403752d66a2c4775f00affd4fa8431b29c5b68c /frontend/beta/js/YUI-extensions/widgets/TemplateView.js
parent597ecfbc0249d83e1b856cbd558340c01237a360 (diff)
downloadclipperz-ef68436ac04da078ffdcacd7e1f785473a303d45.zip
clipperz-ef68436ac04da078ffdcacd7e1f785473a303d45.tar.gz
clipperz-ef68436ac04da078ffdcacd7e1f785473a303d45.tar.bz2
First version of the newly restructured repository
Diffstat (limited to 'frontend/beta/js/YUI-extensions/widgets/TemplateView.js') (more/less context) (show whitespace changes)
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/TemplateView.js766
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>
8var dataModel = new YAHOO.ext.grid.XMLDataModel(...);
9var view = new YAHOO.ext.View('my-element',
10 '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11 dataModel, {
12 singleSelect: true,
13 selectedClass: 'ydataview-selected'
14 });
15
16// listen for node click?
17view.on('click', function(vw, index, node, e){
18 alert('Node "' + node.id + '" at index: ' + index + ' was clicked.');
19});
20
21// load XML data
22dataModel.load('foobar.xml');
23</code></pre>
24For 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*/
35YAHOO.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
132YAHOO.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>
531var view = new YAHOO.ext.JsonView('my-element',
532 '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
533 { multiSelect: true, jsonRoot: 'data' });
534
535// listen for node click?
536view.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
541view.load('foobar.php');
542
543
544// Example from my blog list
545var tpl = new YAHOO.ext.Template(
546 '&lt;div class="entry"&gt;' +
547 '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
548 '&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}' +
549 '&lt;/div&gt;&lt;hr /&gt;'
550);
551
552var moreView = new YAHOO.ext.JsonView('entry-list', tpl, {
553 jsonRoot: 'posts'
554});
555moreView.on('beforerender', this.sortEntries, this, true);
556moreView.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 */
568YAHOO.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};
597YAHOO.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>
602view.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&amp;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});