1/** 2 * $Id: helpers.js 156 2006-12-23 08:48:25Z wingedfox $ 3 * $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.003/helpers.js $ 4 * 5 * File contains differrent helper functions 6 * 7 * @author Ilya Lebedev <ilya@lebedev.net> 8 * @license LGPL 9 * @version $Rev: 156 $ 10 */ 11//----------------------------------------------------------------------------- 12// Variable/property checks 13//----------------------------------------------------------------------------- 14/** 15 * Checks if property is undefined 16 * 17 * @param {Object} prop value to check 18 * @return {Boolean} true if matched 19 * @scope public 20 */ 21function isUndefined (prop /* :Object */) /* :Boolean */ { 22 return (typeof prop == 'undefined'); 23} 24/** 25 * Checks if property is function 26 * 27 * @param {Object} prop value to check 28 * @return {Boolean} true if matched 29 * @scope public 30 */ 31function isFunction (prop /* :Object */) /* :Boolean */ { 32 return (typeof prop == 'function'); 33} 34/** 35 * Checks if property is string 36 * 37 * @param {Object} prop value to check 38 * @return {Boolean} true if matched 39 * @scope public 40 */ 41function isString (prop /* :Object */) /* :Boolean */ { 42 return (typeof prop == 'string'); 43} 44/** 45 * Checks if property is number 46 * 47 * @param {Object} prop value to check 48 * @return {Boolean} true if matched 49 * @scope public 50 */ 51function isNumber (prop /* :Object */) /* :Boolean */ { 52 return (typeof prop == 'number'); 53} 54/** 55 * Checks if property is the calculable number 56 * 57 * @param {Object} prop value to check 58 * @return {Boolean} true if matched 59 * @scope public 60 */ 61function isNumeric (prop /* :Object */) /* :Boolean */ { 62 return isNumber(prop)&&!isNaN(prop)&&isFinite(prop); 63} 64/** 65 * Checks if property is array 66 * 67 * @param {Object} prop value to check 68 * @return {Boolean} true if matched 69 * @scope public 70 */ 71function isArray (prop /* :Object */) /* :Boolean */ { 72 return (prop instanceof Array); 73} 74/** 75 * Checks if property is regexp 76 * 77 * @param {Object} prop value to check 78 * @return {Boolean} true if matched 79 * @scope public 80 */ 81function isRegExp (prop /* :Object */) /* :Boolean */ { 82 return (prop instanceof RegExp); 83} 84/** 85 * Checks if property is a boolean value 86 * 87 * @param {Object} prop value to check 88 * @return {Boolean} true if matched 89 * @scope public 90 */ 91function isBoolean (prop /* :Object */) /* :Boolean */ { 92 return ('boolean' == typeof prop); 93} 94/** 95 * Checks if property is a scalar value (value that could be used as the hash key) 96 * 97 * @param {Object} prop value to check 98 * @return {Boolean} true if matched 99 * @scope public 100 */ 101function isScalar (prop /* :Object */) /* :Boolean */ { 102 return isNumeric(prop)||isString(prop); 103} 104/** 105 * Checks if property is empty 106 * 107 * @param {Object} prop value to check 108 * @return {Boolean} true if matched 109 * @scope public 110 */ 111function isEmpty (prop /* :Object */) /* :Boolean */ { 112 if (isBoolean(prop)) return false; 113 if (isRegExp(prop) && new RegExp("").toString() == prop.toString()) return true; 114 if (isString(prop) || isNumber(prop)) return !prop; 115 if (Boolean(prop)&&false != prop) { 116 for (var i in prop) if(prop.hasOwnProperty(i)) return false 117 } 118 return true; 119} 120 121function dump (v, l) { 122 var s = []; 123 if (!l) l=0; 124 if (l>2) return "***********8Recursion*************"; 125 var sp = ""; 126 for (var i=0;i<l;i++) sp+=" "; 127 for (var i in v) { 128// if (!v.hasOwnProperty(i)) continue; 129 var q = [sp,i,': ']; 130 try { 131 if (!isScalar(v[i]) && !isFunction(v[i])) { 132 q[q.length] = '{'; 133 s[s.length] = q.join(""); 134 s[s.length] = dump(v[i],l+1); 135 s[s.length] = '}'; 136 } else { 137 q[q.length] = v[i]; 138 s[s.length] = q.join(""); 139 } 140 } catch (err) {} 141 } 142 return s.join("\n"); 143} 144//----------------------------------------------------------------------------- 145// File paths functions 146//----------------------------------------------------------------------------- 147/** 148 * used to glue path's 149 * 150 * @param {String} number of strings 151 * @return {String} glued path 152 * @scope public 153 */ 154function gluePath () /* :String */ { 155 var aL=arguments.length, i=aL-2, s = arguments[aL-1]; 156 for(;i>=0;i--) 157 s = ((!isString(arguments[i])&&!isNumber(arguments[i]))||isEmpty(arguments[i]) 158 ?s 159 :arguments[i]+'\x00'+s); 160 return s?s.replace(/\/*\x00+\/*/g,"/"):""; 161} 162 163/** 164 * return full path to the script 165 * 166 * @param {String} sname script name 167 * @return {String, Null} mixed string full path or null 168 * @scope public 169 */ 170function findPath (sname /* :String */) /* :String */{ 171 var sc = document.getElementsByTagName('script'), 172 sr = new RegExp('^(.*/|)('+sname+')([#?]|$)'); 173 for (var i=0,scL=sc.length; i<scL; i++) { 174 // matched desired script 175 var m = String(sc[i].src).match(sr); 176 if (m) { 177 /* 178 * we've matched the full path 179 */ 180 if (m[1].match(/^((https?|file)\:\/{2,}|\w:[\\])/)) return m[1]; 181 /* 182 * we've matched absolute path from the site root 183 */ 184 if (m[1].indexOf("/")==0) return m[1]; 185 b = document.getElementsByTagName('base'); 186 if (b[0] && b[0].href) return b[0].href+m[1]; 187 /* 188 * return matching part of the document location and path to js file 189 */ 190 return (document.location.href.match(/(.*[\/\\])/)[0]+m[1]).replace(/^\/+(?=\w:)/,""); 191 } 192 } 193 return null; 194} 195 196//----------------------------------------------------------------------------- 197// DOM related stuff 198//----------------------------------------------------------------------------- 199if (isUndefined(DOM)) var DOM = {}; 200/** 201 * Performs parent lookup by 202 * - node object: actually it's "is child of" check 203 * - tagname: getParent(el, 'li') == getParent(el, 'tagName', 'LI') 204 * - any node attribute 205 * 206 * @param {HTMLElement} el source element 207 * @param {HTMLElement, String} cp DOMNode or string tagname or string attribute name 208 * @param {String} vl optional attribute value 209 * @return {HTMLElement, Null} 210 * @scope public 211 */ 212DOM.getParent = function (el /* : HTMLElement */, cp /* :String, HTMLElement */, vl /* :String */) /* :HTMLElement */ { 213 if (el == null) return null; 214 else if (el.nodeType == 1 && 215 ((!isUndefined(vl) && el[cp] == vl) || 216 ('string' == typeof cp && DOM.hasTagName(el, cp)) || 217 el == cp)) return el; 218 else return arguments.callee(el.parentNode, cp, vl); 219} 220/** 221 * Calculates the offset for the DOM node from top left corner 222 * 223 * @author Matt Kruse 224 * @see http://javascripttoolbox.com/lib/objectposition/index.php 225 * @param {HTMLElement} el 226 * @return {Object} x: horizontal offset, y: vertical offset 227 * @scope public 228 */ 229DOM.getOffset = function (el /* :HTMLElement */) /* :Object */ { 230 var fixBrowserQuirks = true 231 ,o = el 232 ,left = 0 233 ,top = 0 234 ,width = 0 235 ,height = 0 236 ,parentNode = null 237 ,offsetParent = null; 238 239 if (o==null) return null; 240 241 offsetParent = o.offsetParent; 242 var originalObject = o 243 ,el = o; // "el" will be nodes as we walk up, "o" will be saved for offsetParent references 244 while (el.parentNode!=null) { 245 el = el.parentNode; 246 if (el.offsetParent==null) { 247 } 248 else { 249 var considerScroll = true; 250 /* 251 In Opera, if parentNode of the first object is scrollable, then offsetLeft/offsetTop already 252 take its scroll position into account. If elements further up the chain are scrollable, their 253 scroll offsets still need to be added in. And for some reason, TR nodes have a scrolltop value 254 which must be ignored. 255 */ 256 if (fixBrowserQuirks && window.opera) { 257 if (el==originalObject.parentNode || el.nodeName=="TR") { 258 considerScroll = false; 259 } 260 } 261 if (considerScroll) { 262 if (el.scrollTop && el.scrollTop>0) { 263 top -= el.scrollTop; 264 } 265 if (el.scrollLeft && el.scrollLeft>0) { 266 left -= el.scrollLeft; 267 } 268 } 269 } 270 // If this node is also the offsetParent, add on the offsets and reset to the new offsetParent 271 if (el == offsetParent) { 272 left += o.offsetLeft; 273 if (el.clientLeft && el.nodeName!="TABLE") { 274 left += el.clientLeft; 275 } 276 top += o.offsetTop; 277 if (el.clientTop && el.nodeName!="TABLE") { 278 top += el.clientTop; 279 } 280 o = el; 281 if (o.offsetParent==null) { 282 if (o.offsetLeft) { 283 left += o.offsetLeft; 284 } 285 if (o.offsetTop) { 286 top += o.offsetTop; 287 } 288 } 289 offsetParent = o.offsetParent; 290 } 291 } 292 293 294 if (originalObject.offsetWidth) { 295 width = originalObject.offsetWidth; 296 } 297 if (originalObject.offsetHeight) { 298 height = originalObject.offsetHeight; 299 } 300 301 return {'x':left, 'y':top, 'width':width, 'height':height}; 302 }; 303 304//DOM.getOffset = function (el /* :HTMLElement */) /* :Object */ { 305/* 306 var xy = {'x' : el.offsetLeft , 'y' : el.offsetTop}; 307 if (el.offsetParent) { 308 var xy1 = arguments.callee(el.offsetParent); 309 xy.x += xy1.x; 310 xy.y += xy1.y; 311 } 312 return xy; 313} 314*/ 315/** 316 * Returns the width of the window canvas 317 * 318 * @return {Number} 319 * @scope public 320 */ 321DOM.getClientWidth = function () /* :Number */{ 322 var w=0; 323 if (self.innerHeight) w = self.innerWidth; 324 else if (document.documentElement && document.documentElement.clientWidth) w = document.documentElement.clientWidth; 325 else if (document.body) w = document.body.clientWidth; 326 return w; 327} 328/** 329 * Returns the height of the window canvas 330 * 331 * @return {Number} 332 * @scope public 333 */ 334DOM.getClientHeight = function () /* :Number */{ 335 var h=0; 336 if (self.innerHeight) h = self.innerHeight; 337 else if (document.documentElement && document.documentElement.clientHeight) h = document.documentElement.clientHeight; 338 else if (document.body) h = document.body.clientHeight; 339 return h; 340} 341/** 342 * Returns the height of the scrolled area for the body 343 * 344 * @return {Number} 345 * @scope public 346 */ 347DOM.getBodyScrollTop = function () /* :Number */{ 348 return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop); 349} 350/** 351 * Returns the height of the scrolled area for the body 352 * 353 * @return {Number} 354 * @scope public 355 */ 356DOM.getBodyScrollLeft = function () /* :Number */{ 357 return self.pageXOffset || (document.documentElement && document.documentElement.scrollLeft) || (document.body && document.body.scrollLeft); 358} 359/** 360 * Checks, if property matches a tagname(s) 361 * 362 * @param {HTMLElement} prop 363 * @param {String, Array} tags 364 * @return {Boolean} 365 * @scope public 366 */ 367DOM.hasTagName = function (prop /* :HTMLElement */, tags /* :String, Array */) { 368 if (isString(tags)) tags = [tags]; 369 if (!isArray(tags) || isEmpty(tags) || isUndefined(prop) || isEmpty(prop.tagName)) return false; 370 var t = prop.tagName.toLowerCase(); 371 for (var i=0, tL=tags.length; i<tL; i++) { 372 if (tags[i].toLowerCase() == t) return true; 373 } 374 return false; 375} 376/** 377 * Calculates client area width 378 * 379 * @return {Number} 380 * @scope public 381 */ 382function getClientWidth() { 383 var w=0; 384 if (self.innerHeight) w = self.innerWidth; 385 else if (document.documentElement && document.documentElement.clientWidth) w = document.documentElement.clientWidth; 386 else if (document.body) w = document.body.clientWidth; 387 return w; 388} 389/** 390 * Calculates client area height 391 * 392 * @return {Number} 393 * @scope public 394 */ 395function getClientHeight() { 396 var h=0; 397 if (self.innerHeight) h = self.innerHeight; 398 else if (document.documentElement && document.documentElement.clientHeight) h = document.documentElement.clientHeight; 399 else if (document.body) h = document.body.clientHeight; 400 return h; 401} 402/** 403 * Calculates the center of client area by the X axis 404 * 405 * @return {Number} 406 * @scope public 407 */ 408function getClientCenterX() { 409 return parseInt(getClientWidth()/2)+getBodyScrollLeft(); 410} 411/** 412 * Calculates the center of client area by the Y axis 413 * 414 * @return {Number} 415 * @scope public 416 */ 417function getClientCenterY() { 418 return parseInt(getClientHeight()/2)+getBodyScrollTop(); 419} 420/** 421 * Calculates scroll height 422 * 423 * @return {Number} 424 * @scope public 425 */ 426function getBodyScrollTop() { 427 return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop); 428} 429/** 430 * Calculates scroll width 431 * 432 * @return {Number} 433 * @scope public 434 */ 435function getBodyScrollLeft() { 436 return self.pageXOffset || (document.documentElement && document.documentElement.scrollLeft) || (document.body && document.body.scrollLeft); 437} 438 439/** 440 * Method is used to convert table into the array 441 * 442 * @param {String, HTMLTableElement, HTMLTBodyElement, HTMLTHeadElement, HTMLTFootElement} id 443 * @param {Number} ci column indexes to put in the array 444 * @param {String} section optional section type 445 * @param {Object} subsection optional subsection index 446 * @return {NULL, Array} 447 * @scope public 448 */ 449 450//----------------------------------------------------------------------------- 451// Misc helpers 452//----------------------------------------------------------------------------- 453function table2array (id, ci, section, subsection) { 454 if (isString(id)) id = document.getElementById(id); 455 if (!id || !DOM.hasTagName(id, ['table','tbody,','thead','tfoot'])) return null; 456 if (!isEmpty(section) && (!isString(section) || !(id = id.getElementsByTagName(section)))) return null; 457 if (!isEmpty(subsection) && (!isNumber(subsection) || subsection<0 || !(id = id[subsection]))) return null; 458 if (isUndefined(id.rows)) return null; 459 460 var res = []; 461 for (var i=0, rL=id.rows.length; i<rL; i++) { 462 var tr = []; 463 if (isArray(ci)) { 464 for (var o=0, cL=ci.length; o<cL; o++) 465 if (id.rows[i].cells[ci[o]]) tr[tr.length] = id.rows[i].cells[ci[o]].innerHTML.replace(/<[^>]*?>/g,""); 466 467 } else { 468 for (var z=0, tL=id.rows[i].cells.length; z<tL; z++) 469 tr[tr.length] = id.rows[i].cells[z].innerHTML.replace(/<[^>]*?>/g,""); 470 } 471 if (!isEmpty(tr)) res[res.length] = tr; 472 } 473 return res; 474} 475 476/** 477 * Creates element all-at-once 478 * 479 * @param {String} tag name 480 * @param {Object} p element properties { 'class' : 'className', 481 * 'style' : { 'property' : value, ... }, 482 * 'event' : { 'eventType' : handler, ... }, 483 * 'child' : [ child1, child2, ...], 484 * 'param' : { 'property' : value, ... }, 485 * @return {HTMLElement} created element or null 486 * @scope public 487 */ 488document.createElementExt = function (tag /* :String */, p /* :Object */ ) /* :HTMLElement */{ 489 var L, i, k, el = document.createElement(tag); 490 if (!el) return null; 491 for (i in p) { 492 if (!p.hasOwnProperty(i)) continue; 493 switch (i) { 494 case "class" : el.setAttribute('className',p[i]); el.setAttribute('class',p[i]); break; 495 case "style" : for (k in p[i]) { if (!p[i].hasOwnProperty(k)) continue; el.style[k] = p[i][k]; } break; 496 case "event" : for (k in p[i]) { if (!p[i].hasOwnProperty(k)) continue; el.attachEvent(k,p[i][k]); } break; 497 case "child" : L = p[i].length; for (k = 0; k<L; k++) el.appendChild(p[i][k]); break; 498 case "param" : for (k in p[i]) { if (!p[i].hasOwnProperty(k)) continue; try { el[k] = p[i][k] } catch(e) {} } break; 499 } 500 } 501 return el; 502} 503 504/** 505 * simple setInterval/setTimout wrappers 506 * 507 * @param {Function} f function to be launched 508 * @param {Number} i interval 509 * @param {Array} o optional function parameters to be applied 510 * @return {Number} interval id 511 * @scope public 512 */ 513function playInterval (f /* :Function */, i /* :Number */, o /* :Array */) /* :Number */ { return setInterval(function(){(o instanceof Array)?f.apply(this,o):f.call(this,o)},i) } 514function playTimeout (f /* :Function */, i /* :Number */, o /* :Array */) /* :Number */ { return setTimeout(function(){(o instanceof Array)?f.apply(this,o):f.call(this,o)},i) } 515