Merge branch '3.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WoltLabSuite / Core / Dom / Traverse.js
CommitLineData
4bbf6ff1
AE
1/**
2 * Provides helper functions to traverse the DOM.
3 *
4 * @author Alexander Ebert
c839bd49 5 * @copyright 2001-2018 WoltLab GmbH
4bbf6ff1 6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
58d7e8f8 7 * @module WoltLabSuite/Core/Dom/Traverse
4bbf6ff1 8 */
431e4cb4 9define([], function() {
565853e8
TD
10 "use strict";
11
4bbf6ff1
AE
12 /** @const */ var NONE = 0;
13 /** @const */ var SELECTOR = 1;
14 /** @const */ var CLASS_NAME = 2;
15 /** @const */ var TAG_NAME = 3;
16
17 var _probe = [
18 function(el, none) { return true; },
431e4cb4 19 function(el, selector) { return el.matches(selector); },
4bbf6ff1
AE
20 function(el, className) { return el.classList.contains(className); },
21 function(el, tagName) { return el.nodeName === tagName; }
22 ];
23
c318ad3b 24 var _children = function(el, type, value) {
21adc182
AE
25 if (!(el instanceof Element)) {
26 throw new TypeError("Expected a valid element as first argument.");
27 }
28
c318ad3b
AE
29 var children = [];
30
31 for (var i = 0; i < el.childElementCount; i++) {
32 if (_probe[type](el.children[i], value)) {
33 children.push(el.children[i]);
34 }
35 }
36
37 return children;
38 };
39
a76a4654 40 var _parent = function(el, type, value, untilElement) {
21adc182
AE
41 if (!(el instanceof Element)) {
42 throw new TypeError("Expected a valid element as first argument.");
43 }
44
4bbf6ff1
AE
45 el = el.parentNode;
46
47 while (el instanceof Element) {
a76a4654
AE
48 if (el === untilElement) {
49 return null;
50 }
51
4bbf6ff1
AE
52 if (_probe[type](el, value)) {
53 return el;
54 }
55
56 el = el.parentNode;
57 }
58
59 return null;
60 };
61
62 var _sibling = function(el, siblingType, type, value) {
21adc182
AE
63 if (!(el instanceof Element)) {
64 throw new TypeError("Expected a valid element as first argument.");
65 }
66
4bbf6ff1
AE
67 if (el instanceof Element) {
68 if (el[siblingType] !== null && _probe[type](el[siblingType], value)) {
69 return el[siblingType];
70 }
71 }
72
73 return null;
74 };
75
76 /**
58d7e8f8 77 * @exports WoltLabSuite/Core/Dom/Traverse
4bbf6ff1 78 */
431e4cb4 79 return {
fa033f4f
AE
80 /**
81 * Examines child elements and returns the first child matching the given selector.
82 *
83 * @param {Element} el element
84 * @param {string} selector CSS selector to match child elements against
85 * @return {(Element|null)} null if there is no child node matching the selector
86 */
87 childBySel: function(el, selector) {
88 return _children(el, SELECTOR, selector)[0] || null;
89 },
90
91 /**
92 * Examines child elements and returns the first child that has the given CSS class set.
93 *
94 * @param {Element} el element
95 * @param {string} className CSS class name
96 * @return {(Element|null)} null if there is no child node with given CSS class
97 */
98 childByClass: function(el, className) {
99 return _children(el, CLASS_NAME, className)[0] || null;
100 },
101
102 /**
103 * Examines child elements and returns the first child which equals the given tag.
104 *
105 * @param {Element} el element
106 * @param {string} tagName element tag name
107 * @return {(Element|null)} null if there is no child node which equals given tag
108 */
109 childByTag: function(el, tagName) {
110 return _children(el, TAG_NAME, tagName)[0] || null;
111 },
112
c318ad3b
AE
113 /**
114 * Examines child elements and returns all children matching the given selector.
115 *
116 * @param {Element} el element
117 * @param {string} selector CSS selector to match child elements against
118 * @return {array<Element>} list of children matching the selector
119 */
120 childrenBySel: function(el, selector) {
121 return _children(el, SELECTOR, selector);
122 },
123
124 /**
125 * Examines child elements and returns all children that have the given CSS class set.
126 *
127 * @param {Element} el element
128 * @param {string} className CSS class name
129 * @return {array<Element>} list of children with the given class
130 */
131 childrenByClass: function(el, className) {
132 return _children(el, CLASS_NAME, className);
133 },
134
135 /**
136 * Examines child elements and returns all children which equal the given tag.
137 *
138 * @param {Element} el element
139 * @param {string} tagName element tag name
140 * @return {array<Element>} list of children equaling the tag name
141 */
142 childrenByTag: function(el, tagName) {
143 return _children(el, TAG_NAME, tagName);
144 },
145
4bbf6ff1
AE
146 /**
147 * Examines parent nodes and returns the first parent that matches the given selector.
148 *
149 * @param {Element} el child element
150 * @param {string} selector CSS selector to match parent nodes against
a76a4654 151 * @param {Element=} untilElement stop when reaching this element
4bbf6ff1
AE
152 * @return {(Element|null)} null if no parent node matched the selector
153 */
a76a4654
AE
154 parentBySel: function(el, selector, untilElement) {
155 return _parent(el, SELECTOR, selector, untilElement);
4bbf6ff1
AE
156 },
157
158 /**
159 * Examines parent nodes and returns the first parent that has the given CSS class set.
160 *
161 * @param {Element} el child element
162 * @param {string} className CSS class name
a76a4654 163 * @param {Element=} untilElement stop when reaching this element
4bbf6ff1
AE
164 * @return {(Element|null)} null if there is no parent node with given class
165 */
a76a4654
AE
166 parentByClass: function(el, className, untilElement) {
167 return _parent(el, CLASS_NAME, className, untilElement);
4bbf6ff1
AE
168 },
169
170 /**
171 * Examines parent nodes and returns the first parent which equals the given tag.
172 *
173 * @param {Element} el child element
174 * @param {string} tagName element tag name
a76a4654 175 * @param {Element=} untilElement stop when reaching this element
4bbf6ff1
AE
176 * @return {(Element|null)} null if there is no parent node of given tag type
177 */
a76a4654
AE
178 parentByTag: function(el, tagName, untilElement) {
179 return _parent(el, TAG_NAME, tagName, untilElement);
4bbf6ff1
AE
180 },
181
182 /**
183 * Returns the next element sibling.
184 *
185 * @param {Element} el element
186 * @return {(Element|null)} null if there is no next sibling element
187 */
188 next: function(el) {
189 return _sibling(el, 'nextElementSibling', NONE, null);
190 },
191
192 /**
193 * Returns the next element sibling that matches the given selector.
194 *
195 * @param {Element} el element
196 * @param {string} selector CSS selector to match parent nodes against
197 * @return {(Element|null)} null if there is no next sibling element or it does not match the selector
198 */
199 nextBySel: function(el, selector) {
200 return _sibling(el, 'nextElementSibling', SELECTOR, selector);
201 },
202
203 /**
204 * Returns the next element sibling with given CSS class.
205 *
206 * @param {Element} el element
207 * @param {string} className CSS class name
208 * @return {(Element|null)} null if there is no next sibling element or it does not have the class set
209 */
210 nextByClass: function(el, className) {
211 return _sibling(el, 'nextElementSibling', CLASS_NAME, className);
212 },
213
214 /**
215 * Returns the next element sibling with given CSS class.
216 *
217 * @param {Element} el element
431e4cb4 218 * @param {string} tagName element tag name
4bbf6ff1
AE
219 * @return {(Element|null)} null if there is no next sibling element or it does not have the class set
220 */
221 nextByTag: function(el, tagName) {
431e4cb4 222 return _sibling(el, 'nextElementSibling', TAG_NAME, tagName);
4bbf6ff1
AE
223 },
224
225 /**
226 * Returns the previous element sibling.
227 *
228 * @param {Element} el element
229 * @return {(Element|null)} null if there is no previous sibling element
230 */
231 prev: function(el) {
232 return _sibling(el, 'previousElementSibling', NONE, null);
233 },
234
235 /**
236 * Returns the previous element sibling that matches the given selector.
237 *
238 * @param {Element} el element
239 * @param {string} selector CSS selector to match parent nodes against
240 * @return {(Element|null)} null if there is no previous sibling element or it does not match the selector
241 */
242 prevBySel: function(el, selector) {
243 return _sibling(el, 'previousElementSibling', SELECTOR, selector);
244 },
245
246 /**
247 * Returns the previous element sibling with given CSS class.
248 *
249 * @param {Element} el element
250 * @param {string} className CSS class name
251 * @return {(Element|null)} null if there is no previous sibling element or it does not have the class set
252 */
253 prevByClass: function(el, className) {
254 return _sibling(el, 'previousElementSibling', CLASS_NAME, className);
255 },
256
257 /**
258 * Returns the previous element sibling with given CSS class.
259 *
260 * @param {Element} el element
431e4cb4 261 * @param {string} tagName element tag name
4bbf6ff1
AE
262 * @return {(Element|null)} null if there is no previous sibling element or it does not have the class set
263 */
264 prevByTag: function(el, tagName) {
431e4cb4 265 return _sibling(el, 'previousElementSibling', TAG_NAME, tagName);
4bbf6ff1
AE
266 }
267 };
565853e8 268});