1 (function(window
, document
, undefined) {
9 /* URL construction helpers */
10 path: function(prefix
, parts
) {
11 var url
= [ prefix
|| '' ];
13 for (var i
= 0; i
< parts
.length
; i
++)
14 if (/^(?:[a-zA-Z0-9_.%,;-]+\/)*[a-zA-Z0-9_.%,;-]+$/.test(parts
[i
]))
15 url
.push('/', parts
[i
]);
24 return this.path(this.env
.scriptname
, arguments
);
27 resource: function() {
28 return this.path(this.env
.resource
, arguments
);
31 location: function() {
32 return this.path(this.env
.scriptname
, this.env
.requestpath
);
36 /* HTTP resource fetching */
37 get: function(url
, args
, cb
) {
38 return this.poll(0, url
, args
, cb
, false);
41 post: function(url
, args
, cb
) {
42 return this.poll(0, url
, args
, cb
, true);
45 poll: function(interval
, url
, args
, cb
, post
) {
46 var data
= post
? { token
: this.env
.token
} : null;
48 if (!/^(?:\/|\S+:\/\/)/.test(url
))
51 if (typeof(args
) === 'object' && args
!== null) {
55 if (args
.hasOwnProperty(key
))
56 switch (typeof(args
[key
])) {
60 data
[key
] = args
[key
];
64 data
[key
] = JSON
.stringify(args
[key
]);
70 return XHR
.poll(interval
, url
, data
, cb
, post
);
72 return XHR
.post(url
, data
, cb
);
74 return XHR
.get(url
, data
, cb
);
77 halt: function() { XHR
.halt() },
78 run: function() { XHR
.run() },
82 showModal: function(title
, children
) {
83 var dlg
= modalDiv
.firstElementChild
;
85 dlg
.setAttribute('class', 'modal');
87 this.dom
.content(dlg
, this.dom
.create('h4', {}, title
));
88 this.dom
.append(dlg
, children
);
90 document
.body
.classList
.add('modal-overlay-active');
95 hideModal: function() {
96 document
.body
.classList
.remove('modal-overlay-active');
101 showTooltip: function(ev
) {
102 var target
= findParent(ev
.target
, '[data-tooltip]');
107 if (tooltipTimeout
!== null) {
108 window
.clearTimeout(tooltipTimeout
);
109 tooltipTimeout
= null;
112 var rect
= target
.getBoundingClientRect(),
113 x
= rect
.left
+ window
.pageXOffset
,
114 y
= rect
.top
+ rect
.height
+ window
.pageYOffset
;
116 tooltipDiv
.className
= 'cbi-tooltip';
117 tooltipDiv
.innerHTML
= '▲ ';
118 tooltipDiv
.firstChild
.data
+= target
.getAttribute('data-tooltip');
120 if (target
.hasAttribute('data-tooltip-style'))
121 tooltipDiv
.classList
.add(target
.getAttribute('data-tooltip-style'));
123 if ((y
+ tooltipDiv
.offsetHeight
) > (window
.innerHeight
+ window
.pageYOffset
)) {
124 y
-= (tooltipDiv
.offsetHeight
+ target
.offsetHeight
);
125 tooltipDiv
.firstChild
.data
= '▼ ' + tooltipDiv
.firstChild
.data
.substr(2);
128 tooltipDiv
.style
.top
= y
+ 'px';
129 tooltipDiv
.style
.left
= x
+ 'px';
130 tooltipDiv
.style
.opacity
= 1;
133 hideTooltip: function(ev
) {
134 if (ev
.target
=== tooltipDiv
|| ev
.relatedTarget
=== tooltipDiv
)
137 if (tooltipTimeout
!== null) {
138 window
.clearTimeout(tooltipTimeout
);
139 tooltipTimeout
= null;
142 tooltipDiv
.style
.opacity
= 0;
143 tooltipTimeout
= window
.setTimeout(function() { tooltipDiv
.removeAttribute('style'); }, 250);
148 itemlist: function(node
, items
, separators
) {
151 if (!Array
.isArray(separators
))
152 separators
= [ separators
|| E('br') ];
154 for (var i
= 0; i
< items
.length
; i
+= 2) {
155 if (items
[i
+1] !== null && items
[i
+1] !== undefined) {
156 var sep
= separators
[(i
/2) % separators
.length
],
159 children
.push(E('span', { class: 'nowrap' }, [
160 items
[i
] ? E('strong', items
[i
] + ': ') : '',
164 if ((i
+2) < items
.length
)
165 children
.push(this.dom
.elem(sep
) ? sep
.cloneNode(true) : sep
);
169 this.dom
.content(node
, children
);
175 /* DOM manipulation */
176 LuCI
.prototype.dom
= {
178 return (typeof(e
) === 'object' && e
!== null && 'nodeType' in e
);
185 domParser
= domParser
|| new DOMParser();
186 elem
= domParser
.parseFromString(s
, 'text/html').body
.firstChild
;
192 dummyElem
= dummyElem
|| document
.createElement('div');
193 dummyElem
.innerHTML
= s
;
194 elem
= dummyElem
.firstChild
;
202 matches: function(node
, selector
) {
203 var m
= this.elem(node
) ? node
.matches
|| node
.msMatchesSelector
: null;
204 return m
? m
.call(node
, selector
) : false;
207 parent: function(node
, selector
) {
208 if (this.elem(node
) && node
.closest
)
209 return node
.closest(selector
);
211 while (this.elem(node
))
212 if (this.matches(node
, selector
))
215 node
= node
.parentNode
;
220 append: function(node
, children
) {
221 if (!this.elem(node
))
224 if (Array
.isArray(children
)) {
225 for (var i
= 0; i
< children
.length
; i
++)
226 if (this.elem(children
[i
]))
227 node
.appendChild(children
[i
]);
228 else if (children
!== null && children
!== undefined)
229 node
.appendChild(document
.createTextNode('' + children
[i
]));
231 return node
.lastChild
;
233 else if (typeof(children
) === 'function') {
234 return this.append(node
, children(node
));
236 else if (this.elem(children
)) {
237 return node
.appendChild(children
);
239 else if (children
!== null && children
!== undefined) {
240 node
.innerHTML
= '' + children
;
241 return node
.lastChild
;
247 content: function(node
, children
) {
248 if (!this.elem(node
))
251 while (node
.firstChild
)
252 node
.removeChild(node
.firstChild
);
254 return this.append(node
, children
);
257 attr: function(node
, key
, val
) {
258 if (!this.elem(node
))
263 if (typeof(key
) === 'object' && key
!== null)
265 else if (typeof(key
) === 'string')
266 attr
= {}, attr
[key
] = val
;
269 if (!attr
.hasOwnProperty(key
) || attr
[key
] === null || attr
[key
] === undefined)
272 switch (typeof(attr
[key
])) {
274 node
.addEventListener(key
, attr
[key
]);
278 node
.setAttribute(key
, JSON
.stringify(attr
[key
]));
282 node
.setAttribute(key
, attr
[key
]);
288 var html
= arguments
[0],
289 attr
= (arguments
[1] instanceof Object
&& !Array
.isArray(arguments
[1])) ? arguments
[1] : null,
290 data
= attr
? arguments
[2] : arguments
[1],
295 else if (html
.charCodeAt(0) === 60)
296 elem
= this.parse(html
);
298 elem
= document
.createElement(html
);
303 this.attr(elem
, attr
);
304 this.append(elem
, data
);
313 modalDiv
= document
.body
.appendChild(this.dom
.create('div', { id
: 'modal_overlay' }, this.dom
.create('div', { class: 'modal' })));
314 tooltipDiv
= document
.body
.appendChild(this.dom
.create('div', { class: 'cbi-tooltip' }));
316 document
.addEventListener('mouseover', this.showTooltip
.bind(this), true);
317 document
.addEventListener('mouseout', this.hideTooltip
.bind(this), true);
318 document
.addEventListener('focus', this.showTooltip
.bind(this), true);
319 document
.addEventListener('blur', this.hideTooltip
.bind(this), true);
323 })(window
, document
);