UNPKG

nakedobjects.spa

Version:

Single Page Application client for a Naked Objects application.

404 lines 18.3 kB
import { CommandResult } from './command-result'; import * as Routedata from '../route-data'; import * as Models from '../models'; import * as Usermessages from '../user-messages'; import map from 'lodash/map'; import some from 'lodash/some'; import filter from 'lodash/filter'; import every from 'lodash/every'; import each from 'lodash/each'; import keys from 'lodash/keys'; import forEach from 'lodash/forEach'; import findIndex from 'lodash/findIndex'; import * as Commandresult from './command-result'; var Command = (function () { function Command(urlManager, location, commandFactory, context, mask, error, configService, ciceroContext, ciceroRenderer) { this.urlManager = urlManager; this.location = location; this.commandFactory = commandFactory; this.context = context; this.mask = mask; this.error = error; this.configService = configService; this.ciceroContext = ciceroContext; this.ciceroRenderer = ciceroRenderer; this.keySeparator = configService.config.keySeparator; } Command.prototype.execute = function () { var result = new CommandResult(); //TODO Create outgoing Vm and copy across values as needed if (!this.isAvailableInCurrentContext()) { return this.returnResult("", Usermessages.commandNotAvailable(this.fullCommand)); } //TODO: This could be moved into a pre-parse method as it does not depend on context if (this.argString == null) { if (this.minArguments > 0) { return this.returnResult("", Usermessages.noArguments); } } else { var args = this.argString.split(","); if (args.length < this.minArguments) { return this.returnResult("", Usermessages.tooFewArguments); } else if (args.length > this.maxArguments) { return this.returnResult("", Usermessages.tooManyArguments); } } return this.doExecute(this.argString, this.chained, result); }; Command.prototype.returnResult = function (input, output, changeState, stopChain) { changeState = changeState ? changeState : function () { }; return Promise.resolve({ input: input, output: output, changeState: changeState, stopChain: !!stopChain }); }; Command.prototype.mayNotBeChained = function (rider) { if (rider === void 0) { rider = ""; } return Usermessages.mayNotbeChainedMessage(this.fullCommand, rider); }; Command.prototype.checkMatch = function (matchText) { if (this.fullCommand.indexOf(matchText) !== 0) { throw new Error(Usermessages.noSuchCommand(matchText)); } }; //argNo starts from 0. //If argument does not parse correctly, message will be passed to UI and command aborted. Command.prototype.argumentAsString = function (argString, argNo, optional, toLower) { if (optional === void 0) { optional = false; } if (toLower === void 0) { toLower = true; } if (!argString) return undefined; if (!optional && argString.split(",").length < argNo + 1) { throw new Error(Usermessages.tooFewArguments); } var args = argString.split(","); if (args.length < argNo + 1) { if (optional) { return undefined; } else { throw new Error(Usermessages.missingArgument(argNo + 1)); } } return toLower ? args[argNo].trim().toLowerCase() : args[argNo].trim(); // which may be "" if argString ends in a ',' }; //argNo starts from 0. Command.prototype.argumentAsNumber = function (args, argNo, optional) { if (optional === void 0) { optional = false; } var arg = this.argumentAsString(args, argNo, optional); if (!arg && optional) return null; var number = parseInt(arg); if (isNaN(number)) { throw new Error(Usermessages.wrongTypeArgument(argNo + 1)); } return number; }; Command.prototype.parseInt = function (input) { var number = parseInt(input); if (isNaN(number)) { throw new Error(Usermessages.isNotANumber(input)); } return number; }; //Parses '17, 3-5, -9, 6-' into two numbers Command.prototype.parseRange = function (arg) { if (!arg) { arg = "1-"; } var clauses = arg.split("-"); var range = { start: null, end: null }; switch (clauses.length) { case 1: { var firstValue = clauses[0]; range.start = firstValue ? this.parseInt(firstValue) : null; range.end = range.start; break; } case 2: { var firstValue = clauses[0]; var secondValue = clauses[1]; range.start = firstValue ? this.parseInt(firstValue) : null; range.end = secondValue ? this.parseInt(secondValue) : null; break; } default: throw new Error(Usermessages.tooManyDashes); } if ((range.start != null && range.start < 1) || (range.end != null && range.end < 1)) { throw new Error(Usermessages.mustBeGreaterThanZero); } return range; }; Command.prototype.getContextDescription = function () { //todo return null; }; Command.prototype.routeData = function () { return this.urlManager.getRouteData().pane1; }; //Helpers delegating to RouteData Command.prototype.isHome = function () { return this.urlManager.isHome(); }; Command.prototype.isObject = function () { return this.urlManager.isObject(); }; Command.prototype.getObject = function () { var _this = this; var oid = Models.ObjectIdWrapper.fromObjectId(this.routeData().objectId, this.keySeparator); //TODO: Consider view model & transient modes? return this.context.getObject(1, oid, this.routeData().interactionMode).then(function (obj) { if (_this.routeData().interactionMode === Routedata.InteractionMode.Edit) { return _this.context.getObjectForEdit(1, obj); } else { return obj; //To wrap a known object as a promise } }); }; Command.prototype.isList = function () { return this.urlManager.isList(); }; Command.prototype.getList = function () { var routeData = this.routeData(); //TODO: Currently covers only the list-from-menu; need to cover list from object action return this.context.getListFromMenu(routeData, routeData.page, routeData.pageSize); }; Command.prototype.isMenu = function () { return !!this.routeData().menuId; }; Command.prototype.getMenu = function () { return this.context.getMenu(this.routeData().menuId); }; Command.prototype.isDialog = function () { return !!this.routeData().dialogId; }; Command.prototype.isMultiChoiceField = function (field) { var entryType = field.entryType(); return entryType === Models.EntryType.MultipleChoices || entryType === Models.EntryType.MultipleConditionalChoices; }; Command.prototype.getActionForCurrentDialog = function () { var _this = this; var dialogId = this.routeData().dialogId; if (this.isObject()) { return this.getObject().then(function (obj) { return _this.context.getInvokableAction(obj.actionMember(dialogId)); }); } else if (this.isMenu()) { return this.getMenu().then(function (menu) { return _this.context.getInvokableAction(menu.actionMember(dialogId)); }); //i.e. return a promise } return Promise.reject(new Models.ErrorWrapper(Models.ErrorCategory.ClientError, Models.ClientErrorCode.NotImplemented, "List actions not implemented yet")); }; //Tests that at least one collection is open (should only be one). //TODO: assumes that closing collection removes it from routeData NOT sets it to Summary Command.prototype.isCollection = function () { return this.isObject() && some(this.routeData().collections); }; Command.prototype.closeAnyOpenCollections = function () { var _this = this; var open = this.ciceroRenderer.openCollectionIds(this.routeData()); forEach(open, function (id) { return _this.urlManager.setCollectionMemberState(id, Routedata.CollectionViewState.Summary); }); }; Command.prototype.isTable = function () { return false; //TODO }; Command.prototype.isEdit = function () { return this.routeData().interactionMode === Routedata.InteractionMode.Edit; }; Command.prototype.isForm = function () { return this.routeData().interactionMode === Routedata.InteractionMode.Form; }; Command.prototype.isTransient = function () { return this.routeData().interactionMode === Routedata.InteractionMode.Transient; }; Command.prototype.matchingProperties = function (obj, match) { var props = map(obj.propertyMembers(), function (prop) { return prop; }); if (match) { props = this.matchFriendlyNameAndOrMenuPath(props, match); } return props; }; Command.prototype.matchingCollections = function (obj, match) { var allColls = map(obj.collectionMembers(), function (action) { return action; }); if (match) { return this.matchFriendlyNameAndOrMenuPath(allColls, match); } else { return allColls; } }; Command.prototype.matchingParameters = function (action, match) { var params = map(action.parameters(), function (p) { return p; }); if (match) { params = this.matchFriendlyNameAndOrMenuPath(params, match); } return params; }; Command.prototype.matchFriendlyNameAndOrMenuPath = function (reps, match) { var clauses = match ? match.split(" ") : []; //An exact match has preference over any partial match var exactMatches = filter(reps, function (rep) { var path = rep.extensions().menuPath(); var name = rep.extensions().friendlyName().toLowerCase(); return match === name || (!!path && match === path.toLowerCase() + " " + name) || every(clauses, function (clause) { return name === clause || (!!path && path.toLowerCase() === clause); }); }); if (exactMatches.length > 0) return exactMatches; return filter(reps, function (rep) { var path = rep.extensions().menuPath(); var name = rep.extensions().friendlyName().toLowerCase(); return every(clauses, function (clause) { return name.indexOf(clause) >= 0 || (!!path && path.toLowerCase().indexOf(clause) >= 0); }); }); }; Command.prototype.findMatchingChoicesForRef = function (choices, titleMatch) { return choices ? filter(choices, function (v) { return v.toString().toLowerCase().indexOf(titleMatch.toLowerCase()) >= 0; }) : []; }; Command.prototype.findMatchingChoicesForScalar = function (choices, titleMatch) { if (choices == null) { return []; } var labels = keys(choices); var matchingLabels = filter(labels, function (l) { return l.toString().toLowerCase().indexOf(titleMatch.toLowerCase()) >= 0; }); var result = new Array(); switch (matchingLabels.length) { case 0: break; //leave result empty case 1: //Push the VALUE for the key //For simple scalars they are the same, but not for Enums result.push(choices[matchingLabels[0]]); break; default: //Push the matching KEYs, wrapped as (pseudo) Values for display in message to user //For simple scalars the values would also be OK, but not for Enums forEach(matchingLabels, function (label) { return result.push(new Models.Value(label)); }); break; } return result; }; Command.prototype.handleErrorResponse = function (err, getFriendlyName) { var _this = this; if (err.invalidReason()) { return this.returnResult("", err.invalidReason()); } var msg = Usermessages.pleaseCompleteOrCorrect; each(err.valuesMap(), function (errorValue, fieldId) { msg += _this.fieldValidationMessage(errorValue, function () { return getFriendlyName(fieldId); }); }); return this.returnResult("", msg); }; Command.prototype.handleErrorResponseNew = function (err, getFriendlyName) { var _this = this; if (err.invalidReason()) { return this.returnResult("", err.invalidReason()); } var msg = Usermessages.pleaseCompleteOrCorrect; each(err.valuesMap(), function (errorValue, fieldId) { msg += _this.fieldValidationMessage(errorValue, function () { return getFriendlyName(fieldId); }); }); return this.returnResult("", msg); }; Command.prototype.validationMessage = function (reason, value, fieldFriendlyName) { if (reason) { var prefix = fieldFriendlyName + ": "; var suffix = reason === Usermessages.mandatory ? Usermessages.required : value + " " + reason; return "" + prefix + suffix + "\n"; } return ""; }; Command.prototype.fieldValidationMessage = function (errorValue, fieldFriendlyName) { var reason = errorValue.invalidReason; return this.validationMessage(reason, errorValue.value, fieldFriendlyName()); }; Command.prototype.valueForUrl = function (val, field) { var _this = this; if (val.isNull()) return val; var fieldEntryType = field.entryType(); if (fieldEntryType !== Models.EntryType.FreeForm || field.isCollectionContributed()) { if (this.isMultiChoiceField(field) || field.isCollectionContributed()) { var valuesFromRouteData_1 = new Array(); if (field instanceof Models.Parameter) { var rd = Commandresult.getParametersAndCurrentValue(field.parent, this.context)[field.id()]; if (rd) valuesFromRouteData_1 = rd.list(); //TODO: what if only one? } else if (field instanceof Models.PropertyMember) { var obj = field.parent; var props = this.context.getObjectCachedValues(obj.id()); var rd = props[field.id()]; if (rd) valuesFromRouteData_1 = rd.list(); //TODO: what if only one? } var vals = []; if (val.isReference() || val.isScalar()) { vals = new Array(val); } else if (val.isList()) { vals = val.list(); } valuesFromRouteData_1 = valuesFromRouteData_1 || []; forEach(vals, function (v) { return _this.addOrRemoveValue(valuesFromRouteData_1, v); }); if (vals[0].isScalar()) { var scalars = map(valuesFromRouteData_1, function (v) { return v.scalar(); }); return new Models.Value(scalars); } else { var links = map(valuesFromRouteData_1, function (v) { return ({ href: v.link().href(), title: Models.withUndefined(v.link().title()) }); }); return new Models.Value(links.length > 0 ? links : null); } } if (val.isScalar()) { return val; } // reference return this.leanLink(val); } if (val.isScalar()) { if (val.isNull()) { return new Models.Value(""); } return val; //TODO: consider these options: // if (from.value instanceof Date) { // return new Value((from.value as Date).toISOString()); // } // return new Value(from.value as number | string | boolean); } if (val.isReference()) { return this.leanLink(val); } return null; }; Command.prototype.leanLink = function (val) { return new Models.Value({ href: val.link().href(), title: val.link().title() }); }; Command.prototype.addOrRemoveValue = function (valuesFromRouteData, val) { var index; var valToAdd; if (val.isScalar()) { valToAdd = val; index = findIndex(valuesFromRouteData, function (v) { return v.scalar() === val.scalar(); }); } else { valToAdd = this.leanLink(val); index = findIndex(valuesFromRouteData, function (v) { return v.link().href() === valToAdd.link().href(); }); } if (index > -1) { valuesFromRouteData.splice(index, 1); } else { valuesFromRouteData.push(valToAdd); } }; Command.prototype.setFieldValueInContext = function (field, val) { this.context.cacheFieldValue(this.routeData().dialogId, field.id(), val); }; Command.prototype.setPropertyValueinContext = function (obj, property, urlVal) { this.context.cachePropertyValue(obj, property, urlVal); }; return Command; }()); export { Command }; //# sourceMappingURL=Command.js.map