var DotvvmDomUtils = (function () { function DotvvmDomUtils() { } DotvvmDomUtils.prototype.onDocumentReady = function (callback) { // many thanks to http://dustindiaz.com/smallest-domready-ever /in/.test(document.readyState) ? setTimeout('dotvvm.domUtils.onDocumentReady(' + callback + ')', 9) : callback(); }; DotvvmDomUtils.prototype.attachEvent = function (target, name, callback, useCapture) { if (useCapture === void 0) { useCapture = false; } if (target.addEventListener) { target.addEventListener(name, callback, useCapture); } else { target.attachEvent("on" + name, callback); } }; return DotvvmDomUtils; })(); var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var DotvvmEvents = (function () { function DotvvmEvents() { this.init = new DotvvmEvent("dotvvm.events.init", true); this.beforePostback = new DotvvmEvent("dotvvm.events.beforePostback"); this.afterPostback = new DotvvmEvent("dotvvm.events.afterPostback"); this.error = new DotvvmEvent("dotvvm.events.error"); this.spaNavigating = new DotvvmEvent("dotvvm.events.spaNavigating"); this.spaNavigated = new DotvvmEvent("dotvvm.events.spaNavigated"); this.redirect = new DotvvmEvent("dotvvm.events.redirect"); } return DotvvmEvents; })(); // DotvvmEvent is used because CustomEvent is not browser compatible and does not support // calling missed events for handler that subscribed too late. var DotvvmEvent = (function () { function DotvvmEvent(name, triggerMissedEventsOnSubscribe) { if (triggerMissedEventsOnSubscribe === void 0) { triggerMissedEventsOnSubscribe = false; } this.name = name; this.triggerMissedEventsOnSubscribe = triggerMissedEventsOnSubscribe; this.handlers = []; this.history = []; } DotvvmEvent.prototype.subscribe = function (handler) { this.handlers.push(handler); if (this.triggerMissedEventsOnSubscribe) { for (var i = 0; i < this.history.length; i++) { handler(history[i]); } } }; DotvvmEvent.prototype.unsubscribe = function (handler) { var index = this.handlers.indexOf(handler); if (index >= 0) { this.handlers = this.handlers.splice(index, 1); } }; DotvvmEvent.prototype.trigger = function (data) { for (var i = 0; i < this.handlers.length; i++) { this.handlers[i](data); } if (this.triggerMissedEventsOnSubscribe) { this.history.push(data); } }; return DotvvmEvent; })(); var DotvvmEventArgs = (function () { function DotvvmEventArgs(viewModel) { this.viewModel = viewModel; } return DotvvmEventArgs; })(); var DotvvmErrorEventArgs = (function (_super) { __extends(DotvvmErrorEventArgs, _super); function DotvvmErrorEventArgs(viewModel, xhr, isSpaNavigationError) { if (isSpaNavigationError === void 0) { isSpaNavigationError = false; } _super.call(this, viewModel); this.viewModel = viewModel; this.xhr = xhr; this.isSpaNavigationError = isSpaNavigationError; this.handled = false; } return DotvvmErrorEventArgs; })(DotvvmEventArgs); var DotvvmBeforePostBackEventArgs = (function (_super) { __extends(DotvvmBeforePostBackEventArgs, _super); function DotvvmBeforePostBackEventArgs(sender, viewModel, viewModelName, validationTargetPath) { _super.call(this, viewModel); this.sender = sender; this.viewModel = viewModel; this.viewModelName = viewModelName; this.validationTargetPath = validationTargetPath; this.cancel = false; this.clientValidationFailed = false; } return DotvvmBeforePostBackEventArgs; })(DotvvmEventArgs); var DotvvmAfterPostBackEventArgs = (function (_super) { __extends(DotvvmAfterPostBackEventArgs, _super); function DotvvmAfterPostBackEventArgs(sender, viewModel, viewModelName, validationTargetPath, serverResponseObject) { _super.call(this, viewModel); this.sender = sender; this.viewModel = viewModel; this.viewModelName = viewModelName; this.validationTargetPath = validationTargetPath; this.serverResponseObject = serverResponseObject; this.isHandled = false; this.wasInterrupted = false; } return DotvvmAfterPostBackEventArgs; })(DotvvmEventArgs); var DotvvmSpaNavigatingEventArgs = (function (_super) { __extends(DotvvmSpaNavigatingEventArgs, _super); function DotvvmSpaNavigatingEventArgs(viewModel, viewModelName, newUrl) { _super.call(this, viewModel); this.viewModel = viewModel; this.viewModelName = viewModelName; this.newUrl = newUrl; this.cancel = false; } return DotvvmSpaNavigatingEventArgs; })(DotvvmEventArgs); var DotvvmSpaNavigatedEventArgs = (function (_super) { __extends(DotvvmSpaNavigatedEventArgs, _super); function DotvvmSpaNavigatedEventArgs(viewModel, viewModelName, serverResponseObject) { _super.call(this, viewModel); this.viewModel = viewModel; this.viewModelName = viewModelName; this.serverResponseObject = serverResponseObject; this.isHandled = false; } return DotvvmSpaNavigatedEventArgs; })(DotvvmEventArgs); var DotvvmRedirectEventArgs = (function (_super) { __extends(DotvvmRedirectEventArgs, _super); function DotvvmRedirectEventArgs(viewModel, viewModelName, url, replace) { _super.call(this, viewModel); this.viewModel = viewModel; this.viewModelName = viewModelName; this.url = url; this.replace = replace; this.isHandled = false; } return DotvvmRedirectEventArgs; })(DotvvmEventArgs); var DotvvmEvaluator = (function () { function DotvvmEvaluator() { } DotvvmEvaluator.prototype.evaluateOnViewModel = function (context, expression) { var result; if (context && context.$data) { result = eval("(function ($context) { with($context) { with ($data) { return " + expression + "; } } })")(context); } else { result = eval("(function ($context) { var $data=$context; with($context) { return " + expression + "; } })")(context); } if (result && result.$data) { result = result.$data; } return result; }; DotvvmEvaluator.prototype.evaluateOnContext = function (context, expression) { var startsWithProperty = false; for (var prop in context) { if (expression.indexOf(prop) === 0) { startsWithProperty = true; break; } } if (!startsWithProperty) expression = "$data." + expression; return this.evaluateOnViewModel(context, expression); }; DotvvmEvaluator.prototype.getDataSourceItems = function (viewModel) { var value = ko.unwrap(viewModel); if (typeof value === "undefined" || value == null) return []; return ko.unwrap(value.Items || value); }; DotvvmEvaluator.prototype.tryEval = function (func) { try { return func(); } catch (error) { return null; } }; return DotvvmEvaluator; })(); var DotvvmGlobalize = (function () { function DotvvmGlobalize() { } DotvvmGlobalize.prototype.format = function (format) { var _this = this; var values = []; for (var _i = 1; _i < arguments.length; _i++) { values[_i - 1] = arguments[_i]; } return format.replace(/\{([1-9]?[0-9]+)(:[^}])?\}/g, function (match, group0, group1) { var value = values[parseInt(group0)]; if (group1) { return _this.formatString(group1, value); } else { return value; } }); }; DotvvmGlobalize.prototype.formatString = function (format, value) { value = ko.unwrap(value); if (value == null) return ""; if (typeof value === "string") { // JSON date in string value = this.parseDotvvmDate(value); } if (format === "" || format === null) { format = "G"; } return dotvvm_Globalize.format(value, format, dotvvm.culture); }; DotvvmGlobalize.prototype.parseDotvvmDate = function (value) { var match = value.match("^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]{3,7})$"); if (match) { return new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), parseInt(match[4]), parseInt(match[5]), parseInt(match[6]), match.length > 7 ? parseInt(match[7].substring(1, 4)) : 0); } return null; }; DotvvmGlobalize.prototype.parseNumber = function (value) { return dotvvm_Globalize.parseFloat(value, 10, dotvvm.culture); }; DotvvmGlobalize.prototype.parseDate = function (value, format, previousValue) { return dotvvm_Globalize.parseDate(value, format, dotvvm.culture, previousValue); }; return DotvvmGlobalize; })(); var DotvvmPostBackHandler = (function () { function DotvvmPostBackHandler() { } DotvvmPostBackHandler.prototype.execute = function (callback, sender) { }; return DotvvmPostBackHandler; })(); var ConfirmPostBackHandler = (function (_super) { __extends(ConfirmPostBackHandler, _super); function ConfirmPostBackHandler(message) { _super.call(this); this.message = message; } ConfirmPostBackHandler.prototype.execute = function (callback, sender) { if (confirm(this.message)) { callback(); } }; return ConfirmPostBackHandler; })(DotvvmPostBackHandler); var DotvvmPostBackHandlers = (function () { function DotvvmPostBackHandlers() { this.confirm = function (options) { return new ConfirmPostBackHandler(options.message); }; } return DotvvmPostBackHandlers; })(); var DotvvmPromiseState; (function (DotvvmPromiseState) { DotvvmPromiseState[DotvvmPromiseState["Pending"] = 0] = "Pending"; DotvvmPromiseState[DotvvmPromiseState["Done"] = 1] = "Done"; DotvvmPromiseState[DotvvmPromiseState["Failed"] = 2] = "Failed"; })(DotvvmPromiseState || (DotvvmPromiseState = {})); var DotvvmPromise = (function () { function DotvvmPromise() { this.callbacks = []; this.errorCallbacks = []; this.state = DotvvmPromiseState.Pending; } DotvvmPromise.prototype.done = function (callback, forceAsync) { var _this = this; if (forceAsync === void 0) { forceAsync = false; } if (this.state === DotvvmPromiseState.Done) { if (forceAsync) setTimeout(function () { return callback(_this.argument); }, 4); else callback(this.argument); } else if (this.state === DotvvmPromiseState.Pending) { this.callbacks.push(callback); } }; DotvvmPromise.prototype.fail = function (callback, forceAsync) { var _this = this; if (forceAsync === void 0) { forceAsync = false; } if (this.state === DotvvmPromiseState.Failed) { if (forceAsync) setTimeout(function () { return callback(_this.error); }, 4); else callback(this.error); } else if (this.state === DotvvmPromiseState.Pending) { this.errorCallbacks.push(callback); } return this; }; DotvvmPromise.prototype.resolve = function (arg) { if (this.state !== DotvvmPromiseState.Pending) throw new Error("Can not resolve " + this.state + " promise."); this.state = DotvvmPromiseState.Done; this.argument = arg; for (var _i = 0, _a = this.callbacks; _i < _a.length; _i++) { var c = _a[_i]; c(arg); } this.callbacks = null; this.errorCallbacks = null; return this; }; DotvvmPromise.prototype.reject = function (error) { if (this.state != DotvvmPromiseState.Pending) throw new Error("Can not reject " + this.state + " promise."); this.state = DotvvmPromiseState.Failed; this.error = error; for (var _i = 0, _a = this.errorCallbacks; _i < _a.length; _i++) { var c = _a[_i]; c(error); } this.callbacks = null; this.errorCallbacks = null; return this; }; DotvvmPromise.prototype.chainFrom = function (promise) { var _this = this; promise.done(function (a) { return _this.resolve(a); }); promise.fail(function (e) { return _this.reject(e); }); return this; }; return DotvvmPromise; })(); var DotvvmSerialization = (function () { function DotvvmSerialization() { } DotvvmSerialization.prototype.deserialize = function (viewModel, target, deserializeAll) { if (deserializeAll === void 0) { deserializeAll = false; } if (typeof (viewModel) == "undefined" || viewModel == null) { return viewModel; } if (typeof (viewModel) == "string" || typeof (viewModel) == "number" || typeof (viewModel) == "boolean") { return viewModel; } if (viewModel instanceof Date) { return viewModel; } // handle arrays if (viewModel instanceof Array) { if (ko.isObservable(target) && target.removeAll && target() != null && target().length === viewModel.length) { // the array has the same number of items, update it var targetArray = target(); for (var i = 0; i < viewModel.length; i++) { var targetItem = targetArray[i](); var deserialized = this.deserialize(viewModel[i], targetItem, deserializeAll); if (targetItem !== deserialized) { // update the observable only if the item has changed targetArray[i](deserialized); } } } else { // rebuild the array because it is different var array = []; for (var i = 0; i < viewModel.length; i++) { array.push(this.wrapObservable(this.deserialize(viewModel[i], {}, deserializeAll))); } if (ko.isObservable(target)) { if (!target.removeAll) { // if the previous value was null, the property is not an observable array - make it ko.utils.extend(target, ko.observableArray['fn']); target = target.extend({ 'trackArrayChanges': true }); } target(array); } else { target = ko.observableArray(array); } } return target; } // handle objects if (typeof (target) === "undefined") { target = {}; } var result = ko.unwrap(target); if (result == null) { target = result = {}; } for (var prop in viewModel) { if (viewModel.hasOwnProperty(prop) && !/\$options$/.test(prop)) { var value = viewModel[prop]; if (typeof (value) === "undefined") { continue; } if (!ko.isObservable(value) && typeof (value) === "function") { continue; } var options = viewModel[prop + "$options"]; if (!deserializeAll && options && options.doNotUpdate) { continue; } // deserialize value var deserialized = ko.isObservable(value) ? value : this.deserialize(value, result[prop], deserializeAll); // update the property if (ko.isObservable(deserialized)) { if (ko.isObservable(result[prop])) { if (deserialized() !== result[prop]()) { result[prop](deserialized()); } } else { result[prop] = deserialized; } } else { if (ko.isObservable(result[prop])) { if (deserialized !== result[prop]()) result[prop](deserialized); } else { result[prop] = ko.observable(deserialized); } } if (options && options.clientExtenders && ko.isObservable(result[prop])) { for (var j = 0; j < options.clientExtenders.length; j++) { var extenderOptions = {}; var extenderInfo = options.clientExtenders[j]; extenderOptions[extenderInfo.name] = extenderInfo.parameter; result[prop].extend(extenderOptions); } } } } // copy the property options metadata for (var prop in viewModel) { if (viewModel.hasOwnProperty(prop) && /\$options$/.test(prop)) { result[prop] = viewModel[prop]; var originalName = prop.substring(0, prop.length - "$options".length); if (typeof result[originalName] === "undefined") { result[originalName] = ko.observable(); } } } return target; }; DotvvmSerialization.prototype.wrapObservable = function (obj) { if (!ko.isObservable(obj)) return ko.observable(obj); return obj; }; DotvvmSerialization.prototype.serialize = function (viewModel, opt) { if (opt === void 0) { opt = {}; } opt = ko.utils.extend({}, opt); if (opt.pathOnly && opt.path && opt.path.length === 0) opt.pathOnly = false; if (typeof (viewModel) === "undefined" || viewModel == null) { return null; } if (typeof (viewModel) === "string" || typeof (viewModel) === "number" || typeof (viewModel) === "boolean") { return viewModel; } if (ko.isObservable(viewModel)) { return this.serialize(ko.unwrap(viewModel), opt); } if (typeof (viewModel) === "function") { return null; } if (viewModel instanceof Array) { if (opt.pathOnly) { var index = parseInt(opt.path.pop()); var array = new Array(index + 1); array[index] = this.serialize(viewModel[index], opt); opt.path.push(index.toString()); return array; } else { var array = []; for (var i = 0; i < viewModel.length; i++) { array.push(this.serialize(viewModel[i], opt)); } return array; } } if (viewModel instanceof Date) { return this.serializeDate(viewModel); } var pathProp = opt.path && opt.path.pop(); var result = {}; for (var prop in viewModel) { if (viewModel.hasOwnProperty(prop)) { if (opt.pathOnly && prop !== pathProp) { continue; } var value = viewModel[prop]; if (opt.ignoreSpecialProperties && prop[0] === "$") continue; if (!opt.serializeAll && (/\$options$/.test(prop) || prop == "$validationErrors")) { continue; } if (typeof (value) === "undefined") { continue; } if (!ko.isObservable(value) && typeof (value) === "function") { continue; } var options = viewModel[prop + "$options"]; if (!opt.serializeAll && options && options.doNotPost) { } else if (opt.oneLevel) { result[prop] = ko.unwrap(value); } else if (!opt.serializeAll && options && options.pathOnly) { var path = options.pathOnly; if (!(path instanceof Array)) { path = opt.path || this.findObject(value, opt.pathMatcher); } if (path) { if (path.length === 0) { result[prop] = this.serialize(value, opt); } else { result[prop] = this.serialize(value, { ignoreSpecialProperties: opt.ignoreSpecialProperties, serializeAll: opt.serializeAll, path: path, pathOnly: true }); } } } else { result[prop] = this.serialize(value, opt); } if (options && options.type && !this.validateType(result[prop], options.type)) { delete result[prop]; options.wasInvalid = true; } } } if (pathProp) opt.path.push(pathProp); return result; }; DotvvmSerialization.prototype.validateType = function (value, type) { var nullable = type[type.length - 1] === "?"; if (nullable) { type = type.substr(0, type.length - 1); } if (nullable && (value == null || value == "")) { return true; } var intmatch = /(u?)int(\d*)/.exec(type); if (intmatch) { if (!/^-?\d*$/.test(value)) return false; var unsigned = intmatch[1] === "u"; var bits = parseInt(intmatch[2]); var minValue = 0; var maxValue = Math.pow(2, bits) - 1; if (!unsigned) { minValue = -((maxValue / 2) | 0); maxValue = maxValue + minValue; } var int = parseInt(value); return int >= minValue && int <= maxValue && int === parseFloat(value); } if (type === "number" || type === "single" || type === "double" || type === "decimal") { // should check if the value is numeric or number in a string return +value === value || (!isNaN(+value) && typeof value == "string"); } return true; }; DotvvmSerialization.prototype.findObject = function (obj, matcher) { if (matcher(obj)) return []; obj = ko.unwrap(obj); if (matcher(obj)) return []; if (typeof obj != "object") return null; for (var p in obj) { if (obj.hasOwnProperty(p)) { var match = this.findObject(obj[p], matcher); if (match) { match.push(p); return match; } } } return null; }; DotvvmSerialization.prototype.flatSerialize = function (viewModel) { return this.serialize(viewModel, { ignoreSpecialProperties: true, oneLevel: true, serializeAll: true }); }; DotvvmSerialization.prototype.getPureObject = function (viewModel) { viewModel = ko.unwrap(viewModel); if (viewModel instanceof Array) return viewModel.map(this.getPureObject.bind(this)); var result = {}; for (var prop in viewModel) { if (prop[0] != '$') result[prop] = viewModel[prop]; } return result; }; DotvvmSerialization.prototype.pad = function (value, digits) { while (value.length < digits) { value = "0" + value; } return value; }; DotvvmSerialization.prototype.serializeDate = function (date, convertToUtc) { if (convertToUtc === void 0) { convertToUtc = true; } var date2 = new Date(date.getTime()); if (convertToUtc) { date2.setMinutes(date.getMinutes() + date.getTimezoneOffset()); } else { date2 = date; } var y = this.pad(date2.getFullYear().toString(), 4); var m = this.pad((date2.getMonth() + 1).toString(), 2); var d = this.pad(date2.getDate().toString(), 2); var h = this.pad(date2.getHours().toString(), 2); var mi = this.pad(date2.getMinutes().toString(), 2); var s = this.pad(date2.getSeconds().toString(), 2); var ms = this.pad(date2.getMilliseconds().toString(), 3); return y + "-" + m + "-" + d + "T" + h + ":" + mi + ":" + s + "." + ms + "0000"; }; return DotvvmSerialization; })(); /// var DotvvmValidationContext = (function () { function DotvvmValidationContext(valueToValidate, parentViewModel, parameters) { this.valueToValidate = valueToValidate; this.parentViewModel = parentViewModel; this.parameters = parameters; } return DotvvmValidationContext; })(); var DotvvmValidationObservableMetadata = (function () { function DotvvmValidationObservableMetadata() { } return DotvvmValidationObservableMetadata; })(); var DotvvmValidationElementMetadata = (function () { function DotvvmValidationElementMetadata() { this.elementValidationState = true; } return DotvvmValidationElementMetadata; })(); var DotvvmValidatorBase = (function () { function DotvvmValidatorBase() { } DotvvmValidatorBase.prototype.isValid = function (context, property) { return false; }; DotvvmValidatorBase.prototype.isEmpty = function (value) { return value == null || (typeof value == "string" && value.trim() === ""); }; DotvvmValidatorBase.prototype.getValidationMetadata = function (property) { return property.dotvvmMetadata; }; return DotvvmValidatorBase; })(); var DotvvmRequiredValidator = (function (_super) { __extends(DotvvmRequiredValidator, _super); function DotvvmRequiredValidator() { _super.apply(this, arguments); } DotvvmRequiredValidator.prototype.isValid = function (context) { var value = context.valueToValidate; return !this.isEmpty(value); }; return DotvvmRequiredValidator; })(DotvvmValidatorBase); var DotvvmRegularExpressionValidator = (function (_super) { __extends(DotvvmRegularExpressionValidator, _super); function DotvvmRegularExpressionValidator() { _super.apply(this, arguments); } DotvvmRegularExpressionValidator.prototype.isValid = function (context) { var value = context.valueToValidate; var expr = context.parameters[0]; return this.isEmpty(value) || new RegExp(expr).test(value); }; return DotvvmRegularExpressionValidator; })(DotvvmValidatorBase); var DotvvmIntRangeValidator = (function (_super) { __extends(DotvvmIntRangeValidator, _super); function DotvvmIntRangeValidator() { _super.apply(this, arguments); } DotvvmIntRangeValidator.prototype.isValid = function (context) { var val = context.valueToValidate; var from = context.parameters[0]; var to = context.parameters[1]; return val % 1 === 0 && val >= from && val <= to; }; return DotvvmIntRangeValidator; })(DotvvmValidatorBase); var DotvvmEnforceClientFormatValidator = (function (_super) { __extends(DotvvmEnforceClientFormatValidator, _super); function DotvvmEnforceClientFormatValidator() { _super.apply(this, arguments); } DotvvmEnforceClientFormatValidator.prototype.isValid = function (context, property) { // parameters order: AllowNull, AllowEmptyString, AllowEmptyStringOrWhitespaces var valid = true; if (!context.parameters[0] && context.valueToValidate == null) { valid = false; } if (!context.parameters[1] && context.valueToValidate.length === 0) { valid = false; } if (!context.parameters[2] && this.isEmpty(context.valueToValidate)) { valid = false; } var metadata = this.getValidationMetadata(property); if (metadata && metadata.elementsMetadata) { for (var _i = 0, _a = metadata.elementsMetadata; _i < _a.length; _i++) { var metaElement = _a[_i]; if (!metaElement.elementValidationState) { valid = false; } } } return valid; }; return DotvvmEnforceClientFormatValidator; })(DotvvmValidatorBase); var DotvvmRangeValidator = (function (_super) { __extends(DotvvmRangeValidator, _super); function DotvvmRangeValidator() { _super.apply(this, arguments); } DotvvmRangeValidator.prototype.isValid = function (context, property) { var val = context.valueToValidate; var from = context.parameters[0]; var to = context.parameters[1]; return val >= from && val <= to; }; return DotvvmRangeValidator; })(DotvvmValidatorBase); var DotvvmNotNullValidator = (function (_super) { __extends(DotvvmNotNullValidator, _super); function DotvvmNotNullValidator() { _super.apply(this, arguments); } DotvvmNotNullValidator.prototype.isValid = function (context) { return context.valueToValidate !== null && context.valueToValidate !== undefined; }; return DotvvmNotNullValidator; })(DotvvmValidatorBase); var ValidationError = (function () { function ValidationError(targetObservable, errorMessage) { this.targetObservable = targetObservable; this.errorMessage = errorMessage; } ValidationError.getOrCreate = function (targetObservable) { if (targetObservable["wrappedProperty"]) { var newOne = targetObservable["wrappedProperty"](); if (ko.isObservable(newOne)) targetObservable = newOne; } if (!targetObservable.validationErrors) { targetObservable.validationErrors = ko.observableArray(); } return targetObservable.validationErrors; }; ValidationError.isValid = function (observable) { return !observable.validationErrors || observable.validationErrors().length === 0; }; ValidationError.clear = function (observable) { if (observable.validationErrors != null) { observable.validationErrors.removeAll(); } }; return ValidationError; })(); var DotvvmValidation = (function () { function DotvvmValidation(dotvvm) { var _this = this; this.rules = { "required": new DotvvmRequiredValidator(), "regularExpression": new DotvvmRegularExpressionValidator(), "intrange": new DotvvmIntRangeValidator(), "range": new DotvvmRangeValidator(), "notnull": new DotvvmNotNullValidator(), "enforceClientFormat": new DotvvmEnforceClientFormatValidator() }; this.errors = ko.observableArray([]); this.events = { validationErrorsChanged: new DotvvmEvent("dotvvm.validation.events.validationErrorsChanged") }; this.elementUpdateFunctions = { // shows the element when it is valid hideWhenValid: function (element, errorMessages, param) { if (errorMessages.length > 0) { element.style.display = ""; } else { element.style.display = "none"; } }, // adds a CSS class when the element is not valid invalidCssClass: function (element, errorMessages, className) { if (errorMessages.length > 0) { element.className += " " + className; } else { element.className = element.className.split(' ').filter(function (c) { return c != className; }).join(' '); } }, // sets the error message as the title attribute setToolTipText: function (element, errorMessages, param) { if (errorMessages.length > 0) { element.title = errorMessages.join(", "); } else { element.title = ""; } }, // displays the error message showErrorMessageText: function (element, errorMessages, param) { element[element.innerText ? "innerText" : "textContent"] = errorMessages.join(", "); } }; // perform the validation before postback dotvvm.events.beforePostback.subscribe(function (args) { if (args.validationTargetPath) { // resolve target var context = ko.contextFor(args.sender); var validationTarget = dotvvm.evaluator.evaluateOnViewModel(context, args.validationTargetPath); // validate the object _this.clearValidationErrors(args.viewModel); _this.validateViewModel(validationTarget); if (_this.errors().length > 0) { console.warn("Validation failed: postback aborted; errors: ", _this.errors()); args.cancel = true; args.clientValidationFailed = true; } } _this.events.validationErrorsChanged.trigger(args); }); dotvvm.events.afterPostback.subscribe(function (args) { if (!args.wasInterrupted && args.serverResponseObject) { if (args.serverResponseObject.action === "successfulCommand") { // merge validation rules from postback with those we already have (required when a new type appears in the view model) _this.mergeValidationRules(args); args.isHandled = true; } else if (args.serverResponseObject.action === "validationErrors") { // apply validation errors from server _this.showValidationErrorsFromServer(args); args.isHandled = true; } } _this.events.validationErrorsChanged.trigger(args); }); // add knockout binding handler ko.bindingHandlers["dotvvmValidation"] = { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { var observableProperty = valueAccessor(); if (ko.isObservable(observableProperty)) { // try to get the options var options = allBindingsAccessor.get("dotvvmValidationOptions"); var updateFunction = function (element, errorMessages) { for (var option in options) { if (options.hasOwnProperty(option)) { _this.elementUpdateFunctions[option](element, errorMessages.map(function (v) { return v.errorMessage; }), options[option]); } } }; // subscribe to the observable property changes var validationErrors = ValidationError.getOrCreate(observableProperty); validationErrors.subscribe(function (newValue) { return updateFunction(element, newValue); }); updateFunction(element, validationErrors()); } } }; } /// Validates the specified view model DotvvmValidation.prototype.validateViewModel = function (viewModel) { if (!viewModel || !dotvvm.viewModels['root'].validationRules) return; // find validation rules var type = ko.unwrap(viewModel.$type); if (!type) return; var rulesForType = dotvvm.viewModels['root'].validationRules[type] || {}; // validate all properties for (var property in viewModel) { if (!viewModel.hasOwnProperty(property) || property.indexOf("$") === 0) continue; var viewModelProperty = viewModel[property]; if (!viewModelProperty || !ko.isObservable(viewModelProperty)) continue; var value = viewModel[property](); // run validation rules if (rulesForType.hasOwnProperty(property)) { this.validateProperty(viewModel, viewModelProperty, value, rulesForType[property]); } var options = viewModel[property + "$options"]; if (options && options.type && ValidationError.isValid(viewModelProperty) && !dotvvm.serialization.validateType(value, options.type)) { var error = new ValidationError(viewModelProperty, "The value of property " + property + " (" + value + ") is invalid value for type " + options.type + "."); ValidationError.getOrCreate(viewModelProperty).push(error); this.addValidationError(viewModel, error); } if (value) { if (Array.isArray(value)) { // handle collections for (var _i = 0; _i < value.length; _i++) { var item = value[_i]; this.validateViewModel(ko.unwrap(item)); } } else if (value.$type) { // handle nested objects this.validateViewModel(value); } } } }; // validates the specified property in the viewModel DotvvmValidation.prototype.validateProperty = function (viewModel, property, value, rulesForProperty) { for (var _i = 0; _i < rulesForProperty.length; _i++) { var rule = rulesForProperty[_i]; // validate the rules var ruleTemplate = this.rules[rule.ruleName]; var context = new DotvvmValidationContext(value, viewModel, rule.parameters); if (!ruleTemplate.isValid(context, property)) { var validationErrors = ValidationError.getOrCreate(property); // add error message var validationError = new ValidationError(property, rule.errorMessage); validationErrors.push(validationError); this.addValidationError(viewModel, validationError); } } }; // merge validation rules DotvvmValidation.prototype.mergeValidationRules = function (args) { if (args.serverResponseObject.validationRules) { var existingRules = dotvvm.viewModels[args.viewModelName].validationRules; if (typeof existingRules === "undefined") { dotvvm.viewModels[args.viewModelName].validationRules = {}; existingRules = dotvvm.viewModels[args.viewModelName].validationRules; } for (var type in args.serverResponseObject) { if (!args.serverResponseObject.hasOwnProperty(type)) continue; existingRules[type] = args.serverResponseObject[type]; } } }; DotvvmValidation.prototype.clearValidationErrors = function (viewModel) { this.clearValidationErrorsCore(viewModel); this.errors.removeAll(); }; DotvvmValidation.prototype.clearValidationErrorsCore = function (viewModel) { viewModel = ko.unwrap(viewModel); if (!viewModel || !viewModel.$type) return; // clear validation errors if (viewModel.$validationErrors) { viewModel.$validationErrors.removeAll(); } else { viewModel.$validationErrors = ko.observableArray([]); } // validate all properties for (var property in viewModel) { if (!viewModel.hasOwnProperty(property) || property.indexOf("$") === 0) continue; var viewModelProperty = viewModel[property]; if (!viewModelProperty || !ko.isObservable(viewModelProperty)) continue; ValidationError.clear(viewModel[property]); var value = viewModel[property](); if (value) { if (Array.isArray(value)) { // handle collections for (var i = 0; i < value.length; i++) { this.clearValidationErrorsCore(ko.unwrap(value[i])); } } else if (value.$type) { // handle nested objects this.clearValidationErrorsCore(value); } } } }; // get validation errors DotvvmValidation.prototype.getValidationErrors = function (viewModel, recursive) { viewModel = ko.unwrap(viewModel); if (!viewModel || !viewModel.$type) return []; if (viewModel.$validationErrors == null) viewModel.$validationErrors = ko.observableArray([]); var errors = viewModel.$validationErrors(); if (recursive) { // get child validation errors for (var property in viewModel) { if (!viewModel.hasOwnProperty(property) || property.indexOf("$") === 0) continue; var viewModelProperty = viewModel[property]; if (!viewModelProperty || !ko.isObservable(viewModelProperty)) continue; var value = viewModel[property](); if (value) { if (Array.isArray(value)) { // handle collections for (var _i = 0; _i < value.length; _i++) { var item = value[_i]; errors = errors.concat(this.getValidationErrors(ko.unwrap(item), recursive)); } } else if (value.$type) { // handle nested objects errors = errors.concat(this.getValidationErrors(value, recursive)); } } } } return errors; }; // shows the validation errors from server DotvvmValidation.prototype.showValidationErrorsFromServer = function (args) { // resolve validation target var context = ko.contextFor(args.sender); var validationTarget = dotvvm.evaluator.evaluateOnViewModel(context, args.validationTargetPath); validationTarget = ko.unwrap(validationTarget); if (validationTarget == null) return; // add validation errors this.clearValidationErrors(args.viewModel); var modelState = args.serverResponseObject.modelState; for (var i = 0; i < modelState.length; i++) { // find the observable property var propertyPath = modelState[i].propertyPath; var observable = dotvvm.evaluator.evaluateOnViewModel(validationTarget, propertyPath); var parentPath = propertyPath.substring(0, propertyPath.lastIndexOf(".")); var parent = parentPath ? dotvvm.evaluator.evaluateOnViewModel(validationTarget, parentPath) : validationTarget; parent = ko.unwrap(parent); if (!ko.isObservable(observable) || !parent || !parent.$validationErrors) { throw "Invalid validation path!"; } // add the error to appropriate collections var errors = ValidationError.getOrCreate(observable); var error = new ValidationError(observable, modelState[i].errorMessage); errors.push(error); this.addValidationError(parent, error); } }; DotvvmValidation.prototype.addValidationError = function (viewModel, error) { if (viewModel.$validationErrors == null) viewModel.$validationErrors = ko.observableArray([]); if (viewModel.$validationErrors.indexOf(error) < 0) { viewModel.$validationErrors.push(error); this.errors.push(error); } }; return DotvvmValidation; })(); ; /// /// /// /// /// /// /// /// /// /// /// /// /// document.getElementByDotvvmId = function (id) { return document.querySelector("[data-dotvvm-id='" + id + "']"); }; var DotVVM = (function () { function DotVVM() { this.postBackCounter = 0; this.resourceSigns = {}; this.isViewModelUpdating = true; this.viewModelObservables = {}; this.isSpaReady = ko.observable(false); this.viewModels = {}; this.serialization = new DotvvmSerialization(); this.postBackHandlers = new DotvvmPostBackHandlers(); this.events = new DotvvmEvents(); this.globalize = new DotvvmGlobalize(); this.evaluator = new DotvvmEvaluator(); this.domUtils = new DotvvmDomUtils(); this.fileUpload = new DotvvmFileUpload(); this.extensions = {}; this.isPostbackRunning = ko.observable(false); } DotVVM.prototype.init = function (viewModelName, culture) { var _this = this; this.addKnockoutBindingHandlers(); // load the viewmodel var thisViewModel = this.viewModels[viewModelName] = JSON.parse(document.getElementById("__dot_viewmodel_" + viewModelName).value); if (thisViewModel.resources) { for (var r in thisViewModel.resources) { this.resourceSigns[r] = true; } } if (thisViewModel.renderedResources) { thisViewModel.renderedResources.forEach(function (r) { return _this.resourceSigns[r] = true; }); } var idFragment = thisViewModel.resultIdFragment; var viewModel = thisViewModel.viewModel = this.serialization.deserialize(this.viewModels[viewModelName].viewModel, {}, true); // initialize services this.culture = culture; this.validation = new DotvvmValidation(this); // wrap it in the observable this.viewModelObservables[viewModelName] = ko.observable(viewModel); ko.applyBindings(this.viewModelObservables[viewModelName], document.documentElement); // trigger the init event this.events.init.trigger(new DotvvmEventArgs(viewModel)); // handle SPA requests var spaPlaceHolder = this.getSpaPlaceHolder(); if (spaPlaceHolder) { this.domUtils.attachEvent(window, "hashchange", function () { return _this.handleHashChange(viewModelName, spaPlaceHolder, false); }); this.handleHashChange(viewModelName, spaPlaceHolder, true); } this.isViewModelUpdating = false; if (idFragment) { if (spaPlaceHolder) { var element = document.getElementById(idFragment); if (element && "function" == typeof element.scrollIntoView) element.scrollIntoView(true); } else location.hash = idFragment; } // persist the viewmodel in the hidden field so the Back button will work correctly this.domUtils.attachEvent(window, "beforeunload", function (e) { _this.persistViewModel(viewModelName); }); }; DotVVM.prototype.handleHashChange = function (viewModelName, spaPlaceHolder, isInitialPageLoad) { if (document.location.hash.indexOf("#!/") === 0) { // the user requested navigation to another SPA page this.navigateCore(viewModelName, document.location.hash.substring(2)); } else { var url = spaPlaceHolder.getAttribute("data-dotvvm-spacontentplaceholder-defaultroute"); if (url) { // perform redirect to default page url = "#!/" + url; url = this.fixSpaUrlPrefix(url); this.performRedirect(url, isInitialPageLoad); } else if (!isInitialPageLoad) { // get startup URL and redirect there url = document.location.toString(); var slashIndex = url.indexOf('/', 'https://'.length); if (slashIndex > 0) { url = url.substring(slashIndex); } else { url = "/"; } this.navigateCore(viewModelName, url); } else { // the page was loaded for the first time this.isSpaReady(true); spaPlaceHolder.style.display = ""; } } }; // binding helpers DotVVM.prototype.postbackScript = function (bindingId) { var _this = this; return function (pageArea, sender, pathFragments, controlId, useWindowSetTimeout, validationTarget, context, handlers) { _this.postBack(pageArea, sender, pathFragments, bindingId, controlId, useWindowSetTimeout, validationTarget, context, handlers); }; }; DotVVM.prototype.persistViewModel = function (viewModelName) { var viewModel = this.viewModels[viewModelName]; var persistedViewModel = {}; for (var p in viewModel) { if (viewModel.hasOwnProperty(p)) { persistedViewModel[p] = viewModel[p]; } } persistedViewModel["viewModel"] = this.serialization.serialize(persistedViewModel["viewModel"], { serializeAll: true }); document.getElementById("__dot_viewmodel_" + viewModelName).value = JSON.stringify(persistedViewModel); }; DotVVM.prototype.backUpPostBackConter = function () { this.postBackCounter++; return this.postBackCounter; }; DotVVM.prototype.isPostBackStillActive = function (currentPostBackCounter) { return this.postBackCounter === currentPostBackCounter; }; DotVVM.prototype.staticCommandPostback = function (viewModelName, sender, command, args, callback, errorCallback) { var _this = this; if (callback === void 0) { callback = function (_) { }; } if (errorCallback === void 0) { errorCallback = function (xhr, error) { }; } if (this.isPostBackProhibited(sender)) return; // TODO: events for static command postback // prevent double postbacks var currentPostBackCounter = this.backUpPostBackConter(); var data = this.serialization.serialize({ "args": args, "command": command, "$csrfToken": this.viewModels[viewModelName].viewModel.$csrfToken }); this.postJSON(this.viewModels[viewModelName].url, "POST", ko.toJSON(data), function (response) { if (!_this.isPostBackStillActive(currentPostBackCounter)) return; try { _this.isViewModelUpdating = true; callback(JSON.parse(response.responseText)); } catch (error) { errorCallback(response, error); } finally { _this.isViewModelUpdating = false; } }, errorCallback, function (xhr) { xhr.setRequestHeader("X-PostbackType", "StaticCommand"); }); }; DotVVM.prototype.processPassedId = function (id, context) { if (typeof id == "string" || id == null) return id; if (typeof id == "object" && id.expr) return this.evaluator.evaluateOnViewModel(context, id.expr); throw new Error("invalid argument"); }; DotVVM.prototype.postBack = function (viewModelName, sender, path, command, controlUniqueId, useWindowSetTimeout, validationTargetPath, context, handlers) { var _this = this; if (this.isPostBackProhibited(sender)) return new DotvvmPromise().reject("rejected"); var promise = new DotvvmPromise(); this.isPostbackRunning(true); promise.done(function () { return _this.isPostbackRunning(false); }); promise.fail(function () { return _this.isPostbackRunning(false); }); if (useWindowSetTimeout) { window.setTimeout(function () { return promise.chainFrom(_this.postBack(viewModelName, sender, path, command, controlUniqueId, false, validationTargetPath, context, handlers)); }, 0); return promise; } // apply postback handlers if (handlers && handlers.length > 0) { var handler = this.postBackHandlers[handlers[0].name]; var options = this.evaluator.evaluateOnViewModel(ko.contextFor(sender), "(" + handlers[0].options.toString() + ")()"); var handlerInstance = handler(options); var nextHandler = function () { return promise.chainFrom(_this.postBack(viewModelName, sender, path, command, controlUniqueId, false, validationTargetPath, context, handlers.slice(1))); }; if (options.enabled) { handlerInstance.execute(nextHandler, sender); } else { nextHandler(); } return promise; } var viewModel = this.viewModels[viewModelName].viewModel; // prevent double postbacks var currentPostBackCounter = this.backUpPostBackConter(); // trigger beforePostback event var beforePostbackArgs = new DotvvmBeforePostBackEventArgs(sender, viewModel, viewModelName, validationTargetPath); this.events.beforePostback.trigger(beforePostbackArgs); if (beforePostbackArgs.cancel) { // trigger afterPostback event var afterPostBackArgsCanceled = new DotvvmAfterPostBackEventArgs(sender, viewModel, viewModelName, validationTargetPath, null); afterPostBackArgsCanceled.wasInterrupted = true; this.events.afterPostback.trigger(afterPostBackArgsCanceled); return promise.reject("canceled"); } // perform the postback if (!context) { context = ko.contextFor(sender); } this.updateDynamicPathFragments(context, path); var data = { viewModel: this.serialization.serialize(viewModel, { pathMatcher: function (val) { return context && val == context.$data; } }), currentPath: path, command: command, controlUniqueId: this.processPassedId(controlUniqueId, context), validationTargetPath: validationTargetPath || null, renderedResources: this.viewModels[viewModelName].renderedResources }; this.postJSON(this.viewModels[viewModelName].url, "POST", ko.toJSON(data), function (result) { // if another postback has already been passed, don't do anything if (!_this.isPostBackStillActive(currentPostBackCounter)) { var afterPostBackArgsCanceled = new DotvvmAfterPostBackEventArgs(sender, viewModel, viewModelName, validationTargetPath, null); afterPostBackArgsCanceled.wasInterrupted = true; _this.events.afterPostback.trigger(afterPostBackArgsCanceled); promise.reject("postback collision"); return; } try { var resultObject, locationHeader = result.getResponseHeader("Location"); if (locationHeader != null && locationHeader.length > 0) { resultObject = { action: "redirect", url: locationHeader }; } else { resultObject = JSON.parse(result.responseText); } if (!resultObject.viewModel && resultObject.viewModelDiff) { // TODO: patch (~deserialize) it to ko.observable viewModel resultObject.viewModel = _this.patch(data.viewModel, resultObject.viewModelDiff); } _this.loadResourceList(resultObject.resources, function () { var isSuccess = false; if (resultObject.action === "successfulCommand") { try { _this.isViewModelUpdating = true; // remove updated controls var updatedControls = _this.cleanUpdatedControls(resultObject); // update the viewmodel if (resultObject.viewModel) { ko.delaySync.pause(); _this.serialization.deserialize(resultObject.viewModel, _this.viewModels[viewModelName].viewModel); ko.delaySync.resume(); } isSuccess = true; // remove updated controls which were previously hidden _this.cleanUpdatedControls(resultObject, updatedControls); // add updated controls _this.restoreUpdatedControls(resultObject, updatedControls, true); } finally { _this.isViewModelUpdating = false; } } else if (resultObject.action === "redirect") { // redirect _this.handleRedirect(resultObject, viewModelName); return; } var idFragment = resultObject.resultIdFragment; if (idFragment) { if (_this.getSpaPlaceHolder() || location.hash == "#" + idFragment) { var element = document.getElementById(idFragment); if (element && "function" == typeof element.scrollIntoView) element.scrollIntoView(true); } else location.hash = idFragment; } // trigger afterPostback event var afterPostBackArgs = new DotvvmAfterPostBackEventArgs(sender, viewModel, viewModelName, validationTargetPath, resultObject); promise.resolve(afterPostBackArgs); _this.events.afterPostback.trigger(afterPostBackArgs); if (!isSuccess && !afterPostBackArgs.isHandled) { _this.error(viewModel, result, promise); } }); } catch (error) { _this.error(viewModel, result, promise); } }, function (xhr) { // if another postback has already been passed, don't do anything if (!_this.isPostBackStillActive(currentPostBackCounter)) return; _this.error(viewModel, xhr, promise); }); return promise; }; DotVVM.prototype.error = function (viewModel, xhr, promise) { if (promise === void 0) { promise = null; } // execute error handlers var errArgs = new DotvvmErrorEventArgs(viewModel, xhr); if (promise != null) promise.reject(errArgs); this.events.error.trigger(errArgs); if (!errArgs.handled) { alert("unhandled error during postback"); } }; DotVVM.prototype.loadResourceList = function (resources, callback) { var html = ""; for (var name in resources) { if (!/^__noname_\d+$/.test(name)) { if (this.resourceSigns[name]) continue; this.resourceSigns[name] = true; } html += resources[name] + " "; } if (html.trim() === "") { setTimeout(callback, 4); return; } else { var tmp = document.createElement("div"); tmp.innerHTML = html; var elements = []; for (var i = 0; i < tmp.children.length; i++) { elements.push(tmp.children.item(i)); } this.loadResourceElements(elements, 0, callback); } }; DotVVM.prototype.loadResourceElements = function (elements, offset, callback) { var _this = this; if (offset >= elements.length) { callback(); return; } var el = elements[offset]; var waitForScriptLoaded = false; if (el.tagName.toLowerCase() == "script") { // create the script element var script = document.createElement("script"); if (el.src) { script.src = el.src; waitForScriptLoaded = true; } if (el.type) { script.type = el.type; } if (el.text) { script.text = el.text; } el = script; } else if (el.tagName.toLowerCase() == "link") { // create link var link = document.createElement("link"); if (el.href) { link.href = el.href; } if (el.rel) { link.rel = el.rel; } if (el.type) { link.type = el.type; } el = link; } // load next script when this is finished if (waitForScriptLoaded) { el.onload = function () { return _this.loadResourceElements(elements, offset + 1, callback); }; } document.head.appendChild(el); if (!waitForScriptLoaded) { this.loadResourceElements(elements, offset + 1, callback); } }; DotVVM.prototype.getSpaPlaceHolder = function () { var elements = document.getElementsByName("__dot_SpaContentPlaceHolder"); if (elements.length == 1) { return elements[0]; } return null; }; DotVVM.prototype.navigateCore = function (viewModelName, url) { var _this = this; var viewModel = this.viewModels[viewModelName].viewModel; // prevent double postbacks var currentPostBackCounter = this.backUpPostBackConter(); // trigger spaNavigating event var spaNavigatingArgs = new DotvvmSpaNavigatingEventArgs(viewModel, viewModelName, url); this.events.spaNavigating.trigger(spaNavigatingArgs); if (spaNavigatingArgs.cancel) { return; } // add virtual directory prefix url = "/___dotvvm-spa___" + this.addLeadingSlash(url); var fullUrl = this.addLeadingSlash(this.concatUrl(this.viewModels[viewModelName].virtualDirectory || "", url)); // find SPA placeholder var spaPlaceHolder = this.getSpaPlaceHolder(); if (!spaPlaceHolder) { document.location.href = fullUrl; return; } // send the request var spaPlaceHolderUniqueId = spaPlaceHolder.attributes["data-dotvvm-spacontentplaceholder"].value; this.getJSON(fullUrl, "GET", spaPlaceHolderUniqueId, function (result) { // if another postback has already been passed, don't do anything if (!_this.isPostBackStillActive(currentPostBackCounter)) return; var resultObject = JSON.parse(result.responseText); _this.loadResourceList(resultObject.resources, function () { var isSuccess = false; if (resultObject.action === "successfulCommand" || !resultObject.action) { try { _this.isViewModelUpdating = true; // remove updated controls var updatedControls = _this.cleanUpdatedControls(resultObject); // update the viewmodel _this.viewModels[viewModelName] = {}; for (var p in resultObject) { if (resultObject.hasOwnProperty(p)) { _this.viewModels[viewModelName][p] = resultObject[p]; } } ko.delaySync.pause(); _this.serialization.deserialize(resultObject.viewModel, _this.viewModels[viewModelName].viewModel); ko.delaySync.resume(); isSuccess = true; // add updated controls _this.viewModelObservables[viewModelName](_this.viewModels[viewModelName].viewModel); _this.restoreUpdatedControls(resultObject, updatedControls, true); _this.isSpaReady(true); } finally { _this.isViewModelUpdating = false; } } else if (resultObject.action === "redirect") { _this.handleRedirect(resultObject, viewModelName, true); return; } // trigger spaNavigated event var spaNavigatedArgs = new DotvvmSpaNavigatedEventArgs(viewModel, viewModelName, resultObject); _this.events.spaNavigated.trigger(spaNavigatedArgs); if (!isSuccess && !spaNavigatedArgs.isHandled) { throw "Invalid response from server!"; } }); }, function (xhr) { // if another postback has already been passed, don't do anything if (!_this.isPostBackStillActive(currentPostBackCounter)) return; // execute error handlers var errArgs = new DotvvmErrorEventArgs(viewModel, xhr, true); _this.events.error.trigger(errArgs); if (!errArgs.handled) { alert(xhr.responseText); } }); }; DotVVM.prototype.handleRedirect = function (resultObject, viewModelName, replace) { if (resultObject.replace != null) replace = resultObject.replace; var url; // redirect if (this.getSpaPlaceHolder() && resultObject.url.indexOf("//") < 0 && resultObject.allowSpa) { // relative URL - keep in SPA mode, but remove the virtual directory url = "#!" + this.removeVirtualDirectoryFromUrl(resultObject.url, viewModelName); if (url === "#!") { url = "#!/"; } // verify that the URL prefix is correct, if not - add it before the fragment url = this.fixSpaUrlPrefix(url); } else { // absolute URL - load the URL url = resultObject.url; } // trigger redirect event var redirectArgs = new DotvvmRedirectEventArgs(dotvvm.viewModels[viewModelName], viewModelName, url, replace); this.events.redirect.trigger(redirectArgs); this.performRedirect(url, replace); }; DotVVM.prototype.performRedirect = function (url, replace) { if (replace) { location.replace(url); } else { var fakeAnchor = this.fakeRedirectAnchor; if (!fakeAnchor) { fakeAnchor = document.createElement("a"); fakeAnchor.style.display = "none"; fakeAnchor.setAttribute("data-dotvvm-fake-id", "dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"); document.body.appendChild(fakeAnchor); this.fakeRedirectAnchor = fakeAnchor; } fakeAnchor.href = url; fakeAnchor.click(); } }; DotVVM.prototype.fixSpaUrlPrefix = function (url) { var attr = this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"]; if (!attr) { return url; } var correctPrefix = attr.value; var currentPrefix = document.location.pathname; if (correctPrefix !== currentPrefix) { if (correctPrefix === "") { correctPrefix = "/"; } url = correctPrefix + url; } return url; }; DotVVM.prototype.removeVirtualDirectoryFromUrl = function (url, viewModelName) { var virtualDirectory = "/" + this.viewModels[viewModelName].virtualDirectory; if (url.indexOf(virtualDirectory) == 0) { return this.addLeadingSlash(url.substring(virtualDirectory.length)); } else { return url; } }; DotVVM.prototype.addLeadingSlash = function (url) { if (url.length > 0 && url.substring(0, 1) != "/") { return "/" + url; } return url; }; DotVVM.prototype.concatUrl = function (url1, url2) { if (url1.length > 0 && url1.substring(url1.length - 1) == "/") { url1 = url1.substring(0, url1.length - 1); } return url1 + this.addLeadingSlash(url2); }; DotVVM.prototype.patch = function (source, patch) { var _this = this; if (source instanceof Array && patch instanceof Array) { return patch.map(function (val, i) { return _this.patch(source[i], val); }); } else if (source instanceof Array || patch instanceof Array) return patch; else if (typeof source == "object" && typeof patch == "object") { for (var p in patch) { if (patch[p] == null) source[p] = null; else if (source[p] == null) source[p] = patch[p]; else source[p] = this.patch(source[p], patch[p]); } } else return patch; return source; }; DotVVM.prototype.updateDynamicPathFragments = function (context, path) { for (var i = path.length - 1; i >= 0; i--) { if (path[i].indexOf("[$index]") >= 0) { path[i] = path[i].replace("[$index]", "[" + context.$index() + "]"); } context = context.$parentContext; } }; DotVVM.prototype.postJSON = function (url, method, postData, success, error, preprocessRequest) { if (preprocessRequest === void 0) { preprocessRequest = function (xhr) { }; } var xhr = this.getXHR(); xhr.open(method, url, true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("X-DotVVM-PostBack", "true"); xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); preprocessRequest(xhr); xhr.onreadystatechange = function () { if (xhr.readyState !== XMLHttpRequest.DONE) return; if (xhr.status < 400) { success(xhr); } else { error(xhr); } }; xhr.send(postData); }; DotVVM.prototype.getJSON = function (url, method, spaPlaceHolderUniqueId, success, error) { var xhr = this.getXHR(); xhr.open(method, url, true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("X-DotVVM-SpaContentPlaceHolder", spaPlaceHolderUniqueId); xhr.onreadystatechange = function () { if (xhr.readyState !== XMLHttpRequest.DONE) return; if (xhr.status < 400) { success(xhr); } else { error(xhr); } }; xhr.send(); }; DotVVM.prototype.getXHR = function () { return XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); }; DotVVM.prototype.cleanUpdatedControls = function (resultObject, updatedControls) { if (updatedControls === void 0) { updatedControls = {}; } for (var id in resultObject.updatedControls) { if (resultObject.updatedControls.hasOwnProperty(id)) { var control = document.getElementByDotvvmId(id); if (control) { var dataContext = ko.contextFor(control); var nextSibling = control.nextSibling; var parent = control.parentNode; ko.removeNode(control); updatedControls[id] = { control: control, nextSibling: nextSibling, parent: parent, dataContext: dataContext }; } } } return updatedControls; }; DotVVM.prototype.restoreUpdatedControls = function (resultObject, updatedControls, applyBindingsOnEachControl) { var _this = this; for (var id in resultObject.updatedControls) { if (resultObject.updatedControls.hasOwnProperty(id)) { var updatedControl = updatedControls[id]; if (updatedControl) { var wrapper = document.createElement(updatedControls[id].parent.tagName || "div"); wrapper.innerHTML = resultObject.updatedControls[id]; if (wrapper.childElementCount > 1) throw new Error("Postback.Update control can not render more than one element"); var element = wrapper.firstElementChild; if (element.id == null) throw new Error("Postback.Update control always has to render id attribute."); if (element.id !== updatedControls[id].control.id) console.log("Postback.Update control changed id from '" + updatedControls[id].control.id + "' to '" + element.id + "'"); wrapper.removeChild(element); if (updatedControl.nextSibling) { updatedControl.parent.insertBefore(element, updatedControl.nextSibling); } else { updatedControl.parent.appendChild(element); } } } } if (applyBindingsOnEachControl) { window.setTimeout(function () { try { _this.isViewModelUpdating = true; for (var id in resultObject.updatedControls) { var updatedControl = document.getElementByDotvvmId(id); if (updatedControl) { ko.applyBindings(updatedControls[id].dataContext, updatedControl); } } } finally { _this.isViewModelUpdating = false; } }, 0); } }; DotVVM.prototype.unwrapArrayExtension = function (array) { return ko.unwrap(ko.unwrap(array)); }; DotVVM.prototype.buildRouteUrl = function (routePath, params) { return routePath.replace(/\{([^\}]+?)\??(:(.+?))?\}/g, function (s, paramName, hsjdhsj, type) { if (!paramName) return ""; return ko.unwrap(params[paramName.toLowerCase()]) || ""; }); }; DotVVM.prototype.isPostBackProhibited = function (element) { if (element && element.tagName && element.tagName.toLowerCase() === "a" && element.getAttribute("disabled")) { return true; } return false; }; DotVVM.prototype.addKnockoutBindingHandlers = function () { function createWrapperComputed(accessor, propertyDebugInfo) { if (propertyDebugInfo === void 0) { propertyDebugInfo = null; } var computed = ko.pureComputed({ read: function () { var property = accessor(); var propertyValue = ko.unwrap(property); // has to call that always as it is a dependency return propertyValue; }, write: function (value) { var val = accessor(); if (ko.isObservable(val)) { val(value); } else { console.warn("Attempted to write to readonly property" + (propertyDebugInfo == null ? "" : " " + propertyDebugInfo) + "."); } } }); computed["wrappedProperty"] = accessor; return computed; } ko.virtualElements.allowedBindings["dotvvm_withControlProperties"] = true; ko.bindingHandlers["dotvvm_withControlProperties"] = { init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { var value = valueAccessor(); for (var prop in value) { value[prop] = createWrapperComputed(function () { return valueAccessor()[this.prop]; }.bind({ prop: prop }), "'" + prop + "' at '" + valueAccessor.toString() + "'"); } var innerBindingContext = bindingContext.extend({ $control: value }); element.innerBindingContext = innerBindingContext; ko.applyBindingsToDescendants(innerBindingContext, element); return { controlsDescendantBindings: true }; // do not apply binding again }, update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { } }; ko.virtualElements.allowedBindings["dotvvm_introduceAlias"] = true; ko.bindingHandlers["dotvvm_introduceAlias"] = { init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { var value = valueAccessor(); var extendBy = {}; for (var prop in value) { var propPath = prop.split('.'); var obj = extendBy; for (var i = 0; i < propPath.length - 1; i) { obj = extendBy[propPath[i]] || (extendBy[propPath[i]] = {}); } obj[propPath[propPath.length - 1]] = createWrapperComputed(function () { return valueAccessor()[this.prop]; }.bind({ prop: prop }), "'" + prop + "' at '" + valueAccessor.toString() + "'"); } var innerBindingContext = bindingContext.extend(extendBy); element.innerBindingContext = innerBindingContext; ko.applyBindingsToDescendants(innerBindingContext, element); return { controlsDescendantBindings: true }; // do not apply binding again } }; ko.virtualElements.allowedBindings["withGridViewDataSet"] = true; ko.bindingHandlers["withGridViewDataSet"] = { init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { var value = valueAccessor(); var innerBindingContext = bindingContext.extend({ $gridViewDataSet: value }); element.innerBindingContext = innerBindingContext; ko.applyBindingsToDescendants(innerBindingContext, element); return { controlsDescendantBindings: true }; // do not apply binding again }, update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { } }; ko.bindingHandlers['dotvvmEnable'] = { 'update': function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); if (value && element.disabled) { element.disabled = false; element.removeAttribute("disabled"); } else if ((!value) && (!element.disabled)) { element.disabled = true; element.setAttribute("disabled", "disabled"); } } }; ko.bindingHandlers['dotvvm-checkbox-updateAfterPostback'] = { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { dotvvm.events.afterPostback.subscribe(function (e) { var bindings = allBindingsAccessor(); if (bindings["dotvvm-checked-pointer"]) { var checked = bindings[bindings["dotvvm-checked-pointer"]]; if (ko.isObservable(checked)) { if (checked.valueHasMutated) { checked.valueHasMutated(); } else { checked.notifySubscribers(); } } } }); } }; ko.bindingHandlers['dotvvm-checked-pointer'] = { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { } }; ko.bindingHandlers["dotvvm-UpdateProgress-Visible"] = { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { element.style.display = "none"; dotvvm.events.beforePostback.subscribe(function (e) { element.style.display = ""; }); dotvvm.events.spaNavigating.subscribe(function (e) { element.style.display = ""; }); dotvvm.events.afterPostback.subscribe(function (e) { element.style.display = "none"; }); dotvvm.events.redirect.subscribe(function (e) { element.style.display = "none"; }); dotvvm.events.spaNavigated.subscribe(function (e) { element.style.display = "none"; }); dotvvm.events.error.subscribe(function (e) { element.style.display = "none"; }); } }; ko.bindingHandlers['dotvvm-table-columnvisible'] = { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { var lastDisplay = ""; var currentVisible = true; function changeVisibility(table, columnIndex, visible) { if (currentVisible == visible) return; currentVisible = visible; for (var i = 0; i < table.rows.length; i++) { var row = table.rows.item(i); var style = row.cells[columnIndex].style; if (visible) { style.display = lastDisplay; } else { lastDisplay = style.display; style.display = "none"; } } } if (!(element instanceof HTMLTableCellElement)) return; // find parent table var table = element; while (!(table instanceof HTMLTableElement)) table = table.parentElement; var colIndex = [].slice.call(table.rows.item(0).cells).indexOf(element); element['dotvvmChangeVisibility'] = changeVisibility.bind(null, table, colIndex); }, update: function (element, valueAccessor) { element.dotvvmChangeVisibility(ko.unwrap(valueAccessor())); } }; ko.bindingHandlers['dotvvm-textbox-text'] = { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { var obs = valueAccessor(); //generate metadata func var elmMetadata = new DotvvmValidationElementMetadata(); elmMetadata.dataType = (element.attributes["data-dotvvm-value-type"] || { value: "" }).value; elmMetadata.format = (element.attributes["data-dotvvm-format"] || { value: "" }).value; //add metadata for validation if (!obs.dotvvmMetadata) { obs.dotvvmMetadata = new DotvvmValidationObservableMetadata(); obs.dotvvmMetadata.elementsMetadata = [elmMetadata]; } else { if (!obs.dotvvmMetadata.elementsMetadata) { obs.dotvvmMetadata.elementsMetadata = []; } obs.dotvvmMetadata.elementsMetadata.push(elmMetadata); } setTimeout(function (metaArray, element) { // remove element from collection when its removed from dom ko.utils.domNodeDisposal.addDisposeCallback(element, function () { for (var _i = 0; _i < metaArray.length; _i++) { var meta = metaArray[_i]; if (meta.element === element) { metaArray.splice(metaArray.indexOf(meta), 1); break; } } }); }, 0, obs.dotvvmMetadata.elementsMetadata, element); dotvvm.domUtils.attachEvent(element, "blur", function () { // parse the value var result, isEmpty, newValue; if (elmMetadata.dataType === "datetime") { // parse date var currentValue = obs(); if (currentValue != null) { currentValue = dotvvm.globalize.parseDotvvmDate(currentValue); } result = dotvvm.globalize.parseDate(element.value, elmMetadata.format, currentValue); isEmpty = result === null; newValue = isEmpty ? null : dotvvm.serialization.serializeDate(result, false); } else { // parse number result = dotvvm.globalize.parseNumber(element.value); isEmpty = result === null || isNaN(result); newValue = isEmpty ? null : result; } // update element validation metadata if (newValue == null && element.value !== null && element.value !== "") { element.attributes["data-dotvvm-value-type-valid"] = false; elmMetadata.elementValidationState = false; } else { element.attributes["data-dotvvm-value-type-valid"] = true; elmMetadata.elementValidationState = true; } if (obs() === newValue) { if (obs.valueHasMutated) { obs.valueHasMutated(); } else { obs.notifySubscribers(); } } else { obs(newValue); } }); }, update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { var value = ko.unwrap(valueAccessor()); if (element.attributes["data-dotvvm-value-type-valid"] != false) { var format = (element.attributes["data-dotvvm-format"] || { value: "" }).value; if (format) { element.value = dotvvm.globalize.formatString(format, value); } else { element.value = value; } } } }; }; return DotVVM; })(); /// var DotvvmFileUpload = (function () { function DotvvmFileUpload() { } DotvvmFileUpload.prototype.showUploadDialog = function (sender) { // trigger the file upload dialog var iframe = this.getIframe(sender); this.createUploadId(sender, iframe); this.openUploadDialog(iframe); }; DotvvmFileUpload.prototype.getIframe = function (sender) { return sender.parentElement.previousSibling; }; DotvvmFileUpload.prototype.openUploadDialog = function (iframe) { var fileUpload = iframe.contentWindow.document.getElementById('upload'); fileUpload.click(); }; DotvvmFileUpload.prototype.createUploadId = function (sender, iframe) { iframe = iframe || this.getIframe(sender); var uploadId = "DotVVM_upl" + new Date().getTime().toString(); sender.parentElement.parentElement.setAttribute("data-dotvvm-upload-id", uploadId); iframe.setAttribute("data-dotvvm-upload-id", uploadId); }; DotvvmFileUpload.prototype.reportProgress = function (targetControlId, isBusy, progress, result) { // find target control viewmodel var targetControl = document.querySelector("div[data-dotvvm-upload-id='" + targetControlId.value + "']"); var viewModel = ko.dataFor(targetControl.firstChild); // determine the status if (typeof result === "string") { // error during upload viewModel.Error(result); } else { // files were uploaded successfully viewModel.Error(""); for (var i = 0; i < result.length; i++) { viewModel.Files.push(dotvvm.serialization.wrapObservable(dotvvm.serialization.deserialize(result[i]))); } // call the handler if ((targetControl.attributes["data-dotvvm-upload-completed"] || { value: null }).value) { new Function(targetControl.attributes["data-dotvvm-upload-completed"].value).call(targetControl); } } viewModel.Progress(progress); viewModel.IsBusy(isBusy); }; return DotvvmFileUpload; })(); var DotvvmFileUploadCollection = (function () { function DotvvmFileUploadCollection() { this.Files = ko.observableArray(); this.Progress = ko.observable(0); this.Error = ko.observable(); this.IsBusy = ko.observable(); } return DotvvmFileUploadCollection; })(); var DotvvmFileUploadData = (function () { function DotvvmFileUploadData() { this.FileId = ko.observable(); this.FileName = ko.observable(); this.FileSize = ko.observable(); this.IsFileTypeAllowed = ko.observable(); this.IsMaxSizeExceeded = ko.observable(); this.IsAllowed = ko.observable(); } return DotvvmFileUploadData; })(); var DotvvmFileSize = (function () { function DotvvmFileSize() { this.Bytes = ko.observable(); this.FormattedText = ko.observable(); } return DotvvmFileSize; })(); //# sourceMappingURL=DotVVM.js.map