1 /**
  2  * Namespace for entire Xquared classes
  3  */
  4 var xq = {
  5 	majorVersion: '0.2',
  6 	minorVersion: '20071205'
  7 };
  8 
  9 
 10 
 11 /**
 12  * Add prototype.js like functions
 13  */
 14 xq.Class = function() { // TODO
 15 	var parent = null, properties = xq.$A(arguments);
 16 	if (typeof properties[0] == "function")
 17 		parent = properties.shift();
 18 	
 19 	function klass() {
 20 		this.initialize.apply(this, arguments);
 21 	}
 22 	
 23 	if(parent) {
 24 		for (var key in parent.prototype)
 25 			klass.prototype[key] = parent.prototype[key];
 26 	}
 27 		
 28 	for (var key in properties[0])
 29 		klass.prototype[key] = properties[0][key];
 30 	
 31 	if (!klass.prototype.initialize)
 32 		klass.prototype.initialize = function() {};
 33 	
 34 	klass.prototype.constructor = klass;
 35 	
 36 	return klass;
 37 }
 38 
 39 xq.observe = function(element, eventName, handler) {
 40 	if (element.addEventListener) {
 41 		element.addEventListener(eventName, handler, false);
 42 	} else {
 43 		element.attachEvent('on' + eventName, handler);
 44 	}
 45 	element = null;
 46 }
 47 
 48 xq.stopObserving = function(element, eventName, handler) {
 49 	if (element.removeEventListener) {
 50 		element.removeEventListener(eventName, handler, false);
 51 	} else {
 52 		element.detachEvent("on" + eventName, handler);
 53 	}
 54 	element = null;
 55 }
 56 
 57 xq.cancelHandler = function(e) {xq.stopEvent(e); return false};
 58 
 59 xq.stopEvent = function(event) {
 60       if(event.preventDefault) event.preventDefault();
 61       if(event.stopPropagation) event.stopPropagation();
 62       event.returnValue = false;
 63       event.cancelBubble = true;
 64       event.stopped = true;
 65 }
 66 
 67 xq.isButton = function(event, code) {
 68      return event.which ? (event.which === code + 1) : (event.button === code);
 69 }
 70 xq.isLeftClick = function(event) {return isButton(event, 0);}
 71 xq.isMiddleClick = function(event) {return isButton(event, 1);}
 72 xq.isRightClick = function(event) {return isButton(event, 2);}
 73 
 74 xq.getEventPoint = function(event) {
 75 	return {
 76 		x: event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)),
 77 		y: event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop))
 78 	};
 79 }
 80 
 81 xq.getCumulativeOffset = function(element) {
 82 	var top = 0, left = 0;
 83 	
 84 	do {
 85 		top += element.offsetTop  || 0;
 86 		left += element.offsetLeft || 0;
 87 		element = element.offsetParent;
 88 	} while (element);
 89 	
 90 	return {top:top, left:left};
 91 }
 92 
 93 xq.$ = function(id) {
 94 	return document.getElementById(id);
 95 }
 96 
 97 xq.isEmptyHash = function(h) {
 98 	for(var key in h) {
 99 		return false;
100 	}
101 	return true;
102 }
103 
104 xq.$A = function(arraylike) {
105 	var len = arraylike.length, a = new Array(len);
106 	while (len--) a[len] = arraylike[len];
107 	return a;
108 }
109 
110 xq.hasClassName = function(element, className) {
111 	var classNames = element.className;
112 	return (classNames.length > 0 && (classNames == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(classNames)));
113 }
114 
115 xq.serializeForm = function(f) {
116 
117 try{
118 	var options = {hash: true};
119 	var data = {};
120 	var elements = f.getElementsByTagName("*");
121 	for(var i = 0; i < elements.length; i++) {
122 		var element = elements[i];
123 		var tagName = element.tagName.toLowerCase();
124 		if(element.disabled || !element.name || ['input', 'textarea', 'option'].indexOf(tagName) == -1) continue;
125 		
126 		var key = element.name;
127 		var value = xq.getValueOfElement(element);
128 		
129 		if(value === undefined) continue;
130 		
131 		if(key in data) {
132 			if(data[key].constructor == Array) data[key] = [data[key]];
133 			data[key].push(value);
134 		} else {
135 			data[key] = value;
136 		}
137 	}
138 	return data;
139 } catch(e) {alert(e)}
140 }
141 
142 xq.getValueOfElement = function(e) {
143 	var type = e.type.toLowerCase();
144 	if(type == 'checkbox' || type == 'radio') return e.checked ? e.value : undefined;
145 	return e.value;
146 }
147 
148 xq.getElementsByClassName = function(element, className) {
149 	if(element.getElementsByClassName) return element.getElementsByClassName(className);
150 	
151 	var elements = element.getElementsByTagName("*");
152 	var len = elements.length;
153 	var result = [];
154 	var p = new RegExp("(^|\\s)" + className + "($|\\s)");
155 	for(var i = 0; i < len; i++) {
156 		var cur = elements[i];
157 		if(p.test(cur.className)) result.push(cur);
158 	}
159 	return result;
160 }
161 
162 try {Prototype.version;	__prototype = true;} catch(ignored) {__prototype = false;}
163 
164 if(!__prototype) {
165 	if(!Function.prototype.bind) {
166 		Function.prototype.bind = function() {
167 			var __m = this, arg = xq.$A(arguments), o = arg.shift();
168 			return function() {
169 				return __m.apply(o, arg.concat(xq.$A(arguments)));
170 			}
171 		}
172 	}
173 	
174 	if(!Function.prototype.bindAsEventListener) {
175 		Function.prototype.bindAsEventListener = function() {
176 			var __m = this, arg = xq.$A(arguments), o = arg.shift();
177 			return function(event) {
178 				return __m.apply(o, [event || window.event].concat(arg));
179 			}
180 		}
181 	}
182 	
183 	Array.prototype.find = function(f) {
184 		for(var i = 0; i < this.length; i++) {
185 			if(f(this[i])) return this[i];
186 		}
187 	}
188 	
189 	Array.prototype.findAll = function(f) {
190 		var result = [];
191 		for(var i = 0; i < this.length; i++) {
192 			if(f(this[i])) result.push(this[i]);
193 		}
194 		return result;
195 	}
196 	
197 	Array.prototype.first = function() {return this[0]}
198 	
199 	Array.prototype.last = function() {return this[this.length - 1]}
200 	
201 	Array.prototype.include = function(o) {
202 		if (this.indexOf(o) != -1) return true;
203 	
204 	    var found = false;
205 	    for(var i = 0; i < this.length; i++) {
206 	    	if(this[i] == o) return true;
207 	    }
208 	    
209 	    return false;
210 	}
211 	
212 	Array.prototype.flatten = function() {
213 		var result = [];
214 		var _flatten = function(array) {
215 			for(var i = 0; i < array.length; i++) {
216 				if(array[i].constructor === Array) {
217 					_flatten(array[i]);
218 				} else {
219 					result.push(array[i]);
220 				}
221 			}
222 		}
223 		_flatten(this);
224 		
225 		return result;
226 	}
227 	
228 	String.prototype.blank = function() {
229 		return /^\s*$/.test(this);
230 	}
231 	String.prototype.stripTags = function() {
232 	    return this.replace(/<\/?[^>]+>/gi, '');
233 	}
234 	String.prototype.escapeHTML = function() {
235 		xq._text.data = this;
236 		return xq._div.innerHTML;
237 	}
238 	xq._text = document.createTextNode('');
239 	xq._div = document.createElement('div');
240 	xq._div.appendChild(xq._text);
241 	
242 	String.prototype.strip = function() {
243 		return this.replace(/^\s+/, '').replace(/\s+$/, '');
244 	}
245 	
246 	Array.prototype.indexOf = function(n) {
247 		for(var i = 0; i < this.length; i++) {
248 			if(this[i] == n) return i;
249 		}
250 		
251 		return -1;
252 	}
253 }
254 
255 
256 
257 /**
258  * Make given object as event source
259  *
260  * @param {Object} object target object
261  * @param {String} prefix prefix for generated functions
262  * @param {Array} events array of string which contains name of events
263  */
264 xq.asEventSource = function(object, prefix, events) {
265 	object._listeners = []
266 	object._registerEventFirer = function(prefix, name) {
267 		this["_fireOn" + name] = function() {
268 			for(var i = 0; i < this._listeners.length; i++) {
269 				var listener = this._listeners[i];
270 				var func = listener["on" + prefix + name];
271 				if(func) func.apply(listener, xq.$A(arguments));
272 			}
273 		}
274 	}
275 	object.addListener = function(l) {
276 		this._listeners.push(l);
277 	}
278 	
279 	for(var i = 0; i < events.length; i++) {
280 		object._registerEventFirer(prefix, events[i]);
281 	}
282 }
283 
284 
285 
286 Date.preset = null;
287 Date.pass = function(msec) {
288 	if(Date.preset == null) return;
289 	Date.preset = new Date(Date.preset.getTime() + msec);
290 }
291 Date.get = function() {
292 	return Date.preset == null ? new Date() : Date.preset;
293 }
294 Date.prototype.elapsed = function(msec) {
295 	return Date.get().getTime() - this.getTime() >= msec;
296 }
297 
298 String.prototype.merge = function(data) {
299 	var newString = this;
300 	for(k in data) {
301 		newString = newString.replace("{" + k + "}", data[k]);
302 	}
303 	return newString;
304 }
305 
306 String.prototype.parseURL = function() {
307 	var m = this.match(/((((\w+):\/\/(((([^@:]+)(:([^@]+))?)@)?([^:\/\?#]+)?(:(\d+))?))?([^\?#]+)?)(\?([^#]+))?)(#(.+))?/);
308 	
309 	var includeAnchor = m[0];
310 	var includeQuery = m[1] || undefined;
311 	var includePath = m[2] || undefined;
312 	var includeHost = m[3] || undefined;
313 	var includeBase = null;
314 	var protocol = m[4] || undefined;
315 	var user = m[8] || undefined;
316 	var password = m[10] || undefined;
317 	var domain = m[11] || undefined;
318 	var port = m[13] || undefined;
319 	var path = m[14] || undefined;
320 	var query = m[16] || undefined;
321 	var anchor = m[18] || undefined;
322 	
323 	if(!path || path == '/') {
324 		includeBase = includeHost + '/';
325 	} else {
326 		var index = path.lastIndexOf('/');
327 		includeBase = includeHost + path.substring(0, index + 1);
328 	}
329 	
330 	return {
331 		includeAnchor: includeAnchor,
332 		includeQuery: includeQuery,
333 		includePath: includePath,
334 		includeBase: includeBase,
335 		includeHost: includeHost,
336 		protocol: protocol,
337 		user: user,
338 		password: password,
339 		domain: domain,
340 		port: port,
341 		path: path,
342 		query: query,
343 		anchor: anchor
344 	};
345 }
346 
347 
348 
349 /**
350  * Automatic finalizer
351  */
352 xq.autoFinalizeQueue = [];
353 
354 xq.addToFinalizeQueue = function(obj) {
355 	xq.autoFinalizeQueue.push(obj);
356 }
357 
358 xq.finalize = function(obj) {
359 	if(typeof obj.finalize == "function") {
360 		try {obj.finalize();} catch(ignored) {}
361 	}
362 	
363 	for(key in obj) obj[key] = null;
364 }
365 
366 xq.observe(window, "unload", function() {
367 	for(var i = 0; i < xq.autoFinalizeQueue.length; i++) xq.finalize(xq.autoFinalizeQueue[i]);
368 	xq = null;
369 });
370 
371 
372 
373 /**
374  * Script loader
375  */
376 xq.findXquaredScript = function() {
377     return xq.$A(document.getElementsByTagName("script")).find(function(script) {
378     	return script.src && script.src.match(/xquared\.js/i);
379     });
380 }
381 xq.shouldLoadOthers = function() {
382 	var script = xq.findXquaredScript();
383     return script && !!script.src.match(/xquared\.js\?load_others=1/i);
384 }
385 xq.loadScript = function(url) {
386     document.write('<script type="text/javascript" src="' + url + '"></script>');
387 }
388 xq.loadOthers = function() {
389 	var script = xq.findXquaredScript();
390 	var basePath = script.src.match(/(.*\/)xquared\.js.*/i)[1];
391 	var others = [
392 		'Editor.js',
393 		'Browser.js',
394 		'Shortcut.js',
395 		'DomTree.js',
396 		'RichDom.js',
397 		'RichDomW3.js',
398 		'RichDomGecko.js',
399 		'RichDomWebkit.js',
400 		'RichDomTrident.js',
401 		'RichTable.js',
402 		'Validator.js',
403 		'ValidatorW3.js',
404 		'ValidatorGecko.js',
405 		'ValidatorWebkit.js',
406 		'ValidatorTrident.js',
407 		'EditHistory.js',
408 		'Controls.js',
409 		'_ui_templates.js'
410 	];
411 	for(var i = 0; i < others.length; i++) {
412 		xq.loadScript(basePath + others[i]);
413 	};
414 }
415 
416 if(xq.shouldLoadOthers()) xq.loadOthers();
417