/** @private */
handleModalCancel: function(modalMap, ev) {
- return Promise.resolve(ui.hideModal());
+ var prevNode = this.getPreviousModalMap();
+
+ if (prevNode) {
+ var heading = prevNode.parentNode.querySelector('h4');
+
+ prevNode.classList.add('flash');
+ prevNode.classList.remove('hidden');
+ prevNode.parentNode.removeChild(prevNode.nextElementSibling);
+
+ heading.removeChild(heading.lastElementChild);
+
+ if (!this.getPreviousModalMap())
+ prevNode.parentNode
+ .querySelector('div.right > button')
+ .firstChild.data = _('Dismiss');
+ }
+ else {
+ ui.hideModal();
+ }
+
+ return Promise.resolve();
},
/** @private */
handleModalSave: function(modalMap, ev) {
- return modalMap.save(null, true)
- .then(L.bind(this.map.load, this.map))
- .then(L.bind(this.map.reset, this.map))
- .then(ui.hideModal)
+ var mapNode = this.getActiveModalMap(),
+ activeMap = dom.findClassInstance(mapNode),
+ saveTasks = activeMap.save(null, true);
+
+ while (activeMap.parent) {
+ activeMap = activeMap.parent;
+ saveTasks = saveTasks
+ .then(L.bind(activeMap.load, activeMap))
+ .then(L.bind(activeMap.reset, activeMap));
+ }
+
+ return saveTasks
+ .then(L.bind(this.handleModalCancel, this, modalMap, ev))
.catch(function() {});
},
},
+ /** @private */
+ getActiveModalMap: function() {
+ return document.querySelector('body.modal-overlay-active > #modal_overlay > .modal.cbi-modal > .cbi-map:not(.hidden)');
+ },
+
+ /** @private */
+ getPreviousModalMap: function() {
+ var mapNode = this.getActiveModalMap(),
+ prevNode = mapNode ? mapNode.previousElementSibling : null;
+
+ return (prevNode && prevNode.matches('.cbi-map.hidden')) ? prevNode : null;
+ },
+
/** @private */
renderMoreOptionsModal: function(section_id, ev) {
var parent = this.map,
}
return Promise.resolve(this.addModalOptions(s, section_id, ev)).then(L.bind(m.render, m)).then(L.bind(function(nodes) {
- ui.showModal(title, [
- nodes,
- E('div', { 'class': 'right' }, [
- E('button', {
- 'class': 'cbi-button',
- 'click': ui.createHandlerFn(this, 'handleModalCancel', m)
- }, [ _('Dismiss') ]), ' ',
- E('button', {
- 'class': 'cbi-button cbi-button-positive important',
- 'click': ui.createHandlerFn(this, 'handleModalSave', m),
- 'disabled': m.readonly || null
- }, [ _('Save') ])
- ])
- ], 'cbi-modal');
+ var modalMap = this.getActiveModalMap();
+
+ if (modalMap) {
+ modalMap.parentNode
+ .querySelector('h4')
+ .appendChild(E('span', title ? ' ยป ' + title : ''));
+
+ modalMap.parentNode
+ .querySelector('div.right > button')
+ .firstChild.data = _('Back');
+
+ modalMap.classList.add('hidden');
+ modalMap.parentNode.insertBefore(nodes, modalMap.nextElementSibling);
+ nodes.classList.add('flash');
+ }
+ else {
+ ui.showModal(title, [
+ nodes,
+ E('div', { 'class': 'right' }, [
+ E('button', {
+ 'class': 'cbi-button',
+ 'click': ui.createHandlerFn(this, 'handleModalCancel', m)
+ }, [ _('Dismiss') ]), ' ',
+ E('button', {
+ 'class': 'cbi-button cbi-button-positive important',
+ 'click': ui.createHandlerFn(this, 'handleModalSave', m),
+ 'disabled': m.readonly || null
+ }, [ _('Save') ])
+ ])
+ ], 'cbi-modal');
+ }
}, this)).catch(L.error);
}
});
/** @private */
handleAdd: function(ev, name) {
var config_name = this.uciconfig || this.map.config,
- section_id = this.map.data.add(config_name, this.sectiontype, name);
+ section_id = this.map.data.add(config_name, this.sectiontype, name),
+ mapNode = this.getPreviousModalMap(),
+ prevMap = mapNode ? dom.findClassInstance(mapNode) : this.map;
+
+ prevMap.addedSection = section_id;
- this.addedSection = section_id;
return this.renderMoreOptionsModal(section_id);
},
/** @private */
handleModalSave: function(/* ... */) {
+ var mapNode = this.getPreviousModalMap(),
+ prevMap = mapNode ? dom.findClassInstance(mapNode) : this.map;
+
return this.super('handleModalSave', arguments)
- .then(L.bind(function() { this.addedSection = null }, this));
+ .then(function() { delete prevMap.addedSection });
},
/** @private */
handleModalCancel: function(/* ... */) {
- var config_name = this.uciconfig || this.map.config;
+ var config_name = this.uciconfig || this.map.config,
+ mapNode = this.getPreviousModalMap(),
+ prevMap = mapNode ? dom.findClassInstance(mapNode) : this.map;
- if (this.addedSection != null) {
- this.map.data.remove(config_name, this.addedSection);
- this.addedSection = null;
+ if (prevMap.addedSection != null) {
+ this.map.data.remove(config_name, prevMap.addedSection);
+ delete prevMap.addedSection;
}
return this.super('handleModalCancel', arguments);
}
catch(e) {}
- o = s.taboption('peers', form.SectionValue, '_peers', form.TypedSection, 'wireguard_%s'.format(s.section));
+ o = s.taboption('peers', form.SectionValue, '_peers', form.GridSection, 'wireguard_%s'.format(s.section));
o.depends('proto', 'wireguard');
ss = o.subsection;
ss.anonymous = true;
ss.addremove = true;
ss.addbtntitle = _('Add peer');
+ ss.nodescriptions = true;
+ ss.modaltitle = _('Edit peer');
ss.renderSectionPlaceholder = function() {
return E([], [
o = ss.option(form.Flag, 'disabled', _('Peer disabled'), _('Enable / Disable peer. Restart wireguard interface to apply changes.'));
o.optional = true;
+ o.editable = true;
o = ss.option(form.Value, 'description', _('Description'), _('Optional. Description of peer.'));
o.placeholder = 'My Peer';
o.optional = true;
o = ss.option(form.Value, 'description', _('QR-Code'));
+ o.modalonly = true;
o.render = L.bind(function (view, section_id) {
var sections = uci.sections('network');
var client = findSection(sections, section_id);
}, this);
o = ss.option(form.Value, 'public_key', _('Public Key'), _('Required. Base64-encoded public key of peer.'));
+ o.modalonly = true;
o.validate = validateBase64;
o.rmempty = false;
o = ss.option(form.Value, 'preshared_key', _('Preshared Key'), _('Optional. Base64-encoded preshared key. Adds in an additional layer of symmetric-key cryptography for post-quantum resistance.'));
+ o.modalonly = true;
o.password = true;
o.validate = validateBase64;
o.optional = true;
o.optional = true;
o = ss.option(form.Flag, 'route_allowed_ips', _('Route Allowed IPs'), _('Optional. Create routes for Allowed IPs for this peer.'));
+ o.modalonly = true;
o = ss.option(form.Value, 'endpoint_host', _('Endpoint Host'), _('Optional. Host of peer. Names are resolved prior to bringing up the interface.'));
o.placeholder = 'vpn.example.com';
o.datatype = 'port';
o = ss.option(form.Value, 'persistent_keepalive', _('Persistent Keep Alive'), _('Optional. Seconds between keep alive messages. Default is 0 (disabled). Recommended value if this device is behind a NAT is 25.'));
+ o.modalonly = true;
o.datatype = 'range(0,65535)';
o.placeholder = '0';
},