mirror of
https://github.com/lxsang/antos-frontend.git
synced 2025-04-17 05:16:45 +02:00
715 lines
23 KiB
JavaScript
715 lines
23 KiB
JavaScript
/*
|
|
* decaffeinate suggestions:
|
|
* DS101: Remove unnecessary use of Array.from
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
* DS202: Simplify dynamic range loops
|
|
* DS205: Consider reworking code to avoid use of IIFEs
|
|
* DS208: Avoid top-level this
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
*/
|
|
// Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>
|
|
|
|
// AnTOS Web desktop is is licensed under the GNU General Public
|
|
// License v3.0, see the LICENCE file for more information
|
|
|
|
// This program is free software: you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 3 of
|
|
// the License, or (at your option) any later version.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
//along with this program. If not, see https://www.gnu.org/licenses/.
|
|
|
|
'use strict';
|
|
const Ant = this;
|
|
|
|
class FormatedString {
|
|
constructor(fs, args) {
|
|
this.fs = fs;
|
|
this.values = [];
|
|
if (!args) { return; }
|
|
for (let i = 0, end = args.length - 1, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { this.values[i] = args[i]; }
|
|
}
|
|
toString() {
|
|
return this.__();
|
|
}
|
|
__() {
|
|
return this.fs.l().replace(/{(\d+)}/g, (match, number) => {
|
|
if (typeof this.values[number] !== 'undefined') { return this.values[number].__(); } else { return match; }
|
|
});
|
|
}
|
|
hash() {
|
|
return this.__().hash();
|
|
}
|
|
|
|
match(t) {
|
|
return this.__().match(t);
|
|
}
|
|
|
|
asBase64() {
|
|
return this.__().asBase64();
|
|
}
|
|
|
|
unescape() {
|
|
return this.__().unescape();
|
|
}
|
|
|
|
asUint8Array() {
|
|
return this.__().asUint8Array();
|
|
}
|
|
|
|
format() {
|
|
const args = arguments;
|
|
return __range__(0, args.length - 1, true).map((i) => (this.values[i] = args[i]));
|
|
}
|
|
}
|
|
|
|
class Version {
|
|
constructor(string) {
|
|
this.string = string;
|
|
const arr = this.string.split("-");
|
|
const br = {
|
|
"r": 3,
|
|
"b": 2,
|
|
"a": 1
|
|
};
|
|
this.branch = 3;
|
|
if ((arr.length === 2) && br[arr[1]]) { this.branch = br[arr[1]]; }
|
|
const mt = arr[0].match(/\d+/g);
|
|
if (!mt) { throw new Error(__("Version string is in invalid format: {0}", this.string)); }
|
|
this.major = 0;
|
|
this.minor = 0;
|
|
this.patch = 0;
|
|
if (mt.length >= 1) { this.major = Number(mt[0]); }
|
|
if (mt.length >= 2) { this.minor = Number(mt[1]); }
|
|
if (mt.length >= 3) { this.patch = Number(mt[2]); }
|
|
}
|
|
|
|
// this function return
|
|
// 0 if the version is unchanged
|
|
// 1 if the current version is newer
|
|
// -1 if the current version is older
|
|
compare(o) {
|
|
const other = o.__v();
|
|
if (this.branch > other.branch) { return 1; }
|
|
if (this.branch < other.branch) { return -1; }
|
|
if ((this.major === other.major) && (this.minor === other.minor) && (this.patch === other.patch)) { return 0; }
|
|
if (this.major > other.major) { return 1; }
|
|
if (this.major < other.major) { return -1; }
|
|
if (this.minor > other.minor) { return 1; }
|
|
if (this.minor < other.minor) { return -1; }
|
|
if (this.patch > other.patch) { return 1; }
|
|
return -1;
|
|
}
|
|
nt(o) {
|
|
return (this.compare(o)) === 1;
|
|
}
|
|
ot(o) {
|
|
return (this.compare(o)) === -1;
|
|
}
|
|
__v() { return this; }
|
|
toString() { return this.string; }
|
|
}
|
|
|
|
Object.defineProperty(Object.prototype, '__', {
|
|
value() {
|
|
return this.toString();
|
|
},
|
|
enumerable: false,
|
|
writable: true
|
|
}
|
|
);
|
|
|
|
String.prototype.hash = function() {
|
|
let hash = 5381;
|
|
let i = this.length;
|
|
while (i) { hash = (hash * 33) ^ this.charCodeAt(--i); }
|
|
return hash >>> 0;
|
|
};
|
|
String.prototype.__v = function() {
|
|
return new Version(this);
|
|
};
|
|
String.prototype.asBase64 = function() {
|
|
const tmp = encodeURIComponent(this);
|
|
return btoa(( tmp.replace(/%([0-9A-F]{2})/g, (match, p1) => String.fromCharCode((parseInt(p1, 16)))))
|
|
);
|
|
};
|
|
String.prototype.unescape = function() {
|
|
let d = this;
|
|
d = d.replace(/\\\\/g, "\\");
|
|
d = d.replace(/\\"/g, '"');
|
|
d = d.replace(/\\n/g, "\n");
|
|
d = d.replace(/\\t/g, "\t");
|
|
d = d.replace(/\\b/g, "\b");
|
|
d = d.replace(/\\f/g, "\f");
|
|
d = d.replace(/\\r/g, "\r");
|
|
return d;
|
|
};
|
|
String.prototype.asUint8Array = function() {
|
|
let bytes = [];
|
|
for (let i = 0, end = this.length - 1, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) {
|
|
bytes.push(this.charCodeAt(i));
|
|
}
|
|
bytes = new Uint8Array(bytes);
|
|
return bytes;
|
|
};
|
|
|
|
if (!String.prototype.format) {
|
|
String.prototype.format = function() {
|
|
const args = arguments;
|
|
return this.replace(/{(\d+)}/g, function(match, number) {
|
|
if (typeof args[number] !== 'undefined') { return args[number].__(); } else { return match; }
|
|
});
|
|
};
|
|
}
|
|
|
|
String.prototype.f = function() {
|
|
const args = arguments;
|
|
return new FormatedString(this, args);
|
|
};
|
|
|
|
String.prototype.__ = function() {
|
|
const match = this.match(/^__\((.*)\)$/);
|
|
if (match) { return match[1].l(); }
|
|
return this;
|
|
};
|
|
String.prototype.l = function() {
|
|
if (!Ant.OS.API.lang[this]) { Ant.OS.API.lang[this] = this; }
|
|
return Ant.OS.API.lang[this];
|
|
};
|
|
// language directive
|
|
|
|
this.__ = function() {
|
|
const args = arguments;
|
|
if (!(args.length > 0)) { return "Undefined"; }
|
|
const d = args[0];
|
|
d.l();
|
|
return new FormatedString(d, (__range__(1, args.length - 1, true).map((i) => args[i])));
|
|
};
|
|
|
|
Date.prototype.toString = function() {
|
|
let dd = this.getDate();
|
|
let mm = this.getMonth() + 1;
|
|
const yyyy = this.getFullYear();
|
|
let hh = this.getHours();
|
|
let mi = this.getMinutes();
|
|
let se = this.getSeconds();
|
|
|
|
if (dd < 10) { dd = `0${dd}`; }
|
|
if (mm < 10) { mm = `0${mm}`; }
|
|
if (hh < 10) { hh = `0${hh}`; }
|
|
if (mi < 10) { mi = `0${mi}`; }
|
|
if (se < 10) { se = `0${se}`; }
|
|
return `${dd}/${mm}/${yyyy} ${hh}:${mi}:${se}`;
|
|
};
|
|
|
|
Date.prototype.timestamp = function() {
|
|
return (this.getTime() / 1000) | 0;
|
|
};
|
|
|
|
// chaning error
|
|
this.__e = function(e) {
|
|
const reason = new Error(e.toString());
|
|
reason.stack += "\nCaused By:\n" + e.stack;
|
|
return reason;
|
|
};
|
|
|
|
|
|
//define the OS object
|
|
if (!Ant.OS) { Ant.OS = {
|
|
|
|
API: {},
|
|
GUI: {},
|
|
APP: {},
|
|
setting: {
|
|
user: {},
|
|
applications: {},
|
|
desktop: {},
|
|
appearance: {},
|
|
VFS: {},
|
|
system: {}
|
|
},
|
|
register(name, x) {
|
|
if (x.type === 3) { return Ant.OS.GUI.subwindows[name] = x; } else { return Ant.OS.APP[name] = x; }
|
|
},
|
|
|
|
// import proprety from an App
|
|
|
|
|
|
PM: {
|
|
pidalloc: 0,
|
|
processes: {},
|
|
createProcess(app, cls, args) {
|
|
return new Promise(function(resolve, reject) {
|
|
const f = function() {
|
|
//if it is single ton
|
|
// and a process is existing
|
|
// just return it
|
|
let obj;
|
|
if (cls.singleton && Ant.OS.PM.processes[app] && (Ant.OS.PM.processes[app].length === 1)) {
|
|
obj = Ant.OS.PM.processes[app][0];
|
|
obj.show();
|
|
} else {
|
|
if (!Ant.OS.PM.processes[app]) { Ant.OS.PM.processes[app] = []; }
|
|
obj = new cls(args);
|
|
obj.birth = (new Date).getTime();
|
|
Ant.OS.PM.pidalloc++;
|
|
obj.pid = Ant.OS.PM.pidalloc;
|
|
Ant.OS.PM.processes[app].push(obj);
|
|
if (cls.type === 1) { Ant.OS.GUI.dock(obj, cls.meta); } else { Ant.OS.GUI.attachservice(obj); }
|
|
}
|
|
return obj;
|
|
};
|
|
if (cls.dependencies) {
|
|
const libs = (Array.from(cls.dependencies));
|
|
return Ant.OS.API.require(libs)
|
|
.then(() => resolve(f())).catch(e => reject(__e(e)));
|
|
} else {
|
|
return resolve(f());
|
|
}
|
|
});
|
|
},
|
|
appByPid(pid) {
|
|
let app = undefined;
|
|
const find = function(l) {
|
|
for (let a of Array.from(l)) { if (a.pid === pid) { return a; } }
|
|
};
|
|
for (let k in Ant.OS.PM.processes) {
|
|
const v = Ant.OS.PM.processes[k];
|
|
app = find(v);
|
|
if (app) { break; }
|
|
}
|
|
return app;
|
|
},
|
|
|
|
kill(app) {
|
|
if (!app.name || !Ant.OS.PM.processes[app.name]) { return; }
|
|
|
|
const i = Ant.OS.PM.processes[app.name].indexOf(app);
|
|
if (i >= 0) {
|
|
if (Ant.OS.APP[app.name].type === 1) { Ant.OS.GUI.undock(app); } else { Ant.OS.GUI.detachservice(app); }
|
|
Ant.OS.announcer.unregister(app);
|
|
delete Ant.OS.PM.processes[app.name][i];
|
|
return Ant.OS.PM.processes[app.name].splice(i, 1);
|
|
}
|
|
},
|
|
|
|
killAll(app, force) {
|
|
if (!Ant.OS.PM.processes[app]) { return; }
|
|
return Array.from(Ant.OS.PM.processes[app]).map((a) => a.quit(force));
|
|
}
|
|
},
|
|
|
|
cleanup() {
|
|
console.log("Clean up system");
|
|
for (let a in Ant.OS.PM.processes) { const v = Ant.OS.PM.processes[a]; Ant.OS.PM.killAll(a, true); }
|
|
if (Ant.OS.announcer.observable) { Ant.OS.announcer.observable.off("*"); }
|
|
$(window).off('keydown');
|
|
($("#workspace")).off("mouseover");
|
|
delete Ant.OS.announcer.observable;
|
|
($("#wrapper")).empty();
|
|
Ant.OS.GUI.clearTheme();
|
|
Ant.OS.announcer.observable = new Ant.OS.API.Announcer();
|
|
Ant.OS.announcer.quota = 0;
|
|
Ant.OS.APP = {};
|
|
Ant.OS.setting = {
|
|
user: {},
|
|
applications: {},
|
|
desktop: {},
|
|
appearance: {},
|
|
VFS: {},
|
|
system: {}
|
|
};
|
|
Ant.OS.PM.processes = {};
|
|
return Ant.OS.PM.pidalloc = 0;
|
|
},
|
|
|
|
boot() {
|
|
//first login
|
|
console.log("Booting sytem");
|
|
return Ant.OS.API.handle.auth()
|
|
.then(function(d) {
|
|
// in case someone call it more than once :)
|
|
if (d.error) {
|
|
// show login screen
|
|
return Ant.OS.GUI.login();
|
|
} else {
|
|
// startX :)
|
|
return Ant.OS.GUI.startAntOS(d.result);
|
|
}}).catch(e => console.error(e));
|
|
},
|
|
|
|
cleanupHandles: {},
|
|
|
|
exit() {
|
|
//do clean up first
|
|
for (let n in Ant.OS.cleanupHandles) { const f = Ant.OS.cleanupHandles[n]; f(); }
|
|
return Ant.OS.API.handle.setting()
|
|
.then(function(r) {
|
|
Ant.OS.cleanup();
|
|
return Ant.OS.API.handle.logout()
|
|
.then(d => Ant.OS.boot());}).catch(e => console.error(e));
|
|
},
|
|
|
|
onexit(n, f) {
|
|
if (!Ant.OS.cleanupHandles[n]) { return Ant.OS.cleanupHandles[n] = f; }
|
|
}
|
|
}; }
|
|
|
|
|
|
|
|
Ant.OS.API = {
|
|
// the handle object could be a any remote or local handle to
|
|
// fetch user data, used by the API to make requests
|
|
// handles are defined in /src/handles
|
|
handle: {},
|
|
shared: {}, // shared libraries
|
|
searchHandle: {},
|
|
lang: {},
|
|
//request a user data
|
|
mid() {
|
|
return Ant.OS.announcer.getMID();
|
|
},
|
|
post(p, d) {
|
|
return new Promise(function(resolve, reject) {
|
|
const q = Ant.OS.announcer.getMID();
|
|
Ant.OS.API.loading(q, p);
|
|
return $.ajax({
|
|
type: 'POST',
|
|
url: p,
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(d,
|
|
function(k, v) {
|
|
if (k === "domel") { return undefined; }
|
|
return v;
|
|
}
|
|
, 4),
|
|
dataType: 'json',
|
|
success: null
|
|
})
|
|
.done(function(data) {
|
|
Ant.OS.API.loaded(q, p, "OK");
|
|
return resolve(data);}).fail(function(j, s, e) {
|
|
Ant.OS.API.loaded(q, p, "FAIL");
|
|
if (e) {
|
|
return reject(__e(e));
|
|
} else {
|
|
return reject(Ant.OS.API.throwe(s));
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
blob(p) {
|
|
return new Promise(function(resolve, reject) {
|
|
const q = Ant.OS.announcer.getMID();
|
|
const r = new XMLHttpRequest();
|
|
r.open("GET", p, true);
|
|
r.responseType = "arraybuffer";
|
|
r.onload = function(e) {
|
|
if ((this.status === 200) && (this.readyState === 4)) {
|
|
Ant.OS.API.loaded(q, p, "OK");
|
|
return resolve(this.response);
|
|
} else {
|
|
Ant.OS.API.loaded(q, p, "FAIL");
|
|
return reject(Ant.OS.API.throwe(__("Unable to get blob: {0}", p)));
|
|
}
|
|
};
|
|
Ant.OS.API.loading(q, p);
|
|
return r.send();
|
|
});
|
|
},
|
|
|
|
upload(p, d) {
|
|
return new Promise(function(resolve, reject) {
|
|
const q = Ant.OS.announcer.getMID();
|
|
//insert a temporal file selector
|
|
const o = ($('<input>')).attr('type', 'file').css("display", "none");
|
|
o.change(function() {
|
|
Ant.OS.API.loading(q, p);
|
|
const formd = new FormData();
|
|
formd.append('path', d);
|
|
// TODO: only one file is selected at this time
|
|
formd.append('upload', o[0].files[0]);
|
|
|
|
return $.ajax({
|
|
url: p,
|
|
data: formd,
|
|
type: 'POST',
|
|
contentType: false,
|
|
processData: false,
|
|
})
|
|
.done(function(data) {
|
|
Ant.OS.API.loaded(q, p, "OK");
|
|
resolve(data);
|
|
return o.remove();}).fail(function(j, s, e) {
|
|
Ant.OS.API.loaded(q, p, "FAIL");
|
|
if (e) {
|
|
reject(__e(e));
|
|
} else {
|
|
reject(Ant.OS.API.throwe(s));
|
|
}
|
|
return o.remove();
|
|
});
|
|
});
|
|
return o.click();
|
|
});
|
|
},
|
|
|
|
saveblob(name, b) {
|
|
const url = window.URL.createObjectURL(b);
|
|
const o = ($('<a>'))
|
|
.attr("href", url)
|
|
.attr("download", name)
|
|
.css("display", "none")
|
|
.appendTo("body");
|
|
o[0].click();
|
|
window.URL.revokeObjectURL(url);
|
|
return o.remove();
|
|
},
|
|
|
|
loading(q, p) {
|
|
return Ant.OS.announcer.trigger("loading", { id: q, data: { m: `${p}`, s: true }, name: "OS" });
|
|
},
|
|
|
|
loaded(q, p, m ) {
|
|
return Ant.OS.announcer.trigger("loaded", {
|
|
id: q, data: { m: `${m}: ${p}`, s: false }, name: "OS" });
|
|
},
|
|
|
|
get(p, t) {
|
|
return new Promise(function(resolve, reject) {
|
|
const conf = {
|
|
type: 'GET',
|
|
url: p
|
|
};
|
|
if (t) { conf.dataType = t; }
|
|
const q = Ant.OS.announcer.getMID();
|
|
Ant.OS.API.loading(q, p);
|
|
return $.ajax(conf)
|
|
.done(function(data) {
|
|
Ant.OS.API.loaded(q, p, "OK");
|
|
return resolve(data);}).fail(function(j, s, e) {
|
|
Ant.OS.API.loaded(q, p, "FAIL");
|
|
if (e) {
|
|
return reject(__e(e));
|
|
} else {
|
|
return reject(Ant.OS.API.throwe(s));
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
script(p) {
|
|
return Ant.OS.API.get(p, "script");
|
|
},
|
|
|
|
resource(r) {
|
|
const path = `resources/${r}`;
|
|
return Ant.OS.API.get(path);
|
|
},
|
|
|
|
libready(l) {
|
|
return Ant.OS.API.shared[l] || false;
|
|
},
|
|
|
|
requires(l) {
|
|
return new Promise(function(resolve, reject) {
|
|
if (!Ant.OS.API.shared[l]) {
|
|
const libfp = l.asFileHandle();
|
|
switch (libfp.ext) {
|
|
case "css":
|
|
return libfp.onready()
|
|
.then(function() {
|
|
$('<link>', {
|
|
rel: 'stylesheet',
|
|
type: 'text/css',
|
|
'href': `${libfp.getlink()}`
|
|
})
|
|
.appendTo('head');
|
|
Ant.OS.API.shared[l] = true;
|
|
console.log("Loaded :", l);
|
|
Ant.OS.announcer.trigger("sharedlibraryloaded", l);
|
|
return resolve(undefined);}).catch(e => reject(__e(e)));
|
|
case "js":
|
|
return Ant.OS.API.script(libfp.getlink())
|
|
.then(function(data) {
|
|
Ant.OS.API.shared[l] = true;
|
|
console.log("Loaded :", l);
|
|
Ant.OS.announcer.trigger("sharedlibraryloaded", l);
|
|
return resolve(data);}).catch(e => reject(__e(e)));
|
|
default:
|
|
return reject(Ant.OS.API.throwe(__("Invalid library: {0}", l)));
|
|
}
|
|
} else {
|
|
console.log(l, "Library exist, no need to load");
|
|
Ant.OS.announcer.trigger("sharedlibraryloaded", l);
|
|
return resolve();
|
|
}
|
|
});
|
|
},
|
|
|
|
require(libs) {
|
|
return new Promise(function(resolve, reject) {
|
|
if (!(libs.length > 0)) { return resolve(); }
|
|
Ant.OS.announcer.observable.one("sharedlibraryloaded", function(l) {
|
|
libs.splice(0, 1);
|
|
return Ant.OS.API.require(libs)
|
|
.catch(e => reject(__e(e)))
|
|
.then(r => resolve(r));
|
|
});
|
|
return Ant.OS.API.requires(libs[0])
|
|
.catch(e => reject(__e(e)));
|
|
});
|
|
},
|
|
|
|
packages: {
|
|
fetch() {
|
|
return Ant.OS.API.handle.packages({
|
|
command: "list", args: { paths: ((() => {
|
|
const result = [];
|
|
for (let k in Ant.OS.setting.system.pkgpaths) {
|
|
const v = Ant.OS.setting.system.pkgpaths[k];
|
|
result.push(v);
|
|
}
|
|
return result;
|
|
})()) }
|
|
});
|
|
},
|
|
|
|
cache() {
|
|
return Ant.OS.API.handle.packages({
|
|
command: "cache", args: { paths: ((() => {
|
|
const result = [];
|
|
for (let k in Ant.OS.setting.system.pkgpaths) {
|
|
const v = Ant.OS.setting.system.pkgpaths[k];
|
|
result.push(v);
|
|
}
|
|
return result;
|
|
})()) }
|
|
});
|
|
}
|
|
},
|
|
|
|
setting(f) {
|
|
return Ant.OS.API.handle.setting(f);
|
|
},
|
|
|
|
apigateway(d, ws) {
|
|
return Ant.OS.API.handle.apigateway(d, ws);
|
|
},
|
|
|
|
search(text) {
|
|
let r = [];
|
|
|
|
for (let k in Ant.OS.API.searchHandle) {
|
|
const v = Ant.OS.API.searchHandle[k];
|
|
const ret = Ant.OS.API.searchHandle[k](text);
|
|
if (ret.length > 0) {
|
|
ret.unshift({ text: k, class: "search-header", dataid: "header" });
|
|
r = r.concat(ret);
|
|
}
|
|
}
|
|
return r;
|
|
},
|
|
|
|
onsearch(name, fn) {
|
|
if (!Ant.OS.API.searchHandle[name]) { return Ant.OS.API.searchHandle[name] = fn; }
|
|
},
|
|
|
|
setLocale(name) {
|
|
return new Promise(function(resolve, reject) {
|
|
const path = `resources/languages/${name}.json`;
|
|
return Ant.OS.API.get(path, "json")
|
|
.then(function(d) {
|
|
Ant.OS.setting.system.locale = name;
|
|
Ant.OS.API.lang = d;
|
|
Ant.OS.announcer.trigger("systemlocalechange", name);
|
|
return resolve(d);}).catch(e => reject(__e(e)));
|
|
});
|
|
},
|
|
|
|
throwe(n) {
|
|
let err = undefined;
|
|
try {
|
|
throw new Error(n);
|
|
} catch (e) {
|
|
err = e;
|
|
}
|
|
if (!err) { return ""; }
|
|
return err;
|
|
},
|
|
|
|
setClipboard(v) {
|
|
const $el = $("#clipboard");
|
|
$el.val(v);
|
|
$el[0].select();
|
|
$el[0].setSelectionRange(0, 99999);
|
|
return document.execCommand("copy");
|
|
},
|
|
|
|
getClipboard() {
|
|
return new Promise(function(resolve, reject) {
|
|
const $el = $("#clipboard");
|
|
if (!navigator.clipboard) { return resolve($el.val()); }
|
|
return navigator.clipboard.readText().then(d => resolve(d)).catch(e => reject(__e(e)));
|
|
});
|
|
},
|
|
|
|
|
|
// utilities functioncs
|
|
switcher() {
|
|
let k, v;
|
|
const o = {};
|
|
const p = {};
|
|
for (let i = 0, end = arguments.length - 1, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { p[arguments[i]] = false; }
|
|
Object.defineProperty(o, "__p", {
|
|
enumerable: false,
|
|
value: p
|
|
});
|
|
const fn = function(o, v) {
|
|
return Object.defineProperty(o, v, {
|
|
enumerable: true,
|
|
set(value) {
|
|
for (let k in this.__p) {
|
|
const l = this.__p[k];
|
|
this.__p[k] = false;
|
|
}
|
|
return o.__p[v] = value;
|
|
}
|
|
, get() {
|
|
return o.__p[v];
|
|
}
|
|
});
|
|
};
|
|
for (k in o.__p) {
|
|
v = o.__p[k];
|
|
fn(o, k);
|
|
}
|
|
Object.defineProperty(o, "selected", {
|
|
configurable: true,
|
|
enumerable: false,
|
|
get() {
|
|
for (k in o.__p) {
|
|
v = o.__p[k];
|
|
if (v) { return k; }
|
|
}
|
|
}
|
|
});
|
|
return o;
|
|
}
|
|
};
|
|
function __range__(left, right, inclusive) {
|
|
let range = [];
|
|
let ascending = left < right;
|
|
let end = !inclusive ? right : ascending ? right + 1 : right - 1;
|
|
for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
|
|
range.push(i);
|
|
}
|
|
return range;
|
|
} |