assets/bowline.js in bowline-0.6.3 vs assets/bowline.js in bowline-0.9.1
- old
+ new
@@ -1,407 +1,251 @@
-/*
- Bowline JavaScript API
-
- This library lets you call Ruby methods, and bind up elements.
- It requires jQuery, Chain.js and json2:
- http://jquery.com
- http://github.com/raid-ox/chain.js
- http://www.JSON.org/json2.js
-
- = Functions
-
- invoke(klass, method, *args)
- Invoke a class method on a particular class. Usually
- used to invoke methods on a binder. The class needs to
- be exposed to JS (using the Bowline::Desktop::Bridge#js_expose).
- Usage:
- Bowline.invoke('MyClass', 'my_method');
-
- instanceInvoke(klass, id, method, *args)
- Invoke an instance method an a binder.
- Usually called via the jQuery helper functions.
- Usage:
- Bowline.instanceInvoke('UsersBinder', 1, 'charge!');
-
- windowInvoke(method, *args)
- Invoke class method on this window's class.
- Usage:
- Bowline.windowInvoke('close');
-
- helper(method, *args)
- Invoke a method defined in any helper.
-
- bind(element, klass, options = {})
- Bind a element to a Bowline binder.
- Usually called via the jQuery helper functions.
- Usage:
- Bowline.bind('#users', 'UsersBinder');
-
- The options can either be a template hash:
- {
- '.name .first': {
- style: 'color: blue;',
- content: 'First Name: {first}'
- },
- '.name .last': 'Family Name: {last}',
- '.address': function(data, el){
- if(!data.address)
- el.hide();
- return data.address;
- },
- builder: function(){
- var data = this.item();
- this.find('.name').click(function(){alert(data.name)});
- }
- }
-
- Or the options can be a builder function:
- (function(){
- this.bind('click', function(){
- var data = this.item();
- alert(data);
- })
-
- For more documentation, look at the Chain.js library:
- http://wiki.github.com/raid-ox/chain.js/elementchain
-
- = Filtering items
-
- $('#users').items('filter', 'value');
-
- = Sorting items
-
- $('#users').items('sort', 'first_name');
-
- = Update events
-
- $('#users').update(function(){
- //...
- });
-
- = JQuery functions
-
- These are how you usually bind elements, or invoke a binders class/instance methods.
-
- $.fn.bowlineBind(klass, options)
- Associate an an element with a Bowline binder.
- Example:
- $("#users").bowlineBind('UsersBinder');
+var Bowline = new SuperClass();
- $.fn.bowlineUnbind(klass)
- Opposite of bowlineBind.
- Example:
- $("#users").bowlineUnbind('UsersBinder');
-
- $.fn.invoke(method, *args)
- Invoke a class/instance method on a Bowline binder.
-
- If called on the bound element, in this example the #users div, then a class method
- will be called on the binder.
- Example:
- $("#users").invoke("my_class_method", "arg1");
-
- If called on a item inside a bound element, an instance method will be called.
- Example:
- $("#users").items(10).invoke("my_instance_method");
-
- = Debugging
-
- Turn on Bowline.trace to show debugging information:
- Bowline.trace = true
-
- = Using other libraries (e.g. Prototype)
-
- Although this library requires jQuery, its API is not jQuery
- specific. It's perfectly feasible to rewrite to use Prototype instead.
- Additionally, jQuery plays nicely with other libraries using it's noConflict() method.
- So you're still free to use other JavaScript libraries without fear of conflicts.
-
-*/
-
-var BowlineBound = function(klass){
- this.klass = klass;
- this.options = {};
- this.elements = jQuery();
-};
-
-BowlineBound.fn = BowlineBound.prototype;
-
-BowlineBound.fn.updateOptions = function(opts){
- this.options = jQuery.extend({}, this.options, opts);
- this.singleton = this.options.singleton;
-}
-
-BowlineBound.fn.push = function(element){
- this.elements = this.elements.add(element);
- this.setup();
-}
-
-BowlineBound.fn.replace = function(items){
- if(this.singleton) {
- this.elements.item("replace", items);
- } else {
- this.elements.items("replace", items);
- }
-}
-
-BowlineBound.fn.create = function(id, item){
- if(this.singleton) {
- this.elements.item(item);
- } else {
- this.elements.items("add", item);
- }
-}
-
-BowlineBound.fn.update = function(id, item){
- if(!item.id) item.id = id;
- if(this.singleton){
- this.elements.item(item);
- } else {
- this.findElement(id).item(item);
- }
-}
-
-BowlineBound.fn.remove = function(id){
- if(this.singleton) {
- this.elements.item("replace", {});
- } else {
- this.findElement(id).item("remove");
- }
-}
-
-BowlineBound.fn.invoke = function(){
- var args = $.makeArray(arguments);
- args.unshift(this.klass);
- Bowline.invoke.apply(Bowline, args);
-}
-
-BowlineBound.fn.findElement = function(id){
- // TODO - increase efficiency
- var element = jQuery();
- jQuery.each(this.elements.items(true), function(){
- var sameItem = $(this).item().id == id;
- if(sameItem) element = element.add(this);
- });
- return element;
-}
-
-BowlineBound.fn.setup = function(){
- if (this.hasSetup) return;
- this.hasSetup = true;
- var self = this;
- jQuery(function(){
- Bowline.invoke(self.klass, "setup", function(opts){
- self.updateOptions(opts);
- });
- });
-}
-
-var Bowline = {
+Bowline.extend({
callbacks: {},
uuid: 0,
bounds: {},
trace: false,
- // _app is a function defined in Objective C
+ updating: false,
+ // _app is a function defined in bowline-desktop
enabled: typeof(_app) != "undefined",
id: function(){
- return ++Bowline.uuid;
+ return ++this.uuid;
},
// Usage: invoke(klass, method, *args)
invoke: function(){
+ if( this.updating ) return;
var args = jQuery.makeArray(arguments);
var klass = args.shift();
var method = args.shift();
var id = -1;
-
+
var callback = args.pop();
if(typeof(callback) == "function"){
- id = Bowline.id();
- Bowline.callbacks[id] = callback;
+ id = this.id();
+ this.callbacks[id] = callback;
} else if(callback) {
args.push(callback);
- }
+ }
var msg = {
klass: klass,
method: method,
args: args,
id: id,
- };
+ };
- Bowline.log("New message:", msg);
+ this.log("New message:", msg);
- if(Bowline.enabled)
- _app.call(JSON.stringify(msg));
+ // Support for SuperModel instances
+ var serializer = function(key, value){
+ if (value == null) return value;
+ if (typeof value == "object" &&
+ typeof value.attributes == "function"){
+ return value.attributes();
+ } else {
+ return value;
+ }
+ };
+
+ if(this.enabled)
+ _app.call(JSON.stringify(msg, serializer));
},
// Usage: instanceInvoke(klass, id, method, *args)
instanceInvoke: function(){
var args = jQuery.makeArray(arguments);
args.splice(1, 0, "instance_invoke");
- Bowline.invoke.apply(this, args);
+ this.invoke.apply(this, args);
},
// Usage: windowInvoke(method, *args)
windowInvoke: function(){
var args = jQuery.makeArray(arguments);
args.unshift("_window");
- Bowline.invoke.apply(this, args);
+ this.invoke.apply(this, args);
},
helper: function(){
var args = jQuery.makeArray(arguments);
args.unshift("Bowline::Helpers");
- Bowline.invoke.apply(this, args);
+ this.invoke.apply(this, args);
},
- bind: function(el, klass, options){
- el = jQuery(el);
-
- el.data("bowline", klass);
- el.chain(options);
-
- if(!Bowline.bounds[klass])
- Bowline.bounds[klass] = new BowlineBound(klass);
-
- Bowline.bounds[klass].push(el);
-
- // Shortcut
- Bowline[klass] = Bowline.bounds[klass];
- },
-
- unbind: function(el, klass){
- // TODO
- // var array = Bowline.bounds[klass];
- // if(!array) return;
- // array = jQuery.grep(array,
- // function(n){ return n.el != el }
- // );
- // Bowline.bounds[klass] = array;
- },
-
openInspector: function(){
- if(Bowline.enabled)
+ if(this.enabled)
_app.openInspector();
},
+ bind: function(klass, object, options){
+ if ( !this.bounds[klass] ) {
+ this.bounds[klass] = [];
+ this.bounds[klass].push(object);
+ this.invoke(klass, "setup");
+ } else {
+ this.bounds[klass].push(object);
+ }
+ if (object.isModel)
+ this.extendModel(klass, object, options);
+ },
+
// Bowline functions
invokeJS: function(str){
- Bowline.log("Invoking:", str);
+ this.log("Invoking:", str);
+ this.updating = true;
+ var value;
try {
- return JSON.stringify(eval(str));
+ value = JSON.stringify(eval(str));
} catch(e) {
- Bowline.warn(e);
+ this.warn("Error at: " + e.sourceURL + ":" + e.line);
+ this.warn(str);
+ this.error(e);
}
+ this.updating = false;
+ return value;
},
invokeCallback: function(id, res){
- Bowline.log("Callback:", id, res);
- if(!Bowline.callbacks[id]) return;
+ this.log("Callback:", id, res);
+ if(!this.callbacks[id]) return;
try {
- Bowline.callbacks[id](JSON.parse(res));
- delete Bowline.callbacks[id];
+ this.callbacks[id](JSON.parse(res));
+ delete this.callbacks[id];
} catch(e) {
- Bowline.warn(e)
+ this.warn("Error at: " + e.sourceURL + ":" + e.line);
+ this.error(e);
}
},
replace: function(klass, items){
- if(!Bowline.bounds[klass]) return;
- Bowline.bounds[klass].replace(items);
+ if(!this.bounds[klass]) return;
+ this.eachBound(klass, function(object){
+ object.replace(items);
+ });
},
created: function(klass, id, item){
- if(!Bowline.bounds[klass]) return;
- Bowline.bounds[klass].create(id, item);
+ if(!this.bounds[klass]) return;
+ this.eachBound(klass, function(object){
+ object.create(item);
+ });
},
updated: function(klass, id, item){
- if(!Bowline.bounds[klass]) return;
- Bowline.bounds[klass].update(id, item);
+ if(!this.bounds[klass]) return;
+ this.eachBound(klass, function(object){
+ object.update(id, item);
+ });
},
- removed: function(klass, id){
- if(!Bowline.bounds[klass]) return;
- Bowline.bounds[klass].remove(id);
+ destroyed: function(klass, id){
+ if(!this.bounds[klass]) return;
+ this.eachBound(klass, function(object){
+ object.destroy(id);
+ });
},
- trigger: function(klass, event, data){
- if(!Bowline.bounds[klass]) return;
- Bowline.bounds[klass].elements.trigger(event, data);
- },
+ // System functions
- element: function(klass, id){
- if(!Bowline.bounds[klass]) return;
- return Bowline.bounds[klass].findElement(id);
+ eachBound: function(klass, callback){
+ for(var i in this.bounds[klass]){
+ callback(this.bounds[klass][i]);
+ }
},
- // System functions
-
loaded: function(){
- Bowline.windowInvoke("loaded!");
+ this.windowInvoke("loaded!");
},
+ extendModel: function(klass, object, options){
+ var self = this;
+ if ( !options ) options = {};
+
+ if (options.duplex) {
+ object.afterCreate(function(item){
+ object.invoke("create", item);
+ });
+
+ object.afterUpdate(function(item){
+ item.invoke("update", item);
+ });
+
+ object.afterDestroy(function(item){
+ item.invoke("destroy");
+ });
+ }
+ },
+
log: function(){
- if( !Bowline.trace ) return;
+ if( !this.trace ) return;
var args = jQuery.makeArray(arguments);
args.unshift("(Bowline)");
console.log.apply(console, args);
},
warn: function(){
var args = jQuery.makeArray(arguments);
args.unshift("(Bowline)");
console.warn.apply(console, args);
+ },
+
+ error: function(){
+ var args = jQuery.makeArray(arguments);
+ args.unshift("(Bowline)");
+ console.error.apply(console, args);
}
-};
+});
(function($){
- $.fn.invoke = function(){
- if($(this).chain("active")){
- var args = $.makeArray(arguments);
- var element = $(this).item("root");
- var klass = element.data("bowline");
- if(!klass) throw "Unknown class: " + klass;
+ if (typeof SuperModel != "undefined") {
+ SuperModel.extend({
+ bind: function(klass, options){
+ this.boundKlass = klass;
+ Bowline.bind(this.boundKlass, this, options);
+ },
- if(Bowline[klass].singleton){
- var id = element.item().id;
- args.unshift(id);
- args.unshift(klass);
- Bowline.instanceInvoke.apply(Bowline, args);
- } else {
- args.unshift(klass);
- Bowline.invoke.apply(Bowline, args);
+ delegateInvoke: function(){
+ var calls = jQuery.makeArray(arguments);
+ var self = this;
+ jQuery.each(calls, function(i, item){
+ self[item] = function(){
+ var args = jQuery.makeArray(arguments);
+ args.unshift(item);
+ this.invoke.apply(this, args);
+ }
+ });
+ },
+
+ invoke: function(){
+ var args = jQuery.makeArray(arguments);
+ args.unshift(this.boundKlass);
+ Bowline.invoke.apply(Bowline, args);
}
- } else {
- throw 'Chain not active';
- }
- };
+ });
- $.fn.bowlineBind = function(){
- var args = $.makeArray(arguments);
- args.unshift(this);
- Bowline.bind.apply(Bowline, args);
- };
+ SuperModel.fn.delegateInvoke = SuperModel.delegateInvoke;
+ SuperModel.include({
+ invoke: function(){
+ var args = jQuery.makeArray(arguments);
+ args.unshift(this.id);
+ args.unshift(this._class.boundKlass);
+ Bowline.instanceInvoke.apply(Bowline, args);
+ }
+ });
+ }
- $.fn.bowlineUnbind = function(){
- var args = $.makeArray(arguments);
- args.unshift(this);
- Bowline.unbind.apply(Bowline, args);
- };
-
$.fn.bowlineSerialize = function(){
var array = $(this).serializeArray();
var object = {};
$.each(array, function(){
object[this.name] = this.value;
});
return object;
};
-})(jQuery);
-
-jQuery(function($){
- Bowline.loaded();
-})
+
+ $(function(){
+ Bowline.loaded();
+ });
+})(jQuery);
\ No newline at end of file