/*
Copyright (c) 2007-2008 James Coglan, http://jsclass.jcoglan.com
Licensed under the MIT license, http://www.opensource.org/licenses/mit-license.php
*/
JS={extend:function(a,b){for(var c in b)a[c]=b[c]},method:function(a){var b=this,c=b._methods=b._methods||{};if((c[a]||{}).fn==b[a])return c[a].bd;return(c[a]={fn:b[a],bd:b[a].bind(b)}).bd},util:{}};Array.from=function(a){if(!a)return[];if(a.toArray)return a.toArray();var b=a.length,c=[];while(b--)c[b]=a[b];return c};JS.extend(Function.prototype,{bind:function(){var a=this,b=Array.from(arguments),c=b.shift()||null;return function(){return a.apply(c,b.concat(Array.from(arguments)))}},callsSuper:function(){return/\bcallSuper\b/.test(this.toString())},is:function(a){return typeof a=='function'}});JS.Class=function(){var a=Array.from(arguments),b,c=Function.is(a[0])?a.shift():null,d=JS.Class.create(c);while(b=a.shift())d.include(b);c&&Function.is(c.inherited)&&c.inherited(d);return d};JS.extend(JS.Class,{create:function(a){var b=function(){this.initialize.apply(this,arguments)};this.ify(b);a&&this.subclass(a,b);var p=b.prototype;p.klass=p.constructor=b;b.include(this.INSTANCE_METHODS,false);b.instanceMethod('extend',this.INSTANCE_METHODS.extend,false);return b},ify:function(a,b){a.superclass=a.superclass||Object;a.subclasses=a.subclasses||[];if(b===false)return a;for(var c in this.CLASS_METHODS)this.CLASS_METHODS.hasOwnProperty(c)&&(a[c]=this.CLASS_METHODS[c]);return a},subclass:function(a,b){this.ify(a,false);b.superclass=a;a.subclasses.push(b);var c=function(){};c.prototype=a.prototype;b.prototype=new c();b.extend(a);return b},properties:function(a){var b={},c,K=this.ify(function(){});loop:for(var d in a){for(c in K){if(d==c)continue loop}b[d]=a[d]}return b},addMethod:function(e,f,g,h){if(!Function.is(h))return(e[g]=h);if(!h.callsSuper())return(e[g]=h);var j=function(){var a=f[g],b=Array.from(arguments),c=this.callSuper,d;Function.is(a)&&(this.callSuper=function(){var i=arguments.length;while(i--)b[i]=arguments[i];return a.apply(this,b)});d=h.apply(this,arguments);c?this.callSuper=c:delete this.callSuper;return d};j.valueOf=function(){return h};j.toString=function(){return h.toString()};e[g]=j},INSTANCE_METHODS:{initialize:function(){},method:JS.method,extend:function(a){for(var b in a)a.hasOwnProperty(b)&&JS.Class.addMethod(this,this.klass.prototype,b,a[b]);return this},isA:function(a){var b=this.klass;while(b){if(b===a)return true;b=b.superclass}return false}},CLASS_METHODS:{include:function(a,b){var c,i,n,d=a.include,e=a.extend;if(d){c=[].concat(d);for(i=0,n=c.length;i<n;i++)this.include(c[i],b)}if(e){c=[].concat(e);for(i=0,n=c.length;i<n;i++)this.extend(c[i],b)}for(var f in a){!/^(included?|extend(ed)?)$/.test(f)&&this.instanceMethod(f,a[f],b)}Function.is(a.included)&&a.included(this);return this},instanceMethod:function(a,b,c){if(!this.prototype[a]||c!==false)JS.Class.addMethod(this.prototype,this.superclass.prototype,a,b);return this},extend:function(a,b){Function.is(a)&&(a=JS.Class.properties(a));for(var c in a){a.hasOwnProperty(c)&&!/^(included?|extend(ed)?)$/.test(c)&&this.classMethod(c,a[c],b)}Function.is(a.extended)&&a.extended(this);return this},classMethod:function(a,b,c){for(var i=0,n=this.subclasses.length;i<n;i++)this.subclasses[i].classMethod(a,b,false);(!this[a]||c!==false)&&JS.Class.addMethod(this,this.superclass,a,b);return this},method:JS.method}});JS.extend(JS,{Interface:JS.Class({initialize:function(c){this.test=function(a,b){var n=c.length;while(n--){if(!Function.is(a[c[n]]))return b?c[n]:false}return true}},extend:{ensure:function(){var a=Array.from(arguments),b=a.shift(),c,d;while(c=a.shift()){d=c.test(b,true);if(d!==true)throw new Error('object does not implement '+d+'()');}}}}),Singleton:function(){return new(JS.Class.apply(JS,arguments))},Module:function(b){return{included:function(a){a.include(b)},extended:function(a){a.extend(b)}}}});JS.Observable={addObserver:function(a,b){(this._observers=this._observers||[]).push({bk:a,cx:b||null})},removeObserver:function(a,b){this._observers=this._observers||[];b=b||null;for(var i=0,n=this.countObservers();i<n;i++){if(this._observers[i].bk==a&&this._observers[i].cx==b){this._observers.splice(i,1);return}}},removeObservers:function(){this._observers=[]},countObservers:function(){return(this._observers=this._observers||[]).length},notifyObservers:function(){if(!this.isChanged())return;for(var i=0,n=this.countObservers(),a;i<n;i++){a=this._observers[i];a.bk.apply(a.cx,arguments)}},setChanged:function(a){this._changed=!(a===false)},isChanged:function(){if(this._changed===undefined)this._changed=true;return!!this._changed}};JS.Observable.subscribe=JS.Observable.addObserver;JS.Observable.unsubscribe=JS.Observable.removeObserver;JS.Observable=JS.Module(JS.Observable);JS.State=JS.Module({_getState:function(a){return(typeof a=='object'&&a)||(typeof a=='string'&&((this.states||{})[a]||{}))||{}},setState:function(a){this._state=this._getState(a);JS.util.State.addMethods(this._state,this.klass)},inState:function(){for(var i=0,n=arguments.length;i<n;i++){if(this._state==this._getState(arguments[i]))return true}return false}});JS.util.State={stub:function(){return this},buildStubs:function(a,b,c){var d,e;for(d in c){b[d]={};for(e in c[d])a[e]=this.stub}},buildCollection:function(a,b,c,d){var e={},f={},g=c.states||{};this.buildStubs(e,f,d);this.buildStubs(e,f,g);var h,i;for(h in f){i=(g[h]||{}).klass;i=i?JS.Class(i,d[h]):JS.Class(d[h]);i.include(e,false);f[h]=new i}return a.call(JS.Class,b,c,'states',f)},addMethods:function(a,b){for(var c in a)this.addMethod(b,c)},addMethod:function(b,c){b.instanceMethod(c,function(){var a=(this._state||{})[c];return a?a.apply(this,arguments):this},false)}};JS.Class.addMethod=(function(e){return function(a,b,c,d){if(c!='states'||typeof d!='object')return e.apply(JS.Class,arguments);return JS.util.State.buildCollection(e,a,b,d)}})(JS.Class.addMethod);