2 // http://alexsexton.com/blog/2010/02/using-inheritance-patterns-to-organize-large-jquery-applications/
3 // We will use John Resig's proposal
5 // http://pastie.org/517177
7 // NOTE: missing a destroy function
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);
15 instance[options].apply(instance, args);
17 instance = $.data(this, name, new object(options, this));
24 // * false or undefined or none : no debug
25 // * true : trace all event calls
26 // * [ 'in_progress', 'query_done' ] : would only trace to these events
27 var plugin_debug=false;
28 plugin_debug = [ 'in_progress', 'query_done' ];
30 var Plugin = Class.extend({
32 init: function(options, element) {
33 // Mix in the passed in options with the default options
34 this.options = $.extend({}, this.default_options, options);
36 // Save the element reference, both as a jQuery
37 // reference and a normal reference
38 this.element = element;
39 this.$element = $(element);
40 // programmatically add specific class for publishing events
41 // used in manifold.js for triggering API events
42 if ( ! this.$element.hasClass('pubsub')) this.$element.addClass('pubsub');
44 // return this so we can chain/use the bridge with less code.
48 has_query_handler: function() {
49 return (typeof this.on_filter_added === 'function');
52 // do we need to log API calls ?
53 _is_in : function (obj, arr) {
54 for(var i=0; i<arr.length; i++) {
55 if (arr[i] == obj) return true;
58 _deserves_logging: function (event) {
59 if ( ! plugin_debug ) return false;
60 else if ( plugin_debug === true) return true;
61 else if (this._is_in (event, plugin_debug)) return true;
65 _query_handler: function(prefix, event_type, data) {
66 // We suppose this.query_handler_prefix has been defined if this
67 // callback is triggered
71 event = 'filter_added';
74 event = 'filter_removed';
77 event = 'filter_clear';
80 event = 'field_added';
83 event = 'field_removed';
86 event = 'field_clear';
92 fn = 'on_' + prefix + event;
93 if (typeof this[fn] === 'function') {
94 if (this._deserves_logging (event)) {
95 var classname=this.classname;
96 messages.debug("Plugin._query_handler: calling "+fn+" on "+classname);
98 // call with data as parameter
99 // XXX implement anti loop
104 _record_handler: function(prefix, event_type, record) {
105 // We suppose this.query_handler_prefix has been defined if this
106 // callback is triggered
110 event = 'new_record';
113 event = 'clear_records';
116 event = 'query_in_progress';
119 event = 'query_done';
121 case FIELD_STATE_CHANGED:
122 event = 'field_state_changed';
128 fn = 'on_' + prefix + event;
129 if (typeof this[fn] === 'function') {
130 if (this._deserves_logging (event)) {
131 var classname=this.classname;
132 messages.debug("Plugin._record_handler: calling "+fn+" on "+classname);
134 // call with data as parameter
135 // XXX implement anti loop
140 get_handler_function: function(type, prefix) {
142 return $.proxy(function(e, event_type, record) {
143 return this['_' + type + '_handler'](prefix, event_type, record);
147 listen_query: function(query_uuid, prefix) {
148 // default: prefix = ''
149 prefix = (typeof prefix === 'undefined') ? '' : (prefix + '_');
151 this.$element.on(manifold.get_channel('query', query_uuid), this.get_handler_function('query', prefix));
152 this.$element.on(manifold.get_channel('record', query_uuid), this.get_handler_function('record', prefix));
157 /* Helper functions for naming HTML elements (ID, classes), with support for filters and fields */
160 var ret = this.options.plugin_uuid;
161 for (var i = 0; i < arguments.length; i++) {
162 ret = ret + manifold.separator + arguments[i];
168 if (arguments.length == 0) {
169 return $('#' + this.id());
171 // We make sure to search _inside_ the dom tag of the plugin
172 return $('#' + this.id.apply(this, arguments), this.elmt());
176 elts: function(cls) {
177 return $('.' + cls, this.elmt());
180 id_from_filter: function(filter, use_value) {
181 use_value = typeof use_value !== 'undefined' ? use_value : true;
185 var value = filter[2];
186 var op_str = this.getOperatorLabel(op);
187 var s = manifold.separator;
190 return 'filter' + s + key + s + op_str + s + value;
192 return 'filter' + s + key + s + op_str;
196 str_from_filter: function(filter) {
197 return filter[0] + ' ' + filter[1] + ' ' + filter[2];
200 array_from_id: function(id) {
201 var ret = id.split(manifold.separator);
202 ret.shift(); // remove plugin_uuid at the beginning
206 id_from_field: function(field) {
207 return 'field' + manifold.separator + field;
210 field_from_id: function(id) {
212 if (typeof id === 'string') {
213 array = id.split(manifold.separator);
214 } else { // We suppose we have an array ('object')
217 // array = ['field', FIELD_NAME]
221 id_from_key: function(key_field, value) {
223 return key_field + manifold.separator + this.escape_id(value).replace(/\\/g, '');
227 // at some point in time we used to have a helper function named 'flat_id' here
228 // the goals was to sort of normalize id's but it turned out we can get rid of that
229 // in a nutshell, we would have an id (can be urn, hrn, whatever) and
230 // we want to be able to retrieve a DOM element based on that (e.g. a checkbox)
231 // so we did something like <tag id="some-id-that-comes-from-the-db">
232 // and then $("#some-id-that-comes-from-the-db")
233 // however the syntax for that selector prevents from using some characters in id
234 // and so for some of our ids this won't work
235 // instead of 'flattening' we now do this instead
236 // <tag some_id="then!we:can+use.what$we!want">
237 // and to retrieve it
238 // $("[some_id='then!we:can+use.what$we!want']")
239 // which thanks to the quotes, works; and you can use this with id as well in fact
240 // of course if now we have quotes in the id it's going to squeak, but well..
242 // escape (read: backslashes) some meta-chars in input
243 escape_id: function(id) {
244 if( id !== undefined){
245 return id.replace( /(:|\.|\[|\])/g, "\\$1" );
247 return "undefined-id";
251 id_from_record: function(method, record) {
252 var keys = manifold.metadata.get_key(method);
259 switch (Object.toType(key)) {
261 if (!(key in record))
263 return this.id_from_key(key, record[key]);
266 throw 'Not implemented';
270 key_from_id: function(id) {
271 // NOTE this works only for simple keys
274 if (typeof id === 'string') {
275 array = id.split(manifold.separator);
276 } else { // We suppose we have an array ('object')
280 // arguments has the initial id but lacks the key field name (see id_from_key), so we are even
281 // we finally add +1 for the plugin_uuid at the beginning
282 return array[arguments.length + 1];
286 // plugin-helper.js is about managing toggled state
287 // it would be beneficial to merge it in here
288 toggle_on: function () { return this.toggle("true"); },
289 toggle_off: function () { return this.toggle("false"); },
290 toggle: function (status) {
291 plugin_helper.set_toggle_status (this.options.plugin_uuid,status);
295 // use spin() to get our default spin settings (called presets)
296 // use spin(true) to get spin's builtin defaults
297 // you can also call spin_presets() yourself and tweak what you need to, like topmenuvalidation does
298 spin: function (presets) {
299 var presets = ( presets === undefined ) ? spin_presets() : presets;
300 try { this.$element.spin(presets); }
301 catch (err) { messages.debug("Cannot turn on spin " + err); }
305 try { this.$element.spin(false); }
306 catch (err) { messages.debug("Cannot turn off spin " + err); }
311 load_template: function(name, ctx) {
312 return Mustache.render(this.elmt(name).html(), ctx);