1 | |
---|
2 | function wrapNode(node, wrappertype, wrapperclass){ |
---|
3 | /* utility function to wrap a node in an arbitrary element of type "wrappertype" |
---|
4 | * with a class of "wrapperclass" */ |
---|
5 | var wrapper = document.createElement(wrappertype) |
---|
6 | wrapper.className = wrapperclass; |
---|
7 | var innerNode = node.parentNode.replaceChild(wrapper,node); |
---|
8 | wrapper.appendChild(innerNode); |
---|
9 | }; |
---|
10 | |
---|
11 | function nodeContained(innernode, outernode){ |
---|
12 | // check if innernode is contained in outernode |
---|
13 | var node = innernode.parentNode; |
---|
14 | while (node != document) { |
---|
15 | if (node == outernode) { |
---|
16 | return true; |
---|
17 | } |
---|
18 | node=node.parentNode; |
---|
19 | } |
---|
20 | return false; |
---|
21 | }; |
---|
22 | |
---|
23 | function findContainer(node, func) { |
---|
24 | // Starting with the given node, find the nearest containing element |
---|
25 | // for which the given function returns true. |
---|
26 | |
---|
27 | while (node != null) { |
---|
28 | if (func(node)) { |
---|
29 | return node; |
---|
30 | } |
---|
31 | node = node.parentNode; |
---|
32 | } |
---|
33 | return false; |
---|
34 | }; |
---|
35 | |
---|
36 | function hasClassName(node, class_name) { |
---|
37 | return new RegExp('\\b'+class_name+'\\b').test(node.className); |
---|
38 | }; |
---|
39 | |
---|
40 | function addClassName(node, class_name) { |
---|
41 | if (!node.className) { |
---|
42 | node.className = class_name; |
---|
43 | } else if (!hasClassName(node, class_name)) { |
---|
44 | var className = node.className+" "+class_name; |
---|
45 | // cleanup |
---|
46 | node.className = className.split(/\s+/).join(' '); |
---|
47 | } |
---|
48 | }; |
---|
49 | |
---|
50 | function removeClassName(node, class_name) { |
---|
51 | var className = node.className; |
---|
52 | if (className) { |
---|
53 | // remove |
---|
54 | className = className.replace(new RegExp('\\b'+class_name+'\\b'), ''); |
---|
55 | // cleanup |
---|
56 | className = className.replace(/\s+/g, ' '); |
---|
57 | node.className = className.replace(/\s+$/g, ''); |
---|
58 | } |
---|
59 | }; |
---|
60 | |
---|
61 | function replaceClassName(node, old_class, new_class, ignore_missing) { |
---|
62 | if (ignore_missing && !hasClassName(node, old_class)) { |
---|
63 | addClassName(node, new_class); |
---|
64 | } else { |
---|
65 | var className = node.className; |
---|
66 | if (className) { |
---|
67 | // replace |
---|
68 | className = className.replace(new RegExp('\\b'+old_class+'\\b'), new_class); |
---|
69 | // cleanup |
---|
70 | className = className.replace(/\s+/g, ' '); |
---|
71 | node.className = className.replace(/\s+$/g, ''); |
---|
72 | } |
---|
73 | } |
---|
74 | }; |
---|
75 | |
---|
76 | function walkTextNodes(node, func, data) { |
---|
77 | // traverse childnodes and call func when a textnode is found |
---|
78 | if (!node){return false} |
---|
79 | if (node.hasChildNodes) { |
---|
80 | // we can't use for (i in childNodes) here, because the number of |
---|
81 | // childNodes might change (higlightsearchterms) |
---|
82 | for (var i=0;i<node.childNodes.length;i++) { |
---|
83 | walkTextNodes(node.childNodes[i], func, data); |
---|
84 | } |
---|
85 | if (node.nodeType == 3) { |
---|
86 | // this is a text node |
---|
87 | func(node, data); |
---|
88 | } |
---|
89 | } |
---|
90 | }; |
---|
91 | |
---|
92 | /* These are two functions, because getInnerTextFast doesn't always return the |
---|
93 | * the same results, as it depends on the implementation of node.innerText of |
---|
94 | * the browser. getInnerTextCompatible will always return the same values, but |
---|
95 | * is a bit slower. The difference is just in the whitespace, so if this |
---|
96 | * doesn't matter, you should always use getInnerTextFast. |
---|
97 | */ |
---|
98 | |
---|
99 | function getInnerTextCompatible(node) { |
---|
100 | var result = new Array(); |
---|
101 | walkTextNodes(node, |
---|
102 | function(n, d){d.push(n.nodeValue)}, |
---|
103 | result); |
---|
104 | return result.join(""); |
---|
105 | }; |
---|
106 | |
---|
107 | function getInnerTextFast(node) { |
---|
108 | if (node.innerText) { |
---|
109 | return node.innerText; |
---|
110 | } else { |
---|
111 | return getInnerTextCompatible(node); |
---|
112 | } |
---|
113 | }; |
---|
114 | |
---|
115 | /* This function reorder nodes in the DOM. |
---|
116 | * fetch_func - the function which returns the value for comparison |
---|
117 | * cmp_func - the compare function, if not provided then the string of the |
---|
118 | * value returned by fetch_func is used. |
---|
119 | */ |
---|
120 | function sortNodes(nodes, fetch_func, cmp_func) { |
---|
121 | // terminate if we hit a non-compliant DOM implementation |
---|
122 | if (!W3CDOM){return false}; |
---|
123 | |
---|
124 | // wrapper for sorting |
---|
125 | var SortNodeWrapper = function(node) { |
---|
126 | this.value = fetch_func(node); |
---|
127 | this.cloned_node = node.cloneNode(true); |
---|
128 | this.toString = function() { |
---|
129 | if (this.value.toString) { |
---|
130 | return this.value.toString(); |
---|
131 | } else { |
---|
132 | return this.value; |
---|
133 | } |
---|
134 | } |
---|
135 | } |
---|
136 | |
---|
137 | // wrap nodes |
---|
138 | var items = new Array(); |
---|
139 | for (var i=0; i<nodes.length; i++) { |
---|
140 | items.push(new SortNodeWrapper(nodes[i])); |
---|
141 | } |
---|
142 | |
---|
143 | //sort |
---|
144 | if (cmp_func) { |
---|
145 | items.sort(cmp_func); |
---|
146 | } else { |
---|
147 | items.sort(); |
---|
148 | } |
---|
149 | |
---|
150 | // reorder nodes |
---|
151 | for (var i=0; i<items.length; i++) { |
---|
152 | var dest = nodes[i]; |
---|
153 | dest.parentNode.replaceChild(items[i].cloned_node, dest); |
---|
154 | } |
---|
155 | }; |
---|