[25160] | 1 | /*
|
---|
| 2 | * file: Util.js
|
---|
| 3 | *
|
---|
| 4 | * @BEGINLICENSE
|
---|
| 5 | * Copyright 2010 Brook Novak (email : [email protected])
|
---|
| 6 | * This program is free software; you can redistribute it and/or modify
|
---|
| 7 | * it under the terms of the GNU General Public License as published by
|
---|
| 8 | * the Free Software Foundation; either version 2 of the License, or
|
---|
| 9 | * (at your option) any later version.
|
---|
| 10 | * This program is distributed in the hope that it will be useful,
|
---|
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 13 | * GNU General Public License for more details.
|
---|
| 14 | * You should have received a copy of the GNU General Public License
|
---|
| 15 | * along with this program; if not, write to the Free Software
|
---|
| 16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
| 17 | * @ENDLICENSE
|
---|
| 18 | */
|
---|
| 19 | bootstrap.provides("Util");
|
---|
| 20 |
|
---|
| 21 | /**
|
---|
| 22 | * @function
|
---|
| 23 | * Gets a text node or element in the document at a given pixel position.
|
---|
| 24 | *
|
---|
| 25 | * @param Number x The X pixel relative to the window.
|
---|
| 26 | * @param Number y The Y pixel relative to the window.
|
---|
| 27 | * @return {Node} An Element or Text node which i at the given coordinates.
|
---|
| 28 | */
|
---|
| 29 | var _getRenderedNodeAtXY = document.elementFromPoint ?
|
---|
| 30 | /* Use native version if available */
|
---|
| 31 | function(x, y) {
|
---|
| 32 | //return document.elementFromPoint(x, y);
|
---|
| 33 | switch(_engine) {
|
---|
| 34 | case _Platform.GECKO:
|
---|
| 35 | case _Platform.TRIDENT:
|
---|
| 36 | return document.elementFromPoint(x, y);
|
---|
| 37 |
|
---|
| 38 | default:
|
---|
| 39 | // Webkit / presto requires page coordinates instead of client/window coordinates
|
---|
| 40 | var scrollPos = _getDocumentScrollPos();
|
---|
| 41 | return document.elementFromPoint(x + scrollPos.left, y + scrollPos.top); // Opera can return text nodes
|
---|
| 42 | }
|
---|
| 43 |
|
---|
| 44 |
|
---|
| 45 | } : function(x, y) {
|
---|
| 46 |
|
---|
| 47 | var element = null;
|
---|
| 48 |
|
---|
| 49 | searchElement(docBody);
|
---|
| 50 |
|
---|
| 51 | return element;
|
---|
| 52 |
|
---|
| 53 | function searchElement(parent){
|
---|
| 54 |
|
---|
| 55 | // First search deeper nodes
|
---|
| 56 | if (parent.childNodes.length > 0) {
|
---|
| 57 | var child = parent.firstChild;
|
---|
| 58 | while (child) {
|
---|
| 59 | if (searchElement(child))
|
---|
| 60 | return true;
|
---|
| 61 | child = child.nextSibling;
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | // Test this node... if it is an element
|
---|
| 67 | if (parent.nodeType == Node.ELEMENT_NODE && (parent.offsetLeft || parent.offsetLeft == 0)) {
|
---|
| 68 |
|
---|
| 69 | // Get the elements position in the window
|
---|
| 70 | var pos = _getPositionInWindow(parent);
|
---|
| 71 |
|
---|
| 72 | // Then check to see if x/y is inside bounds
|
---|
| 73 | if (y >= pos.y && y <= (pos.y + parent.offsetHeight) &&
|
---|
| 74 | x >= pos.x &&
|
---|
| 75 | x <= (pos.x + parent.offsetWidth)) {
|
---|
| 76 | // Search has finished
|
---|
| 77 | element = parent;
|
---|
| 78 | return true;
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 | return false;
|
---|
| 84 |
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | };
|
---|
| 88 |
|
---|
| 89 |
|
---|
| 90 | /**
|
---|
| 91 | * Gets that position of an element in the window.
|
---|
| 92 | * Supports internal scroll panes - price being slower operation.
|
---|
| 93 | *
|
---|
| 94 | * @param {Object} ele The element to get the position for.
|
---|
| 95 | *
|
---|
| 96 | * @return {Object} The position of the given element {x,y}
|
---|
| 97 | */
|
---|
| 98 | /*function _getPosInWndIntScrollSupport(ele){
|
---|
| 99 | var left = 0, top = 0, parent = ele;
|
---|
| 100 |
|
---|
| 101 | do {
|
---|
| 102 | if (parent.offsetLeft || parent.offsetTop) {
|
---|
| 103 | left += parent.offsetLeft;
|
---|
| 104 | top += parent.offsetTop;
|
---|
| 105 | }
|
---|
| 106 | } while (parent = parent.offsetParent);
|
---|
| 107 |
|
---|
| 108 | parent = ele;
|
---|
| 109 | do {
|
---|
| 110 | if (parent == docBody) break; // already handled in a cross-browser fashion
|
---|
| 111 | if (parent.scrollLeft || parent.scrollTop) {
|
---|
| 112 | left -= parent.scrollLeft;
|
---|
| 113 | top -= parent.scrollTop;
|
---|
| 114 | }
|
---|
| 115 | } while (parent = parent.parentNode); // Notice here: going up parent nodes, not offsets
|
---|
| 116 | // Get the document scroll
|
---|
| 117 | var scrollPos = _getDocumentScrollPos();
|
---|
| 118 |
|
---|
| 119 | // Return coordinates relative to window (using scroll information)
|
---|
| 120 | return {
|
---|
| 121 | x: left - scrollPos.left,
|
---|
| 122 | y: top - scrollPos.top
|
---|
| 123 | };
|
---|
| 124 | }*/
|
---|
| 125 |
|
---|
| 126 | /**
|
---|
| 127 | * Gets the position of an element in the window.
|
---|
| 128 | * Does not garuantee to support internal scrolls. However does support
|
---|
| 129 | * main document scrolling.
|
---|
| 130 | *
|
---|
| 131 | * @param {Node} ele The element to get the position for.
|
---|
| 132 | *
|
---|
| 133 | * @return {Object} The position of the given element {x,y}
|
---|
| 134 | */
|
---|
| 135 | function _getPosInWndFast (ele){
|
---|
| 136 |
|
---|
| 137 | var left = 0,
|
---|
| 138 | top = 0,
|
---|
| 139 | isFixed = 0; // True if encountered fixed element
|
---|
| 140 |
|
---|
| 141 | do {
|
---|
| 142 |
|
---|
| 143 | // Add pixel offset to parent
|
---|
| 144 | if (ele.offsetLeft || ele.offsetTop) {
|
---|
| 145 |
|
---|
| 146 | if (ele == docBody) {
|
---|
| 147 | if (!isFixed) {
|
---|
| 148 | // Gecko browsers can have negitive offsets for the document body
|
---|
| 149 | // if there is a border present.
|
---|
| 150 | left += Math.abs(ele.offsetLeft);
|
---|
| 151 | top += Math.abs(ele.offsetTop);
|
---|
| 152 | }
|
---|
| 153 | } else {
|
---|
| 154 | left += ele.offsetLeft;
|
---|
| 155 | top += ele.offsetTop;
|
---|
| 156 | }
|
---|
| 157 |
|
---|
| 158 | }
|
---|
| 159 |
|
---|
| 160 | // TODO: NEED TO COMPUTE IF FIXED VIA CLASS.. expensive to do every element...
|
---|
| 161 | isFixed |= (ele.style && ele.style.position == "fixed");
|
---|
| 162 |
|
---|
| 163 | } while (ele = ele.offsetParent);
|
---|
| 164 |
|
---|
| 165 | if (!isFixed) {
|
---|
| 166 |
|
---|
| 167 | // Subtract the document scroll for non-fixed elements
|
---|
| 168 | var scrollPos = _getDocumentScrollPos();
|
---|
| 169 |
|
---|
| 170 | left -= scrollPos.left;
|
---|
| 171 | top -= scrollPos.top;
|
---|
| 172 |
|
---|
| 173 |
|
---|
| 174 | // Observations:
|
---|
| 175 | // IE Versions which already includes body border widths
|
---|
| 176 | // IE8 Standards/Quirks
|
---|
| 177 | // IE7 Quirks
|
---|
| 178 |
|
---|
| 179 | // IE Versions which do not include body border widths
|
---|
| 180 | // IE7 Standards
|
---|
| 181 | // IE 6 Standards
|
---|
| 182 | if (_engine == _Platform.TRIDENT && _engineVersion < 8) {
|
---|
| 183 |
|
---|
| 184 | // Some IE verions do not add the body border in any of the offsets (body/immediate children).
|
---|
| 185 | // To get the border thicknesses in IE you can query the client top/left which will not
|
---|
| 186 | // be effected by scrollbars or margins.
|
---|
| 187 |
|
---|
| 188 | left += docBody.clientLeft;
|
---|
| 189 | top += docBody.clientTop;
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 | }
|
---|
| 193 |
|
---|
| 194 | // Return coordinates relative to window
|
---|
| 195 | return {
|
---|
| 196 | x: left,
|
---|
| 197 | y: top
|
---|
| 198 | };
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | /**
|
---|
| 202 | * @type Function
|
---|
| 203 | *
|
---|
| 204 | * Gets the position of an element in the window.
|
---|
| 205 | *
|
---|
| 206 | * @param {Node} ele The element to get the position for.
|
---|
| 207 | *
|
---|
| 208 | * @return {Object} The position of the given element {x,y}
|
---|
| 209 | */
|
---|
| 210 | var _getPositionInWindow = _getPosInWndFast;
|
---|
| 211 |
|
---|
| 212 | /**
|
---|
| 213 | * Inserts a specified DOM node after a reference element as a child of the
|
---|
| 214 | * reference element's parent node.
|
---|
| 215 | *
|
---|
| 216 | * @param {Node} newNode The dom node being inserted
|
---|
| 217 | *
|
---|
| 218 | * @param {Node} refNode The node after which newNode is inserted.
|
---|
| 219 | *
|
---|
| 220 | * @return {Node} newNode passed in
|
---|
| 221 | */
|
---|
| 222 | function _insertAfter(newNode, refNode){
|
---|
| 223 | var sib = refNode.nextSibling;
|
---|
| 224 | if (!sib)
|
---|
| 225 | refNode.parentNode.appendChild(newNode);
|
---|
| 226 | else
|
---|
| 227 | refNode.parentNode.insertBefore(newNode, sib);
|
---|
| 228 | return newNode;
|
---|
| 229 | }
|
---|
| 230 |
|
---|
| 231 | /**
|
---|
| 232 | * Inserts a dom node at a given index.
|
---|
| 233 | *
|
---|
| 234 | * @param {Node} parentNode The parent of the newly inserted node
|
---|
| 235 | * @param {Node} newNode The dom node being inserted
|
---|
| 236 | * @param {Number} index The zero-based index of where in the parents child list the new node should be added.
|
---|
| 237 | */
|
---|
| 238 | function _insertAt(parentNode, newNode, index) {
|
---|
| 239 |
|
---|
| 240 | var i = -1;
|
---|
| 241 | var node = parentNode.firstChild;
|
---|
| 242 |
|
---|
| 243 | while (++i != index && node) {
|
---|
| 244 | node = node.nextSibling;
|
---|
| 245 | }
|
---|
| 246 |
|
---|
| 247 | if (i == index) {
|
---|
| 248 | if (i == parentNode.childNodes.length) parentNode.appendChild(newNode);
|
---|
| 249 | else parentNode.insertBefore(newNode, node);
|
---|
| 250 |
|
---|
| 251 | return newNode;
|
---|
| 252 | }
|
---|
| 253 |
|
---|
| 254 | return null;
|
---|
| 255 | }
|
---|
| 256 |
|
---|
| 257 | /**
|
---|
| 258 | * Returns the combined length of all the descendant text nodes of a given element
|
---|
| 259 | *
|
---|
| 260 | * @param {Node} ele A element to get the text length for
|
---|
| 261 | * @return {Number} The text length for the given element
|
---|
| 262 | */
|
---|
| 263 | function _getDeepTextLength(ele){
|
---|
| 264 | var len = 0;
|
---|
| 265 | _visitTextNodes(ele, true, function(textNode){
|
---|
| 266 | len += _nodeLength(textNode);
|
---|
| 267 | });
|
---|
| 268 | return len;
|
---|
| 269 | }
|
---|
| 270 |
|
---|
| 271 | /**
|
---|
| 272 | * Clones all object members.
|
---|
| 273 | *
|
---|
| 274 | * @param {Object} obj An object to clone
|
---|
| 275 | *
|
---|
| 276 | * @return {Object} The cloned object.
|
---|
| 277 | */
|
---|
| 278 | function _clone(obj) {
|
---|
| 279 | var clone = {};
|
---|
| 280 | for (var i in obj) clone[i] = obj[i];
|
---|
| 281 | return clone;
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 |
|
---|
| 285 | /**
|
---|
| 286 | * Traverses through DOM nodes and applies/maps a function to all nodes
|
---|
| 287 | *
|
---|
| 288 | * @param {Node} root The parent to search from. Inclusive in search. Null to find the root automatically,
|
---|
| 289 | * up to and excluding the document node (if any).
|
---|
| 290 | *
|
---|
| 291 | * @param {Node} start The node at which the traversal should start from. This must be a child of parent, or the same as parent.
|
---|
| 292 | *
|
---|
| 293 | * @param {Boolean} searchRight True to traverse tree preorder from left to right, e.g. current, child1, child2, ...
|
---|
| 294 | * False to traverse tree postorder from right to left, e.g. ... child2, child1, current
|
---|
| 295 | *
|
---|
| 296 | * @param {RegExp} filter A regular expression - the function is only
|
---|
| 297 | * applied to nodes whos names match the regular expression. If null is
|
---|
| 298 | * supplied then all nodes are visited.
|
---|
| 299 | *
|
---|
| 300 | * @param {Function} func The function to apply. One argument is given: the visting nodes.
|
---|
| 301 | * Returning false aborts traversal. Returning 1 in right searches
|
---|
| 302 | * skips traversing into the current node's children. Anything else returned
|
---|
| 303 | * will be ignored and the traversal will continue.
|
---|
| 304 | */
|
---|
| 305 | function _visitNodes(root, start, searchRight, filter, func) {
|
---|
| 306 |
|
---|
| 307 | // Ensure that root is set.
|
---|
| 308 | if (!root)
|
---|
| 309 | root = _getRoot(start, [Node.DOCUMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE]);
|
---|
| 310 |
|
---|
| 311 | var startStack = _getAncestors(start, root, true, true);
|
---|
| 312 | var stackIndex = startStack.length - 1;
|
---|
| 313 |
|
---|
| 314 | (function trav(parent) {
|
---|
| 315 |
|
---|
| 316 | var child, res, skipChildren = false;
|
---|
| 317 |
|
---|
| 318 | // Are we recursing to the starting node (building stack frame)?
|
---|
| 319 | if (stackIndex > 0) { // before start point
|
---|
| 320 |
|
---|
| 321 | stackIndex--;
|
---|
| 322 | child = searchRight ? startStack[stackIndex].nextSibling : startStack[stackIndex].previousSibling;
|
---|
| 323 | if (!trav(startStack[stackIndex]))
|
---|
| 324 | return false;
|
---|
| 325 |
|
---|
| 326 | } else if (stackIndex == 0) { // start point onwards
|
---|
| 327 |
|
---|
| 328 | // Map the function to the parent node if its node name isn't filtered out
|
---|
| 329 | if (searchRight && (!filter || filter.test(_nodeName(parent)))) {
|
---|
| 330 | res = func(parent);
|
---|
| 331 | if (res === false)
|
---|
| 332 | return res;
|
---|
| 333 | skipChildren = (res === 1); // Skip children?
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 | // If we are traverse backwards (postorder + reverse sequence), then
|
---|
| 337 | // dont traverse deeper from the start node... begin moving left/upward
|
---|
| 338 | if (!searchRight && parent == start) child = null;
|
---|
| 339 | else child = searchRight ? parent.firstChild : parent.lastChild;
|
---|
| 340 | }
|
---|
| 341 |
|
---|
| 342 | // Search children
|
---|
| 343 | if (!skipChildren) {
|
---|
| 344 | while (child) {
|
---|
| 345 | if (!trav(child))
|
---|
| 346 | return false;
|
---|
| 347 | child = (searchRight) ? child.nextSibling : child.previousSibling;
|
---|
| 348 | }
|
---|
| 349 | }
|
---|
| 350 |
|
---|
| 351 | // Map the function to the parent node if its node name isn't filtered out
|
---|
| 352 | if (!searchRight && (!filter || filter.test(_nodeName(parent))))
|
---|
| 353 | if (func(parent) === false)
|
---|
| 354 | return false;
|
---|
| 355 |
|
---|
| 356 | return true;
|
---|
| 357 |
|
---|
| 358 | })(root);
|
---|
| 359 |
|
---|
| 360 | }
|
---|
| 361 |
|
---|
| 362 | /**
|
---|
| 363 | * Traverses through DOM nodes and applies/maps a function to text nodes.
|
---|
| 364 | *
|
---|
| 365 | * The tree traversal is preorder.
|
---|
| 366 | *
|
---|
| 367 | * @param {Node} root The parent to search from. Inclusive in search. Null to find the root automatically.
|
---|
| 368 | *
|
---|
| 369 | * @param {Node} start The node at which the traversal should start from.
|
---|
| 370 | *
|
---|
| 371 | * @param {Boolean} searchRight True to traverse tree preorder from left to right, false to traverse from right to left.
|
---|
| 372 | *
|
---|
| 373 | * @param {Function} func The function to apply. One argument is given: the child text nodes.
|
---|
| 374 | * Returning false aborts traversal.
|
---|
| 375 | *
|
---|
| 376 | *
|
---|
| 377 | */
|
---|
| 378 | function _visitTextNodes(root, start, searchRight, func) {
|
---|
| 379 | _visitNodes(root, start, searchRight, /^#text$/, func);
|
---|
| 380 | }
|
---|
| 381 |
|
---|
| 382 | /**
|
---|
| 383 | * Traverses through DOM nodes and applies/maps a function to all nodes.
|
---|
| 384 | *
|
---|
| 385 | * The tree traversal is preorder.
|
---|
| 386 | *
|
---|
| 387 | * @param {Node} root The parent to search from. Inclusive in search. Null to find the root automatically.
|
---|
| 388 | *
|
---|
| 389 | * @param {Node} start The node at which the traversal should start from.
|
---|
| 390 | *
|
---|
| 391 | * @param {Boolean} searchRight True to traverse tree preorder from left to right, false to traverse from right to left.
|
---|
| 392 | *
|
---|
| 393 | * @param {Function} func The function to apply. One argument is given: the child nodes.
|
---|
| 394 | * Returning false aborts traversal.
|
---|
| 395 | */
|
---|
| 396 | function _visitAllNodes(root, start, searchRight, func) {
|
---|
| 397 | _visitNodes(root, start, searchRight, null, func);
|
---|
| 398 | }
|
---|
| 399 |
|
---|
| 400 | /**
|
---|
| 401 | * Determines whether a node is an ansetor of another.
|
---|
| 402 | *
|
---|
| 403 | * @param {Node} ancestor A dom node
|
---|
| 404 | *
|
---|
| 405 | * @param {Node} descendant A dom node
|
---|
| 406 | *
|
---|
| 407 | * @return {Boolean} True if ancestor is an ancestor of descendant
|
---|
| 408 | */
|
---|
| 409 | function _isAncestor(ancestor, descendant) {
|
---|
| 410 | descendant = descendant.parentNode;
|
---|
| 411 | while (descendant) {
|
---|
| 412 | if (descendant == ancestor)
|
---|
| 413 | return true;
|
---|
| 414 | descendant = descendant.parentNode;
|
---|
| 415 | }
|
---|
| 416 |
|
---|
| 417 | return false;
|
---|
| 418 | }
|
---|
| 419 |
|
---|
| 420 | /**
|
---|
| 421 | * Gets ancestors of a dom node.
|
---|
| 422 | *
|
---|
| 423 | * @param {Node} child The node to get ancestors for.
|
---|
| 424 | *
|
---|
| 425 | * @param {Node} endAncestor The last ancestor of the search. This can be null to get all ancestors
|
---|
| 426 | *
|
---|
| 427 | * @param {Boolean} includeChild True to include the child in with the ancestors.
|
---|
| 428 | *
|
---|
| 429 | * @param {Boolean} includeEndAncestor True to include the endAncestor in with the ancestors.
|
---|
| 430 | *
|
---|
| 431 | * @return {Array} An array of dom Node's containinhg the ancestors. Ordered from child to ancestor
|
---|
| 432 | */
|
---|
| 433 | function _getAncestors(child, endAncestor, includeChild, includeEndAncestor) {
|
---|
| 434 |
|
---|
| 435 | if (child == endAncestor)
|
---|
| 436 | return (includeChild || includeEndAncestor) ? [child] : [];
|
---|
| 437 |
|
---|
| 438 | var ancestors = includeChild ? [child] : [];
|
---|
| 439 |
|
---|
| 440 | var nd = child.parentNode;
|
---|
| 441 |
|
---|
| 442 | while (nd && nd != endAncestor) {
|
---|
| 443 | ancestors.push(nd);
|
---|
| 444 | nd = nd.parentNode;
|
---|
| 445 | }
|
---|
| 446 |
|
---|
| 447 | if (includeEndAncestor && endAncestor && nd == endAncestor)
|
---|
| 448 | ancestors.push(endAncestor);
|
---|
| 449 |
|
---|
| 450 | return ancestors;
|
---|
| 451 | }
|
---|
| 452 |
|
---|
| 453 | /**
|
---|
| 454 | * Finds an ancestor for a child up to a given point with a specific condition.
|
---|
| 455 | *
|
---|
| 456 | * @example
|
---|
| 457 | *
|
---|
| 458 | * var firstOccuringBlock = _findAncestor(child, docBody, de.html.isBlockLevel, true);
|
---|
| 459 | *
|
---|
| 460 | * @param {Node} child The child node to begin search from (Inclusive)
|
---|
| 461 | *
|
---|
| 462 | * @param {Node} endAncestorEx (Optional) The ancestor of the child node to stop at,
|
---|
| 463 | * Null will search up to the dom tree root
|
---|
| 464 | *
|
---|
| 465 | * @param {Function} markFunc (Optional) A function which tests a given dom node. Return true to
|
---|
| 466 | * mark the node for being the node to retrieve (depending on stopOnFirst argument).
|
---|
| 467 | * False/null/undefined to continue the search.
|
---|
| 468 | *
|
---|
| 469 | * @param {Boolean} stopOnFirst (Optional) True to stop the search on the first encountered marked node,
|
---|
| 470 | * False/null/undefined to continue search to find last occuring marked node in ancestor path.
|
---|
| 471 | *
|
---|
| 472 | * @return {Node} The querried result - null if could not find
|
---|
| 473 | */
|
---|
| 474 | function _findAncestor(child, endAncestorEx, markFunc, stopOnFirst) {
|
---|
| 475 |
|
---|
| 476 | var lastMarkedNode = null;
|
---|
| 477 |
|
---|
| 478 | while (child) {
|
---|
| 479 | if (markFunc && markFunc(child)) {
|
---|
| 480 | if (stopOnFirst)
|
---|
| 481 | return child;
|
---|
| 482 | lastMarkedNode = child;
|
---|
| 483 | }
|
---|
| 484 | if (child.parentNode == endAncestorEx)
|
---|
| 485 | break;
|
---|
| 486 | child = child.parentNode;
|
---|
| 487 | }
|
---|
| 488 |
|
---|
| 489 | return markFunc ? lastMarkedNode : child;
|
---|
| 490 | }
|
---|
| 491 |
|
---|
| 492 | /**
|
---|
| 493 | * Gets the first common ancestor between two nodes.
|
---|
| 494 | *
|
---|
| 495 | * @param {Node} node1 A dom node
|
---|
| 496 | *
|
---|
| 497 | * @param {Node} node2 A dom node
|
---|
| 498 | *
|
---|
| 499 | * @param {Boolean} inclusive True to count node1 and node2 as being a possible common ancestor.
|
---|
| 500 | *
|
---|
| 501 | * @return {Node} the first common ancestor between two nodes. Null if they share no ancestor.
|
---|
| 502 | */
|
---|
| 503 | function _getCommonAncestor(node1, node2, inclusive){
|
---|
| 504 |
|
---|
| 505 | var ancestors1 = _getAncestors(node1, null, inclusive, 1),
|
---|
| 506 | ancestors2 = _getAncestors(node2, null, inclusive, 1),
|
---|
| 507 | commonParent = null;
|
---|
| 508 |
|
---|
| 509 | for (var i in ancestors1) {
|
---|
| 510 | for (var j in ancestors2) {
|
---|
| 511 | if (ancestors1[i] == ancestors2[j]) {
|
---|
| 512 | return ancestors1[i];
|
---|
| 513 | }
|
---|
| 514 | }
|
---|
| 515 | }
|
---|
| 516 |
|
---|
| 517 | return null;
|
---|
| 518 | }
|
---|
| 519 |
|
---|
| 520 | /**
|
---|
| 521 | * Gets the next node in the preorder/postorder traversal.
|
---|
| 522 | *
|
---|
| 523 | * @param {Node} node The reference point.
|
---|
| 524 | *
|
---|
| 525 | * @param {Boolean} searchRight True to traverse tree preorder from left to right, false to traverse postorder from right to left.
|
---|
| 526 | *
|
---|
| 527 | * @return {Node} The next node. Null if none exists.
|
---|
| 528 | *
|
---|
| 529 | */
|
---|
| 530 | function _nextNode(node, searchRight) {
|
---|
| 531 | var next = null;
|
---|
| 532 |
|
---|
| 533 | _visitNodes(null, node, searchRight, null, function(nd) {
|
---|
| 534 | if (nd == node) return true; // Skip starting node
|
---|
| 535 | next = nd;
|
---|
| 536 | return false;
|
---|
| 537 | });
|
---|
| 538 |
|
---|
| 539 | return next;
|
---|
| 540 | }
|
---|
| 541 |
|
---|
| 542 | /**
|
---|
| 543 | * @param {Node} node The node to get the root for. Must not be null.
|
---|
| 544 | *
|
---|
| 545 | * @param {[Number]} untilNodeTypes Optional, an aray of DOM Node constants. If given, the search for the
|
---|
| 546 | * root node will stop just before the first encountered given node type.
|
---|
| 547 | * For example. [Node.DOCUMENT_NODE] will retreive up to the body element if
|
---|
| 548 | * it has a document node ancestor.
|
---|
| 549 | *
|
---|
| 550 | * @return {Node} the root of the given node
|
---|
| 551 | */
|
---|
| 552 | function _getRoot(node, untilNodeTypes) {
|
---|
| 553 |
|
---|
| 554 | while (node.parentNode){
|
---|
| 555 | if (untilNodeTypes) {
|
---|
| 556 | for (var i in untilNodeTypes) {
|
---|
| 557 | if (node.parentNode.nodeType == untilNodeTypes[i])
|
---|
| 558 | return node;
|
---|
| 559 | }
|
---|
| 560 | }
|
---|
| 561 | node = node.parentNode;
|
---|
| 562 | }
|
---|
| 563 | return node;
|
---|
| 564 | }
|
---|
| 565 |
|
---|
| 566 | /**
|
---|
| 567 | * Gets a dom nodes child index in its parents childrens node list
|
---|
| 568 | *
|
---|
| 569 | * @param {Node} node A dom node
|
---|
| 570 | *
|
---|
| 571 | * @return {Number} The zero-based index of where the node occurs in its parent chilren.
|
---|
| 572 | * -1 if has no parent
|
---|
| 573 | */
|
---|
| 574 | function _indexInParent(node) {
|
---|
| 575 | var index = -1;
|
---|
| 576 | while (node) {
|
---|
| 577 | index++;
|
---|
| 578 | node = node.previousSibling;
|
---|
| 579 | }
|
---|
| 580 | return index;
|
---|
| 581 | }
|
---|
| 582 |
|
---|
| 583 |
|
---|
| 584 |
|
---|
| 585 | /**
|
---|
| 586 | * Returns a string so that special reserved entities and white spaces are escaped.
|
---|
| 587 | * Note that only whitespaces are escaped if they need to be....
|
---|
| 588 | *
|
---|
| 589 | * @param {String} text The text to escape.
|
---|
| 590 | *
|
---|
| 591 | * @param {Boolean} breakNewLines True to replace newline charactors with line breaks. False to treat as whitespace
|
---|
| 592 | *
|
---|
| 593 | * @return {String} The escaped version of text.
|
---|
| 594 | */
|
---|
| 595 | function _escapeTextToHTML(text, breakNewLines) {
|
---|
| 596 |
|
---|
| 597 | var escapedText = "";
|
---|
| 598 | var start = 0;
|
---|
| 599 | var c, i;
|
---|
| 600 |
|
---|
| 601 | for (i = 0; i < text.length; i++) {
|
---|
| 602 | c = text.charAt(i);
|
---|
| 603 |
|
---|
| 604 | var escapedStr = null;
|
---|
| 605 |
|
---|
| 606 | switch(c) {
|
---|
| 607 | case "\"":
|
---|
| 608 | escapedStr = """;
|
---|
| 609 | break;
|
---|
| 610 | case "'":
|
---|
| 611 | escapedStr = "'"; // ' does not work in IE
|
---|
| 612 | break;
|
---|
| 613 | case "&":
|
---|
| 614 | escapedStr = "&";
|
---|
| 615 | break;
|
---|
| 616 | case "<":
|
---|
| 617 | escapedStr = "<";
|
---|
| 618 | break;
|
---|
| 619 | case ">":
|
---|
| 620 | escapedStr = ">";
|
---|
| 621 | break;
|
---|
| 622 | default:
|
---|
| 623 | if (breakNewLines && c == "\n") {
|
---|
| 624 | escapedStr = "<br>";
|
---|
| 625 |
|
---|
| 626 | } else if (_isAllWhiteSpace(c) &&
|
---|
| 627 | (i == 0 ||
|
---|
| 628 | i == (text.length - 1) ||
|
---|
| 629 | text.charAt(i-1) == " " ||
|
---|
| 630 | text.charAt(i+1) == " ")) {
|
---|
| 631 | escapedStr = " ";
|
---|
| 632 | }
|
---|
| 633 | }
|
---|
| 634 |
|
---|
| 635 | // Does this charactor need escaping?
|
---|
| 636 | if (escapedStr) {
|
---|
| 637 | // First append charactors that are previously ok
|
---|
| 638 | if ((i - start) > 0) {
|
---|
| 639 | escapedText += (text.substring(start, i));
|
---|
| 640 | }
|
---|
| 641 |
|
---|
| 642 | // Add the escaped version
|
---|
| 643 | escapedText += escapedStr;
|
---|
| 644 |
|
---|
| 645 | // reset the start
|
---|
| 646 | start = i + 1;
|
---|
| 647 | }
|
---|
| 648 | }
|
---|
| 649 |
|
---|
| 650 | // Add remaining text
|
---|
| 651 | if ((i - start) > 0) {
|
---|
| 652 | escapedText += (text.substring(start, i));
|
---|
| 653 | }
|
---|
| 654 |
|
---|
| 655 | return escapedText;
|
---|
| 656 |
|
---|
| 657 | }
|
---|
| 658 |
|
---|
| 659 |
|
---|
| 660 | /**
|
---|
| 661 | *
|
---|
| 662 | * @param {String} htmlText The html string to parse
|
---|
| 663 | *
|
---|
| 664 | * @return {String} The escaped version of text.
|
---|
| 665 | */
|
---|
| 666 | function _parseHTMLString(htmlText) {
|
---|
| 667 |
|
---|
| 668 | var tmp = $createElement("span");
|
---|
| 669 | tmp.innerHTML = htmlText;
|
---|
| 670 | return tmp.firstChild.nodeValue;
|
---|
| 671 |
|
---|
| 672 | }
|
---|
| 673 |
|
---|
| 674 | /**
|
---|
| 675 | * Determines whether a node is displayed or not depending on its immediate or inherited
|
---|
| 676 | * CSS display style. Note, that if a node's visibility is hidden, is does not mean
|
---|
| 677 | * it is not displayed.
|
---|
| 678 | *
|
---|
| 679 | * @param {Node} node A dom node to test
|
---|
| 680 | *
|
---|
| 681 | * @return {Boolean} True if the dom node is displayed, false if it is not.
|
---|
| 682 | */
|
---|
| 683 | function _isNodeDisplayed(node){
|
---|
| 684 | while(node) {
|
---|
| 685 | if (node.nodeType == Node.ELEMENT_NODE) {
|
---|
| 686 | if (node.style.display == "none")
|
---|
| 687 | return false;
|
---|
| 688 | }
|
---|
| 689 | node = node.parentNode;
|
---|
| 690 | }
|
---|
| 691 | return true;
|
---|
| 692 | }
|
---|
| 693 |
|
---|
| 694 | /**
|
---|
| 695 | * Retreives a CSS style directly set for or inherited by a given dom node.
|
---|
| 696 | *
|
---|
| 697 | * @see www.quirksmode.org/dom/getstyles.html
|
---|
| 698 | *
|
---|
| 699 | * @param {Node} node A dom node.
|
---|
| 700 | * @param {String} styleProp A CSS style property, formatted in CSS notation.
|
---|
| 701 | * @return {String} The inherited style of the given node. Undefined if the node does not have the style.
|
---|
| 702 | * If the node is not an element, then the first ancestor element is selected.
|
---|
| 703 | *
|
---|
| 704 | */
|
---|
| 705 | function _getComputedStyle(node, styleProp) {
|
---|
| 706 |
|
---|
| 707 | while (node && node.nodeType != Node.ELEMENT_NODE) {
|
---|
| 708 | node = node.parentNode;
|
---|
| 709 | }
|
---|
| 710 | if (!node) return;
|
---|
| 711 |
|
---|
| 712 | if (window.getComputedStyle) // DOM Spec
|
---|
| 713 | return document.defaultView.getComputedStyle(node,"").getPropertyValue(styleProp)
|
---|
| 714 |
|
---|
| 715 | else if (node.currentStyle) // MS HTML
|
---|
| 716 | return node.currentStyle[_styleCSSToJSNotation(styleProp)];
|
---|
| 717 |
|
---|
| 718 | debug.println("Warning - could not get style \"" + styleProp + "\" for a \"" + node.nodeName + "\" element");
|
---|
| 719 | // Otherwise undefined...
|
---|
| 720 | }
|
---|
| 721 |
|
---|
| 722 | /**
|
---|
| 723 | * Sets an element's CSS string
|
---|
| 724 | * @param {Node} ele An element node
|
---|
| 725 | * @param {String} css A css style string formatted in CSS notation.
|
---|
| 726 | *
|
---|
| 727 | * @example
|
---|
| 728 | * _setFullStyle(myElement, "color:red; padding:4px; font-size:12px");
|
---|
| 729 | */
|
---|
| 730 | function _setFullStyle(ele, css){
|
---|
| 731 | if (_engine == _Platform.TRIDENT)
|
---|
| 732 | ele.style.setAttribute("cssText", css);
|
---|
| 733 | else
|
---|
| 734 | ele.setAttribute("style", css);
|
---|
| 735 | }
|
---|
| 736 |
|
---|
| 737 | /**
|
---|
| 738 | * Sets a CSS style value for a given element
|
---|
| 739 | *
|
---|
| 740 | * @param {Node} ele An element node
|
---|
| 741 | *
|
---|
| 742 | * @param {String} css The CSS style to set in JS Notation
|
---|
| 743 | *
|
---|
| 744 | * @param {String} val The value of the new style
|
---|
| 745 | */
|
---|
| 746 | function _setStyle(ele, css, val) {
|
---|
| 747 | if (_engine == _Platform.TRIDENT)
|
---|
| 748 | ele.style.setAttribute(css, val);
|
---|
| 749 | else
|
---|
| 750 | ele.style[css] = val;
|
---|
| 751 | }
|
---|
| 752 |
|
---|
| 753 | /**
|
---|
| 754 | * Retrieves the full CSS markup for an elements style. Note that this is not the
|
---|
| 755 | * computed markup - it is the explicitely assigned CSS for the particular node.
|
---|
| 756 | *
|
---|
| 757 | * @param {Node} ele The element to get the style from
|
---|
| 758 | * @return {String} The CSS for the given element. Never null, empty if no explicit styles set
|
---|
| 759 | */
|
---|
| 760 | function _getFullStyle(ele) {
|
---|
| 761 | return (_engine == _Platform.TRIDENT ? ele.style.getAttribute("cssText") : ele.getAttribute("style")) || "";
|
---|
| 762 | }
|
---|
| 763 |
|
---|
| 764 | /**
|
---|
| 765 | * @param {Element} ele The element to check
|
---|
| 766 | * @return {Boolean} Evaluates to true iff the element has an element-level style
|
---|
| 767 | * (i.e not computed).
|
---|
| 768 | */
|
---|
| 769 | function _doesHaveElementStyle(ele) {
|
---|
| 770 |
|
---|
| 771 | var fs = _getFullStyle(ele);
|
---|
| 772 |
|
---|
| 773 | // Check if the CSS text contains non-empty style-values
|
---|
| 774 | if (fs) {
|
---|
| 775 | fs = fs.split(";");
|
---|
| 776 | for (var s in fs) {
|
---|
| 777 | var idx = fs[s].indexOf(':');
|
---|
| 778 | if (idx > 0 &&
|
---|
| 779 | idx < (fs[s].length - 1) &&
|
---|
| 780 | /\s*\S+\s*/.test(fs[s].substr(idx)))
|
---|
| 781 | return 1;
|
---|
| 782 | }
|
---|
| 783 | }
|
---|
| 784 | }
|
---|
| 785 |
|
---|
| 786 | /**
|
---|
| 787 | * @param {String} styleProp A CSS style in JS notation.
|
---|
| 788 | * @return {String} The given CSS style in CSS notation.
|
---|
| 789 | */
|
---|
| 790 | function _styleJSToCSSNotation(styleProp) {
|
---|
| 791 | do {
|
---|
| 792 | var match = /([A-Z])/.exec(styleProp);
|
---|
| 793 | if (match)
|
---|
| 794 | styleProp = styleProp.substr(0, match.index) + "-" + match[1].toLowerCase() + styleProp.substr(match.index+1);
|
---|
| 795 | } while(match);
|
---|
| 796 |
|
---|
| 797 | return styleProp;
|
---|
| 798 | };
|
---|
| 799 |
|
---|
| 800 | /**
|
---|
| 801 | * @param {String} color A CSS Style color. Can be in hex, rgb, percentages or actual names.
|
---|
| 802 | * NOTE: Only supports converting the 16 standardized HTML color names -
|
---|
| 803 | * unstandard color names will return white.
|
---|
| 804 | *
|
---|
| 805 | * @return {[Number]} An array with elements R,G and B respectively. They range from 0-255.
|
---|
| 806 | *
|
---|
| 807 | * DEPRECIATED
|
---|
| 808 | */
|
---|
| 809 | var _getColorRGB = function() {
|
---|
| 810 |
|
---|
| 811 | var colorRegExp = /^\s*rgb\s*\(\s*(\d+)\%?\s*\,\s*(\d+)\%?\s*\,\s*(\d+)\%?\s*\)\s*$/i,
|
---|
| 812 |
|
---|
| 813 | /* Only going to support the 16 standardized colors. All major browsers support a lot more but will seriously bloat the api size. */
|
---|
| 814 | colorWMap = {
|
---|
| 815 | maroon: [128,0,0],
|
---|
| 816 | red: [255,0,0],
|
---|
| 817 | orange: [255,165,0],
|
---|
| 818 | yellow: [255,255,0],
|
---|
| 819 | olive: [128,128,0],
|
---|
| 820 | purple: [128,0,128],
|
---|
| 821 | fuchsia: [255,0,255],
|
---|
| 822 | white: [255,255,255],
|
---|
| 823 | lime: [0,255,0],
|
---|
| 824 | green: [0,128,0],
|
---|
| 825 | navy: [0,0,128],
|
---|
| 826 | blue: [0,0,255],
|
---|
| 827 | aqua: [0,255,255],
|
---|
| 828 | teal: [0,128,128],
|
---|
| 829 | black: [0,0,0],
|
---|
| 830 | silver: [12,12,12],
|
---|
| 831 | gray: [128,128,128]
|
---|
| 832 | };
|
---|
| 833 |
|
---|
| 834 | return function(val){
|
---|
| 835 |
|
---|
| 836 | if (val.charAt(0) == "#") {
|
---|
| 837 | if (val.length < 7)
|
---|
| 838 | val += "000000";
|
---|
| 839 | // Convert to RGB
|
---|
| 840 | return [parseInt(val.substr(1,2),16), parseInt(val.substr(3,2),16), parseInt(val.substr(5,2),16)];
|
---|
| 841 | }
|
---|
| 842 |
|
---|
| 843 | // Is the color in the notation "rbg(r,b,g)" ?
|
---|
| 844 | var match = colorRegExp.exec(val);
|
---|
| 845 | if (match) {
|
---|
| 846 |
|
---|
| 847 | var r = parseInt(match[1]), g = parseInt(match[2]), b = parseInt(match[3]);
|
---|
| 848 |
|
---|
| 849 | if (val.indexOf("%") > -1) { // convert percentages to 255 range
|
---|
| 850 | if (r > 100) r = 100; // clamp;
|
---|
| 851 | r = (255 * r) / 100;
|
---|
| 852 | if (g > 100) g = 100; // clamp;
|
---|
| 853 | g = (255 * g) / 100;
|
---|
| 854 | if (b > 100) b = 100; // clamp;
|
---|
| 855 | b = (255 * b) / 100;
|
---|
| 856 | } else { // Clamp 255 range
|
---|
| 857 | if (r > 255) r = 255;
|
---|
| 858 | if (g > 255) g = 255;
|
---|
| 859 | if (b > 255) b = 255;
|
---|
| 860 | }
|
---|
| 861 |
|
---|
| 862 | return [r,g,b];
|
---|
| 863 | }
|
---|
| 864 |
|
---|
| 865 | return colorWMap[val.toLowerCase()] || [255,255,255];
|
---|
| 866 | }
|
---|
| 867 |
|
---|
| 868 | }();
|
---|
| 869 |
|
---|
| 870 | /**
|
---|
| 871 | * @param {String} cssStyle A CSS Style to check
|
---|
| 872 | * @param {String} val1 The value of a CSS style to compare (with val2)
|
---|
| 873 | * @param {String} val2 The value of a CSS style to compare (with val1)
|
---|
| 874 | * @return {Boolean} True if val1 is equivalent to val2 CSS
|
---|
| 875 | *
|
---|
| 876 | * DEPRECIATED
|
---|
| 877 | *
|
---|
| 878 | */
|
---|
| 879 | var _isCSSValueSame = function(){
|
---|
| 880 |
|
---|
| 881 | var fontWeightMap = {
|
---|
| 882 | bold: "700",
|
---|
| 883 | normal: "400"
|
---|
| 884 | };
|
---|
| 885 |
|
---|
| 886 | function normalizeFontWeight(val){
|
---|
| 887 | return fontWeightMap[val.toLowerCase()] || val;
|
---|
| 888 | }
|
---|
| 889 |
|
---|
| 890 |
|
---|
| 891 | return function(cssStyle, val1, val2){
|
---|
| 892 |
|
---|
| 893 | switch (cssStyle) {
|
---|
| 894 | case "backgroundColor":
|
---|
| 895 | case "borderColor":
|
---|
| 896 | case "outlineColor":
|
---|
| 897 | case "color":
|
---|
| 898 | val1 = _getColorRGB(val1);
|
---|
| 899 | val2 = _getColorRGB(val2);
|
---|
| 900 | return val1[0] == val2[0] && val1[1] == val2[1] && val1[2] == val2[2];
|
---|
| 901 |
|
---|
| 902 | case "fontWeight":
|
---|
| 903 | val1 = normalizeFontWeight(val1);
|
---|
| 904 | val2 = normalizeFontWeight(val2);
|
---|
| 905 | break;
|
---|
| 906 | }
|
---|
| 907 |
|
---|
| 908 | return val1 == val2;
|
---|
| 909 |
|
---|
| 910 | };
|
---|
| 911 |
|
---|
| 912 | }();
|
---|
| 913 |
|
---|
| 914 | /**
|
---|
| 915 | * @param {String} styleProp A CSS style in CSS notation.
|
---|
| 916 | * @return {String} The given CSS style in JS notation.
|
---|
| 917 | */
|
---|
| 918 | function _styleCSSToJSNotation(styleProp) {
|
---|
| 919 | do {
|
---|
| 920 | var index = styleProp.indexOf("-");
|
---|
| 921 | if (index > -1)
|
---|
| 922 | styleProp = (index == (styleProp.length-1)) ?
|
---|
| 923 | styleProp.substr(0, index) :
|
---|
| 924 | styleProp.substr(0, index) + styleProp.charAt(index+1).toUpperCase() + styleProp.substr(index + 2);
|
---|
| 925 | } while(index > -1);
|
---|
| 926 | return styleProp;
|
---|
| 927 | };
|
---|
| 928 |
|
---|
| 929 |
|
---|
| 930 |
|
---|
| 931 | /**
|
---|
| 932 | * Gets the outer HTML content of a given element.
|
---|
| 933 | * NOTE: Can be expensive for large DOM Trees in firefox/konqueror.
|
---|
| 934 | * @param {Node} node An Element to get it's outer HTML for.
|
---|
| 935 | * @return {String} The outer html of the given node.
|
---|
| 936 | */
|
---|
| 937 | function _getOuterHTML(node) {
|
---|
| 938 | if (node.outerHTML) return node.outerHTML;
|
---|
| 939 | else { // Firefox / konqueror
|
---|
| 940 | var tmp = $createElement("span");
|
---|
| 941 | tmp.appendChild(node.cloneNode(true));
|
---|
| 942 | return tmp.innerHTML;
|
---|
| 943 | }
|
---|
| 944 | }
|
---|
| 945 |
|
---|
| 946 | /**
|
---|
| 947 | * @return {Boolean} True if this browser allows you to safely extend the DOM.
|
---|
| 948 | */
|
---|
| 949 | function _isDOMExtendable() {
|
---|
| 950 | /* IE Versions 7 down are not core javascript. */
|
---|
| 951 | return !(_browser == _Platform.IE && _browserVersion < 8);
|
---|
| 952 | }
|
---|
| 953 |
|
---|
| 954 | /**
|
---|
| 955 | * @param {Node} node The node to extract the class name from
|
---|
| 956 | * @param {RegExp} A regular expression.
|
---|
| 957 | * @return {String} the first occurring classname of the node which matches regexp.
|
---|
| 958 | * Null if did not find a match
|
---|
| 959 | */
|
---|
| 960 | function _findClassName(node, regexp){
|
---|
| 961 | if (node.nodeType == Node.ELEMENT_NODE || node == docBody) {
|
---|
| 962 | var clsName = _getClassName(node);
|
---|
| 963 | if (clsName) {
|
---|
| 964 | var classNames = clsName.split(' ');
|
---|
| 965 | for (var i in classNames) {
|
---|
| 966 | if (regexp.test(classNames[i]))
|
---|
| 967 | return classNames[i];
|
---|
| 968 | }
|
---|
| 969 | }
|
---|
| 970 | }
|
---|
| 971 | return null;
|
---|
| 972 | }
|
---|
| 973 |
|
---|
| 974 | /**
|
---|
| 975 | * @param {Node} element A Dom element
|
---|
| 976 | * @return {String} The class name for the given element
|
---|
| 977 | */
|
---|
| 978 | function _getClassName(element) {
|
---|
| 979 | return element.className;
|
---|
| 980 | }
|
---|
| 981 |
|
---|
| 982 | /**
|
---|
| 983 | * @param {Node} element A Dom element
|
---|
| 984 | * @param {String} name The class to set - overrides all classes.
|
---|
| 985 | */
|
---|
| 986 | function _setClassName(element, name) {
|
---|
| 987 | return _browser == _Platform.IE ? element.setAttribute("className", name) :
|
---|
| 988 | element.className = name;
|
---|
| 989 | }
|
---|
| 990 |
|
---|
| 991 | /**
|
---|
| 992 | * Not all browsers support Array.indexOf .. this is a manual impl.
|
---|
| 993 | *
|
---|
| 994 | * @param {Object} obj An object
|
---|
| 995 | * @param {Array} arr An array
|
---|
| 996 | * @return {Number} The index of obj in arr. -1 if obj is not in arr.
|
---|
| 997 | */
|
---|
| 998 | function _indexOf(obj, arr) {
|
---|
| 999 | for (var i in arr) {
|
---|
| 1000 | if (arr[i] == obj)
|
---|
| 1001 | return parseInt(i);
|
---|
| 1002 | }
|
---|
| 1003 | return -1;
|
---|
| 1004 | }
|
---|
| 1005 |
|
---|
| 1006 | /**
|
---|
| 1007 | * @param {Node} node a dom node
|
---|
| 1008 | * @return {String} The dom node's name in lower case
|
---|
| 1009 | */
|
---|
| 1010 | function _nodeName(node) {
|
---|
| 1011 | return node.nodeName.toLowerCase();
|
---|
| 1012 | }
|
---|
| 1013 |
|
---|
| 1014 | /**
|
---|
| 1015 | * Determines whether a node is a text node and returns the text length if it is.
|
---|
| 1016 | *
|
---|
| 1017 | * @param {Node} node The dom node to test
|
---|
| 1018 | *
|
---|
| 1019 | * @param {Object} defaultValue (optional) If the node to test is not a text node then this value will be returned instead.
|
---|
| 1020 | * Defaults to NULL.
|
---|
| 1021 | *
|
---|
| 1022 | * @return {Object} If the node is a text node, then the text length of the node will be returned.
|
---|
| 1023 | * Otherwise defaultValue will be returned.
|
---|
| 1024 | */
|
---|
| 1025 | function _nodeLength(node, defaultValue) {
|
---|
| 1026 | if (typeof defaultValue == "undefined")
|
---|
| 1027 | defaultValue = null;
|
---|
| 1028 | return node.nodeType == Node.TEXT_NODE ? node.nodeValue.length : defaultValue;
|
---|
| 1029 | }
|
---|
| 1030 |
|
---|
| 1031 | /**
|
---|
| 1032 | * Determines if an object is a DOM Node or not.
|
---|
| 1033 | * @param {Object} obj The object to test
|
---|
| 1034 | */
|
---|
| 1035 | function _isDOMNode(obj){
|
---|
| 1036 |
|
---|
| 1037 | // @DEBUG ON
|
---|
| 1038 | // In debug mode, the Node object will be created if it is not available - in order to provide
|
---|
| 1039 | // node type constants. To distinuish from a real node object and the fabricaed one, test if the
|
---|
| 1040 | // _DE_DEBUG_CREATED if there
|
---|
| 1041 | if (Node._DE_DEBUG_CREATED)
|
---|
| 1042 | return typeof obj == "object" && typeof obj.nodeType == "number" && typeof obj.nodeName == "string";
|
---|
| 1043 | // @DEBUG OFF
|
---|
| 1044 |
|
---|
| 1045 | return typeof Node == "object" ? obj instanceof Node : (typeof obj == "object" && typeof obj.nodeType == "number" && typeof obj.nodeName == "string");
|
---|
| 1046 | };
|
---|
| 1047 |
|
---|
| 1048 | /*
|
---|
| 1049 | * Expose internals to public
|
---|
| 1050 | */
|
---|
| 1051 | $extend(de, {
|
---|
| 1052 |
|
---|
| 1053 | /**
|
---|
| 1054 | * Exposure of _visitAllNodes internal
|
---|
| 1055 | * @see _visitAllNodes
|
---|
| 1056 | */
|
---|
| 1057 | visitAllNodes : _visitAllNodes,
|
---|
| 1058 |
|
---|
| 1059 | getCommonAncestor : _getCommonAncestor,
|
---|
| 1060 |
|
---|
| 1061 | /**
|
---|
| 1062 | * @param {Node} node a dom node
|
---|
| 1063 | * @return {String} The inner text of the given node, never null, but can be empty.
|
---|
| 1064 | */
|
---|
| 1065 | getInnerText : function(node) {
|
---|
| 1066 | if (node.nodeType == Node.TEXT_NODE)
|
---|
| 1067 | return node.nodeValue;
|
---|
| 1068 | return node.innerText || node.textContent || "";
|
---|
| 1069 | },
|
---|
| 1070 |
|
---|
| 1071 | /**
|
---|
| 1072 | * Exposure of _parseHTMLString internal
|
---|
| 1073 | * @see _parseHTMLString
|
---|
| 1074 | */
|
---|
| 1075 | parseHTMLString : _parseHTMLString,
|
---|
| 1076 |
|
---|
| 1077 | /**
|
---|
| 1078 | * Exposure of _insertAfter internal
|
---|
| 1079 | * @see _insertAfter
|
---|
| 1080 | */
|
---|
| 1081 | insertAfter : _insertAfter,
|
---|
| 1082 |
|
---|
| 1083 | /**
|
---|
| 1084 | * Exposure of _insertAt internal
|
---|
| 1085 | * @see _insertAt
|
---|
| 1086 | */
|
---|
| 1087 | insertAt : _insertAt,
|
---|
| 1088 |
|
---|
| 1089 | /**
|
---|
| 1090 | * Exposure of _findClassName internal
|
---|
| 1091 | * @see _findClassName
|
---|
| 1092 | */
|
---|
| 1093 | findClassName : _findClassName,
|
---|
| 1094 |
|
---|
| 1095 | /**
|
---|
| 1096 | * Exposure of _getPositionInWindow internal
|
---|
| 1097 | * @see _getPositionInWindow
|
---|
| 1098 | */
|
---|
| 1099 | getPositionInWindow : _getPositionInWindow,
|
---|
| 1100 |
|
---|
| 1101 | getOuterHTML : _getOuterHTML,
|
---|
| 1102 |
|
---|
| 1103 | getComputedStyle : _getComputedStyle
|
---|
| 1104 |
|
---|
| 1105 | });
|
---|