Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • firmware/gluon
  • 0x4A6F/gluon
  • patrick/gluon
3 results
Select Git revision
Loading items
Show changes
Showing
with 357 additions and 213 deletions
......@@ -7,7 +7,7 @@
<%- end -%>
<% if self.subtemplate then include(self.subtemplate) end %>
<% if self.description and #self.description > 0 then -%>
<br />
<br>
<div class="gluon-value-description">
<%=self.description%>
</div>
......
<%- if not self.hide then -%>
<div class="gluon-warning"<%=
attr("id", id) ..
attr("data-index", self.index) ..
attr("data-depends", self:deplist(self.deps))
%>>
<%- if self.content then -%>
<%= self.content %>
<%- else -%>
<b><%= self.title %></b><br>
<%= self.description %>
<%- end -%>
</div>
<%- end -%>
!function(){var f={};function a(e){return/^-?\d+$/.test(e)?+e:NaN}function r(e){return/^-?\d*\.?\d+?$/.test(e)?+e:NaN}var u={integer:function(){return!isNaN(a(this))},uinteger:function(){return 0<=a(this)},float:function(){return!isNaN(r(this))},ufloat:function(){return 0<=r(this)},ipaddr:function(){return u.ip4addr.apply(this)||u.ip6addr.apply(this)},ip4addr:function(){var e;return!!(e=this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))&&(0<=e[1]&&e[1]<=255&&0<=e[2]&&e[2]<=255&&0<=e[3]&&e[3]<=255&&0<=e[4]&&e[4]<=255)},ip6addr:function(){return this.indexOf("::")<0?null!=this.match(/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i):!(0<=this.indexOf(":::")||this.match(/::.+::/)||this.match(/^:[^:]/)||this.match(/[^:]:$/))&&(!!this.match(/^(?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}$/i)||(!!this.match(/^(?:[a-f0-9]{1,4}:){7}:$/i)||!!this.match(/^:(?::[a-f0-9]{1,4}){7}$/i)))},wpakey:function(){var e=this;return 64==e.length?null!=e.match(/^[a-f0-9]{64}$/i):8<=e.length&&e.length<=63},range:function(e,t){var n=r(this);return+e<=n&&n<=+t},min:function(e){return r(this)>=+e},max:function(e){return r(this)<=+e},irange:function(e,t){var n=a(this);return+e<=n&&n<=+t},imin:function(e){return a(this)>=+e},imax:function(e){return a(this)<=+e},minlength:function(e){return+e<=(""+this).length},maxlength:function(e){return(""+this).length<=+e}};function o(e){for(var t=0;t<e.length;t++){var n=!0;for(var a in e[t])n=n&&(r=a,i=e[t][a],o=void 0,(o=document.getElementById(r))?("checkbox"==o.type?o.checked:o.value?o.value:"")==i:!!(o=document.getElementById(r+"."+i))&&"radio"==o.type&&o.checked);if(n)return!0}var r,i,o;return!1}function v(){window.dispatchEvent(new Event("gluon-update"));var e=!1;for(var t in f){var n=f[t],a=document.getElementById(t),r=document.getElementById(n.parent);if(a&&a.parentNode&&!o(n.deps))a.parentNode.removeChild(a),a.dispatchEvent(new Event("gluon-hide")),e=!0;else if(r&&(!a||!a.parentNode)&&o(n.deps)){var i=void 0;for(i=r.firstChild;i&&!(i.getAttribute&&parseInt(i.getAttribute("data-index"),10)>n.index);i=i.nextSibling);i?r.insertBefore(n.node,i):r.appendChild(n.node),n.node.dispatchEvent(new Event("gluon-show")),e=!0}r&&r.parentNode&&r.getAttribute("data-optionals")&&(r.parentNode.style.display=r.options.length<=1?"none":"")}e&&v()}function h(e,t,n,a){return e.addEventListener?e.addEventListener(t,n,!!a):e.attachEvent("on"+t,function(){var e=window.event;return!e.target&&e.srcElement&&(e.target=e.srcElement),!!n(e)}),e}function g(l,s){var c=s.prefix;function o(e,t,n){for(var a=[];l.firstChild;){var r=l.firstChild;(i=+r.index)!=n&&("input"==r.nodeName.toLowerCase()?a.push(r.value||""):"select"==r.nodeName.toLowerCase()&&(a[a.length-1]=r.options[r.selectedIndex].value)),l.removeChild(r)}0<=t?(e=t+1,a.splice(t,0,"")):s.optional||0!=a.length||a.push("");for(var i=1;i<=a.length;i++){var o=document.createElement("input");if(o.id=c+"."+i,o.name=c,o.value=a[i-1],o.type="text",o.index=i,o.className="gluon-input-text",s.size&&(o.size=s.size),s.placeholder&&(o.placeholder=s.placeholder),l.appendChild(o),s.type&&m(o,!1,s.type),h(o,"keydown",f),h(o,"keypress",p),i==e)o.focus();else if(-i==e){o.focus();var d=o.value;o.value=" ",o.value=d}if(s.optional||1<a.length)(u=document.createElement("span")).className="gluon-remove",l.appendChild(u),h(u,"click",v(!1)),l.appendChild(document.createElement("br"))}var u;(u=document.createElement("span")).className="gluon-add",l.appendChild(u),h(u,"click",v(!0))}function p(e){var t=(e=e||window.event).target?e.target:e.srcElement;switch(3==t.nodeType&&(t=t.parentNode),e.keyCode){case 8:case 46:return 0!=t.value.length||(e.preventDefault&&e.preventDefault(),!1);case 13:case 38:case 40:return e.preventDefault&&e.preventDefault(),!1}return!0}function f(e){var t,n,a=(e=e||window.event).target?e.target:e.srcElement,r=0;if(a){for(3==a.nodeType&&(a=a.parentNode),r=a.index,t=a.previousSibling;t&&t.name!=c;)t=t.previousSibling;for(n=a.nextSibling;n&&n.name!=c;)n=n.nextSibling}switch(e.keyCode){case 8:case 46:if("select"==a.nodeName.toLowerCase()||0==a.value.length){e.preventDefault&&e.preventDefault();var i=a.index;return 8==e.keyCode&&(i=1-i),o(i,-1,r),!1}break;case 13:o(-1,r,-1);break;case 38:t&&t.focus();break;case 40:n&&n.focus()}return!0}function v(n){return function(e){for(var t=((e=e||window.event).target?e.target:e.srcElement).previousSibling;t&&t.name!=c;)t=t.previousSibling;return n?f({target:t,keyCode:13}):(t.value="",f({target:t,keyCode:8})),!1}}o(NaN,-1,-1)}function m(t,n,e){var a,r,i,o=(i=(a=e).match(/^([^\(]+)\(([^,]+),([^\)]+)\)$/))&&void 0!==(r=u[i[1]])?function(){return r.apply(this,[i[2],i[3]])}:(i=a.match(/^([^\(]+)\(([^,\)]+)\)$/))&&void 0!==(r=u[i[1]])?function(){return r.apply(this,[i[2]])}:u[a];if(o){var d=function(){if(t.form){t.className=t.className.replace(/ gluon-input-invalid/g,"");var e=t.options&&-1<t.options.selectedIndex?t.options[t.options.selectedIndex].value:t.value;0==e.length&&n||o.apply(e)||(t.className+=" gluon-input-invalid")}};h(t,"blur",d),h(t,"keyup",d),h(t,"gluon-revalidate",d),"select"==t.nodeName.toLowerCase()&&(h(t,"change",d),h(t,"click",d)),d()}}!function(){var e,t,n,a,r;e=document.querySelectorAll("[data-depends]");for(var i=0;void 0!==(p=e[i]);i++){var o=parseInt(p.getAttribute("data-index"),10),d=JSON.parse(p.getAttribute("data-depends"));if(!isNaN(o)&&0<d.length)for(var u=0;u<d.length;u++)t=p,n=d[u],a=o,r=void 0,(r=f[t.id])||(r={node:t,parent:t.parentNode.id,deps:[],index:a},f[t.id]=r),r.deps.push(n)}e=document.querySelectorAll("[data-update]");for(i=0;void 0!==(p=e[i]);i++)for(var l,s=p.getAttribute("data-update").split(" "),c=0;void 0!==(l=s[c]);c++)h(p,l,function(){setTimeout(v,0)});e=document.querySelectorAll("[data-type]");for(i=0;void 0!==(p=e[i]);i++)m(p,"true"===p.getAttribute("data-optional"),p.getAttribute("data-type"));e=document.querySelectorAll("[data-dynlist]");var p;for(i=0;void 0!==(p=e[i]);i++){g(p,JSON.parse(p.getAttribute("data-dynlist")))}v()}()}();
......@@ -10,12 +10,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "One or more fields contain invalid values!"
msgstr "Ein oder mehrere Felder enthalten ungültige Werte!"
msgid "One or more required fields have no value!"
msgstr "Ein oder mehr benötigte Felder sind nicht ausgefüllt!"
msgid "Reset"
msgstr "Zurücksetzen"
......
......@@ -10,12 +10,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "One or more fields contain invalid values!"
msgstr "Un ou plusieurs champs contiennent des valeurs incorrectes !"
msgid "One or more required fields have no value!"
msgstr "Un ou plusieurs champs n'ont pas de valeur !"
msgid "Reset"
msgstr "Remise à zéro"
......
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "One or more fields contain invalid values!"
msgstr ""
msgid "One or more required fields have no value!"
msgstr ""
msgid "Reset"
msgstr ""
......
/*
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008-2012 Jo-Philipp Wich <jow@openwrt.org>
Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
SPDX-License-Identifier: Apache-2.0
SPDX-FileCopyrightText: 2008, Steven Barth <steven@midlink.org>
SPDX-FileCopyrightText: 2008-2012, Jo-Philipp Wich <jow@openwrt.org>
SPDX-FileCopyrightText: 2017, Matthias Schiffer <mschiffer@universe-factory.net>
SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
*/
/*
Build using:
uglifyjs javascript/gluon-web-model.js -o files/lib/gluon/web/www/static/gluon-web-model.js -c -m --support-ie8
uglifyjs javascript/gluon-web-model.js -o javascript/gluon-web-model.min.js -c -m --ie
*/
......@@ -219,6 +215,20 @@
parent.parentNode.style.display = (parent.options.length <= 1) ? 'none' : '';
}
var nodes = document.querySelectorAll('[data-exclusive-with]');
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
var exclusive_with = JSON.parse(node.getAttribute('data-exclusive-with'));
node.disabled = false;
for (var list_item of exclusive_with) {
var el = document.getElementById(node.name + '.' + list_item);
node.disabled ||= el.checked;
}
if (node.disabled)
node.checked = false;
}
if (state) {
update();
}
......@@ -276,7 +286,6 @@
t.value = values[i-1];
t.type = 'text';
t.index = i;
t.className = 'gluon-input-text';
if (attr.size)
t.size = attr.size;
......
!function(){var s={};function a(e){return/^-?\d+$/.test(e)?+e:NaN}function i(e){return/^-?\d*\.?\d+?$/.test(e)?+e:NaN}var d={integer:function(){return!isNaN(a(this))},uinteger:function(){return 0<=a(this)},"float":function(){return!isNaN(i(this))},ufloat:function(){return 0<=i(this)},ipaddr:function(){return d.ip4addr.apply(this)||d.ip6addr.apply(this)},ip4addr:function(){var e;return!!(e=this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))&&0<=e[1]&&e[1]<=255&&0<=e[2]&&e[2]<=255&&0<=e[3]&&e[3]<=255&&0<=e[4]&&e[4]<=255},ip6addr:function(){return this.indexOf("::")<0?null!=this.match(/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i):!(0<=this.indexOf(":::")||this.match(/::.+::/)||this.match(/^:[^:]/)||this.match(/[^:]:$/)||!this.match(/^(?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}$/i)&&!this.match(/^(?:[a-f0-9]{1,4}:){7}:$/i)&&!this.match(/^:(?::[a-f0-9]{1,4}){7}$/i))},wpakey:function(){var e=this;return 64==e.length?null!=e.match(/^[a-f0-9]{64}$/i):8<=e.length&&e.length<=63},range:function(e,t){var n=i(this);return+e<=n&&n<=+t},min:function(e){return i(this)>=+e},max:function(e){return i(this)<=+e},irange:function(e,t){var n=a(this);return+e<=n&&n<=+t},imin:function(e){return a(this)>=+e},imax:function(e){return a(this)<=+e},minlength:function(e){return+e<=(""+this).length},maxlength:function(e){return(""+this).length<=+e}};function p(e){for(var t,n,a,i=0;i<e.length;i++){var r,d=!0;for(r in e[i])d=d&&(t=r,n=e[i][r],a=void 0,(a=document.getElementById(t))?("checkbox"==a.type?a.checked:a.value||"")==n:!!(a=document.getElementById(t+"."+n))&&"radio"==a.type&&a.checked);if(d)return 1}}function f(){window.dispatchEvent(new Event("gluon-update"));var e,t=!1;for(e in s){var n=s[e],a=document.getElementById(e),i=document.getElementById(n.parent);if(a&&a.parentNode&&!p(n.deps))a.parentNode.removeChild(a),a.dispatchEvent(new Event("gluon-hide")),t=!0;else if(i&&(!a||!a.parentNode)&&p(n.deps)){for(var r=undefined,r=i.firstChild;r&&!(r.getAttribute&&parseInt(r.getAttribute("data-index"),10)>n.index);r=r.nextSibling);r?i.insertBefore(n.node,r):i.appendChild(n.node),n.node.dispatchEvent(new Event("gluon-show")),t=!0}i&&i.parentNode&&i.getAttribute("data-optionals")&&(i.parentNode.style.display=i.options.length<=1?"none":"")}for(var d=document.querySelectorAll("[data-exclusive-with]"),o=0;(a=d[o])!==undefined;o++){var u,l=JSON.parse(a.getAttribute("data-exclusive-with"));a.disabled=!1;for(u of l){var c=document.getElementById(a.name+"."+u);a.disabled||=c.checked}a.disabled&&(a.checked=!1)}t&&f()}function v(e,t,n,a){e.addEventListener?e.addEventListener(t,n,!!a):e.attachEvent("on"+t,function(){var e=window.event;return!e.target&&e.srcElement&&(e.target=e.srcElement),!!n(e)})}function m(t,n,e){var a,i,r=(i=(e=e).match(/^([^\(]+)\(([^,]+),([^\)]+)\)$/))&&(a=d[i[1]])!==undefined?function(){return a.apply(this,[i[2],i[3]])}:(i=e.match(/^([^\(]+)\(([^,\)]+)\)$/))&&(a=d[i[1]])!==undefined?function(){return a.apply(this,[i[2]])}:d[e];r&&(v(t,"blur",e=function(){var e;t.form&&(t.className=t.className.replace(/ gluon-input-invalid/g,""),0==(e=(t.options&&-1<t.options.selectedIndex?t.options[t.options.selectedIndex]:t).value).length&&n||r.apply(e)||(t.className+=" gluon-input-invalid"))}),v(t,"keyup",e),v(t,"gluon-revalidate",e),"select"==t.nodeName.toLowerCase()&&(v(t,"change",e),v(t,"click",e)),e())}for(var e,t,n,r,o=document.querySelectorAll("[data-depends]"),u=0;(b=o[u])!==undefined;u++){var l=parseInt(b.getAttribute("data-index"),10),c=JSON.parse(b.getAttribute("data-depends"));if(!isNaN(l)&&0<c.length)for(var h=0;h<c.length;h++)e=b,t=c[h],n=l,r=void 0,(r=s[e.id])||(r={node:e,parent:e.parentNode.id,deps:[],index:n},s[e.id]=r),r.deps.push(t)}for(o=document.querySelectorAll("[data-update]"),u=0;(b=o[u])!==undefined;u++)for(var g,y=b.getAttribute("data-update").split(" "),N=0;(g=y[N])!==undefined;N++)v(b,g,function(){setTimeout(f,0)});for(o=document.querySelectorAll("[data-type]"),u=0;(b=o[u])!==undefined;u++)m(b,"true"===b.getAttribute("data-optional"),b.getAttribute("data-type"));o=document.querySelectorAll("[data-dynlist]");for(var b,u=0;(b=o[u])!==undefined;u++){var w=JSON.parse(b.getAttribute("data-dynlist"));!function(l,c){var s=c.prefix;function d(e,t,n){for(var a=[];l.firstChild;){var i=l.firstChild;(r=+i.index)!=n&&("input"==i.nodeName.toLowerCase()?a.push(i.value||""):"select"==i.nodeName.toLowerCase()&&(a[a.length-1]=i.options[i.selectedIndex].value)),l.removeChild(i)}0<=t?(e=t+1,a.splice(t,0,"")):c.optional||0!=a.length||a.push("");for(var r=1;r<=a.length;r++){var d,o,u=document.createElement("input");u.id=s+"."+r,u.name=s,u.value=a[r-1],u.type="text",u.index=r,c.size&&(u.size=c.size),c.placeholder&&(u.placeholder=c.placeholder),l.appendChild(u),c.type&&m(u,!1,c.type),v(u,"keydown",f),v(u,"keypress",p),r==e?u.focus():-r==e&&(u.focus(),d=u.value,u.value=" ",u.value=d),(c.optional||1<a.length)&&((o=document.createElement("span")).className="gluon-remove",l.appendChild(o),v(o,"click",h(!1)),l.appendChild(document.createElement("br")))}(o=document.createElement("span")).className="gluon-add",l.appendChild(o),v(o,"click",h(!0))}function p(e){var t=(e=e||window.event).target||e.srcElement;switch(3==t.nodeType&&(t=t.parentNode),e.keyCode){case 8:case 46:return 0==t.value.length?(e.preventDefault&&e.preventDefault(),!1):!0;case 13:case 38:case 40:return e.preventDefault&&e.preventDefault(),!1}return!0}function f(e){var t,n,a,i=(e=e||window.event).target||e.srcElement,r=0;if(i){for(r=(i=3==i.nodeType?i.parentNode:i).index,t=i.previousSibling;t&&t.name!=s;)t=t.previousSibling;for(n=i.nextSibling;n&&n.name!=s;)n=n.nextSibling}switch(e.keyCode){case 8:case 46:if("select"==i.nodeName.toLowerCase()||0==i.value.length)return e.preventDefault&&e.preventDefault(),a=i.index,d(a=8==e.keyCode?1-a:a,-1,r),!1;break;case 13:d(-1,r,-1);break;case 38:t&&t.focus();break;case 40:n&&n.focus()}return!0}function h(n){return function(e){for(var t=((e=e||window.event).target||e.srcElement).previousSibling;t&&t.name!=s;)t=t.previousSibling;return n?f({target:t,keyCode:13}):(t.value="",f({target:t,keyCode:8})),!1}}d(NaN,-1,-1)}(b,w)}f()}();
\ No newline at end of file
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
-- SPDX-License-Identifier: Apache-2.0
-- SPDX-FileCopyrightText: 2008, Steven Barth <steven@midlink.org>
-- SPDX-FileCopyrightText: 2017-2018, Matthias Schiffer <mschiffer@universe-factory.net>
-- SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
local util = require "gluon.web.util"
local gluon_util = require "gluon.util"
local datatypes = require "gluon.web.model.datatypes"
local class = util.class
......@@ -47,11 +49,13 @@ M.Node = Node
function Node:__init__(name, title, description)
self.children = {}
self.deps = {}
self.title = title or ""
self.description = description or ""
self.name = name
self.index = nil
self.parent = nil
self.state = M.FORM_NODATA
self.package = 'gluon-web-model'
end
......@@ -71,12 +75,33 @@ function Node:id()
return prefix.."."..self:id_suffix()
end
function Node:reset_node()
self.state = M.FORM_NODATA
for _, child in ipairs(self.children) do
child:reset_node()
end
end
function Node:parse(http)
self.state = M.FORM_VALID
for _, child in ipairs(self.children) do
child:parse(http)
end
end
function Node:propagate_state()
if self.state == M.FORM_NODATA then
return
end
for _, child in ipairs(self.children) do
child:propagate_state()
if child.state == M.FORM_INVALID then
self.state = M.FORM_INVALID
end
end
end
function Node:render(renderer, scope)
if self.template then
local env = setmetatable({
......@@ -94,18 +119,74 @@ function Node:render_children(renderer, scope)
end
end
function Node:depends(field, value)
local deps
if instanceof(field, Node) then
deps = { [field] = value }
else
deps = field
end
table.insert(self.deps, deps)
end
function Node:deplist(deplist)
local deps = {}
for _, d in ipairs(deplist or self.deps) do
local a = {}
for k, v in pairs(d) do
a[k:id()] = v
end
table.insert(deps, a)
end
if next(deps) then
return deps
end
end
function Node:resolve_depends()
local updated = false
local updated = self:resolve_node_depends()
for _, node in ipairs(self.children) do
updated = updated or node:resolve_depends()
end
return updated
end
function Node:resolve_node_depends()
if #self.deps == 0 then
return false
end
for _, d in ipairs(self.deps) do
local valid = true
for k, v in pairs(d) do
if k.state ~= M.FORM_VALID or k.data ~= v then
valid = false
break
end
end
if valid then return false end
end
self:reset_node()
return true
end
-- will be overridden: write(value)
function Node:write()
end
function Node:handle()
if self.state == M.FORM_VALID then
for _, node in ipairs(self.children) do
node:handle()
end
self:write(self.data)
end
end
......@@ -117,13 +198,11 @@ function Template:__init__(template)
self.template = template
end
local AbstractValue = class(Node)
M.AbstractValue = AbstractValue
function AbstractValue:__init__(...)
Node.__init__(self, ...)
self.deps = {}
self.default = nil
self.size = nil
......@@ -131,34 +210,7 @@ function AbstractValue:__init__(...)
self.template = "model/valuewrapper"
self.state = M.FORM_NODATA
end
function AbstractValue:depends(field, value)
local deps
if instanceof(field, Node) then
deps = { [field] = value }
else
deps = field
end
table.insert(self.deps, deps)
end
function AbstractValue:deplist(_, deplist)
local deps = {}
for _, d in ipairs(deplist or self.deps) do
local a = {}
for k, v in pairs(d) do
a[k:id()] = v
end
table.insert(deps, a)
end
if next(deps) then
return deps
end
self.error = false
end
function AbstractValue:defaultvalue()
......@@ -177,23 +229,9 @@ function AbstractValue:cfgvalue()
end
end
function AbstractValue:add_error(type, msg)
self.error = msg or type
if type == "invalid" then
self.tag_invalid = true
elseif type == "missing" then
self.tag_missing = true
end
self.state = M.FORM_INVALID
end
function AbstractValue:reset()
self.error = nil
self.tag_invalid = nil
self.tag_missing = nil
function AbstractValue:reset_node()
self.data = nil
self.error = false
self.state = M.FORM_NODATA
end
......@@ -201,37 +239,21 @@ end
function AbstractValue:parse(http)
self.data = self:formvalue(http)
local ok, err = self:validate()
if not ok then
if type(self.data) ~= "string" or #self.data > 0 then
self:add_error("invalid", err)
else
self:add_error("missing", err)
end
if not self:validate() then
self.error = true
self.state = M.FORM_INVALID
return
end
self.state = M.FORM_VALID
end
function AbstractValue:resolve_depends()
if self.state == M.FORM_NODATA or #self.deps == 0 then
function AbstractValue:resolve_node_depends()
if self.state == M.FORM_NODATA then
return false
end
for _, d in ipairs(self.deps) do
local valid = true
for k, v in pairs(d) do
if k.state ~= M.FORM_VALID or k.data ~= v then
valid = false
break
end
end
if valid then return false end
end
self:reset()
return true
return Node.resolve_node_depends(self)
end
function AbstractValue:validate()
......@@ -251,16 +273,6 @@ function AbstractValue:validate()
end
function AbstractValue:handle()
if self.state == M.FORM_VALID then
self:write(self.data)
end
end
-- will be overridden: write(value)
function AbstractValue:write()
end
local Value = class(AbstractValue)
M.Value = Value
......@@ -351,6 +363,83 @@ function ListValue:validate()
end
local MultiListValue = class(AbstractValue)
M.MultiListValue = MultiListValue
function MultiListValue:__init__(...)
AbstractValue.__init__(self, ...)
self.subtemplate = "model/mlvalue"
self.size = 1
self.keys = {}
self.entry_list = {}
end
function MultiListValue:value(key, val, ...)
key = tostring(key)
if self.keys[key] then
return
end
self.keys[key] = true
self.exclusions = {}
val = val or key
table.insert(self.entry_list, {
key = key,
value = tostring(val),
deps = {...},
})
end
function MultiListValue:entries()
local ret = {unpack(self.entry_list)}
return ret
end
function MultiListValue:validate()
for _, val in ipairs(self.data) do
if not self.keys[val] then
return false
end
end
for key, exclusive_with in pairs(self.exclusions) do
if gluon_util.contains(self.data, key) then
for _, exclusion in ipairs(exclusive_with) do
if gluon_util.contains(self.data, exclusion) then
return false
end
end
end
end
return true
end
function MultiListValue:exclusive(a, b)
if not self.exclusions[a] then
self.exclusions[a] = {}
end
if not self.exclusions[b] then
self.exclusions[b] = {}
end
gluon_util.add_to_set(self.exclusions[a], b)
gluon_util.add_to_set(self.exclusions[b], a)
end
function MultiListValue:defaultvalue()
return self.default or {}
end
function MultiListValue:formvalue(http)
return http:formvaluetable(self:id())
end
local DynamicList = class(AbstractValue)
M.DynamicList = DynamicList
......@@ -400,6 +489,42 @@ function TextValue:__init__(...)
end
local Element = class(AbstractValue)
M.Element = Element
function Element:__init__(template, kv, ...)
AbstractValue.__init__(self, ...)
self.default = nil
self.size = nil
self.optional = false
self.template = template
for key, value in pairs(kv) do
self[key] = value
end
self.error = false
end
function Element:parse(http)
if not self.datatype then
self.state = M.FORM_VALID
return
end
return AbstractValue:parse(http)
end
function Element:validate()
if not self.datatype then
return true
end
AbstractValue:validate()
end
local Section = class(Node)
M.Section = Section
......@@ -416,6 +541,11 @@ function Section:option(t, ...)
return obj
end
function Section:element(...)
local obj = Element(...)
self:append(obj)
return obj
end
local Form = class(Node)
M.Form = Form
......@@ -439,26 +569,7 @@ function Form:parse(http)
while self:resolve_depends() do end
for _, s in ipairs(self.children) do
for _, v in ipairs(s.children) do
if v.state == M.FORM_INVALID then
self.state = M.FORM_INVALID
return
end
end
end
self.state = M.FORM_VALID
end
function Form:handle()
if self.state == M.FORM_VALID then
Node.handle(self)
self:write()
end
end
function Form:write()
self:propagate_state()
end
function Form:section(t, ...)
......
-- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
-- SPDX-License-Identifier: Apache-2.0
-- SPDX-FileCopyrightText: 2010, Jo-Philipp Wich <jow@openwrt.org>
-- SPDX-FileCopyrightText: 2017, Matthias Schiffer <mschiffer@universe-factory.net>
local M = {}
......
......@@ -4,8 +4,6 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-web-network
PKG_VERSION:=1
PKG_RELEASE:=1
include ../gluon.mk
......
......@@ -28,12 +28,6 @@ msgstr "PoE-Passthrough aktivieren"
msgid "Enable PoE Power Port %s"
msgstr "PoE-Ausgabe auf Port %s aktivieren"
msgid "Enable meshing on the LAN interface"
msgstr "Mesh auf dem LAN-Port aktivieren"
msgid "Enable meshing on the WAN interface"
msgstr "Mesh auf dem WAN-Port aktivieren"
msgid "Gateway"
msgstr "Gateway"
......@@ -46,6 +40,12 @@ msgstr "IPv4"
msgid "IPv6"
msgstr "IPv6"
msgid "Interface"
msgstr "Interface"
msgid "LAN Interfaces"
msgstr "LAN-Interfaces"
msgid "Netmask"
msgstr "Netzmaske"
......@@ -58,5 +58,8 @@ msgstr "Statisch"
msgid "Static DNS servers"
msgstr "Statische DNS-Server"
msgid "WAN Interfaces"
msgstr "WAN-Interfaces"
msgid "WAN connection"
msgstr "WAN-Verbindung"
......@@ -28,12 +28,6 @@ msgstr ""
msgid "Enable PoE Power Port %s"
msgstr ""
msgid "Enable meshing on the LAN interface"
msgstr "Activer le réseau MESH sur le port LAN"
msgid "Enable meshing on the WAN interface"
msgstr "Activer le réseau MESH sur les ports WAN"
msgid "Gateway"
msgstr "Passerelle"
......
......@@ -19,12 +19,6 @@ msgstr ""
msgid "Enable PoE Power Port %s"
msgstr ""
msgid "Enable meshing on the LAN interface"
msgstr ""
msgid "Enable meshing on the WAN interface"
msgstr ""
msgid "Gateway"
msgstr ""
......@@ -37,6 +31,12 @@ msgstr ""
msgid "IPv6"
msgstr ""
msgid "Interface"
msgstr ""
msgid "LAN Interfaces"
msgstr ""
msgid "Netmask"
msgstr ""
......@@ -49,5 +49,8 @@ msgstr ""
msgid "Static DNS servers"
msgstr ""
msgid "WAN Interfaces"
msgstr ""
msgid "WAN connection"
msgstr ""
--[[
Copyright 2014 Nils Schneider <nils@nilsschneider.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
-- SPDX-License-Identifier: Apache-2.0
-- SPDX-FileCopyrightText: 2014, Nils Schneider <nils@nilsschneider.net>
-- SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
local uci = require("simple-uci").cursor()
local sysconfig = require 'gluon.sysconfig'
local util = require 'gluon.util'
local wan = uci:get_all("network", "wan")
local wan6 = uci:get_all("network", "wan6")
local dns_static = uci:get_first("gluon-wan-dnsmasq", "static")
local f = Form(translate("WAN connection"))
local s = f:section(Section)
......@@ -76,35 +69,30 @@ end
s = f:section(Section)
local mesh_wan = s:option(Flag, "mesh_wan", translate("Enable meshing on the WAN interface"))
mesh_wan.default = not uci:get_bool("network", "mesh_wan", "disabled")
function mesh_wan:write(data)
uci:set("network", "mesh_wan", "disabled", not data)
end
if sysconfig.lan_ifname then
s = f:section(Section)
local pretty_ifnames = {
["/wan"] = translate("WAN Interfaces"),
["/single"] = translate("Interface"),
["/lan"] = translate("LAN Interfaces")
}
local mesh_lan = s:option(Flag, "mesh_lan", translate("Enable meshing on the LAN interface"))
mesh_lan.default = not uci:get_bool("network", "mesh_lan", "disabled")
uci:foreach('gluon', 'interface', function(config)
local section_name = config['.name']
local ifaces = s:option(MultiListValue, section_name, pretty_ifnames[config.name] or config.name)
function mesh_lan:write(data)
uci:set("network", "mesh_lan", "disabled", not data)
ifaces.orientation = 'horizontal'
ifaces:value('uplink', 'Uplink')
ifaces:value('mesh', 'Mesh')
ifaces:value('client', 'Client')
ifaces:exclusive('uplink', 'client')
ifaces:exclusive('mesh', 'client')
local interfaces = uci:get_list("network", "client", "ifname")
ifaces.default = config.role
for lanif in sysconfig.lan_ifname:gmatch('%S+') do
if data then
util.remove_from_set(interfaces, lanif)
else
util.add_to_set(interfaces, lanif)
end
function ifaces:write(data)
uci:set_list("gluon", section_name, "role", data)
end
end)
uci:set_list("network", "client", "ifname", interfaces)
end
end
local section
uci:foreach("system", "gpio_switch", function(si)
......@@ -114,15 +102,28 @@ uci:foreach("system", "gpio_switch", function(si)
end
local texts = {
["^PoE Power Port(%d*)$"] = function(m) return translatef("Enable PoE Power Port %s", m[1]) end,
["^PoE Passthrough$"] = function() return translate("Enable PoE Passthrough") end,
["^PoE Power Port(%d*)$"] = {
get_title = function(m) return translatef("Enable PoE Power Port %s", m[1]) end,
active_low = false,
},
["^PoE Passthrough$"] = {
get_title = function() return translate("Enable PoE Passthrough") end,
active_low = false,
},
-- Typo in AP-303H (ipq40xx-generic)
["^POE passtrough disable$"] = {
get_title = function() return translate("Enable PoE Passthrough") end,
active_low = true,
},
}
local name
for pattern, func in pairs(texts) do
local active_low = false
for pattern, text_obj in pairs(texts) do
local match = {si.name:match(pattern)}
if match[1] then
name = func(match)
name = text_obj.get_title(match)
active_low = text_obj.active_low
break
end
end
......@@ -132,9 +133,17 @@ uci:foreach("system", "gpio_switch", function(si)
local poe = section:option(Flag, si[".name"], name)
poe.default = uci:get_bool("system", si[".name"], "value")
if active_low then
poe.default = not poe.default
end
function poe:write(data)
uci:set("system", si[".name"], "value", data)
local write_value = data
if active_low then
write_value = not write_value
end
uci:set("system", si[".name"], "value", write_value)
end
end
end)
......@@ -160,9 +169,10 @@ function f:write()
uci:delete("network", "wan6", "ip6gw")
end
uci:commit('gluon')
uci:commit("network")
uci:commit('system')
end
return f
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-web-node-role
PKG_VERSION:=1
include ../gluon.mk
......
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-web-osm
PKG_VERSION:=1
include ../gluon.mk
......@@ -9,4 +8,17 @@ define Package/gluon-web-osm
TITLE:=base src for OSM inlay
endef
PKG_CONFIG_DEPENDS += CONFIG_GLUON_MINIFY
define Package/gluon-web-osm/install
$(Gluon/Build/Install)
$(INSTALL_DIR) $(1)/lib/gluon/web/www/static/
ifdef CONFIG_GLUON_MINIFY
$(INSTALL_DATA) ./javascript/gluon-web-osm.min.js $(1)/lib/gluon/web/www/static/gluon-web-osm.js
else
$(INSTALL_DATA) ./javascript/gluon-web-osm.js $(1)/lib/gluon/web/www/static/
endif
endef
$(eval $(call BuildPackageGluon,gluon-web-osm))
......@@ -15,7 +15,7 @@
<%- end %>
}, {once: true});
initOSM(<%=json(self.openlayers_url)%>, function(createMap) {
initOSM(<%=json(self.options)%>, function(createMap) {
elMap.style.display = '';
var pos = <%=json(self:cfgvalue().pos)%>;
......
"use strict";function initOSM(e,o){var t=document.createElement("link");t.rel="stylesheet",t.type="text/css",t.href=e+"/css/ol.css",document.head.appendChild(t);var n=document.createElement("script"),r=!1;n.onload=n.onreadystatechange=function(){if(!(r||this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)){r=!0;var t=new Image;t.onload=function(){var e=new ol.style.Style({image:new ol.style.Icon({img:t,imgSize:[30,45],anchor:[.5,1]})}),c=new ol.Feature;c.setStyle(e),o(function(e,t,o,n,r){var a=new ol.Map({target:e,layers:[new ol.layer.Tile({source:new ol.source.OSM}),new ol.layer.Vector({source:new ol.source.Vector({features:[c]})})],view:new ol.View({center:ol.proj.fromLonLat(t),zoom:o})}),l=function(e){c.setGeometry(new ol.geom.Point(e))};return a.addEventListener("click",function(e){l(e.coordinate),r(ol.proj.toLonLat(e.coordinate))}),n&&l(ol.proj.fromLonLat(t)),a})},t.src="data:image/svg+xml,"+escape('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="30" height="45"><path d="M2,15A13,13,0,0,1,28,13Q28,28,15,45Q2,28,2,15" fill="#48b" stroke="#369" stroke-width="1.5" /><circle cx="15" cy="15" r="6" fill="#fff" /></svg>')}},n.src=e+"/build/ol.js",document.head.appendChild(n)}
\ No newline at end of file
/*
Build using:
uglifyjs javascript/gluon-web-osm.js -o files/lib/gluon/web/www/static/gluon-web-osm.js -c -m
uglifyjs javascript/gluon-web-osm.js -o javascript/gluon-web-osm.min.js -c -m
*/
'use strict';
function initOSM(openlayers_url, ready) {
function initOSM(options, ready) {
var markerSvg = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="30" height="45">'
+ '<path d="M2,15A13,13,0,0,1,28,13Q28,28,15,45Q2,28,2,15" fill="#48b" stroke="#369" stroke-width="1.5" />'
+ '<circle cx="15" cy="15" r="6" fill="#fff" />'
......@@ -15,7 +15,7 @@ function initOSM(openlayers_url, ready) {
var style = document.createElement('link');
style.rel = 'stylesheet';
style.type = 'text/css';
style.href = openlayers_url + '/css/ol.css';
style.href = options.openlayers_url + '/css/ol.css';
document.head.appendChild(style);
var script = document.createElement('script');
......@@ -42,12 +42,22 @@ function initOSM(openlayers_url, ready) {
var marker = new ol.Feature();
marker.setStyle(markerStyle);
var source;
if (options.tile_layer && options.tile_layer.type === 'XYZ') {
source = new ol.source.XYZ({
url: options.tile_layer.url,
attributions: options.tile_layer.attributions,
});
} else {
source = new ol.source.OSM();
}
ready(function(elMap, pos, zoom, set, onUpdate) {
var map = new ol.Map({
target: elMap,
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
source: source
}),
new ol.layer.Vector({
source: new ol.source.Vector({
......@@ -79,6 +89,6 @@ function initOSM(openlayers_url, ready) {
markerImg.src = 'data:image/svg+xml,' + escape(markerSvg);
};
script.src = openlayers_url + '/build/ol.js';
script.src = options.openlayers_url + '/build/ol.js';
document.head.appendChild(script);
}