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');