dd12379dcc1c2384c08e3dc3543b5980741c8829
[myslice.git] / manifold / js / plugin.js
1 // INHERITANCE
2 // http://alexsexton.com/blog/2010/02/using-inheritance-patterns-to-organize-large-jquery-applications/
3 // We will use John Resig's proposal
4
5 // http://pastie.org/517177
6
7 // NOTE: missing a destroy function
8
9 $.plugin = function(name, object) {
10     $.fn[name] = function(options) {
11         var args = Array.prototype.slice.call(arguments, 1);
12         return this.each(function() {
13             var instance = $.data(this, name);
14             if (instance) {
15                 instance[options].apply(instance, args);
16             } else {
17                 instance = $.data(this, name, new object(options, this));
18             }
19         });
20     };
21 };
22
23 var Plugin = Class.extend({
24
25     init: function(options, element)
26     {
27         // Mix in the passed in options with the default options
28         this.options = $.extend({}, this.default_options, options);
29
30         // Save the element reference, both as a jQuery
31         // reference and a normal reference
32         this.element  = element;
33         this.$element = $(element);
34
35         // return this so we can chain/use the bridge with less code.
36         return this;
37     },
38
39     has_query_handler: function() {
40         return (typeof this.on_filter_added === 'function');
41     },
42
43     _query_handler: function(prefix, event_type, data)
44     {
45         // We suppose this.query_handler_prefix has been defined if this
46         // callback is triggered    
47         var fn;
48         switch(event_type) {
49             case FILTER_ADDED:
50                 fn = 'filter_added';
51                 break;
52             case FILTER_REMOVED:
53                 fn = 'filter_removed';
54                 break;
55             case CLEAR_FILTERS:
56                 fn = 'filter_clear';
57                 break;
58             case FIELD_ADDED:
59                 fn = 'field_added';
60                 break;
61             case FIELD_REMOVED:
62                 fn = 'field_removed';
63                 break;
64             case CLEAR_FIELDS:
65                 fn = 'field_clear';
66                 break;
67             default:
68                 return;
69         } // switch
70         
71         fn = 'on_' + prefix + fn;
72         if (typeof this[fn] === 'function') {
73             // call with data as parameter
74             // XXX implement anti loop
75             this[fn](data);
76         }
77     },
78
79     _record_handler: function(prefix, event_type, record)
80     {
81         // We suppose this.query_handler_prefix has been defined if this
82         // callback is triggered    
83         var fn;
84         switch(event_type) {
85             case NEW_RECORD:
86                 fn = 'new_record';
87                 break;
88             case CLEAR_RECORDS:
89                 fn = 'clear_records';
90                 break;
91             case IN_PROGRESS:
92                 fn = 'query_in_progress';
93                 break;
94             case DONE:
95                 fn = 'query_done';
96                 break;
97             case FIELD_STATE_CHANGED:
98                 fn = 'field_state_changed';
99                 break;
100             default:
101                 return;
102         } // switch
103         
104         fn = 'on_' + prefix + fn;
105         if (typeof this[fn] === 'function') {
106             // call with data as parameter
107             // XXX implement anti loop
108             this[fn](record);
109         }
110     },
111
112     get_handler_function: function(type, prefix)
113     {
114         
115         return $.proxy(function(e, event_type, record) {
116             return this['_' + type + '_handler'](prefix, event_type, record);
117         }, this);
118     },
119
120     listen_query: function(query_uuid, prefix)
121     {
122         // default: prefix = ''
123         prefix = (typeof prefix === 'undefined') ? '' : (prefix + '_');
124
125         this.$element.on(manifold.get_channel('query', query_uuid),  this.get_handler_function('query',  prefix));
126         this.$element.on(manifold.get_channel('record', query_uuid),  this.get_handler_function('record', prefix));
127     },
128
129     default_options: {},
130
131     /* Helper functions for naming HTML elements (ID, classes), with support for filters and fields */
132
133     id: function()
134     {
135         var ret = this.options.plugin_uuid;
136         for (var i = 0; i < arguments.length; i++) {
137             ret = ret + manifold.separator + arguments[i];
138         }
139         return ret;
140     },
141
142     el: function()
143     {
144         if (arguments.length == 0) {
145             return $('#' + this.id());
146         } else {
147             // We make sure to search _inside_ the dom tag of the plugin
148             return $('#' + this.id.apply(this, arguments), this.el());
149         }
150     },
151
152     els: function(cls)
153     {
154         return $('.' + cls, this.el());
155     },
156
157     id_from_filter: function(filter, use_value)
158     {
159         use_value = typeof use_value !== 'undefined' ? use_value : true;
160
161         var key    = filter[0];
162         var op     = filter[1];
163         var value  = filter[2];
164         var op_str = this.getOperatorLabel(op);
165         var s      = manifold.separator;
166
167         if (use_value) {
168             return 'filter' + s + key + s + op_str + s + value;
169         } else {
170             return 'filter' + s + key + s + op_str;
171         }
172     },
173
174     str_from_filter: function(filter)
175     {
176         return filter[0] + ' ' + filter[1] + ' ' + filter[2];
177     },
178
179     array_from_id: function(id)
180     {
181         var ret = id.split(manifold.separator);
182         ret.shift(); // remove plugin_uuid at the beginning
183         return ret;
184     },
185
186     id_from_field: function(field)
187     {
188         return 'field' + manifold.separator + field;
189     },
190
191     field_from_id: function(id)
192     {
193         var array;
194         if (typeof id === 'string') {
195             array = id.split(manifold.separator);
196         } else { // We suppose we have an array ('object')
197             array = id;
198         }
199         // array = ['field', FIELD_NAME]
200         return array[1];
201     },
202
203     id_from_key: function(key_field, value)
204     {
205         
206         return key_field + manifold.separator + unfold.escape_id(value).replace(/\\/g, '');
207     },
208
209     id_from_record: function(method, record)
210     {
211         var keys = manifold.metadata.get_key(method);
212         if (!keys)
213             return;
214         if (keys.length > 1)
215             return;
216
217         var key = keys[0];
218         switch (Object.toType(key)) {
219             case 'string':
220                 if (!(key in record))
221                     return null;
222                 return this.id_from_key(key, record[key]);
223     
224             default:
225                 throw 'Not implemented';
226         }
227     },
228
229     key_from_id: function(id)
230     {
231         // NOTE this works only for simple keys
232
233         var array;
234         if (typeof id === 'string') {
235             array = id.split(manifold.separator);
236         } else { // We suppose we have an array ('object')
237             array = id;
238         }
239
240         // arguments has the initial id but lacks the key field name (see id_from_key), so we are even
241         // we finally add +1 for the plugin_uuid at the beginning
242         return array[arguments.length + 1];
243     },
244
245     /* SPIN */
246
247     spin: function()
248     {
249         manifold.spin(this.element);
250     },
251
252     unspin: function()
253     {
254         manifold.spin(this.element, false);
255     },
256
257     /* TEMPLATE */
258
259     load_template: function(name, ctx)
260     {
261         return Mustache.render(this.el(name).html(), ctx);
262     },
263
264 });