1 /** 2 * @fileoverview 3 * <code>APE</code> provides core features, including namespacing and object creational aspects. 4 * 5 * <h3>APE JavaScript Library</h3> 6 * <p> 7 * Released under Academic Free Licence 3.0. 8 * </p> 9 * 10 * @author Garrett Smith 11 */ 12 13 /** @name APE 14 * @namespace */ 15 if(APE !== undefined) throw Error("APE is already defined."); 16 var APE = { 17 18 /** 19 * @memberOf APE 20 * @description Prototype inheritance. 21 * @param {Object} subclass 22 * @param {Object} superclass 23 * @param {Object} mixin If present, <var>mixin</var>'s own properties are copied to receiver 24 * using APE.mixin(subclass.prototoype, superclass.prototype). 25 */ 26 extend : function(subclass, superclass, mixin) { 27 if(arguments.length === 0) return; 28 var f = arguments.callee, subp; 29 f.prototype = superclass.prototype; 30 subclass.prototype = subp = new f; 31 if(typeof mixin == "object") 32 APE.mixin(subp, mixin); 33 subp.constructor = subclass; 34 return subclass; 35 }, 36 37 /** 38 * Shallow copy of properties; does not look up prototype chain. 39 * Copies all properties in s to r, using hasOwnProperty. 40 * @param {Object} r the receiver of properties. 41 * @param {Object} s the supplier of properties. 42 * Accounts for JScript DontEnum bug for valueOf and toString. 43 * @return {Object} r the receiver. 44 */ 45 mixin : function(r, s) { 46 var jscriptSkips = ['toString', 'valueOf'], 47 prop, 48 i = 0, 49 skipped; 50 for(prop in s) { 51 if(s.hasOwnProperty(prop)) 52 r[prop] = s[prop]; 53 } 54 // JScript DontEnum bug. 55 for( ; i < jscriptSkips.length; i++) { 56 skipped = jscriptSkips[i]; 57 if(s.hasOwnProperty(skipped)) 58 r[skipped] = s[skipped]; 59 } 60 return r; 61 }, 62 63 toString : function() { return "[APE JavaScript Library]"; }, 64 65 /** Creational method meant for being cross-cut. 66 * Uses APE.newApply to create 67 * @param {HTMLElement} el An element. If el does not have 68 * an ID, then an ID will be automatically generated, based on the 69 * constructor's (this) identifier, or, If this is anonymous, "APE". 70 * @requires {Object} an object to be attached to as a property. 71 * @aspect 72 * @scope {Function} that accepts an HTMLElement for 73 * its first argument. 74 * APE.getByNode is intended to be bouund to a constructor function. 75 * @return <code>{new this(el [,args...])}</code> 76 */ 77 getByNode : function(el) { 78 var id = el.id, 79 fName; 80 if(!id) { 81 if(!APE.getByNode._i) APE.getByNode._i = 0; 82 fName = APE.getFunctionName(this); 83 if(!fName) fName = "APE"; 84 id = el.id = fName+"_" + (APE.getByNode._i++); 85 } 86 if(!this.hasOwnProperty("instances")) this.instances = {}; 87 return this.instances[id] || (this.instances[id] = APE.newApply(this, arguments)); 88 }, 89 90 /** Tries to get a name of a function object, returns "" if anonymous. 91 */ 92 getFunctionName : function(fun) { 93 if(typeof fun.name == "string") return fun.name; 94 var name = Function.prototype.toString.call(fun).match(/\s([a-z]+)\(/i); 95 return name && name[1]||""; 96 }, 97 98 /** Creational method meant for being cross-cut. 99 * @param {HTMLElement} el An element that has an id. 100 * @requires {Object} an object to bind to. 101 * @aspect 102 * @description <code>getById</code> must be assigned to a function constructor 103 * that accepts an HTMLElement's <code>id</code> for 104 * its first argument. 105 * @example <pre> 106 * function Slider(el, config){ } 107 * Slider.getById = APE.getById; 108 * </pre> 109 * This allows for implementations to use a factory method with the constructor. 110 * <pre> 111 * Slider.getById( "weight", 1 ); 112 * </pre> 113 * Subsequent calls to: 114 * <pre> 115 * Slider.getById( "weight" ); 116 * </pre> 117 * will return the same Slider instance. 118 * An <code>instances</code> property is added to the constructor object 119 * that <code>getById</code> is assigned to. 120 * @return <pre>new this(id [,args...])</pre> 121 */ 122 getById : function(id) { 123 if(!this.hasOwnProperty("instances")) this.instances = {}; 124 return this.instances[id] || (this.instances[id] = APE.newApply(this, arguments)); 125 }, 126 127 /** 128 * @param {Function} fun constructor to be invoked. 129 * @param {Array} args arguments to pass to the constructor. 130 * Instantiates a constructor and uses apply(). 131 */ 132 newApply : function(fun, args) { 133 if(arguments.length === 0) return; 134 var f = arguments.callee, i; 135 136 f.prototype = fun.prototype;// Copy prototype. 137 f.prototype.constructor = fun; 138 139 i = new f; 140 fun.apply(i, args); // Apply the original constructor. 141 return i; 142 }, 143 144 /** Throws the error in a setTimeout 1ms. 145 * Deferred errors are useful for Event Notification systems, 146 * Animation, and testing. 147 * @param {Error} error that occurred. 148 */ 149 deferError : function(error) { 150 setTimeout(function(){throw error;},1); 151 } 152 }; 153 154 (function(){ 155 156 APE.namespace = namespace; 157 158 /** 159 * @memberOf APE 160 * @description creates a namespace split on "." 161 * does <em>not</em> automatically add APE to the front of the chain, as YUI does. 162 * @param {String} s the namespace. "foo.bar" would create a namespace foo.bar, but only 163 * if that namespace did not exist. 164 * @return {Package} the namespace. 165 */ 166 function namespace(s) { 167 var packages = s.split("."), 168 pkg = window, 169 hasOwnProperty = Object.prototype.hasOwnProperty, 170 qName = pkg.qualifiedName, 171 i = 0, 172 len = packages.length; 173 name; 174 for (; i < len; i++) { 175 name = packages[i]; 176 177 // Internet Explorer does not support 178 // hasOwnProperty on things like window, so call Object.prototype.hasOwnProperty. 179 // Opera does not support the global object or [[Put]] properly (see below) 180 if(!hasOwnProperty.call(pkg, name)) { 181 pkg[name] = new Package((qName||"APE")+"."+name); 182 } 183 pkg = pkg[name]; 184 } 185 186 return pkg; 187 } 188 189 Package.prototype.toString = function(){ 190 return"["+this.qualifiedName+"]"; 191 }; 192 193 /* constructor Package 194 */ 195 function Package(qualifiedName) { 196 this.qualifiedName = qualifiedName; 197 } 198 })(); 199 200 (function(){ 201 /**@class 202 * A safe patch to the Object object. This patch addresses a bug that only affects Opera. 203 * <strong>It does <em>not</em> affect any for-in loops in any browser</strong> (see tests). 204 */ 205 Object=window.Object; 206 207 var O = Object.prototype, hasOwnProperty = O.hasOwnProperty; 208 if(typeof window != "undefined" && hasOwnProperty && !hasOwnProperty.call(window, "Object")) { 209 /** 210 * @overrides Object.prototype.hasOwnProperty 211 * @method 212 * This is a conditional patch that affects some versions of Opera. 213 * It is perfectly safe to do this and does not affect enumeration. 214 */ 215 Object.prototype.hasOwnProperty = function(p) { 216 if(this === window) return (p in this) && (O[p] !== this[p]); 217 return hasOwnProperty.call(this, p); 218 }; 219 } 220 })();