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