/** * @author Ryan Johnson * @copyright 2008 PersonalGrid Corporation * @package LivePipe UI * @license MIT * @url http://livepipe.net/extra/event_behavior * @require prototype.js, livepipe.js * @attribution http://www.adamlogic.com/2007/03/20/3_metaprogramming-javascript-presentation */ /*global Prototype, Class, Event, Try, $, $A, $H */ if(typeof(Prototype) == "undefined") { throw "Event.Behavior requires Prototype to be loaded."; } if(typeof(Object.Event) == "undefined") { throw "Event.Behavior requires Object.Event to be loaded."; } Event.Behavior = { addVerbs: function(verbs){ var v; for (var name in verbs) { if (verbs.hasOwnProperty(name)) { v = new Event.Behavior.Verb(verbs[name]); Event.Behavior.Verbs[name] = v; Event.Behavior[name.underscore()] = Event.Behavior[name] = v.getCallbackForStack.bind(v); }} }, addEvents: function(events){ $H(events).each(function(event_type){ Event.Behavior.Adjective.prototype[event_type.key.underscore()] = Event.Behavior.Adjective.prototype[event_type.key] = function(){ this.nextConditionType = 'and'; this.events.push(event_type.value); this.attachObserver(false); return this; }; }); }, invokeElementMethod: function(element,action,args){ if(typeof(element) == 'function'){ return $A(element()).each(function(e){ if(typeof(args[0]) == 'function'){ return $A(args[0]).each(function(a){ return $(e)[action].apply($(e),(a ? [a] : [])); }); }else { return $(e)[action].apply($(e),args || []); } }); }else { return $(element)[action].apply($(element),args || []); } } }; Event.Behavior.Verbs = $H({}); Event.Behavior.Verb = Class.create(); Object.extend(Event.Behavior.Verb.prototype,{ originalAction: false, execute: false, executeOpposite: false, target: false, initialize: function(action){ this.originalAction = action; this.execute = function(action,target,argument){ return (argument) ? action(target,argument) : action(target); }.bind(this,action); }, setOpposite: function(opposite_verb){ var opposite_action = opposite_verb.originalAction; this.executeOpposite = function(opposite_action,target,argument){ return (argument) ? opposite_action(target,argument) : opposite_action(target); }.bind(this,opposite_action); }, getCallbackForStack: function(argument){ return new Event.Behavior.Noun(this,argument); } }); Event.Behavior.addVerbs({ call: function(callback){ callback(); }, show: function(element){ return Event.Behavior.invokeElementMethod(element,'show'); }, hide: function(element){ return Event.Behavior.invokeElementMethod(element,'hide'); }, remove: function(element){ return Event.Behavior.invokeElementMethod(element,'remove'); }, setStyle: function(element,styles){ return Event.Behavior.invokeElementMethod(element,'setStyle',[(typeof(styles) == 'function' ? styles() : styles)]); }, addClassName: function(element,class_name){ return Event.Behavior.invokeElementMethod(element,'addClassName',[(typeof(class_name) == 'function' ? class_name() : class_name)]); }, removeClassName: function(element,class_name){ return Event.Behavior.invokeElementMethod(element,'removeClassName',[(typeof(class_name) == 'function' ? class_name() : class_name)]); }, setClassName: function(element,class_name){ var c = (typeof(class_name) == 'function') ? class_name() : class_name; if(typeof(element) == 'function'){ return $A(element()).each(function(e){ $(e).className = c; }); }else { c = $(element).className; return c; } }, update: function(content,element){ return Event.Behavior.invokeElementMethod(element,'update',[(typeof(content) == 'function' ? content() : content)]); }, replace: function(content,element){ return Event.Behavior.invokeElementMethod(element,'replace',[(typeof(content) == 'function' ? content() : content)]); } }); Event.Behavior.Verbs.show.setOpposite(Event.Behavior.Verbs.hide); Event.Behavior.Verbs.hide.setOpposite(Event.Behavior.Verbs.show); Event.Behavior.Verbs.addClassName.setOpposite(Event.Behavior.Verbs.removeClassName); Event.Behavior.Verbs.removeClassName.setOpposite(Event.Behavior.Verbs.addClassName); Event.Behavior.Noun = Class.create(); Object.extend(Event.Behavior.Noun.prototype,{ verbs: false, verb: false, argument: false, subject: false, target: false, initialize: function(verb,argument){ //this.verbs = $A([]); this.verb = verb; this.argument = argument; }, execute: function(){ return (this.target) ? this.verb.execute(this.target,this.argument) : this.verb.execute(this.argument); }, executeOpposite: function(){ return (this.target) ? this.verb.executeOpposite(this.target,this.argument) : this.verb.executeOpposite(this.argument); }, when: function(subject){ this.subject = subject; return new Event.Behavior.Adjective(this); }, getValue: function(){ return Try.these( function(){return $(this.subject).getValue();}.bind(this), function(){return $(this.subject).options[$(this.subject).options.selectedIndex].value;}.bind(this), function(){return $(this.subject).value;}.bind(this), function(){return $(this.subject).innerHTML;}.bind(this) ); }, containsValue: function(match){ var value = this.getValue(); if(typeof(match) == 'function'){ return $A(match()).include(value); }else { return value.match(match); } }, setTarget: function(target){ this.target = target; return this; }, and: function(){ } }); Event.Behavior.Noun.prototype._with = Event.Behavior.Noun.prototype.setTarget; Event.Behavior.Noun.prototype.on = Event.Behavior.Noun.prototype.setTarget; Event.Behavior.Noun.prototype.of = Event.Behavior.Noun.prototype.setTarget; Event.Behavior.Noun.prototype.to = Event.Behavior.Noun.prototype.setTarget; Event.Behavior.Noun.prototype.from = Event.Behavior.Noun.prototype.setTarget; Event.Behavior.Adjective = Class.create(); Object.extend(Event.Behavior.Adjective.prototype,{ noun: false, lastConditionName: '', nextConditionType: 'and', conditions: $A([]), events: $A([]), attached: false, initialize: function(noun){ this.conditions = $A([]); this.events = $A([]); this.noun = noun; }, attachObserver: function(execute_on_load){ if(this.attached){ //this may call things multiple times, but is the only way to gaurentee correct state on startup if(execute_on_load) { this.execute(); } return; } this.attached = true; if(typeof(this.noun.subject) == 'function'){ $A(this.noun.subject()).each(function(subject){ (this.events.length > 0 ? this.events : $A(['change'])).each(function(event_name){ (subject.observe ? subject : $(subject)).observe(event_name,function(){ this.execute(); }.bind(this)); }.bind(this)); }.bind(this)); }else{ (this.events.length > 0 ? this.events : $A(['change'])).each(function(event_name){ $(this.noun.subject).observe(event_name,function(){ this.execute(); }.bind(this)); }.bind(this)); } if(execute_on_load) { this.execute(); } }, execute: function(){ if(this.match()) { return this.noun.execute(); } else if(this.noun.verb.executeOpposite) { this.noun.executeOpposite(); } }, attachCondition: function(callback){ this.conditions.push([this.nextConditionType,callback.bind(this)]); }, match: function(){ if(this.conditions.length === 0) { return true; } else { return this.conditions.inject(false, function (bool,condition) { return (condition[0] === 'or') ? (bool && condition[1]()) : (bool || condition[1]()); }); } }, //conditions is: function(item){ this.lastConditionName = 'is'; this.attachCondition(function(item){ return (typeof(item) == 'function' ? item() : item) == this.noun.getValue(); }.bind(this,item)); this.attachObserver(true); return this; }, isNot: function(item){ this.lastConditionName = 'isNot'; this.attachCondition(function(item){ return (typeof(item) == 'function' ? item() : item) != this.noun.getValue(); }.bind(this,item)); this.attachObserver(true); return this; }, contains: function(item){ this.lastConditionName = 'contains'; this.attachCondition(function(item){ return this.noun.containsValue(item); }.bind(this,item)); this.attachObserver(true); return this; }, within: function(item){ this.lastConditionName = 'within'; this.attachCondition(function(item){ }.bind(this,item)); this.attachObserver(true); return this; }, //events change: function(){ this.nextConditionType = 'and'; this.attachObserver(true); return this; }, and: function(condition){ this.attached = false; this.nextConditionType = 'and'; if(condition) { this[this.lastConditionName](condition); } return this; }, or: function(condition){ this.attached = false; this.nextConditionType = 'or'; if(condition) { this[this.lastConditionName](condition); } return this; } }); Event.Behavior.addEvents({ losesFocus: 'blur', gainsFocus: 'focus', isClicked: 'click', isDoubleClicked: 'dblclick', keyPressed: 'keypress' }); Event.Behavior.Adjective.prototype.is_not = Event.Behavior.Adjective.prototype.isNot; Event.Behavior.Adjective.prototype.include = Event.Behavior.Adjective.prototype.contains; Event.Behavior.Adjective.prototype.includes = Event.Behavior.Adjective.prototype.contains; Event.Behavior.Adjective.prototype.are = Event.Behavior.Adjective.prototype.is; Event.Behavior.Adjective.prototype.areNot = Event.Behavior.Adjective.prototype.isNot; Event.Behavior.Adjective.prototype.are_not = Event.Behavior.Adjective.prototype.isNot; Event.Behavior.Adjective.prototype.changes = Event.Behavior.Adjective.prototype.change;