renamed validatebutton into topmenuvalidation
[myslice.git] / manifold / static / 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         // Mix in the passed in options with the default options
27         this.options = $.extend({}, this.default_options, options);
28
29         // Save the element reference, both as a jQuery
30         // reference and a normal reference
31         this.element  = element;
32         this.$element = $(element);
33         // programmatically add specific class for publishing events
34         // used in manifold.js for triggering API events
35         if ( ! this.$element.hasClass('pubsub')) this.$element.addClass('pubsub');
36
37         // return this so we can chain/use the bridge with less code.
38         return this;
39     },
40
41     has_query_handler: function() {
42         return (typeof this.on_filter_added === 'function');
43     },
44
45     _query_handler: function(prefix, event_type, data) {
46         // We suppose this.query_handler_prefix has been defined if this
47         // callback is triggered    
48         var fn;
49         switch(event_type) {
50         case FILTER_ADDED:
51             fn = 'filter_added';
52             break;
53         case FILTER_REMOVED:
54             fn = 'filter_removed';
55             break;
56         case CLEAR_FILTERS:
57             fn = 'filter_clear';
58             break;
59         case FIELD_ADDED:
60             fn = 'field_added';
61             break;
62         case FIELD_REMOVED:
63             fn = 'field_removed';
64             break;
65         case CLEAR_FIELDS:
66             fn = 'field_clear';
67             break;
68         default:
69             return;
70         } // switch
71         
72         fn = 'on_' + prefix + fn;
73         if (typeof this[fn] === 'function') {
74             // call with data as parameter
75             // XXX implement anti loop
76             this[fn](data);
77         }
78     },
79
80     _record_handler: function(prefix, event_type, record) {
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         return $.proxy(function(e, event_type, record) {
115             return this['_' + type + '_handler'](prefix, event_type, record);
116         }, this);
117     },
118
119     listen_query: function(query_uuid, prefix) {
120         // default: prefix = ''
121         prefix = (typeof prefix === 'undefined') ? '' : (prefix + '_');
122
123         this.$element.on(manifold.get_channel('query', query_uuid),  this.get_handler_function('query',  prefix));
124         this.$element.on(manifold.get_channel('record', query_uuid),  this.get_handler_function('record', prefix));
125     },
126
127     default_options: {},
128
129     /* Helper functions for naming HTML elements (ID, classes), with support for filters and fields */
130
131     id: function() {
132         var ret = this.options.plugin_uuid;
133         for (var i = 0; i < arguments.length; i++) {
134             ret = ret + manifold.separator + arguments[i];
135         }
136         return ret;
137     },
138
139     elmt: function() {
140         if (arguments.length == 0) {
141             return $('#' + this.id());
142         } else {
143             // We make sure to search _inside_ the dom tag of the plugin
144             return $('#' + this.id.apply(this, arguments), this.elmt());
145         }
146     },
147
148     elts: function(cls) {
149         return $('.' + cls, this.elmt());
150     },
151
152     id_from_filter: function(filter, use_value) {
153         use_value = typeof use_value !== 'undefined' ? use_value : true;
154
155         var key    = filter[0];
156         var op     = filter[1];
157         var value  = filter[2];
158         var op_str = this.getOperatorLabel(op);
159         var s      = manifold.separator;
160
161         if (use_value) {
162             return 'filter' + s + key + s + op_str + s + value;
163         } else {
164             return 'filter' + s + key + s + op_str;
165         }
166     },
167
168     str_from_filter: function(filter) {
169         return filter[0] + ' ' + filter[1] + ' ' + filter[2];
170     },
171
172     array_from_id: function(id) {
173         var ret = id.split(manifold.separator);
174         ret.shift(); // remove plugin_uuid at the beginning
175         return ret;
176     },
177
178     id_from_field: function(field) {
179         return 'field' + manifold.separator + field;
180     },
181
182     field_from_id: function(id) {
183         var array;
184         if (typeof id === 'string') {
185             array = id.split(manifold.separator);
186         } else { // We suppose we have an array ('object')
187             array = id;
188         }
189         // array = ['field', FIELD_NAME]
190         return array[1];
191     },
192
193     id_from_key: function(key_field, value) {
194         
195         return key_field + manifold.separator + this.escape_id(value).replace(/\\/g, '');
196     },
197
198     // NOTE
199     // at some point in time we used to have a helper function named 'flat_id' here
200     // the goals was to sort of normalize id's but it turned out we can get rid of that
201     // in a nutshell, we would have an id (can be urn, hrn, whatever) and 
202     // we want to be able to retrieve a DOM element based on that (e.g. a checkbox)
203     // so we did something like <tag id="some-id-that-comes-from-the-db">
204     // and then $("#some-id-that-comes-from-the-db")
205     // however the syntax for that selector prevents from using some characters in id
206     // and so for some of our ids this won't work
207     // instead of 'flattening' we now do this instead
208     // <tag some_id="then!we:can+use.what$we!want">
209     // and to retrieve it
210     // $("[some_id='then!we:can+use.what$we!want']")
211     // which thanks to the quotes, works; and you can use this with id as well in fact
212     // of course if now we have quotes in the id it's going to squeak, but well..
213
214     // escape (read: backslashes) some meta-chars in input
215     escape_id: function(id) {
216         if( id !== undefined){
217             return id.replace( /(:|\.|\[|\])/g, "\\$1" );
218         }else{
219             return "undefined-id";
220         }
221     },
222
223     id_from_record: function(method, record) {
224         var keys = manifold.metadata.get_key(method);
225         if (!keys)
226             return;
227         if (keys.length > 1)
228             return;
229
230         var key = keys[0];
231         switch (Object.toType(key)) {
232         case 'string':
233             if (!(key in record))
234                 return null;
235             return this.id_from_key(key, record[key]);
236             
237         default:
238             throw 'Not implemented';
239         }
240     },
241
242     key_from_id: function(id) {
243         // NOTE this works only for simple keys
244
245         var array;
246         if (typeof id === 'string') {
247             array = id.split(manifold.separator);
248         } else { // We suppose we have an array ('object')
249             array = id;
250         }
251
252         // arguments has the initial id but lacks the key field name (see id_from_key), so we are even
253         // we finally add +1 for the plugin_uuid at the beginning
254         return array[arguments.length + 1];
255     },
256
257     // TOGGLE
258     // plugin-helper.js is about managing toggled state
259     // it would be beneficial to merge it in here
260     toggle_on: function () { return this.toggle("true"); },
261     toggle_off: function () { return this.toggle("false"); },
262     toggle: function (status) {
263         plugin_helper.set_toggle_status (this.options.plugin_uuid,status);
264     },
265
266     /* SPIN */
267     // use spin() to get our default spin settings (called presets)
268     // use spin(true) to get spin's builtin defaults
269     // you can also call spin_presets() yourself and tweak what you need to, like topmenuvalidation does
270     spin: function (presets) {
271         var presets = ( presets === undefined ) ? spin_presets() : presets;
272         try { this.$element.spin(presets); }
273         catch (err) { messages.debug("Cannot turn on spin " + err); }
274     },
275
276     unspin: function() {
277         try { this.$element.spin(false); }
278         catch (err) { messages.debug("Cannot turn off spin " + err); }
279     },
280
281     /* TEMPLATE */
282
283     load_template: function(name, ctx) {
284         return Mustache.render(this.elmt(name).html(), ctx);
285     },
286
287 });