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