themes: generalize indicator markup and styling
[project/luci.git] / themes / luci-theme-openwrt-2020 / luasrc / view / themes / openwrt2020 / header.htm
1 <%#
2 Copyright 2020 Jo-Philipp Wich <jo@mein.io>
3 Licensed to the public under the Apache License 2.0.
4 -%>
5
6 <%
7 local sys = require "luci.sys"
8 local util = require "luci.util"
9 local http = require "luci.http"
10 local disp = require "luci.dispatcher"
11 local ver = require "luci.version"
12
13 local boardinfo = util.ubus("system", "board") or { }
14
15 local node = disp.context.dispatched
16 local path = table.concat(disp.context.path, "-")
17
18 http.prepare_content("text/html; charset=UTF-8")
19 -%>
20
21 <html lang="<%=luci.i18n.context.lang%>">
22 <head>
23 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
24 <meta http-equiv="Content-Script-Type" content="text/javascript" />
25 <meta name="viewport" content="width=device-width, initial-scale=1" />
26 <link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />
27 <link rel="icon" href="<%=media%>/favicon.png" type="image/svg+xml" />
28 <script type="text/javascript" src="<%=url('admin/translations', luci.i18n.context.lang)%><%# ?v=PKG_VERSION %>"></script>
29 <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
30 <script type="text/javascript">//<![CDATA[
31 (function() {
32 function get_children(node) {
33 var children = [];
34
35 for (var k in node.children) {
36 if (!node.children.hasOwnProperty(k))
37 continue;
38
39 if (!node.children[k].satisfied)
40 continue;
41
42 if (!node.children[k].hasOwnProperty('title'))
43 continue;
44
45 children.push(Object.assign(node.children[k], { name: k }));
46 }
47
48 return children.sort(function(a, b) {
49 return ((a.order || 1000) - (b.order || 1000));
50 });
51 }
52
53 function handle_mainmenu_expand(ev) {
54 var a = ev.target, ul1 = a.parentNode.parentNode, ul2 = a.nextElementSibling;
55
56 document.querySelectorAll('ul.mainmenu.l1 > li.active').forEach(function(li) {
57 if (li !== a.parentNode)
58 li.classList.remove('active');
59 });
60
61 if (!ul2)
62 return;
63
64 if (ul2.parentNode.offsetLeft + ul2.offsetWidth <= ul1.offsetLeft + ul1.offsetWidth)
65 ul2.classList.add('align-left');
66
67 ul1.classList.add('active');
68 a.parentNode.classList.add('active');
69 a.blur();
70
71 ev.preventDefault();
72 ev.stopPropagation();
73 }
74
75 function render_mainmenu(tree, url, level) {
76 var l = (level || 0) + 1,
77 ul = E('ul', { 'class': 'mainmenu l%d'.format(l) }),
78 children = get_children(tree);
79
80 if (children.length == 0 || l > 2)
81 return E([]);
82
83 for (var i = 0; i < children.length; i++) {
84 var isActive = (L.env.dispatchpath[l] == children[i].name),
85 activeClass = 'mainmenu-item-%s%s'.format(children[i].name, isActive ? ' selected' : '');
86
87 ul.appendChild(E('li', { 'class': activeClass }, [
88 E('a', {
89 'href': L.url(url, children[i].name),
90 'click': (l == 1) ? handle_mainmenu_expand : null,
91 }, [ _(children[i].title) ]),
92 render_mainmenu(children[i], url + '/' + children[i].name, l)
93 ]));
94 }
95
96 if (l == 1) {
97 var container = document.querySelector('#mainmenu');
98
99 container.firstElementChild.appendChild(ul);
100 container.style.display = '';
101 }
102
103 return ul;
104 }
105
106 function render_modemenu(tree) {
107 var menu = document.querySelector('#modemenu'),
108 children = get_children(tree);
109
110 for (var i = 0; i < children.length; i++) {
111 var isActive = (L.env.requestpath.length ? children[i].name == L.env.requestpath[0] : i == 0);
112
113 if (i > 0)
114 menu.appendChild(E([], ['\u00a0|\u00a0']));
115
116 menu.appendChild(E('div', { 'class': isActive ? 'active' : null }, [
117 E('a', { 'href': L.url(children[i].name) }, [ _(children[i].title) ])
118 ]));
119
120 if (isActive)
121 render_mainmenu(children[i], children[i].name);
122 }
123
124 if (menu.children.length > 1)
125 menu.style.display = '';
126 }
127
128 function render_tabmenu(tree, url, level) {
129 var container = document.querySelector('#tabmenu'),
130 l = (level || 0) + 1,
131 ul = E('ul', { 'class': 'cbi-tabmenu' }),
132 children = get_children(tree),
133 activeNode = null;
134
135 if (children.length == 0)
136 return E([]);
137
138 for (var i = 0; i < children.length; i++) {
139 var isActive = (L.env.dispatchpath[l + 2] == children[i].name),
140 activeClass = isActive ? ' cbi-tab' : '',
141 className = 'tabmenu-item-%s %s'.format(children[i].name, activeClass);
142
143 ul.appendChild(E('li', { 'class': className }, [
144 E('a', { 'href': L.url(url, children[i].name) }, [ _(children[i].title) ] )
145 ]));
146
147 if (isActive)
148 activeNode = children[i];
149 }
150
151 container.appendChild(ul);
152 container.style.display = '';
153
154 if (activeNode)
155 container.appendChild(render_tabmenu(activeNode, url + '/' + activeNode.name, l));
156
157 return ul;
158 }
159
160 function toggle_sidebar(ev) {
161 var btn = ev.currentTarget,
162 bar = document.querySelector('#mainmenu');
163
164 if (btn.classList.contains('active')) {
165 btn.classList.remove('active');
166 bar.classList.remove('active');
167 }
168 else {
169 btn.classList.add('active');
170 bar.classList.add('active');
171 }
172 }
173
174 document.addEventListener('luci-loaded', function(ev) {
175 var tree = <%= luci.http.write_json(luci.dispatcher.menu_json() or {}) %>,
176 node = tree,
177 url = '';
178
179 render_modemenu(tree);
180
181 if (L.env.dispatchpath.length >= 3) {
182 for (var i = 0; i < 3 && node; i++) {
183 node = node.children[L.env.dispatchpath[i]];
184 url = url + (url ? '/' : '') + L.env.dispatchpath[i];
185 }
186
187 if (node)
188 render_tabmenu(node, url);
189 }
190
191 document.querySelector('#menubar > .navigation').addEventListener('click', toggle_sidebar);
192 });
193 })();
194 //]]></script>
195 <title><%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI</title>
196 </head>
197 <body class="lang_<%=luci.i18n.context.lang%>" data-page="<%= pcdata(path) %>">
198
199 <p class="skiplink">
200 <span id="skiplink1"><a href="#navigation"><%:Skip to navigation%></a></span>
201 <span id="skiplink2"><a href="#content"><%:Skip to content%></a></span>
202 </p>
203
204 <div id="menubar">
205 <h2 class="navigation"><a id="navigation" name="navigation"><%:Navigation%></a></h2>
206
207 <span class="hostname"><%=(boardinfo.hostname or "?")%></span>
208 <span class="distversion"><%=ver.distversion%></span>
209 <span id="indicators">
210 <span id="xhr_poll_status" style="display:none" onclick="XHR.running() ? XHR.halt() : XHR.run()">
211 <span id="xhr_poll_status_on" style="display:none"><%:Refreshing%></span>
212 <span id="xhr_poll_status_off" style="display:none"><%:Paused%></span>
213 </span>
214 </span>
215 </div>
216
217 <div id="modemenu" style="display:none"></div>
218
219 <div id="maincontainer">
220 <div id="mainmenu" style="display:none">
221 <div></div>
222 </div>
223
224 <div id="maincontent">
225 <%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") and category ~= "failsafe" and path ~= "admin-system-admin-password" then -%>
226 <div class="alert-message warning">
227 <h4><%:No password set!%></h4>
228 <p><%:There is no password set on this router. Please configure a root password to protect the web interface and enable SSH.%></p>
229 <% if disp.lookup("admin/system/admin") then %>
230 <div class="right"><a class="btn" href="<%=url("admin/system/admin")%>"><%:Go to password configuration...%></a></div>
231 <% end %>
232 </div>
233 <%- end -%>
234
235 <div id="tabmenu" style="display:none"></div>