UNPKG

web-atoms-mvvm

Version:

MVVM, REST Json Service, Message Subscriptions for Web Atoms

1,375 lines 117 kB
/** * Easy and Simple Dependency Injection */ var WebAtoms; (function (WebAtoms) { var DIFactory = /** @class */ (function () { function DIFactory(key, factory, transient) { this.transient = transient; this.factory = factory; this.key = key; } DIFactory.prototype.resolve = function () { if (this.transient) { return this.factory(); } return this.instance || (this.instance = this.factory()); }; DIFactory.prototype.push = function (factory, transient) { this.stack = this.stack || []; this.stack.push({ factory: this.factory, instance: this.instance, transient: this.transient }); this.transient = transient; this.instance = undefined; this.factory = factory; }; DIFactory.prototype.pop = function () { if (!(this.stack && this.stack.length)) { throw new Error("Stack in DIFactory is empty"); } var obj = this.stack.pop(); this.factory = obj.factory; this.transient = obj.transient; this.instance = obj.instance; }; return DIFactory; }()); /** * @export * @class DI */ var DI = /** @class */ (function () { function DI() { } /** * @static * @template T * @param {new () => T} key * @param {() => T} factory * @param {boolean} [transient=false] - If true, always new instance will be created * @memberof DI */ DI.register = function (key, factory, transient) { if (transient === void 0) { transient = false; } var k = key; var existing = DI.factory[k]; if (existing) { throw new Error("Factory for " + key.name + " is already registered"); } DI.factory[k] = new DIFactory(key, factory, transient); }; /** * @static * @template T * @param {new () => T} c * @returns {T} * @memberof DI */ DI.resolve = function (c) { var f = DI.factory[c]; if (!f) { throw new Error("No factory registered for " + c); } return f.resolve(); }; /** * Use this for unit testing, this will push existing * DI factory and all instances will be resolved with * given instance * * @static * @param {*} key * @param {*} instance * @memberof DI */ DI.push = function (key, instance) { var f = DI.factory[key]; if (!f) { DI.register(key, function () { return instance; }); } else { f.push(function () { return instance; }, true); } }; /** * @static * @param {*} key * @memberof DI */ DI.pop = function (key) { var f = DI.factory[key]; if (f) { f.pop(); } }; DI.factory = {}; return DI; }()); WebAtoms.DI = DI; /** * This decorator will register given class as singleton instance on DI. * @example * @DIGlobal * class BackendService{ * } * @export * @param {new () => any} c * @returns */ function DIGlobal(c) { DI.register(c, function () { return new c(); }); return c; } WebAtoms.DIGlobal = DIGlobal; /** * This decorator will register given class as transient instance on DI. * @example * @DIAlwaysNew * class StringHelper{ * } * @export * @param {new () => any} c * @returns */ function DIAlwaysNew(c) { DI.register(c, function () { return new c(); }, true); return c; } WebAtoms.DIAlwaysNew = DIAlwaysNew; })(WebAtoms || (WebAtoms = {})); var DIGlobal = WebAtoms.DIGlobal; var DIAlwaysNew = WebAtoms.DIAlwaysNew; // tslint:disable function classCreator__(name, basePrototype, classConstructor, classPrototype, classProperties, thisPrototype, thisProperties) { var baseClass = basePrototype ? basePrototype.constructor : null; var old = classConstructor || (function () { }); var cp = classProperties; var f = null; if (baseClass) { if (classProperties) { f = function () { this.constructor = classPrototype; for (var k in cp) { this["_" + k] = cp[k]; } baseClass.apply(this, arguments); this.__typeName = name; //var cp = Atom.clone(classProperties); old.apply(this, arguments); }; } else { f = function () { this.constructor = classPrototype; baseClass.apply(this, arguments); this.__typeName = name; old.apply(this, arguments); }; } var bpt = baseClass.prototype; // extend for (var k in bpt) { if (classPrototype[k]) continue; if (bpt.hasOwnProperty(k)) { var pd = Object.getOwnPropertyDescriptor(bpt, k); if (!pd) { classPrototype[k] = bpt[k]; } else { Object.defineProperty(classPrototype, k, pd); } } } } else { if (classProperties) { f = function () { this.__typeName = name; //var cp = Atom.clone(classProperties); for (var k in cp) { this["_" + k] = cp[k]; } old.apply(this, arguments); }; } else { f = function () { this.__typeName = name; old.apply(this, arguments); }; } } if (classProperties) { for (var k in classProperties) { if (!classPrototype["get_" + k]) { classPrototype["get_" + k] = createProperty("_" + k, true); } if (!classPrototype["set_" + k]) { classPrototype["set_" + k] = createProperty("_" + k); } } } for (var k in classPrototype) { if (/^get\_/.test(k)) { var gx = classPrototype[k]; var nx = k.substr(4); var sx = classPrototype["set_" + nx]; Object.defineProperty(classPrototype, nx, { get: gx, set: sx ? createProperty("_" + nx, false, nx) : undefined, enumerable: true, configurable: true }); } } f.__typeName = name; if (baseClass) { f.__baseType = baseClass; // var fx = f; // function __() { // var args = []; // for (var _i = 0; _i < arguments.length; _i++) { // args[_i] = arguments[_i]; // } // fx.call(this, args); // this.constructor = classPrototype; // } // __.prototype = basePrototype; // f = new __(); f.prototype = basePrototype; f = new f(); } else { f.prototype = classPrototype; f.prototype.constructor = f; } //f.constructor = classPrototype; if (!classPrototype.hasOwnProperty("toString")) { f.prototype.toString = function () { return name; }; } mapLibrary(/\./.test(name) ? name : 'WebAtoms.' + name, window, f); return f; } ; /// <reference path="__di.ts" /> var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); if (location) { Atom.designMode = /file/i.test(location.protocol); } /** * This decorator will mark given property as bindable, it will define * getter and setter, and in the setter, it will refresh the property. * * class Customer{ * * @bindableProperty * firstName:string; * * } * * @param {*} target * @param {string} key */ function bindableProperty(target, key) { // property value var _val = this[key]; var keyName = "_" + key; this[keyName] = _val; // property getter var getter = function () { // console.log(`Get: ${key} => ${_val}`); return this[keyName]; }; // property setter var setter = function (newVal) { // console.log(`Set: ${key} => ${newVal}`); // debugger; var oldValue = this[keyName]; // tslint:disable-next-line:triple-equals if (oldValue == newVal) { return; } this[keyName] = newVal; // only if this is not an AtomControl... if (!(this._element && this._element.atomControl === this)) { var c = this._$_supressRefresh; if (!(c && c[key])) { Atom.refresh(this, key); } } if (this.onPropertyChanged) { this.onPropertyChanged(key); } }; // delete property if (delete this[key]) { // create new property with getter and setter Object.defineProperty(target, key, { get: getter, set: setter, enumerable: true, configurable: true }); // tslint:disable-next-line:no-string-literal if (target.constructor.prototype["get_atomParent"]) { target["get_" + key] = getter; target["set_" + key] = setter; } } } Atom.bindable = function (e) { if (!e) { return e; } if (e instanceof Array) { throw new TypeError("Invalid object, try to use AtomList instead of Atom.bindable"); } if (typeof e === "string" || e.constructor === String) { return e; } if (typeof e === "number" || e.constructor === Number) { return e; } if (e.constructor === Date) { return e; } var self = e; if (e._$_isBindable) { return e; } var keys = Object.keys(e); e._$_isBindable = true; for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { var key = keys_1[_i]; var k = key; var v = e[key]; var vk = "_" + key; e[vk] = v; delete e[key]; Object.defineProperty(e, key, { get: function () { return this[vk]; }, set: function (v) { this[vk] = v; Atom.refresh(this, k); }, enumerable: true }); } return e; }; Atom.controlToElement = function (type, tagName) { if (tagName === void 0) { tagName = "div"; } if (!type) { return undefined; } var name = ""; if (!(typeof type === "string")) { // tslint:disable-next-line:no-string-literal name = type["__typeName"]; } else { name = type; } var div = document.createElement("div"); div.setAttribute("atom-type", name); return div; }; var WebAtoms; (function (WebAtoms) { /** * * * @export * @class CancelToken */ var CancelToken = /** @class */ (function () { function CancelToken() { this.listeners = []; } Object.defineProperty(CancelToken.prototype, "cancelled", { get: function () { return this._cancelled; }, enumerable: true, configurable: true }); CancelToken.prototype.cancel = function () { this._cancelled = true; for (var _i = 0, _a = this.listeners; _i < _a.length; _i++) { var fx = _a[_i]; fx(); } }; CancelToken.prototype.reset = function () { this._cancelled = false; this.listeners.length = 0; }; CancelToken.prototype.registerForCancel = function (f) { if (this._cancelled) { f(); this.cancel(); return; } this.listeners.push(f); }; return CancelToken; }()); WebAtoms.CancelToken = CancelToken; var AtomModel = /** @class */ (function () { function AtomModel() { } AtomModel.prototype.refresh = function (name) { Atom.refresh(this, name); }; return AtomModel; }()); WebAtoms.AtomModel = AtomModel; /** * Though you can directly call methods of view model in binding expression, * but we recommend using AtomCommand for two reasons. * * First one, it has enabled bindable property, which can be used to enable/disable UI. * AtomButton already has `command` and `commandParameter` property which automatically * binds execution and disabling the UI. * * Second one, it has busy bindable property, which can be used to display a busy indicator * when corresponding action is a promise and it is yet not resolved. * * @export * @class AtomCommand * @extends {AtomModel} * @template T */ var AtomCommand = /** @class */ (function (_super) { __extends(AtomCommand, _super); function AtomCommand(action) { var _this = _super.call(this) || this; _this.isMVVMAtomCommand = true; _this._enabled = true; _this._busy = false; _this.action = action; _this.execute = function (p) { if (_this.enabled) { _this.executeAction(p); } }; return _this; } Object.defineProperty(AtomCommand.prototype, "enabled", { /** * * * @type {boolean} * @memberof AtomCommand */ get: function () { return this._enabled; }, set: function (v) { this._enabled = v; this.refresh("enabled"); }, enumerable: true, configurable: true }); Object.defineProperty(AtomCommand.prototype, "busy", { /** * * * @type {boolean} * @memberof AtomCommand */ get: function () { return this._busy; }, set: function (v) { this._busy = v; this.refresh("busy"); }, enumerable: true, configurable: true }); AtomCommand.prototype.executeAction = function (p) { var _this = this; if (this._busy) { return; } this.busy = true; var result = this.action(p); if (result) { if (result.catch) { result.catch(function (error) { _this.busy = false; if (error !== "cancelled") { console.error(error); Atom.showError(error); } }); return; } if (result.then) { result.then(function () { _this.busy = false; }); return; } } this.busy = false; }; return AtomCommand; }(AtomModel)); WebAtoms.AtomCommand = AtomCommand; })(WebAtoms || (WebAtoms = {})); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [0, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var WebAtoms; (function (WebAtoms) { /** * DisposableAction holds an action that * will be executed when dispose will be called. * * subscribe(m,f):AtomDisposable{ * //... * //subscribe to something... * //... * return new AtomDisposable(()=>{ * * //... * //unsubscribe from something * // * * }); * } * * User can simply call dispose to make sure subscription was unsubscribed. * * @export * @class DisposableAction * @implements {AtomDisposable} */ var DisposableAction = /** @class */ (function () { function DisposableAction(f) { this.f = f; } DisposableAction.prototype.dispose = function () { this.f(); }; return DisposableAction; }()); WebAtoms.DisposableAction = DisposableAction; // tslint:disable-next-line var AtomUI = window["AtomUI"]; // tslint:disable-next-line var oldCreateControl = AtomUI.createControl; // tslint:disable-next-line AtomUI.createControl = function (element, type, data, newScope) { if (type) { if (type.constructor === String || (typeof type) === "string") { var t = WebAtoms[type] || Atom.get(window, type); if (t) { type = t; } } } return oldCreateControl.call(Atom, element, type, data, newScope); }; Atom.post = function (f) { WebAtoms.dispatcher.callLater(f); }; Atom.postAsync = function (f) { var _this = this; return new Promise(function (resolve, reject) { WebAtoms.dispatcher.callLater(function () { return __awaiter(_this, void 0, void 0, function () { var p, e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, , 4]); p = f(); if (!(p && p.then && p.catch)) return [3 /*break*/, 2]; return [4 /*yield*/, p]; case 1: _a.sent(); _a.label = 2; case 2: resolve(); return [3 /*break*/, 4]; case 3: e_1 = _a.sent(); reject(e_1); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }); }); }; Atom.using = function (d, f) { try { f(); } finally { d.dispose(); } }; Atom.usingAsync = function (d, f) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, , 2, 3]); return [4 /*yield*/, f()]; case 1: _a.sent(); return [3 /*break*/, 3]; case 2: d.dispose(); return [7 /*endfinally*/]; case 3: return [2 /*return*/]; } }); }); }; Atom.watch = function (item, property, f) { AtomBinder.add_WatchHandler(item, property, f); return new DisposableAction(function () { AtomBinder.remove_WatchHandler(item, property, f); }); }; Atom.delay = function (n, ct) { return new Promise(function (resolve, reject) { var t = setTimeout(function () { resolve(); }, (n)); if (ct) { ct.registerForCancel(function () { clearTimeout(t); reject("cancelled"); }); } }); }; var AtomHandler = /** @class */ (function () { function AtomHandler(message) { this.message = message; this.list = new Array(); } return AtomHandler; }()); var AtomMessageAction = /** @class */ (function () { function AtomMessageAction(msg, a) { this.message = msg; this.action = a; } return AtomMessageAction; }()); WebAtoms.AtomMessageAction = AtomMessageAction; /** * Device (usually browser), instance of which supports * singleton instance to provide broadcast/messaging * * @export * @class AtomDevice */ var AtomDevice = /** @class */ (function () { function AtomDevice() { this.bag = {}; } /** * This method will run any asynchronous method * and it will display an error if it will fail * asynchronously * * @template T * @param {() => Promise<T>} tf * @memberof AtomDevice */ AtomDevice.prototype.runAsync = function (tf) { var task = tf(); task.catch(function (error) { console.error(error); Atom.showError(error); }); // tslint:disable-next-line task.then(function () { }); }; /** * Broadcast given data to channel, only within the current window. * * @param {string} channel * @param {*} data * @returns * @memberof AtomDevice */ AtomDevice.prototype.broadcast = function (channel, data) { var ary = this.bag[channel]; if (!ary) { return; } for (var _i = 0, _a = ary.list; _i < _a.length; _i++) { var entry = _a[_i]; entry.call(this, channel, data); } }; /** * Subscribe for given channel with action that will be * executed when anyone will broadcast (this only works within the * current browser window) * * This method returns a disposable, when you call `.dispose()` it will * unsubscribe for current subscription * * @param {string} channel * @param {AtomAction} action * @returns {AtomDisposable} Disposable that supports removal of subscription * @memberof AtomDevice */ AtomDevice.prototype.subscribe = function (channel, action) { var _this = this; var ary = this.bag[channel]; if (!ary) { ary = new AtomHandler(channel); this.bag[channel] = ary; } ary.list.push(action); return new DisposableAction(function () { ary.list = ary.list.filter(function (a) { return a !== action; }); if (!ary.list.length) { _this.bag[channel] = null; } }); }; /** * * * @static * @type {AtomDevice} * @memberof AtomDevice */ AtomDevice.instance = new AtomDevice(); return AtomDevice; }()); WebAtoms.AtomDevice = AtomDevice; })(WebAtoms || (WebAtoms = {})); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var WebAtoms; (function (WebAtoms) { var AtomPageView = /** @class */ (function (_super) { __extends(AtomPageView, _super); function AtomPageView(e) { var _this = _super.call(this, e) || this; _this.disposables = []; _this.stack = []; _this.keepStack = true; _this.current = null; _this.currentDisposable = null; e.style.position = "relative"; _this.backCommand = function () { _this.onBackCommand(); }; return _this; } AtomPageView.prototype.onBackCommand = function () { if (!this.stack.length) { console.warn("FrameStack is empty !!"); return; } var ctrl = this.current; var e = ctrl._element; ctrl.dispose(); e.remove(); this.current = this.stack.pop(); this.current._element.style.display = ""; }; AtomPageView.prototype.canChange = function () { return __awaiter(this, void 0, void 0, function () { var ctrl, vm; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.current) { return [2 /*return*/, true]; } ctrl = this.current; vm = ctrl.viewModel; if (!vm.closeWarning) return [3 /*break*/, 2]; return [4 /*yield*/, WebAtoms.WindowService.instance.confirm(vm.closeWarning, "Are you sure?")]; case 1: if (_a.sent()) { return [2 /*return*/, true]; } return [2 /*return*/, false]; case 2: return [2 /*return*/, true]; } }); }); }; AtomPageView.prototype.push = function (ctrl) { if (this.current) { if (this.keepStack) { this.current._element.style.display = "none"; this.stack.push(this.current); } else { var c1 = this.current; var e = c1._element; c1.dispose(); e.remove(); } } var element = ctrl._element; element.style.position = "absolute"; element.style.top = element.style.bottom = element.style.left = element.style.right = "0"; this._element.appendChild(element); this.current = ctrl; }; AtomPageView.prototype.init = function () { var _this = this; _super.prototype.init.call(this); this.disposables.push(Atom.watch(this, "url", function () { _this.load(_this.url); })); }; AtomPageView.prototype.dispose = function (e) { _super.prototype.dispose.call(this, e); if (!e) { for (var _i = 0, _a = this.disposables; _i < _a.length; _i++) { var d = _a[_i]; d.dispose(); } this.disposables = []; } }; AtomPageView.prototype.createControl = function (c, vmt, q) { var div = document.createElement("div"); div.id = this._element.id + "_" + (this.stack.length + 1); var ctrl = AtomUI.createControl(div, c); div.setAttribute("atom-local-scope", "true"); var vm = null; if (vmt) { vm = new (vmt)(q); ctrl.viewModel = vm; } ctrl.init(); return ctrl; }; AtomPageView.prototype.load = function (url) { return __awaiter(this, void 0, void 0, function () { var _this = this; var uri, fragments, scope, vm, _i, fragments_1, f, q, ctrl; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.canChange()]; case 1: if (!(_a.sent())) { return [2 /*return*/]; } uri = new AtomUri(url); fragments = uri.path.split(".") .map(function (f) { return _this.toUpperCase(f); }); scope = window; vm = null; for (_i = 0, fragments_1 = fragments; _i < fragments_1.length; _i++) { f = fragments_1[_i]; vm = scope[f + "ViewModel"]; if (!vm) { vm = scope[f + "Model"]; } scope = scope[f]; if (!scope) { throw new Error("No " + f + " in " + url + " found."); } } q = uri.query; ctrl = this.createControl(scope, vm, q); Atom.post(function () { vm = ctrl.viewModel; if (vm) { if (vm instanceof WebAtoms.AtomPageViewModel) { var pvm = vm; pvm.pageId = ctrl._element.id; } } }); this.push(ctrl); return [2 /*return*/]; } }); }); }; AtomPageView.prototype.toUpperCase = function (s) { return s.split("-") .filter(function (t) { return t; }) .map(function (t) { return t.substr(0, 1).toUpperCase() + t.substr(1); }) .join(""); }; __decorate([ bindableProperty ], AtomPageView.prototype, "url", void 0); __decorate([ bindableProperty ], AtomPageView.prototype, "keepStack", void 0); __decorate([ bindableProperty ], AtomPageView.prototype, "current", void 0); return AtomPageView; }(WebAtoms.AtomControl)); WebAtoms.AtomPageView = AtomPageView; })(WebAtoms || (WebAtoms = {})); var WebAtoms; (function (WebAtoms) { /** * * * @export * @class AtomList * @extends {Array<T>} * @template T */ var AtomList = /** @class */ (function (_super) { __extends(AtomList, _super); function AtomList() { var _this = _super.call(this) || this; _this._start = 0; _this._total = 0; _this._size = 10; // tslint:disable-next-line _this["__proto__"] = AtomList.prototype; _this.next = function () { _this.start = _this.start + _this.size; }; _this.prev = function () { if (_this.start >= _this.size) { _this.start = _this.start - _this.size; } }; return _this; } Object.defineProperty(AtomList.prototype, "start", { get: function () { return this._start; }, set: function (v) { if (v === this._start) { return; } this._start = v; Atom.refresh(this, "start"); }, enumerable: true, configurable: true }); Object.defineProperty(AtomList.prototype, "total", { get: function () { return this._total; }, set: function (v) { if (v === this._total) { return; } this._total = v; Atom.refresh(this, "total"); }, enumerable: true, configurable: true }); Object.defineProperty(AtomList.prototype, "size", { get: function () { return this._size; }, set: function (v) { if (v === this._size) { return; } this._size = v; Atom.refresh(this, "size"); }, enumerable: true, configurable: true }); /** * Adds the item in the list and refresh bindings * @param {T} item * @returns {number} * @memberof AtomList */ AtomList.prototype.add = function (item) { var i = this.length; var n = this.push(item); AtomBinder.invokeItemsEvent(this, "add", i, item); Atom.refresh(this, "length"); return n; }; /** * Add given items in the list and refresh bindings * @param {Array<T>} items * @memberof AtomList */ AtomList.prototype.addAll = function (items) { for (var _i = 0, items_1 = items; _i < items_1.length; _i++) { var item = items_1[_i]; var i = this.length; this.push(item); AtomBinder.invokeItemsEvent(this, "add", i, item); Atom.refresh(this, "length"); } // tslint:disable-next-line:no-string-literal var t = items["total"]; if (t) { this.total = t; } }; /** * Replaces list with given items, use this * to avoid flickering in screen * @param {T[]} items * @memberof AtomList */ AtomList.prototype.replace = function (items, start, size) { this.length = items.length; for (var i = 0; i < items.length; i++) { this[i] = items[i]; } this.refresh(); // tslint:disable-next-line:no-string-literal var t = items["total"]; if (t) { this.total = t; } if (start !== undefined) { this.start = start; } if (size !== undefined) { this.size = size; } }; /** * Inserts given number in the list at position `i` * and refreshes the bindings. * @param {number} i * @param {T} item * @memberof AtomList */ AtomList.prototype.insert = function (i, item) { var n = this.splice(i, 0, item); AtomBinder.invokeItemsEvent(this, "add", i, item); Atom.refresh(this, "length"); }; /** * Removes item at given index i and refresh the bindings * @param {number} i * @memberof AtomList */ AtomList.prototype.removeAt = function (i) { var item = this[i]; this.splice(i, 1); AtomBinder.invokeItemsEvent(this, "remove", i, item); Atom.refresh(this, "length"); }; /** * Removes given item or removes all items that match * given lambda as true and refresh the bindings * @param {(T | ((i:T) => boolean))} item * @returns {boolean} `true` if any item was removed * @memberof AtomList */ AtomList.prototype.remove = function (item) { if (item instanceof Function) { var index = 0; var removed = false; for (var _i = 0, _a = this; _i < _a.length; _i++) { var it = _a[_i]; if (item(it)) { this.removeAt(index); removed = true; } index++; } return removed; } var n = this.indexOf(item); if (n !== -1) { this.removeAt(n); return true; } return false; }; /** * Removes all items from the list and refreshes the bindings * @memberof AtomList */ AtomList.prototype.clear = function () { this.length = 0; this.refresh(); }; AtomList.prototype.refresh = function () { AtomBinder.invokeItemsEvent(this, "refresh", -1, null); Atom.refresh(this, "length"); }; AtomList.prototype.watch = function (f) { var _this = this; AtomBinder.add_CollectionChanged(this, f); return new WebAtoms.DisposableAction(function () { AtomBinder.remove_CollectionChanged(_this, f); }); }; return AtomList; }(Array)); WebAtoms.AtomList = AtomList; // tslint:disable Array.prototype["add"] = AtomList.prototype.add; Array.prototype["addAll"] = AtomList.prototype.addAll; Array.prototype["clear"] = AtomList.prototype.clear; Array.prototype["refresh"] = AtomList.prototype.refresh; Array.prototype["remove"] = AtomList.prototype.remove; Array.prototype["removeAt"] = AtomList.prototype.removeAt; Array.prototype["watch"] = AtomList.prototype.watch; })(WebAtoms || (WebAtoms = {})); /// <reference path="atom-device.ts"/> /// <reference path="atom-command.ts"/> var WebAtoms; (function (WebAtoms) { /** * * * @export * @class AtomViewModel * @extends {AtomModel} */ var AtomViewModel = /** @class */ (function (_super) { __extends(AtomViewModel, _super); function AtomViewModel() { var _this = _super.call(this) || this; _this._channelPrefix = ""; _this._isReady = false; _this.validations = []; WebAtoms.AtomDevice.instance.runAsync(function () { return _this.privateInit(); }); return _this; } Object.defineProperty(AtomViewModel.prototype, "channelPrefix", { get: function () { return this._channelPrefix; }, set: function (v) { this._channelPrefix = v; var temp = this.subscriptions; if (temp) { this.subscriptions = []; for (var _i = 0, temp_1 = temp; _i < temp_1.length; _i++) { var s = temp_1[_i]; s.disposable.dispose(); } for (var _a = 0, temp_2 = temp; _a < temp_2.length; _a++) { var s1 = temp_2[_a]; this.subscribe(s.channel, s.action); } } Atom.refresh(this, "channelPrefix"); }, enumerable: true, configurable: true }); Object.defineProperty(AtomViewModel.prototype, "isReady", { get: function () { return this._isReady; }, enumerable: true, configurable: true }); AtomViewModel.prototype.privateInit = function () { return __awaiter(this, void 0, void 0, function () { var _this = this; var _i, _a, i; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, , 3, 4]); return [4 /*yield*/, Atom.postAsync(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { this.runDecoratorInits(); return [2 /*return*/]; }); }); })]; case 1: _b.sent(); return [4 /*yield*/, Atom.postAsync(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.init()]; case 1: _a.sent(); this.onReady(); return [2 /*return*/]; } }); }); })]; case 2: _b.sent(); if (this.postInit) { for (_i = 0, _a = this.postInit; _i < _a.length; _i++) { i = _a[_i]; i(); } this.postInit = null; } return [3 /*break*/, 4]; case 3: this._isReady = true; return [7 /*endfinally*/]; case 4: return [2 /*return*/]; } }); }); }; AtomViewModel.prototype.waitForReady = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!!this._isReady) return [3 /*break*/, 2]; return [4 /*yield*/, Atom.delay(100)]; case 1: _a.sent(); return [3 /*break*/, 0]; case 2: return [2 /*return*/]; } }); }); }; // tslint:disable-next-line:no-empty AtomViewModel.prototype.onReady = function () { }; AtomViewModel.prototype.runDecoratorInits = function () { var v = this.constructor.prototype; if (!v) { return; } var ris = v._$_inits; if (ris) { for (var _i = 0, ris_1 = ris; _i < ris_1.length; _i++) { var ri = ris_1[_i]; ri.call(this, this); } } }; /** * Internal method, do not use, instead use errors.hasErrors() * * @memberof AtomViewModel */ AtomViewModel.prototype.validate = function () { for (var _i = 0, _a = this.validations; _i < _a.length; _i++) { var v = _a[_i]; v.evaluate(true); } }; /** * Adds validation expression to be executed when any bindable expression is updated. * * `target` must always be set to `this`. * * this.addValidation(() => { * this.errors.nameError = this.data.firstName ? "" : "Name cannot be empty"; * }); * * Only difference here is, validation will not kick in first time, where else watch will * be invoked as soon as it is setup. * * Validation will be invoked when any bindable property in given expression is updated. * * Validation can be invoked explicitly only by calling `errors.hasErrors()`. * * @protected * @template T * @param {() => any} ft * @returns {AtomDisposable} * @memberof AtomViewModel */ AtomViewModel.prototype.addValidation = function () { var _this = this; var fts = []; for (var _i = 0; _i < arguments.length; _i++) { fts[_i] = arguments[_i]; } var ds = []; for (var _a = 0, fts_1 = fts; _a < fts_1.length; _a++) { var ft = fts_1[_a]; var d = new WebAtoms.AtomWatcher(this, ft, false, true); this.validations.push(d); this.registerDisposable(d); ds.push(d); } return new WebAtoms.DisposableAction(function () { _this.disposables = _this.disposables.filter(function (f) { return !ds.find(function (fd) { return f === fd; }); }); for (var _i = 0, ds_1 = ds; _i < ds_1.length; _i++) { var dispsoable = ds_1[_i]; dispsoable.dispose(); } }); }; /** * Execute given expression whenever any bindable expression changes * in the expression. * * For correct generic type resolution, target must always be `this`. * * this.watch(() => { * if(!this.data.fullName){ * this.data.fullName = `${this.data.firstName} ${this.data.lastName}`; * } * });