From 3c1104dbb63e23cbb3f61e08c1930294d674988c Mon Sep 17 00:00:00 2001 From: Gerard Smit Date: Sat, 27 Apr 2019 13:41:34 +0200 Subject: [PATCH 1/4] Fixed Chrome and Edge persisted viewmodel --- .../Resources/Scripts/DotVVM.DomUtils.ts | 2 +- .../Resources/Scripts/DotVVM.d.ts | 90 ++++++++++--------- .../Resources/Scripts/DotVVM.js | 74 ++++++++++----- .../Resources/Scripts/DotVVM.min.js | 2 +- .../Resources/Scripts/DotVVM.ts | 54 ++++++++++- 5 files changed, 154 insertions(+), 68 deletions(-) diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts b/src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts index 101a841e63..38f1192ff7 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts @@ -14,4 +14,4 @@ } } -} \ No newline at end of file +} diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts b/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts index fd6fbb9cf6..e999c1de50 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts @@ -97,7 +97,7 @@ declare class DotvvmAfterPostBackEventArgs implements PostbackEventArgs { postbackOptions: PostbackOptions; serverResponseObject: any; commandResult: any; - xhr?: XMLHttpRequest | undefined; + xhr: XMLHttpRequest | undefined; isHandled: boolean; wasInterrupted: boolean; readonly postbackClientId: number; @@ -117,7 +117,7 @@ declare class DotvvmSpaNavigatedEventArgs implements DotvvmEventArgs { viewModel: any; viewModelName: string; serverResponseObject: any; - xhr?: XMLHttpRequest | undefined; + xhr: XMLHttpRequest | undefined; isHandled: boolean; constructor(viewModel: any, viewModelName: string, serverResponseObject: any, xhr?: XMLHttpRequest | undefined); } @@ -131,8 +131,8 @@ declare class DotvvmRedirectEventArgs implements DotvvmEventArgs { } declare class DotvvmFileUpload { showUploadDialog(sender: HTMLElement): void; - private getIframe; - private openUploadDialog; + private getIframe(sender); + private openUploadDialog(iframe); createUploadId(sender: HTMLElement, iframe: HTMLElement): void; reportProgress(targetControlId: any, isBusy: boolean, progress: number, result: DotvvmFileUploadData[] | string): void; } @@ -155,7 +155,7 @@ declare class DotvvmFileSize { FormattedText: KnockoutObservable; } declare class DotvvmGlobalize { - private getGlobalize; + private getGlobalize(); format(format: string, ...values: any[]): string; formatString(format: string, value: any): any; parseDotvvmDate(value: string): Date | null; @@ -192,10 +192,10 @@ interface AdditionalPostbackData { } declare class PostbackOptions { readonly postbackId: number; - readonly sender?: HTMLElement | undefined; + readonly sender: HTMLElement | undefined; readonly args: any[]; - readonly viewModel?: any; - readonly viewModelName?: string | undefined; + readonly viewModel: any; + readonly viewModelName: string | undefined; readonly additionalPostbackData: AdditionalPostbackData; constructor(postbackId: number, sender?: HTMLElement | undefined, args?: any[], viewModel?: any, viewModelName?: string | undefined); } @@ -228,10 +228,10 @@ declare class DotvvmSerialization { wrapObservable(obj: T): KnockoutObservable; serialize(viewModel: any, opt?: ISerializationOptions): any; validateType(value: any, type: string): boolean; - private findObject; + private findObject(obj, matcher); flatSerialize(viewModel: any): any; getPureObject(viewModel: any): {}; - private pad; + private pad(value, digits); serializeDate(date: string | Date | null, convertToUtc?: boolean): string | null; } interface Document { @@ -250,6 +250,10 @@ interface IDotvvmViewModelInfo { renderedResources?: string[]; url?: string; virtualDirectory?: string; + resultIdFragment?: string; + resources?: { + [name: string]: boolean; + }; } interface IDotvvmViewModels { [name: string]: IDotvvmViewModelInfo; @@ -281,7 +285,7 @@ declare class DotVVM { private suppressOnDisabledElementHandler; private beforePostbackEventPostbackHandler; private isPostBackRunningHandler; - private createWindowSetTimeoutHandler; + private createWindowSetTimeoutHandler(time); private windowSetTimeoutHandler; private commonConcurrencyHandler; private defaultConcurrencyPostbackHandler; @@ -303,47 +307,49 @@ declare class DotVVM { extensions: IDotvvmExtensions; useHistoryApiSpaNavigation: boolean; isPostbackRunning: KnockoutObservable; + useHistoryApiViewModel: boolean; + private isBrowserReload(); init(viewModelName: string, culture: string): void; - private handlePopState; - private handleHashChangeWithHistory; - private handleHashChange; - private persistViewModel; - private backUpPostBackConter; - private isPostBackStillActive; + private handlePopState(viewModelName, event, inSpaPage); + private handleHashChangeWithHistory(viewModelName, spaPlaceHolder, isInitialPageLoad); + private handleHashChange(viewModelName, spaPlaceHolder, isInitialPageLoad); + private persistViewModel(viewModelName); + private backUpPostBackConter(); + private isPostBackStillActive(currentPostBackCounter); staticCommandPostback(viewModelName: string, sender: HTMLElement, command: string, args: any[], callback?: (_: any) => void, errorCallback?: (xhr: XMLHttpRequest, error?: any) => void): void; - private processPassedId; + private processPassedId(id, context); protected getPostbackHandler(name: string): (options: any) => DotvvmPostbackHandler; - private isPostbackHandler; + private isPostbackHandler(obj); findPostbackHandlers(knockoutContext: any, config: ClientFriendlyPostbackHandlerConfiguration[]): DotvvmPostbackHandler[]; - private sortHandlers; - private applyPostbackHandlersCore; + private sortHandlers(handlers); + private applyPostbackHandlersCore(callback, options, handlers?); applyPostbackHandlers(callback: (options: PostbackOptions) => Promise, sender: HTMLElement, handlers?: ClientFriendlyPostbackHandlerConfiguration[], args?: any[], context?: any, viewModel?: any, viewModelName?: string): Promise; postbackCore(options: PostbackOptions, path: string[], command: string, controlUniqueId: string, context: any, commandArgs?: any[]): Promise<() => Promise>; handleSpaNavigation(element: HTMLElement): boolean; handleSpaNavigationCore(url: string | null): boolean; postBack(viewModelName: string, sender: HTMLElement, path: string[], command: string, controlUniqueId: string, context?: any, handlers?: ClientFriendlyPostbackHandlerConfiguration[], commandArgs?: any[]): Promise; - private loadResourceList; - private loadResourceElements; - private getSpaPlaceHolder; - private navigateCore; - private handleRedirect; - private performRedirect; - private fixSpaUrlPrefix; - private removeVirtualDirectoryFromUrl; - private addLeadingSlash; - private concatUrl; + private loadResourceList(resources, callback); + private loadResourceElements(elements, offset, callback); + private getSpaPlaceHolder(); + private navigateCore(viewModelName, url, handlePageNavigating?); + private handleRedirect(resultObject, viewModelName, replace?); + private performRedirect(url, replace, useHistoryApiSpaRedirect?); + private fixSpaUrlPrefix(url); + private removeVirtualDirectoryFromUrl(url, viewModelName); + private addLeadingSlash(url); + private concatUrl(url1, url2); patch(source: any, patch: any): any; - private updateDynamicPathFragments; - private postJSON; - private getJSON; + private updateDynamicPathFragments(context, path); + private postJSON(url, method, postData, success, error, preprocessRequest?); + private getJSON(url, method, spaPlaceHolderUniqueId, success, error); getXHR(): XMLHttpRequest; - private cleanUpdatedControls; - private restoreUpdatedControls; + private cleanUpdatedControls(resultObject, updatedControls?); + private restoreUpdatedControls(resultObject, updatedControls, applyBindingsOnEachControl); unwrapArrayExtension(array: any): any; buildRouteUrl(routePath: string, params: any): string; buildUrlSuffix(urlSuffix: string, query: any): string; - private isPostBackProhibited; - private addKnockoutBindingHandlers; + private isPostBackProhibited(element); + private addKnockoutBindingHandlers(); } declare class DotvvmValidationContext { valueToValidate: any; @@ -449,7 +455,7 @@ declare class DotvvmValidation { * Adds validation errors from the server to the appropriate arrays */ showValidationErrorsFromServer(args: DotvvmAfterPostBackEventArgs): void; - private addValidationError; + private addValidationError(validatedProperty, error); } declare var dotvvm: DotVVM; declare class DotvvmEvaluator { @@ -459,9 +465,9 @@ declare class DotvvmEvaluator { tryEval(func: () => any): any; isObservableArray(instance: any): instance is KnockoutObservableArray; wrapObservable(func: () => any, isArray?: boolean): KnockoutComputed; - private updateObservable; - private updateObservableArray; - private getExpressionResult; + private updateObservable(getObservable, value); + private updateObservableArray(getObservableArray, fnName, args); + private getExpressionResult(func); } declare type ApiComputed = KnockoutObservable & { refreshValue: (throwOnError?: boolean) => PromiseLike | undefined; diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js index 4da1b9adf3..c929b5133b 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js @@ -1,21 +1,15 @@ -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; }; var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - 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 extendStatics(d, b); - }; + 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; } @@ -970,6 +964,7 @@ var DotVVM = /** @class */ (function () { this.fileUpload = new DotvvmFileUpload(); this.extensions = {}; this.isPostbackRunning = ko.observable(false); + this.useHistoryApiViewModel = history && ((/Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)) || /Edge/.test(navigator.userAgent)); } DotVVM.prototype.createWindowSetTimeoutHandler = function (time) { return { @@ -987,11 +982,35 @@ var DotVVM = /** @class */ (function () { this.postbackQueues[name] = { queue: [], noRunning: 0 }; return this.postbackQueues[name]; }; + DotVVM.prototype.isBrowserReload = function () { + if (performance) { + if (performance.getEntriesByType) { + var entries = performance.getEntriesByType("navigation"); + if (entries.length > 0) { + return entries[0].type === "reload"; + } + } + // deprecated in Navigation Timing Level 2 specification + if (performance.navigation) { + return performance.navigation.type === 1; + } + } + return 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); + var thisViewModel; + var shouldLoadFromHistoryApi = this.useHistoryApiViewModel && !this.isBrowserReload() && history.state && history.state.dotvvm_viewmodels && history.state.dotvvm_viewmodels[viewModelName]; + if (shouldLoadFromHistoryApi) { + // create a new object, otherwise the object in the history state will be changed which will result in serialize errors. + thisViewModel = __assign({}, history.state.dotvvm_viewmodels[viewModelName]); + } + else { + thisViewModel = JSON.parse(document.getElementById("__dot_viewmodel_" + viewModelName).value); + } + this.viewModels[viewModelName] = thisViewModel; if (thisViewModel.resources) { for (var r in thisViewModel.resources) { this.resourceSigns[r] = true; @@ -1102,6 +1121,14 @@ var DotVVM = /** @class */ (function () { }; DotVVM.prototype.persistViewModel = function (viewModelName) { var viewModel = this.viewModels[viewModelName]; + if (this.useHistoryApiViewModel) { + var currentState = history.state ? history.state : {}; + var persistedViewModels = currentState.dotvvm_viewmodels ? currentState.dotvvm_viewmodels : {}; + // add the new viewmodel to the existing state, otherwise SPA mode will break. + var state = __assign({ dotvvm_viewmodels: __assign((_a = {}, _a[viewModelName] = ko.toJS(viewModel), _a), persistedViewModels) }, currentState); + console.log(JSON.stringify(state)); + history.replaceState(state, document.title); + } var persistedViewModel = {}; for (var p in viewModel) { if (viewModel.hasOwnProperty(p)) { @@ -1110,6 +1137,7 @@ var DotVVM = /** @class */ (function () { } persistedViewModel["viewModel"] = this.serialization.serialize(persistedViewModel["viewModel"], { serializeAll: true }); document.getElementById("__dot_viewmodel_" + viewModelName).value = JSON.stringify(persistedViewModel); + var _a; }; DotVVM.prototype.backUpPostBackConter = function () { return ++this.postBackCounter; @@ -1186,8 +1214,8 @@ var DotVVM = /** @class */ (function () { DotVVM.prototype.sortHandlers = function (handlers) { var getHandler = (function () { var handlerMap = {}; - for (var _i = 0, handlers_2 = handlers; _i < handlers_2.length; _i++) { - var h = handlers_2[_i]; + for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) { + var h = handlers_1[_i]; if (h.name != null) { handlerMap[h.name] = h; } @@ -1195,8 +1223,8 @@ var DotVVM = /** @class */ (function () { return function (s) { return typeof s == "string" ? handlerMap[s] : s; }; })(); var dependencies = handlers.map(function (handler, i) { return (handler["@sort_index"] = i, ({ handler: handler, deps: (handler.after || []).map(getHandler) })); }); - for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) { - var h = handlers_1[_i]; + for (var _i = 0, handlers_2 = handlers; _i < handlers_2.length; _i++) { + var h = handlers_2[_i]; if (h.before) for (var _a = 0, _b = h.before.map(getHandler); _a < _b.length; _a++) { var before = _b[_a]; @@ -1870,7 +1898,6 @@ var DotVVM = /** @class */ (function () { ko.virtualElements.allowedBindings["dotvvm-SSR-foreach"] = true; ko.bindingHandlers["dotvvm-SSR-foreach"] = { init: function (element, valueAccessor, _allBindings, _viewModel, bindingContext) { - var _a; if (!bindingContext) throw new Error(); var value = valueAccessor(); @@ -1878,6 +1905,7 @@ var DotVVM = /** @class */ (function () { element.innerBindingContext = innerBindingContext; ko.applyBindingsToDescendants(innerBindingContext, element); return { controlsDescendantBindings: true }; // do not apply binding again + var _a; } }; ko.virtualElements.allowedBindings["dotvvm-SSR-item"] = true; @@ -1896,7 +1924,6 @@ var DotVVM = /** @class */ (function () { ko.virtualElements.allowedBindings["withGridViewDataSet"] = true; ko.bindingHandlers["withGridViewDataSet"] = { init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { - var _a; if (!bindingContext) throw new Error(); var value = valueAccessor(); @@ -1904,6 +1931,7 @@ var DotVVM = /** @class */ (function () { element.innerBindingContext = innerBindingContext; ko.applyBindingsToDescendants(innerBindingContext, element); return { controlsDescendantBindings: true }; // do not apply binding again + var _a; }, update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { } diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js index 750cd3bd64..573a412c80 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js @@ -1 +1 @@ -var __assign=this&&this.__assign||function(){return(__assign=Object.assign||function(e){for(var t,n=1,r=arguments.length;n'");return e},e.prototype.format=function(e){for(var o=this,a=[],t=1;t=e.length)n();else{var o=e[t],a=!1;if("script"==o.tagName.toLowerCase()){var i=document.createElement("script");o.src&&(i.src=o.src,a=!0),o.type&&(i.type=o.type),o.text&&(i.text=o.text),o=i}else if("link"==o.tagName.toLowerCase()){var s=document.createElement("link");o.href&&(s.href=o.href),o.rel&&(s.rel=o.rel),o.type&&(s.type=o.type),o=s}a&&(o.onload=function(){return r.loadResourceElements(e,t+1,n)}),document.head.appendChild(o),a||this.loadResourceElements(e,t+1,n)}},e.prototype.getSpaPlaceHolder=function(){var e=document.getElementsByName("__dot_SpaContentPlaceHolder");return 1==e.length?e[0]:null},e.prototype.navigateCore=function(i,e,t){var s=this,n=this.viewModels[i].viewModel,r=this.backUpPostBackConter(),o=new DotvvmSpaNavigatingEventArgs(n,i,e);if(this.events.spaNavigating.trigger(o),!o.cancel){var a=this.viewModels[i].virtualDirectory||"",l="/___dotvvm-spa___"+this.addLeadingSlash(e),u=this.addLeadingSlash(this.concatUrl(a,l)),d=this.getSpaPlaceHolder();if(d){t&&t(this.addLeadingSlash(this.concatUrl(a,this.addLeadingSlash(e))));var v=d.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(u,"GET",v,function(o){if(s.isPostBackStillActive(r)){var a=JSON.parse(o.responseText);s.loadResourceList(a.resources,function(){var e=!1;if("successfulCommand"!==a.action&&a.action){if("redirect"===a.action)return void s.handleRedirect(a,i,!0)}else try{s.isViewModelUpdating=!0;var t=s.cleanUpdatedControls(a);for(var n in s.viewModels[i]={},a)a.hasOwnProperty(n)&&(s.viewModels[i][n]=a[n]);ko.delaySync.pause(),s.serialization.deserialize(a.viewModel,s.viewModels[i].viewModel),ko.delaySync.resume(),e=!0,s.viewModelObservables[i](s.viewModels[i].viewModel),s.restoreUpdatedControls(a,t,!0),s.isSpaReady(!0)}finally{s.isViewModelUpdating=!1}var r=new DotvvmSpaNavigatedEventArgs(s.viewModels[i].viewModel,i,a,o);if(s.events.spaNavigated.trigger(r),!e&&!r.isHandled)throw"Invalid response from server!"})}},function(e){if(s.isPostBackStillActive(r)){var t=new DotvvmErrorEventArgs(void 0,n,i,e,-1,void 0,!0);s.events.error.trigger(t),t.handled||alert(e.responseText)}})}else document.location.href=u}},e.prototype.handleRedirect=function(e,t,n){var r;void 0===n&&(n=!1),null!=e.replace&&(n=e.replace),r=this.getSpaPlaceHolder()&&!this.useHistoryApiSpaNavigation&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),this.fixSpaUrlPrefix(r)):e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n,e.allowSpa&&this.useHistoryApiSpaNavigation)},e.prototype.performRedirect=function(e,t,n){if(t)location.replace(e);else if(n)this.handleSpaNavigationCore(e);else{var r=this.fakeRedirectAnchor;r||((r=document.createElement("a")).style.display="none",r.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(r),this.fakeRedirectAnchor=r),r.href=e,r.click()}},e.prototype.fixSpaUrlPrefix=function(e){var t=this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"];if(!t)return e;var n=t.value;return n!==document.location.pathname&&(""===n&&(n="/"),e=n+e),e},e.prototype.removeVirtualDirectoryFromUrl=function(e,t){var n="/"+this.viewModels[t].virtualDirectory;return 0==e.indexOf(n)?this.addLeadingSlash(e.substring(n.length)):e},e.prototype.addLeadingSlash=function(e){return 0'");return e},e.prototype.format=function(e){for(var o=this,i=[],t=1;t=e.length)n();else{var o=e[t],i=!1;if("script"==o.tagName.toLowerCase()){var a=document.createElement("script");o.src&&(a.src=o.src,i=!0),o.type&&(a.type=o.type),o.text&&(a.text=o.text),o=a}else if("link"==o.tagName.toLowerCase()){var s=document.createElement("link");o.href&&(s.href=o.href),o.rel&&(s.rel=o.rel),o.type&&(s.type=o.type),o=s}i&&(o.onload=function(){return r.loadResourceElements(e,t+1,n)}),document.head.appendChild(o),i||this.loadResourceElements(e,t+1,n)}},e.prototype.getSpaPlaceHolder=function(){var e=document.getElementsByName("__dot_SpaContentPlaceHolder");return 1==e.length?e[0]:null},e.prototype.navigateCore=function(a,e,t){var s=this,n=this.viewModels[a].viewModel,r=this.backUpPostBackConter(),o=new DotvvmSpaNavigatingEventArgs(n,a,e);if(this.events.spaNavigating.trigger(o),!o.cancel){var i=this.viewModels[a].virtualDirectory||"",l="/___dotvvm-spa___"+this.addLeadingSlash(e),d=this.addLeadingSlash(this.concatUrl(i,l)),u=this.getSpaPlaceHolder();if(u){t&&t(this.addLeadingSlash(this.concatUrl(i,this.addLeadingSlash(e))));var v=u.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(d,"GET",v,function(o){if(s.isPostBackStillActive(r)){var i=JSON.parse(o.responseText);s.loadResourceList(i.resources,function(){var e=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void s.handleRedirect(i,a,!0)}else try{s.isViewModelUpdating=!0;var t=s.cleanUpdatedControls(i);for(var n in s.viewModels[a]={},i)i.hasOwnProperty(n)&&(s.viewModels[a][n]=i[n]);ko.delaySync.pause(),s.serialization.deserialize(i.viewModel,s.viewModels[a].viewModel),ko.delaySync.resume(),e=!0,s.viewModelObservables[a](s.viewModels[a].viewModel),s.restoreUpdatedControls(i,t,!0),s.isSpaReady(!0)}finally{s.isViewModelUpdating=!1}var r=new DotvvmSpaNavigatedEventArgs(s.viewModels[a].viewModel,a,i,o);if(s.events.spaNavigated.trigger(r),!e&&!r.isHandled)throw"Invalid response from server!"})}},function(e){if(s.isPostBackStillActive(r)){var t=new DotvvmErrorEventArgs(void 0,n,a,e,-1,void 0,!0);s.events.error.trigger(t),t.handled||alert(e.responseText)}})}else document.location.href=d}},e.prototype.handleRedirect=function(e,t,n){var r;void 0===n&&(n=!1),null!=e.replace&&(n=e.replace),r=this.getSpaPlaceHolder()&&!this.useHistoryApiSpaNavigation&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),this.fixSpaUrlPrefix(r)):e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n,e.allowSpa&&this.useHistoryApiSpaNavigation)},e.prototype.performRedirect=function(e,t,n){if(t)location.replace(e);else if(n)this.handleSpaNavigationCore(e);else{var r=this.fakeRedirectAnchor;r||((r=document.createElement("a")).style.display="none",r.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(r),this.fakeRedirectAnchor=r),r.href=e,r.click()}},e.prototype.fixSpaUrlPrefix=function(e){var t=this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"];if(!t)return e;var n=t.value;return n!==document.location.pathname&&(""===n&&(n="/"),e=n+e),e},e.prototype.removeVirtualDirectoryFromUrl=function(e,t){var n="/"+this.viewModels[t].virtualDirectory;return 0==e.indexOf(n)?this.addLeadingSlash(e.substring(n.length)):e},e.prototype.addLeadingSlash=function(e){return 0 0) { + return (entries[0]).type === "reload"; + } + } + + // deprecated in Navigation Timing Level 2 specification + if (performance.navigation) { + return performance.navigation.type === 1; + } + } + + return false; + } + public init(viewModelName: string, culture: string): void { this.addKnockoutBindingHandlers(); // load the viewmodel - var thisViewModel = this.viewModels[viewModelName] = JSON.parse((document.getElementById("__dot_viewmodel_" + viewModelName)).value); + var thisViewModel: IDotvvmViewModelInfo; + var shouldLoadFromHistoryApi = this.useHistoryApiViewModel && !this.isBrowserReload() && history.state && history.state.dotvvm_viewmodels && history.state.dotvvm_viewmodels[viewModelName]; + + if (shouldLoadFromHistoryApi) { + // create a new object, otherwise the object in the history state will be changed which will result in serialize errors. + thisViewModel = { ...history.state.dotvvm_viewmodels[viewModelName] }; + } else { + thisViewModel = JSON.parse((document.getElementById("__dot_viewmodel_" + viewModelName)).value); + } + + this.viewModels[viewModelName] = thisViewModel; + if (thisViewModel.resources) { for (var r in thisViewModel.resources) { this.resourceSigns[r] = true; @@ -347,6 +381,24 @@ class DotVVM { private persistViewModel(viewModelName: string) { var viewModel = this.viewModels[viewModelName]; + + if (this.useHistoryApiViewModel) { + var currentState = history.state ? history.state : {}; + var persistedViewModels = currentState.dotvvm_viewmodels ? currentState.dotvvm_viewmodels : {}; + + // add the new viewmodel to the existing state, otherwise SPA mode will break. + var state = { + dotvvm_viewmodels: { + [viewModelName]: ko.toJS(viewModel), + ...persistedViewModels + }, + ...currentState + }; + + console.log(JSON.stringify(state)) + history.replaceState(state, document.title); + } + var persistedViewModel = {}; for (var p in viewModel) { if (viewModel.hasOwnProperty(p)) { From c3151c0ad14c5816b46c6b6be1a22415434014ec Mon Sep 17 00:00:00 2001 From: Gerard Smit Date: Sat, 27 Apr 2019 13:46:37 +0200 Subject: [PATCH 2/4] Removed debugging line Also reverted DotVVM.DomUtils.ts which included an unnecessary line change --- src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts | 2 +- src/DotVVM.Framework/Resources/Scripts/DotVVM.js | 1 - src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js | 2 +- src/DotVVM.Framework/Resources/Scripts/DotVVM.ts | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts b/src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts index 38f1192ff7..101a841e63 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.DomUtils.ts @@ -14,4 +14,4 @@ } } -} +} \ No newline at end of file diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js index c929b5133b..2fa5e58cc7 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js @@ -1126,7 +1126,6 @@ var DotVVM = /** @class */ (function () { var persistedViewModels = currentState.dotvvm_viewmodels ? currentState.dotvvm_viewmodels : {}; // add the new viewmodel to the existing state, otherwise SPA mode will break. var state = __assign({ dotvvm_viewmodels: __assign((_a = {}, _a[viewModelName] = ko.toJS(viewModel), _a), persistedViewModels) }, currentState); - console.log(JSON.stringify(state)); history.replaceState(state, document.title); } var persistedViewModel = {}; diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js index 573a412c80..27ea2b8527 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js @@ -1 +1 @@ -var __assign=this&&this.__assign||Object.assign||function(e){for(var t,n=1,r=arguments.length;n'");return e},e.prototype.format=function(e){for(var o=this,i=[],t=1;t=e.length)n();else{var o=e[t],i=!1;if("script"==o.tagName.toLowerCase()){var a=document.createElement("script");o.src&&(a.src=o.src,i=!0),o.type&&(a.type=o.type),o.text&&(a.text=o.text),o=a}else if("link"==o.tagName.toLowerCase()){var s=document.createElement("link");o.href&&(s.href=o.href),o.rel&&(s.rel=o.rel),o.type&&(s.type=o.type),o=s}i&&(o.onload=function(){return r.loadResourceElements(e,t+1,n)}),document.head.appendChild(o),i||this.loadResourceElements(e,t+1,n)}},e.prototype.getSpaPlaceHolder=function(){var e=document.getElementsByName("__dot_SpaContentPlaceHolder");return 1==e.length?e[0]:null},e.prototype.navigateCore=function(a,e,t){var s=this,n=this.viewModels[a].viewModel,r=this.backUpPostBackConter(),o=new DotvvmSpaNavigatingEventArgs(n,a,e);if(this.events.spaNavigating.trigger(o),!o.cancel){var i=this.viewModels[a].virtualDirectory||"",l="/___dotvvm-spa___"+this.addLeadingSlash(e),d=this.addLeadingSlash(this.concatUrl(i,l)),u=this.getSpaPlaceHolder();if(u){t&&t(this.addLeadingSlash(this.concatUrl(i,this.addLeadingSlash(e))));var v=u.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(d,"GET",v,function(o){if(s.isPostBackStillActive(r)){var i=JSON.parse(o.responseText);s.loadResourceList(i.resources,function(){var e=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void s.handleRedirect(i,a,!0)}else try{s.isViewModelUpdating=!0;var t=s.cleanUpdatedControls(i);for(var n in s.viewModels[a]={},i)i.hasOwnProperty(n)&&(s.viewModels[a][n]=i[n]);ko.delaySync.pause(),s.serialization.deserialize(i.viewModel,s.viewModels[a].viewModel),ko.delaySync.resume(),e=!0,s.viewModelObservables[a](s.viewModels[a].viewModel),s.restoreUpdatedControls(i,t,!0),s.isSpaReady(!0)}finally{s.isViewModelUpdating=!1}var r=new DotvvmSpaNavigatedEventArgs(s.viewModels[a].viewModel,a,i,o);if(s.events.spaNavigated.trigger(r),!e&&!r.isHandled)throw"Invalid response from server!"})}},function(e){if(s.isPostBackStillActive(r)){var t=new DotvvmErrorEventArgs(void 0,n,a,e,-1,void 0,!0);s.events.error.trigger(t),t.handled||alert(e.responseText)}})}else document.location.href=d}},e.prototype.handleRedirect=function(e,t,n){var r;void 0===n&&(n=!1),null!=e.replace&&(n=e.replace),r=this.getSpaPlaceHolder()&&!this.useHistoryApiSpaNavigation&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),this.fixSpaUrlPrefix(r)):e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n,e.allowSpa&&this.useHistoryApiSpaNavigation)},e.prototype.performRedirect=function(e,t,n){if(t)location.replace(e);else if(n)this.handleSpaNavigationCore(e);else{var r=this.fakeRedirectAnchor;r||((r=document.createElement("a")).style.display="none",r.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(r),this.fakeRedirectAnchor=r),r.href=e,r.click()}},e.prototype.fixSpaUrlPrefix=function(e){var t=this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"];if(!t)return e;var n=t.value;return n!==document.location.pathname&&(""===n&&(n="/"),e=n+e),e},e.prototype.removeVirtualDirectoryFromUrl=function(e,t){var n="/"+this.viewModels[t].virtualDirectory;return 0==e.indexOf(n)?this.addLeadingSlash(e.substring(n.length)):e},e.prototype.addLeadingSlash=function(e){return 0'");return e},e.prototype.format=function(e){for(var o=this,i=[],t=1;t=e.length)n();else{var o=e[t],i=!1;if("script"==o.tagName.toLowerCase()){var a=document.createElement("script");o.src&&(a.src=o.src,i=!0),o.type&&(a.type=o.type),o.text&&(a.text=o.text),o=a}else if("link"==o.tagName.toLowerCase()){var s=document.createElement("link");o.href&&(s.href=o.href),o.rel&&(s.rel=o.rel),o.type&&(s.type=o.type),o=s}i&&(o.onload=function(){return r.loadResourceElements(e,t+1,n)}),document.head.appendChild(o),i||this.loadResourceElements(e,t+1,n)}},e.prototype.getSpaPlaceHolder=function(){var e=document.getElementsByName("__dot_SpaContentPlaceHolder");return 1==e.length?e[0]:null},e.prototype.navigateCore=function(a,e,t){var s=this,n=this.viewModels[a].viewModel,r=this.backUpPostBackConter(),o=new DotvvmSpaNavigatingEventArgs(n,a,e);if(this.events.spaNavigating.trigger(o),!o.cancel){var i=this.viewModels[a].virtualDirectory||"",l="/___dotvvm-spa___"+this.addLeadingSlash(e),d=this.addLeadingSlash(this.concatUrl(i,l)),u=this.getSpaPlaceHolder();if(u){t&&t(this.addLeadingSlash(this.concatUrl(i,this.addLeadingSlash(e))));var v=u.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(d,"GET",v,function(o){if(s.isPostBackStillActive(r)){var i=JSON.parse(o.responseText);s.loadResourceList(i.resources,function(){var e=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void s.handleRedirect(i,a,!0)}else try{s.isViewModelUpdating=!0;var t=s.cleanUpdatedControls(i);for(var n in s.viewModels[a]={},i)i.hasOwnProperty(n)&&(s.viewModels[a][n]=i[n]);ko.delaySync.pause(),s.serialization.deserialize(i.viewModel,s.viewModels[a].viewModel),ko.delaySync.resume(),e=!0,s.viewModelObservables[a](s.viewModels[a].viewModel),s.restoreUpdatedControls(i,t,!0),s.isSpaReady(!0)}finally{s.isViewModelUpdating=!1}var r=new DotvvmSpaNavigatedEventArgs(s.viewModels[a].viewModel,a,i,o);if(s.events.spaNavigated.trigger(r),!e&&!r.isHandled)throw"Invalid response from server!"})}},function(e){if(s.isPostBackStillActive(r)){var t=new DotvvmErrorEventArgs(void 0,n,a,e,-1,void 0,!0);s.events.error.trigger(t),t.handled||alert(e.responseText)}})}else document.location.href=d}},e.prototype.handleRedirect=function(e,t,n){var r;void 0===n&&(n=!1),null!=e.replace&&(n=e.replace),r=this.getSpaPlaceHolder()&&!this.useHistoryApiSpaNavigation&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),this.fixSpaUrlPrefix(r)):e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n,e.allowSpa&&this.useHistoryApiSpaNavigation)},e.prototype.performRedirect=function(e,t,n){if(t)location.replace(e);else if(n)this.handleSpaNavigationCore(e);else{var r=this.fakeRedirectAnchor;r||((r=document.createElement("a")).style.display="none",r.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(r),this.fakeRedirectAnchor=r),r.href=e,r.click()}},e.prototype.fixSpaUrlPrefix=function(e){var t=this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"];if(!t)return e;var n=t.value;return n!==document.location.pathname&&(""===n&&(n="/"),e=n+e),e},e.prototype.removeVirtualDirectoryFromUrl=function(e,t){var n="/"+this.viewModels[t].virtualDirectory;return 0==e.indexOf(n)?this.addLeadingSlash(e.substring(n.length)):e},e.prototype.addLeadingSlash=function(e){return 0 Date: Mon, 29 Apr 2019 19:40:55 +0200 Subject: [PATCH 3/4] Added hidden input for the persisted viewmodel This way we can see if the browser supports reloading the hidden input fields. If this is not the case the History API will be used. --- .../Infrastructure/BodyResourceLinks.cs | 5 ++ .../Resources/Scripts/DotVVM.d.ts | 86 +++++++++---------- .../Resources/Scripts/DotVVM.js | 71 ++++++++------- .../Resources/Scripts/DotVVM.min.js | 2 +- .../Resources/Scripts/DotVVM.ts | 48 ++++++----- 5 files changed, 114 insertions(+), 98 deletions(-) diff --git a/src/DotVVM.Framework/Controls/Infrastructure/BodyResourceLinks.cs b/src/DotVVM.Framework/Controls/Infrastructure/BodyResourceLinks.cs index 19f0480d9e..55d92c2dd2 100644 --- a/src/DotVVM.Framework/Controls/Infrastructure/BodyResourceLinks.cs +++ b/src/DotVVM.Framework/Controls/Infrastructure/BodyResourceLinks.cs @@ -35,6 +35,11 @@ protected override void RenderControl(IHtmlWriter writer, IDotvvmRequestContext writer.AddAttribute("value", serializedViewModel); writer.RenderSelfClosingTag("input"); + + writer.AddAttribute("type", "hidden"); + writer.AddAttribute("id", "__dot_persisted_viewmodel_root"); + writer.RenderSelfClosingTag("input"); + // init on load writer.RenderBeginTag("script"); writer.WriteUnencodedText($@" diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts b/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts index e999c1de50..3b6ea21cb9 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts @@ -97,7 +97,7 @@ declare class DotvvmAfterPostBackEventArgs implements PostbackEventArgs { postbackOptions: PostbackOptions; serverResponseObject: any; commandResult: any; - xhr: XMLHttpRequest | undefined; + xhr?: XMLHttpRequest | undefined; isHandled: boolean; wasInterrupted: boolean; readonly postbackClientId: number; @@ -117,7 +117,7 @@ declare class DotvvmSpaNavigatedEventArgs implements DotvvmEventArgs { viewModel: any; viewModelName: string; serverResponseObject: any; - xhr: XMLHttpRequest | undefined; + xhr?: XMLHttpRequest | undefined; isHandled: boolean; constructor(viewModel: any, viewModelName: string, serverResponseObject: any, xhr?: XMLHttpRequest | undefined); } @@ -131,8 +131,8 @@ declare class DotvvmRedirectEventArgs implements DotvvmEventArgs { } declare class DotvvmFileUpload { showUploadDialog(sender: HTMLElement): void; - private getIframe(sender); - private openUploadDialog(iframe); + private getIframe; + private openUploadDialog; createUploadId(sender: HTMLElement, iframe: HTMLElement): void; reportProgress(targetControlId: any, isBusy: boolean, progress: number, result: DotvvmFileUploadData[] | string): void; } @@ -155,7 +155,7 @@ declare class DotvvmFileSize { FormattedText: KnockoutObservable; } declare class DotvvmGlobalize { - private getGlobalize(); + private getGlobalize; format(format: string, ...values: any[]): string; formatString(format: string, value: any): any; parseDotvvmDate(value: string): Date | null; @@ -192,10 +192,10 @@ interface AdditionalPostbackData { } declare class PostbackOptions { readonly postbackId: number; - readonly sender: HTMLElement | undefined; + readonly sender?: HTMLElement | undefined; readonly args: any[]; - readonly viewModel: any; - readonly viewModelName: string | undefined; + readonly viewModel?: any; + readonly viewModelName?: string | undefined; readonly additionalPostbackData: AdditionalPostbackData; constructor(postbackId: number, sender?: HTMLElement | undefined, args?: any[], viewModel?: any, viewModelName?: string | undefined); } @@ -228,10 +228,10 @@ declare class DotvvmSerialization { wrapObservable(obj: T): KnockoutObservable; serialize(viewModel: any, opt?: ISerializationOptions): any; validateType(value: any, type: string): boolean; - private findObject(obj, matcher); + private findObject; flatSerialize(viewModel: any): any; getPureObject(viewModel: any): {}; - private pad(value, digits); + private pad; serializeDate(date: string | Date | null, convertToUtc?: boolean): string | null; } interface Document { @@ -285,7 +285,7 @@ declare class DotVVM { private suppressOnDisabledElementHandler; private beforePostbackEventPostbackHandler; private isPostBackRunningHandler; - private createWindowSetTimeoutHandler(time); + private createWindowSetTimeoutHandler; private windowSetTimeoutHandler; private commonConcurrencyHandler; private defaultConcurrencyPostbackHandler; @@ -308,48 +308,48 @@ declare class DotVVM { useHistoryApiSpaNavigation: boolean; isPostbackRunning: KnockoutObservable; useHistoryApiViewModel: boolean; - private isBrowserReload(); + private isBrowserReload; init(viewModelName: string, culture: string): void; - private handlePopState(viewModelName, event, inSpaPage); - private handleHashChangeWithHistory(viewModelName, spaPlaceHolder, isInitialPageLoad); - private handleHashChange(viewModelName, spaPlaceHolder, isInitialPageLoad); - private persistViewModel(viewModelName); - private backUpPostBackConter(); - private isPostBackStillActive(currentPostBackCounter); + private handlePopState; + private handleHashChangeWithHistory; + private handleHashChange; + private persistViewModel; + private backUpPostBackConter; + private isPostBackStillActive; staticCommandPostback(viewModelName: string, sender: HTMLElement, command: string, args: any[], callback?: (_: any) => void, errorCallback?: (xhr: XMLHttpRequest, error?: any) => void): void; - private processPassedId(id, context); + private processPassedId; protected getPostbackHandler(name: string): (options: any) => DotvvmPostbackHandler; - private isPostbackHandler(obj); + private isPostbackHandler; findPostbackHandlers(knockoutContext: any, config: ClientFriendlyPostbackHandlerConfiguration[]): DotvvmPostbackHandler[]; - private sortHandlers(handlers); - private applyPostbackHandlersCore(callback, options, handlers?); + private sortHandlers; + private applyPostbackHandlersCore; applyPostbackHandlers(callback: (options: PostbackOptions) => Promise, sender: HTMLElement, handlers?: ClientFriendlyPostbackHandlerConfiguration[], args?: any[], context?: any, viewModel?: any, viewModelName?: string): Promise; postbackCore(options: PostbackOptions, path: string[], command: string, controlUniqueId: string, context: any, commandArgs?: any[]): Promise<() => Promise>; handleSpaNavigation(element: HTMLElement): boolean; handleSpaNavigationCore(url: string | null): boolean; postBack(viewModelName: string, sender: HTMLElement, path: string[], command: string, controlUniqueId: string, context?: any, handlers?: ClientFriendlyPostbackHandlerConfiguration[], commandArgs?: any[]): Promise; - private loadResourceList(resources, callback); - private loadResourceElements(elements, offset, callback); - private getSpaPlaceHolder(); - private navigateCore(viewModelName, url, handlePageNavigating?); - private handleRedirect(resultObject, viewModelName, replace?); - private performRedirect(url, replace, useHistoryApiSpaRedirect?); - private fixSpaUrlPrefix(url); - private removeVirtualDirectoryFromUrl(url, viewModelName); - private addLeadingSlash(url); - private concatUrl(url1, url2); + private loadResourceList; + private loadResourceElements; + private getSpaPlaceHolder; + private navigateCore; + private handleRedirect; + private performRedirect; + private fixSpaUrlPrefix; + private removeVirtualDirectoryFromUrl; + private addLeadingSlash; + private concatUrl; patch(source: any, patch: any): any; - private updateDynamicPathFragments(context, path); - private postJSON(url, method, postData, success, error, preprocessRequest?); - private getJSON(url, method, spaPlaceHolderUniqueId, success, error); + private updateDynamicPathFragments; + private postJSON; + private getJSON; getXHR(): XMLHttpRequest; - private cleanUpdatedControls(resultObject, updatedControls?); - private restoreUpdatedControls(resultObject, updatedControls, applyBindingsOnEachControl); + private cleanUpdatedControls; + private restoreUpdatedControls; unwrapArrayExtension(array: any): any; buildRouteUrl(routePath: string, params: any): string; buildUrlSuffix(urlSuffix: string, query: any): string; - private isPostBackProhibited(element); - private addKnockoutBindingHandlers(); + private isPostBackProhibited; + private addKnockoutBindingHandlers; } declare class DotvvmValidationContext { valueToValidate: any; @@ -455,7 +455,7 @@ declare class DotvvmValidation { * Adds validation errors from the server to the appropriate arrays */ showValidationErrorsFromServer(args: DotvvmAfterPostBackEventArgs): void; - private addValidationError(validatedProperty, error); + private addValidationError; } declare var dotvvm: DotVVM; declare class DotvvmEvaluator { @@ -465,9 +465,9 @@ declare class DotvvmEvaluator { tryEval(func: () => any): any; isObservableArray(instance: any): instance is KnockoutObservableArray; wrapObservable(func: () => any, isArray?: boolean): KnockoutComputed; - private updateObservable(getObservable, value); - private updateObservableArray(getObservableArray, fnName, args); - private getExpressionResult(func); + private updateObservable; + private updateObservableArray; + private getExpressionResult; } declare type ApiComputed = KnockoutObservable & { refreshValue: (throwOnError?: boolean) => PromiseLike | undefined; diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js index 2fa5e58cc7..b36af20b27 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js @@ -1,15 +1,21 @@ -var __assign = (this && this.__assign) || Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); }; 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]; }; + var extendStatics = function (d, b) { + 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 extendStatics(d, b); + }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } @@ -1001,15 +1007,18 @@ var DotVVM = /** @class */ (function () { var _this = this; this.addKnockoutBindingHandlers(); // load the viewmodel - var thisViewModel; - var shouldLoadFromHistoryApi = this.useHistoryApiViewModel && !this.isBrowserReload() && history.state && history.state.dotvvm_viewmodels && history.state.dotvvm_viewmodels[viewModelName]; - if (shouldLoadFromHistoryApi) { - // create a new object, otherwise the object in the history state will be changed which will result in serialize errors. - thisViewModel = __assign({}, history.state.dotvvm_viewmodels[viewModelName]); + var persistedJson = document.getElementById("__dot_persisted_viewmodel_" + viewModelName).value; + var json; + if (persistedJson) { + json = persistedJson; + } + else if (!this.isBrowserReload() && history.state && history.state.dotvvm_viewmodels && history.state.dotvvm_viewmodels[viewModelName]) { + json = history.state.dotvvm_viewmodels[viewModelName]; } else { - thisViewModel = JSON.parse(document.getElementById("__dot_viewmodel_" + viewModelName).value); + json = document.getElementById("__dot_viewmodel_" + viewModelName).value; } + var thisViewModel = thisViewModel = JSON.parse(json); this.viewModels[viewModelName] = thisViewModel; if (thisViewModel.resources) { for (var r in thisViewModel.resources) { @@ -1120,14 +1129,8 @@ var DotVVM = /** @class */ (function () { } }; DotVVM.prototype.persistViewModel = function (viewModelName) { + var _a; var viewModel = this.viewModels[viewModelName]; - if (this.useHistoryApiViewModel) { - var currentState = history.state ? history.state : {}; - var persistedViewModels = currentState.dotvvm_viewmodels ? currentState.dotvvm_viewmodels : {}; - // add the new viewmodel to the existing state, otherwise SPA mode will break. - var state = __assign({ dotvvm_viewmodels: __assign((_a = {}, _a[viewModelName] = ko.toJS(viewModel), _a), persistedViewModels) }, currentState); - history.replaceState(state, document.title); - } var persistedViewModel = {}; for (var p in viewModel) { if (viewModel.hasOwnProperty(p)) { @@ -1135,8 +1138,14 @@ var DotVVM = /** @class */ (function () { } } persistedViewModel["viewModel"] = this.serialization.serialize(persistedViewModel["viewModel"], { serializeAll: true }); - document.getElementById("__dot_viewmodel_" + viewModelName).value = JSON.stringify(persistedViewModel); - var _a; + var json = JSON.stringify(persistedViewModel); + var currentState = history.state ? history.state : {}; + var persistedViewModels = currentState.dotvvm_viewmodels ? currentState.dotvvm_viewmodels : {}; + // add the new viewmodel to the existing state, otherwise SPA mode will break. + var state = __assign({}, currentState, { dotvvm_viewmodels: __assign({}, persistedViewModels, (_a = {}, _a[viewModelName] = json, _a)) }); + document.getElementById("__dot_viewmodel_" + viewModelName).value = json; + document.getElementById("__dot_persisted_viewmodel_" + viewModelName).value = json; + history.replaceState(state, document.title); }; DotVVM.prototype.backUpPostBackConter = function () { return ++this.postBackCounter; @@ -1213,8 +1222,8 @@ var DotVVM = /** @class */ (function () { DotVVM.prototype.sortHandlers = function (handlers) { var getHandler = (function () { var handlerMap = {}; - for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) { - var h = handlers_1[_i]; + for (var _i = 0, handlers_2 = handlers; _i < handlers_2.length; _i++) { + var h = handlers_2[_i]; if (h.name != null) { handlerMap[h.name] = h; } @@ -1222,8 +1231,8 @@ var DotVVM = /** @class */ (function () { return function (s) { return typeof s == "string" ? handlerMap[s] : s; }; })(); var dependencies = handlers.map(function (handler, i) { return (handler["@sort_index"] = i, ({ handler: handler, deps: (handler.after || []).map(getHandler) })); }); - for (var _i = 0, handlers_2 = handlers; _i < handlers_2.length; _i++) { - var h = handlers_2[_i]; + for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) { + var h = handlers_1[_i]; if (h.before) for (var _a = 0, _b = h.before.map(getHandler); _a < _b.length; _a++) { var before = _b[_a]; @@ -1897,6 +1906,7 @@ var DotVVM = /** @class */ (function () { ko.virtualElements.allowedBindings["dotvvm-SSR-foreach"] = true; ko.bindingHandlers["dotvvm-SSR-foreach"] = { init: function (element, valueAccessor, _allBindings, _viewModel, bindingContext) { + var _a; if (!bindingContext) throw new Error(); var value = valueAccessor(); @@ -1904,7 +1914,6 @@ var DotVVM = /** @class */ (function () { element.innerBindingContext = innerBindingContext; ko.applyBindingsToDescendants(innerBindingContext, element); return { controlsDescendantBindings: true }; // do not apply binding again - var _a; } }; ko.virtualElements.allowedBindings["dotvvm-SSR-item"] = true; @@ -1923,6 +1932,7 @@ var DotVVM = /** @class */ (function () { ko.virtualElements.allowedBindings["withGridViewDataSet"] = true; ko.bindingHandlers["withGridViewDataSet"] = { init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { + var _a; if (!bindingContext) throw new Error(); var value = valueAccessor(); @@ -1930,7 +1940,6 @@ var DotVVM = /** @class */ (function () { element.innerBindingContext = innerBindingContext; ko.applyBindingsToDescendants(innerBindingContext, element); return { controlsDescendantBindings: true }; // do not apply binding again - var _a; }, update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { } diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js index 27ea2b8527..b8fe42bd6b 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js @@ -1 +1 @@ -var __assign=this&&this.__assign||Object.assign||function(e){for(var t,n=1,r=arguments.length;n'");return e},e.prototype.format=function(e){for(var o=this,i=[],t=1;t=e.length)n();else{var o=e[t],i=!1;if("script"==o.tagName.toLowerCase()){var a=document.createElement("script");o.src&&(a.src=o.src,i=!0),o.type&&(a.type=o.type),o.text&&(a.text=o.text),o=a}else if("link"==o.tagName.toLowerCase()){var s=document.createElement("link");o.href&&(s.href=o.href),o.rel&&(s.rel=o.rel),o.type&&(s.type=o.type),o=s}i&&(o.onload=function(){return r.loadResourceElements(e,t+1,n)}),document.head.appendChild(o),i||this.loadResourceElements(e,t+1,n)}},e.prototype.getSpaPlaceHolder=function(){var e=document.getElementsByName("__dot_SpaContentPlaceHolder");return 1==e.length?e[0]:null},e.prototype.navigateCore=function(a,e,t){var s=this,n=this.viewModels[a].viewModel,r=this.backUpPostBackConter(),o=new DotvvmSpaNavigatingEventArgs(n,a,e);if(this.events.spaNavigating.trigger(o),!o.cancel){var i=this.viewModels[a].virtualDirectory||"",l="/___dotvvm-spa___"+this.addLeadingSlash(e),d=this.addLeadingSlash(this.concatUrl(i,l)),u=this.getSpaPlaceHolder();if(u){t&&t(this.addLeadingSlash(this.concatUrl(i,this.addLeadingSlash(e))));var v=u.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(d,"GET",v,function(o){if(s.isPostBackStillActive(r)){var i=JSON.parse(o.responseText);s.loadResourceList(i.resources,function(){var e=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void s.handleRedirect(i,a,!0)}else try{s.isViewModelUpdating=!0;var t=s.cleanUpdatedControls(i);for(var n in s.viewModels[a]={},i)i.hasOwnProperty(n)&&(s.viewModels[a][n]=i[n]);ko.delaySync.pause(),s.serialization.deserialize(i.viewModel,s.viewModels[a].viewModel),ko.delaySync.resume(),e=!0,s.viewModelObservables[a](s.viewModels[a].viewModel),s.restoreUpdatedControls(i,t,!0),s.isSpaReady(!0)}finally{s.isViewModelUpdating=!1}var r=new DotvvmSpaNavigatedEventArgs(s.viewModels[a].viewModel,a,i,o);if(s.events.spaNavigated.trigger(r),!e&&!r.isHandled)throw"Invalid response from server!"})}},function(e){if(s.isPostBackStillActive(r)){var t=new DotvvmErrorEventArgs(void 0,n,a,e,-1,void 0,!0);s.events.error.trigger(t),t.handled||alert(e.responseText)}})}else document.location.href=d}},e.prototype.handleRedirect=function(e,t,n){var r;void 0===n&&(n=!1),null!=e.replace&&(n=e.replace),r=this.getSpaPlaceHolder()&&!this.useHistoryApiSpaNavigation&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),this.fixSpaUrlPrefix(r)):e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n,e.allowSpa&&this.useHistoryApiSpaNavigation)},e.prototype.performRedirect=function(e,t,n){if(t)location.replace(e);else if(n)this.handleSpaNavigationCore(e);else{var r=this.fakeRedirectAnchor;r||((r=document.createElement("a")).style.display="none",r.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(r),this.fakeRedirectAnchor=r),r.href=e,r.click()}},e.prototype.fixSpaUrlPrefix=function(e){var t=this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"];if(!t)return e;var n=t.value;return n!==document.location.pathname&&(""===n&&(n="/"),e=n+e),e},e.prototype.removeVirtualDirectoryFromUrl=function(e,t){var n="/"+this.viewModels[t].virtualDirectory;return 0==e.indexOf(n)?this.addLeadingSlash(e.substring(n.length)):e},e.prototype.addLeadingSlash=function(e){return 0'");return e},e.prototype.format=function(e){for(var o=this,i=[],t=1;t=e.length)n();else{var o=e[t],i=!1;if("script"==o.tagName.toLowerCase()){var a=document.createElement("script");o.src&&(a.src=o.src,i=!0),o.type&&(a.type=o.type),o.text&&(a.text=o.text),o=a}else if("link"==o.tagName.toLowerCase()){var s=document.createElement("link");o.href&&(s.href=o.href),o.rel&&(s.rel=o.rel),o.type&&(s.type=o.type),o=s}i&&(o.onload=function(){return r.loadResourceElements(e,t+1,n)}),document.head.appendChild(o),i||this.loadResourceElements(e,t+1,n)}},e.prototype.getSpaPlaceHolder=function(){var e=document.getElementsByName("__dot_SpaContentPlaceHolder");return 1==e.length?e[0]:null},e.prototype.navigateCore=function(a,e,t){var s=this,n=this.viewModels[a].viewModel,r=this.backUpPostBackConter(),o=new DotvvmSpaNavigatingEventArgs(n,a,e);if(this.events.spaNavigating.trigger(o),!o.cancel){var i=this.viewModels[a].virtualDirectory||"",l="/___dotvvm-spa___"+this.addLeadingSlash(e),d=this.addLeadingSlash(this.concatUrl(i,l)),u=this.getSpaPlaceHolder();if(u){t&&t(this.addLeadingSlash(this.concatUrl(i,this.addLeadingSlash(e))));var v=u.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(d,"GET",v,function(o){if(s.isPostBackStillActive(r)){var i=JSON.parse(o.responseText);s.loadResourceList(i.resources,function(){var e=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void s.handleRedirect(i,a,!0)}else try{s.isViewModelUpdating=!0;var t=s.cleanUpdatedControls(i);for(var n in s.viewModels[a]={},i)i.hasOwnProperty(n)&&(s.viewModels[a][n]=i[n]);ko.delaySync.pause(),s.serialization.deserialize(i.viewModel,s.viewModels[a].viewModel),ko.delaySync.resume(),e=!0,s.viewModelObservables[a](s.viewModels[a].viewModel),s.restoreUpdatedControls(i,t,!0),s.isSpaReady(!0)}finally{s.isViewModelUpdating=!1}var r=new DotvvmSpaNavigatedEventArgs(s.viewModels[a].viewModel,a,i,o);if(s.events.spaNavigated.trigger(r),!e&&!r.isHandled)throw"Invalid response from server!"})}},function(e){if(s.isPostBackStillActive(r)){var t=new DotvvmErrorEventArgs(void 0,n,a,e,-1,void 0,!0);s.events.error.trigger(t),t.handled||alert(e.responseText)}})}else document.location.href=d}},e.prototype.handleRedirect=function(e,t,n){var r;void 0===n&&(n=!1),null!=e.replace&&(n=e.replace),r=this.getSpaPlaceHolder()&&!this.useHistoryApiSpaNavigation&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),this.fixSpaUrlPrefix(r)):e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n,e.allowSpa&&this.useHistoryApiSpaNavigation)},e.prototype.performRedirect=function(e,t,n){if(t)location.replace(e);else if(n)this.handleSpaNavigationCore(e);else{var r=this.fakeRedirectAnchor;r||((r=document.createElement("a")).style.display="none",r.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(r),this.fakeRedirectAnchor=r),r.href=e,r.click()}},e.prototype.fixSpaUrlPrefix=function(e){var t=this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"];if(!t)return e;var n=t.value;return n!==document.location.pathname&&(""===n&&(n="/"),e=n+e),e},e.prototype.removeVirtualDirectoryFromUrl=function(e,t){var n="/"+this.viewModels[t].virtualDirectory;return 0==e.indexOf(n)?this.addLeadingSlash(e.substring(n.length)):e},e.prototype.addLeadingSlash=function(e){return 0document.getElementById("__dot_persisted_viewmodel_" + viewModelName)).value; + var json: string; - if (shouldLoadFromHistoryApi) { - // create a new object, otherwise the object in the history state will be changed which will result in serialize errors. - thisViewModel = { ...history.state.dotvvm_viewmodels[viewModelName] }; + if (persistedJson) { + json = persistedJson; + } else if (!this.isBrowserReload() && history.state && history.state.dotvvm_viewmodels && history.state.dotvvm_viewmodels[viewModelName]) { + json = history.state.dotvvm_viewmodels[viewModelName]; } else { - thisViewModel = JSON.parse((document.getElementById("__dot_viewmodel_" + viewModelName)).value); + json = (document.getElementById("__dot_viewmodel_" + viewModelName)).value; } + var thisViewModel = thisViewModel = JSON.parse(json); this.viewModels[viewModelName] = thisViewModel; if (thisViewModel.resources) { @@ -382,22 +384,6 @@ class DotVVM { private persistViewModel(viewModelName: string) { var viewModel = this.viewModels[viewModelName]; - if (this.useHistoryApiViewModel) { - var currentState = history.state ? history.state : {}; - var persistedViewModels = currentState.dotvvm_viewmodels ? currentState.dotvvm_viewmodels : {}; - - // add the new viewmodel to the existing state, otherwise SPA mode will break. - var state = { - dotvvm_viewmodels: { - [viewModelName]: ko.toJS(viewModel), - ...persistedViewModels - }, - ...currentState - }; - - history.replaceState(state, document.title); - } - var persistedViewModel = {}; for (var p in viewModel) { if (viewModel.hasOwnProperty(p)) { @@ -405,7 +391,23 @@ class DotVVM { } } persistedViewModel["viewModel"] = this.serialization.serialize(persistedViewModel["viewModel"], { serializeAll: true }); - (document.getElementById("__dot_viewmodel_" + viewModelName)).value = JSON.stringify(persistedViewModel); + + var json = JSON.stringify(persistedViewModel); + var currentState = history.state ? history.state : {}; + var persistedViewModels = currentState.dotvvm_viewmodels ? currentState.dotvvm_viewmodels : {}; + + // add the new viewmodel to the existing state, otherwise SPA mode will break. + var state = { + ...currentState, + dotvvm_viewmodels: { + ...persistedViewModels, + [viewModelName]: json + } + }; + + (document.getElementById("__dot_viewmodel_" + viewModelName)).value = json; + (document.getElementById("__dot_persisted_viewmodel_" + viewModelName)).value = json; + history.replaceState(state, document.title); } private backUpPostBackConter(): number { From 22aa52451dde461a8423d3187743341aaa58da95 Mon Sep 17 00:00:00 2001 From: Gerard Smit Date: Mon, 29 Apr 2019 19:46:05 +0200 Subject: [PATCH 4/4] Removed unused variable --- src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts | 1 - src/DotVVM.Framework/Resources/Scripts/DotVVM.js | 1 - src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js | 2 +- src/DotVVM.Framework/Resources/Scripts/DotVVM.ts | 2 -- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts b/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts index 3b6ea21cb9..d15103c545 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.d.ts @@ -307,7 +307,6 @@ declare class DotVVM { extensions: IDotvvmExtensions; useHistoryApiSpaNavigation: boolean; isPostbackRunning: KnockoutObservable; - useHistoryApiViewModel: boolean; private isBrowserReload; init(viewModelName: string, culture: string): void; private handlePopState; diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js index b36af20b27..fbc12ab91b 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js @@ -970,7 +970,6 @@ var DotVVM = /** @class */ (function () { this.fileUpload = new DotvvmFileUpload(); this.extensions = {}; this.isPostbackRunning = ko.observable(false); - this.useHistoryApiViewModel = history && ((/Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)) || /Edge/.test(navigator.userAgent)); } DotVVM.prototype.createWindowSetTimeoutHandler = function (time) { return { diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js index b8fe42bd6b..9fce65345b 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js @@ -1 +1 @@ -var __assign=this&&this.__assign||function(){return(__assign=Object.assign||function(e){for(var t,n=1,r=arguments.length;n'");return e},e.prototype.format=function(e){for(var o=this,i=[],t=1;t=e.length)n();else{var o=e[t],i=!1;if("script"==o.tagName.toLowerCase()){var a=document.createElement("script");o.src&&(a.src=o.src,i=!0),o.type&&(a.type=o.type),o.text&&(a.text=o.text),o=a}else if("link"==o.tagName.toLowerCase()){var s=document.createElement("link");o.href&&(s.href=o.href),o.rel&&(s.rel=o.rel),o.type&&(s.type=o.type),o=s}i&&(o.onload=function(){return r.loadResourceElements(e,t+1,n)}),document.head.appendChild(o),i||this.loadResourceElements(e,t+1,n)}},e.prototype.getSpaPlaceHolder=function(){var e=document.getElementsByName("__dot_SpaContentPlaceHolder");return 1==e.length?e[0]:null},e.prototype.navigateCore=function(a,e,t){var s=this,n=this.viewModels[a].viewModel,r=this.backUpPostBackConter(),o=new DotvvmSpaNavigatingEventArgs(n,a,e);if(this.events.spaNavigating.trigger(o),!o.cancel){var i=this.viewModels[a].virtualDirectory||"",l="/___dotvvm-spa___"+this.addLeadingSlash(e),d=this.addLeadingSlash(this.concatUrl(i,l)),u=this.getSpaPlaceHolder();if(u){t&&t(this.addLeadingSlash(this.concatUrl(i,this.addLeadingSlash(e))));var v=u.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(d,"GET",v,function(o){if(s.isPostBackStillActive(r)){var i=JSON.parse(o.responseText);s.loadResourceList(i.resources,function(){var e=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void s.handleRedirect(i,a,!0)}else try{s.isViewModelUpdating=!0;var t=s.cleanUpdatedControls(i);for(var n in s.viewModels[a]={},i)i.hasOwnProperty(n)&&(s.viewModels[a][n]=i[n]);ko.delaySync.pause(),s.serialization.deserialize(i.viewModel,s.viewModels[a].viewModel),ko.delaySync.resume(),e=!0,s.viewModelObservables[a](s.viewModels[a].viewModel),s.restoreUpdatedControls(i,t,!0),s.isSpaReady(!0)}finally{s.isViewModelUpdating=!1}var r=new DotvvmSpaNavigatedEventArgs(s.viewModels[a].viewModel,a,i,o);if(s.events.spaNavigated.trigger(r),!e&&!r.isHandled)throw"Invalid response from server!"})}},function(e){if(s.isPostBackStillActive(r)){var t=new DotvvmErrorEventArgs(void 0,n,a,e,-1,void 0,!0);s.events.error.trigger(t),t.handled||alert(e.responseText)}})}else document.location.href=d}},e.prototype.handleRedirect=function(e,t,n){var r;void 0===n&&(n=!1),null!=e.replace&&(n=e.replace),r=this.getSpaPlaceHolder()&&!this.useHistoryApiSpaNavigation&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),this.fixSpaUrlPrefix(r)):e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n,e.allowSpa&&this.useHistoryApiSpaNavigation)},e.prototype.performRedirect=function(e,t,n){if(t)location.replace(e);else if(n)this.handleSpaNavigationCore(e);else{var r=this.fakeRedirectAnchor;r||((r=document.createElement("a")).style.display="none",r.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(r),this.fakeRedirectAnchor=r),r.href=e,r.click()}},e.prototype.fixSpaUrlPrefix=function(e){var t=this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"];if(!t)return e;var n=t.value;return n!==document.location.pathname&&(""===n&&(n="/"),e=n+e),e},e.prototype.removeVirtualDirectoryFromUrl=function(e,t){var n="/"+this.viewModels[t].virtualDirectory;return 0==e.indexOf(n)?this.addLeadingSlash(e.substring(n.length)):e},e.prototype.addLeadingSlash=function(e){return 0'");return e},e.prototype.format=function(e){for(var o=this,i=[],t=1;t=e.length)n();else{var o=e[t],i=!1;if("script"==o.tagName.toLowerCase()){var a=document.createElement("script");o.src&&(a.src=o.src,i=!0),o.type&&(a.type=o.type),o.text&&(a.text=o.text),o=a}else if("link"==o.tagName.toLowerCase()){var s=document.createElement("link");o.href&&(s.href=o.href),o.rel&&(s.rel=o.rel),o.type&&(s.type=o.type),o=s}i&&(o.onload=function(){return r.loadResourceElements(e,t+1,n)}),document.head.appendChild(o),i||this.loadResourceElements(e,t+1,n)}},e.prototype.getSpaPlaceHolder=function(){var e=document.getElementsByName("__dot_SpaContentPlaceHolder");return 1==e.length?e[0]:null},e.prototype.navigateCore=function(a,e,t){var s=this,n=this.viewModels[a].viewModel,r=this.backUpPostBackConter(),o=new DotvvmSpaNavigatingEventArgs(n,a,e);if(this.events.spaNavigating.trigger(o),!o.cancel){var i=this.viewModels[a].virtualDirectory||"",l="/___dotvvm-spa___"+this.addLeadingSlash(e),d=this.addLeadingSlash(this.concatUrl(i,l)),u=this.getSpaPlaceHolder();if(u){t&&t(this.addLeadingSlash(this.concatUrl(i,this.addLeadingSlash(e))));var v=u.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(d,"GET",v,function(o){if(s.isPostBackStillActive(r)){var i=JSON.parse(o.responseText);s.loadResourceList(i.resources,function(){var e=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void s.handleRedirect(i,a,!0)}else try{s.isViewModelUpdating=!0;var t=s.cleanUpdatedControls(i);for(var n in s.viewModels[a]={},i)i.hasOwnProperty(n)&&(s.viewModels[a][n]=i[n]);ko.delaySync.pause(),s.serialization.deserialize(i.viewModel,s.viewModels[a].viewModel),ko.delaySync.resume(),e=!0,s.viewModelObservables[a](s.viewModels[a].viewModel),s.restoreUpdatedControls(i,t,!0),s.isSpaReady(!0)}finally{s.isViewModelUpdating=!1}var r=new DotvvmSpaNavigatedEventArgs(s.viewModels[a].viewModel,a,i,o);if(s.events.spaNavigated.trigger(r),!e&&!r.isHandled)throw"Invalid response from server!"})}},function(e){if(s.isPostBackStillActive(r)){var t=new DotvvmErrorEventArgs(void 0,n,a,e,-1,void 0,!0);s.events.error.trigger(t),t.handled||alert(e.responseText)}})}else document.location.href=d}},e.prototype.handleRedirect=function(e,t,n){var r;void 0===n&&(n=!1),null!=e.replace&&(n=e.replace),r=this.getSpaPlaceHolder()&&!this.useHistoryApiSpaNavigation&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),this.fixSpaUrlPrefix(r)):e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n,e.allowSpa&&this.useHistoryApiSpaNavigation)},e.prototype.performRedirect=function(e,t,n){if(t)location.replace(e);else if(n)this.handleSpaNavigationCore(e);else{var r=this.fakeRedirectAnchor;r||((r=document.createElement("a")).style.display="none",r.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(r),this.fakeRedirectAnchor=r),r.href=e,r.click()}},e.prototype.fixSpaUrlPrefix=function(e){var t=this.getSpaPlaceHolder().attributes["data-dotvvm-spacontentplaceholder-urlprefix"];if(!t)return e;var n=t.value;return n!==document.location.pathname&&(""===n&&(n="/"),e=n+e),e},e.prototype.removeVirtualDirectoryFromUrl=function(e,t){var n="/"+this.viewModels[t].virtualDirectory;return 0==e.indexOf(n)?this.addLeadingSlash(e.substring(n.length)):e},e.prototype.addLeadingSlash=function(e){return 0