ports.js's source
1 /** 2 * @name port 3 * @overview The core class of the ports.js. 4 * @requires ports.jsonp 5 * @version 1.01 6 * @updated 2007/09/05 7 * @author inamorix <<a href="mailto:inamorix@metatype.jp">inamorix@metatype.jp</a>> 8 * @copyright Copyright (c) 2007, <a href="http://metatype.jp/">metatype</a>. 9 * @license The MIT-style license. 10 */ 11 12 13 14 /** 15 * The core class of the ports.js. 16 * @type {object} 17 * @property {number} version The version of the ports.js. 18 * @property {object} libs The list of JavaScript libraries on the ports.js. 19 * @property {object} loaded The list of already loaded files on the ports.js. 20 * @property {object} installed The list of already installed libraries on the ports.js. 21 * @property {array} standby The list of functions of standby.<br /> 22 * Executes that when every DOM is accessible. 23 * @property {string} base The directory of the ports.js. 24 * @property {string} base_f The directory of the ports.js on "fetch". 25 * @property {string} query The query to the ports.js. 26 */ 27 port = { 28 /** 29 * Initialization of the members of this class. 30 * @private 31 * @return {instance} The instance of this class. 32 */ 33 init: function () { 34 this.version = 1.00; 35 this.libs = {}; 36 this.loaded = {}; 37 this.installed = {}; 38 this.standby = []; 39 this.base = this.myself().replace(/[^\/]+\.js([^\/]*)$/, ''); 40 this.base_f = 'http://s3.amazonaws.com/metatype/js/'; 41 this.query = RegExp.$1; 42 this.complete(); 43 44 return this; 45 }, 46 47 48 49 /** 50 * Returns the URL of a JavaScript file. 51 * @param {element} [el] The element to search for a script tag.<br /> 52 * Defaults to 'document'. 53 * @return {string} The URL of a JavaScript file. 54 * @example 55 * port.myself(); 56 */ 57 myself: function (el) { 58 var el = this.isset(el) ? el : document; 59 return (el.nodeName == 'SCRIPT') ? el.src : this.myself(el.lastChild); 60 }, 61 62 63 64 /** 65 * Loads a file. 66 * @param {string} file The URL of file.<br /> 67 * File extension reads "css" as a CSS file. 68 * @example 69 * port.load('example.js'); 70 */ 71 load: function (file) { 72 if (!this.loaded[file]) { 73 this.loaded[file] = true; 74 document.write( 75 /\.css$/i.test(file) 76 ? ('<link href="' + file + '" type="text/css" rel="stylesheet" media="all" />') 77 : ('<script src="' + file + '" type="text/javascript"></script>') 78 ); 79 } 80 }, 81 82 83 84 /** 85 * Configures the list of JavaScript libraries. 86 * @private 87 * @param {object} libs The list of JavaScript libraries. 88 */ 89 conf: function (libs) { 90 this.libs = libs; 91 this.query.match(/^\?([fim])\=(.*)$/); 92 93 var q = RegExp.$2.split(','); 94 switch (RegExp.$1) { 95 case 'i': this.install(q); break; 96 case 'm': this.make(q); break; 97 case 'f': this.fetch(q); break; 98 } 99 }, 100 101 102 103 /** 104 * Executes functions of standby. 105 * @private 106 */ 107 complete: function () { 108 var fn = this.bind(function () { 109 this.each(this.standby, function (standby) { 110 standby(); 111 }); 112 }); 113 114 var d = document; 115 if (/safari/i.test(navigator.userAgent)) { 116 var onreadystatechange = function () { 117 if (/loaded|complete/.test(d.readyState)) { 118 clearInterval(timer); 119 fn(); 120 } 121 } 122 var timer = setInterval(onreadystatechange, 50); 123 } 124 else if (d.all && !window.opera) { 125 var src = (location.protocol == 'https:') ? '://0' : 'javascript:void(0)'; 126 d.write('<script src="' + src + '" defer="defer" id="__tmp__"></script>'); 127 d.getElementById('__tmp__').onreadystatechange = function(){ 128 if (this.readyState == 'complete') { 129 this.parentNode.removeChild(this); 130 fn(); 131 } 132 }; 133 } 134 else { 135 d.addEventListener('DOMContentLoaded', fn, false); 136 } 137 }, 138 139 140 141 /** 142 * Installs a JavaScript library.<br /> 143 * The automatically load dependency files on based libraries. 144 * @param {string|array} keys The keyword of library.<br /> 145 * Array is more keywords.<br /> 146 * See <a href="00_file.php">the keywords of JavaScript library</a>. 147 * @param {string} [mode] The mode for <a href="#port.make">make</a> 148 * and <a href="#port.fetch">fetch</a>.<br /> 149 * Defaults to 'null'. 150 * @example 151 * // String (single) 152 * <script>port.install('prototype');</script> 153 * <script>alert($);</script> 154 * 155 * // Array (multiple) 156 * <script>port.install(['xxx', 'yyy', 'zzz']);</script> 157 * @example 158 * // And the ports.js supports the install from query. 159 * // The query of the install is "i". 160 * 161 * // From query (single) 162 * <script src="ports.js?i=prototype"></script> 163 * <script>alert($);</script> 164 * 165 * // From query (multiple) 166 * <script src="ports.js?i=xxx,yyy,zzz"></script> 167 */ 168 install: function (keys, mode) { 169 var keys = (typeof(keys) != 'object') ? [keys] : keys; 170 var mode = this.isset(mode) ? mode : null; 171 this.each(keys, function (key) { 172 if (this.libs[key]) { 173 if (!this.installed[key]) { 174 this.installed[key] = true; 175 if(this.libs[key].extend) { 176 this.install(this.libs[key].extend, mode); 177 } 178 this.each(this.libs[key].require, function (file) { 179 var base = (mode == 'f') ? this.base_f : this.base; 180 var file = (mode == 'm') ? 'COMPRESSED/' + file : file; 181 this.load(base + file); 182 }); 183 } 184 } 185 }); 186 }, 187 188 189 190 /** 191 * This is work same as <a href="#port.install">install</a>.<br /> 192 * To be different is to use a lightweight compressed file. 193 * @param {string|array} keys See <a href="#port.install">install</a>. 194 * @example 195 * <script>port.make('prototype');</script> 196 * @example 197 * // The query of the make is "m". 198 * <script src="ports.js?m=prototype"></script> 199 */ 200 make: function (keys) { 201 this.install(keys, 'm'); 202 }, 203 204 205 206 /** 207 * This is work same as <a href="#port.install">install</a>.<br /> 208 * To be different is to use a online file with download.<br /> 209 * When you use only fetch, the ports.js works only in "ports.js" and "ports.jsonp". 210 * @param {string|array} keys See <a href="#port.install">install</a>. 211 * @example 212 * <script>port.fetch('prototype');</script> 213 * @example 214 * // The query of the fetch is "f". 215 * <script src="ports.js?f=prototype"></script> 216 */ 217 fetch: function (keys) { 218 this.install(keys, 'f'); 219 }, 220 221 222 223 /** 224 * Extends the ports.js. 225 * @param {object} obj The list of object of new members.<br /> 226 * Some keyword works special.<br /><br /> 227 * <table cellspacing="0"> 228 * <tr> 229 * <th>keywords</th> 230 * <th>description</th> 231 * </tr> 232 * <tr> 233 * <th>init</th> 234 * <td>The function of initialize.<br />That must return 'this'.</td> 235 * </tr> 236 * <tr> 237 * <th>standby</th> 238 * <td>The function of standby.<br />Executes that when every DOM is accessible.</td> 239 * </tr> 240 * </table> 241 * @example 242 * port.extend({ 243 * init: function () { 244 * this.msg = 'hello'; 245 * return this; 246 * }, 247 * standby: function () { 248 * document.getElementById('x').innerHTML = this.msg; 249 * } 250 * }); 251 */ 252 extend: function (obj) { 253 var obj = this.isset(obj.init) ? obj.init() : obj; 254 this.each(obj, function (key, val) { 255 if (key == 'standby') { 256 this.standby.push(this.bind(val)); 257 } 258 else if (key != 'init') { 259 this[key] = val; 260 } 261 }); 262 }, 263 264 265 266 /** 267 * Returns 'true' if the variable is defined. 268 * @param {mixed} val The variable. 269 * @return {boolean} The result of if the variable is defined. 270 * @example 271 * var x; 272 * var y = false; 273 * var z = null; 274 * port.isset(x); // false 275 * port.isset(y); // true 276 * port.isset(z); // true 277 */ 278 isset: function (val) { 279 return (typeof(val) != 'undefined'); 280 }, 281 282 283 284 /** 285 * Returns 'true' if the property of object is own property. 286 * @param {array|object} obj The array or object. 287 * @param {number|string} key The key of property. 288 * @return {boolean} The result of the property of object is own property. 289 * @example 290 * Object.prototype.x = 'X'; 291 * var obj = { y: 'Y', z: 'Z' }; 292 * for (i in obj) { 293 * port.isown(obj, i); // x: false, y: true, z: true 294 * } 295 */ 296 isown: function (obj, key) { 297 if (!window.opera && obj.hasOwnProperty) { 298 return obj.hasOwnProperty(key); 299 } 300 else { 301 return !obj.constructor.prototype[key]; 302 } 303 }, 304 305 306 307 /** 308 * Returns the found first argument in arguments if that is defined. 309 * @param {mixed} [...] Any number of variable. 310 * @return {mixed|null} The found first argument.<br />Returns 'null' when there is not it. 311 * @example 312 * function fn (el) { 313 * return port.any(el, document); 314 * } 315 * fn(); // document 316 */ 317 any: function () { 318 var args = arguments; 319 for (var i = 0, ix = args.length; i < ix; i++) { 320 if (this.isset(args[i])) { 321 return args[i]; 322 } 323 } 324 325 return null; 326 }, 327 328 329 330 /** 331 * Binds a scope of 'this' at inside block of function. 332 * @param {function} fn The function.<br /> 333 * The first argument is originally 'this'. 334 * @param {object} [scope] The object of scope of 'this'.<br /> 335 * Defaults to 'port'. 336 * @return {function} The bound function. 337 * @example 338 * var obj = { 339 * fn: function (msg) { 340 * return msg; 341 * } 342 * }; 343 * document.getElementById('x').onclick = port.bind(function (el) { 344 * el.innerHTML = this.fn('hello'); 345 * }, obj); 346 */ 347 bind: function (fn, scope) { 348 var scope = this.any(scope, this); 349 350 return function () { 351 var args = [this]; 352 for (var i = 0, ix = arguments.length; i < ix; i++) { 353 args.push(arguments[i]); 354 } 355 356 return fn.apply(scope, args); 357 }; 358 }, 359 360 361 362 /** 363 * Iterates through an array or object. 364 * @param {array|object} obj The array or object. 365 * @param {function} fn The function to execute on iteration. 366 * @param {object} [scope] The object of scope of 'this' on iteration.<br /> 367 * Defaults to 'port'. 368 * @example 369 * var obj = { 370 * x: { a: 1, b: 2, c: 3 }, 371 * fn: function (key, val) { 372 * return key + ': ' + (n * 100); 373 * } 374 * }; 375 * port.each(obj.x, function (val) { 376 * alert(val); // 1, 2, 3 377 * }); 378 * port.each(obj.x, function (key, val) { 379 * alert(this.fn(key, val)); // 'a: 100', 'b: 200', 'c: 300' 380 * }, obj); 381 */ 382 each: function (obj, fn, scope) { 383 var scope = this.any(scope, this); 384 385 for (var i in obj) { 386 if (this.isown(obj, i)) { 387 var args = (fn.length == 2) ? [i, obj[i]] : [obj[i]]; 388 if (fn.apply(scope, args) === false) { 389 break; 390 } 391 } 392 } 393 } 394 }.init(); 395 port.load(port.base + 'ports.jsonp');