diff --git a/src/DotVVM.Framework/Controls/RouteLinkHelpers.cs b/src/DotVVM.Framework/Controls/RouteLinkHelpers.cs index ca94f74437..201019b1b2 100644 --- a/src/DotVVM.Framework/Controls/RouteLinkHelpers.cs +++ b/src/DotVVM.Framework/Controls/RouteLinkHelpers.cs @@ -111,8 +111,9 @@ private static string GenerateRouteLinkCore(string routeName, RouteLink control, // generate the function call return - parametersExpression.Length > 0 ? $"dotvvm.buildRouteUrl({JsonConvert.ToString(route.Url)}, {{{parametersExpression}}})" : - JsonConvert.ToString(route.Url); + route.ParameterNames.Any() + ? $"dotvvm.buildRouteUrl({JsonConvert.ToString(route.Url)}, {{{parametersExpression}}})" + : JsonConvert.ToString(route.Url); } private static string TranslateRouteParameter(DotvvmBindableObject control, KeyValuePair param, bool caseSensitive = false) diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js index 1749f76ee9..6ee28380a9 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.js @@ -1596,11 +1596,13 @@ var DotVVM = /** @class */ (function () { return ko.unwrap(ko.unwrap(array)); }; DotVVM.prototype.buildRouteUrl = function (routePath, params) { - var url = routePath.replace(/\{([^\}]+?)\??(:(.+?))?\}/g, function (s, paramName, hsjdhsj, type) { + // prepend url with backslash to correctly handle optional parameters at start + routePath = '/' + routePath; + var url = routePath.replace(/(\/[^\/]*?)\{([^\}]+?)\??(:(.+?))?\}/g, function (s, prefix, paramName, _, type) { if (!paramName) return ""; var x = ko.unwrap(params[paramName.toLowerCase()]); - return x == null ? "" : x; + return x == null ? "" : prefix + x; }); if (url.indexOf('/') === 0) { return url.substring(1); diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js index 002fdcbc1f..a318e14fc7 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js @@ -1 +1 @@ -function basicAuthenticatedFetch(e,t){var n=sessionStorage.getItem("someAuth");return null!=n&&(null==t&&(t={}),null==t.headers&&(t.headers={}),null==t.headers.Authorization&&(t.headers.Authorization="Basic "+btoa(n))),null==t&&(t={}),t.cache||(t.cache="no-cache"),window.fetch(e,t).then(function(r){return 401===r.status&&null==n?(null==sessionStorage.getItem("someAuth")&&function(){var t=prompt("You credentials for "+(e.url||e))||"";sessionStorage.setItem("someAuth",t)}(),basicAuthenticatedFetch(e,t)):r})}var __assign=this&&this.__assign||Object.assign||function(e){for(var t,n=1,r=arguments.length;n=0&&(this.handlers=this.handlers.splice(t,1))},e.prototype.trigger=function(e){for(var t=0;t7?parseInt(t[7].substring(1,4)):0):null},e.prototype.parseNumber=function(e){return dotvvm_Globalize.parseFloat(e,10,dotvvm.culture)},e.prototype.parseDate=function(e,t,n){return dotvvm_Globalize.parseDate(e,t,dotvvm.culture,n)},e.prototype.bindingDateToString=function(e,t){var n=this;if(void 0===t&&(t="G"),!e)return"";var r=function(){var t=ko.unwrap(e);return"string"==typeof t?n.parseDotvvmDate(t):t},o=function(){var e=r();return null!=e?dotvvm_Globalize.format(e,t,dotvvm.culture):""};if(ko.isWriteableObservable(e)){var i="string"==typeof r()?function(t){return e(null==t?null:dotvvm.serialization.serializeDate(t,!1))}:e;return ko.pureComputed({read:function(){return o()},write:function(e){return i(dotvvm_Globalize.parseDate(e,t,dotvvm.culture))}})}return ko.pureComputed(function(){return o()})},e.prototype.bindingNumberToString=function(e,t){var n=this;if(void 0===t&&(t="G"),!e)return"";var r=function(){var r=function(){var t=ko.unwrap(e);return"string"==typeof t?n.parseNumber(t):t}();return null!=r?dotvvm_Globalize.format(r,t,dotvvm.culture):""};return ko.isWriteableObservable(e)?ko.pureComputed({read:function(){return r()},write:function(t){var n=dotvvm_Globalize.parseFloat(t,10,dotvvm.culture),r=null==t||null!=n&&!isNaN(n);e(r?n:null)}}):ko.pureComputed(function(){return r()})},e}(),PostbackOptions=function(){return function(e,t,n,r,o){void 0===n&&(n=[]),this.postbackId=e,this.sender=t,this.args=n,this.viewModel=r,this.viewModelName=o,this.additionalPostbackData={}}}(),ConfirmPostBackHandler=function(){function e(e){this.message=e}return e.prototype.execute=function(e,t){var n=this;return new Promise(function(t,r){confirm(n.message)?e().then(t,r):r({type:"handler",handler:n,message:"The postback was not confirmed"})})},e}(),DotvvmSerialization=function(){function e(){}return e.prototype.deserialize=function(e,t,n){if(void 0===n&&(n=!1),void 0===e||null==e)return ko.isObservable(t)&&t(e),e;if("string"==typeof e||"number"==typeof e||"boolean"==typeof e)return ko.isObservable(t)&&t(e),e;if(e instanceof Date)return e=dotvvm.serialization.serializeDate(e),ko.isObservable(t)&&t(e),e;if(e instanceof Array){if(ko.isObservable(t)&&"removeAll"in t&&null!=t()&&t().length===e.length)for(var r=t(),o=0;o=a&&l<=s&&l===parseFloat(e)}return"number"!==t&&"single"!==t&&"double"!==t&&"decimal"!==t||(+e===e||!isNaN(+e)&&"string"==typeof e)},e.prototype.findObject=function(e,t){if(t(e))return[];if(e=ko.unwrap(e),t(e))return[];if("object"!=typeof e)return null;for(var n in e)if(e.hasOwnProperty(n)){var r=this.findObject(e[n],t);if(r)return r.push(n),r}return null},e.prototype.flatSerialize=function(e){return this.serialize(e,{ignoreSpecialProperties:!0,oneLevel:!0,serializeAll:!0})},e.prototype.getPureObject=function(e){if((e=ko.unwrap(e))instanceof Array)return e.map(this.getPureObject.bind(this));var t={};for(var n in e)"$"!=n[0]&&(t[n]=e[n]);return t},e.prototype.pad=function(e,t){for(;e.length0?Promise.reject({type:"handler",handler:this,message:"An postback is already running"}):dotvvm.commonConcurrencyHandler(t(),n,r)}}},"concurrency-queue":function(e){return{name:"concurrency-queue",before:["setIsPostackRunning"],execute:function(t,n){var r=e.q||"default";return dotvvm.getPostbackQueue(r).noRunning>0?new Promise(function(e){dotvvm.getPostbackQueue(r).queue.push(function(){return e(t())})}):dotvvm.commonConcurrencyHandler(t(),n,r)}}},suppressOnUpdating:function(e){return{name:"suppressOnUpdating",before:["setIsPostackRunning","concurrency-none","concurrency-queue","concurrency-deny"],execute:function(e,t){return dotvvm.isViewModelUpdating?Promise.reject({type:"handler",handler:this,message:"ViewModel is updating, so it's probably false onchange event"}):e()}}}},this.beforePostbackEventPostbackHandler={execute:function(t,n){var r=new DotvvmBeforePostBackEventArgs(n.sender,n.viewModel,n.viewModelName,n.postbackId);return e.events.beforePostback.trigger(r),r.cancel?Promise.reject({type:"event",options:n}):t()}},this.isPostBackRunningHandler=function(){var t=0;return{name:"setIsPostbackRunning",before:["eventInvoke-postbackHandlersStarted"],execute:function(n,r){e.isPostbackRunning(!0),t++;var o=n();return o.then(function(){return e.isPostbackRunning(!!--t)},function(){return e.isPostbackRunning(!!--t)}),o}}}(),this.windowSetTimeoutHandler=this.createWindowSetTimeoutHandler(0),this.commonConcurrencyHandler=function(t,n,r){var o=e.getPostbackQueue(r);o.noRunning++;var i=function(){if(o.noRunning--,o.queue.length>0){var e=o.queue.shift();window.setTimeout(e,0)}};return t.then(function(t){var r=e.lastStartedPostack==n.postbackId?t:function(){return Promise.reject(null)};return function(){var e=r();return e.then(i,i),e}},function(e){return i(),Promise.reject(e)})},this.defaultConcurrencyPostbackHandler=this.postbackHandlers["concurrency-none"]({}),this.postbackQueues={},this.postbackHandlersStartedEventHandler={name:"eventInvoke-postbackHandlersStarted",execute:function(e,t){return dotvvm.events.postbackHandlersStarted.trigger(t),e()}},this.postbackHandlersCompletedEventHandler={name:"eventInvoke-postbackHandlersCompleted",after:["eventInvoke-postbackHandlersStarted"],execute:function(e,t){return dotvvm.events.postbackHandlersCompleted.trigger(t),e()}},this.globalPostbackHandlers=[this.isPostBackRunningHandler,this.postbackHandlersStartedEventHandler],this.globalLaterPostbackHandlers=[this.postbackHandlersCompletedEventHandler,this.beforePostbackEventPostbackHandler],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(!1)}return e.prototype.createWindowSetTimeoutHandler=function(e){return{name:"timeout",before:["eventInvoke-postbackHandlersStarted","setIsPostbackRunning"],execute:function(t,n){return new Promise(function(t,n){return window.setTimeout(t,e)}).then(function(){return t()})}}},e.prototype.getPostbackQueue=function(e){return void 0===e&&(e="default"),this.postbackQueues[e]||(this.postbackQueues[e]={queue:[],noRunning:0}),this.postbackQueues[e]},e.prototype.init=function(e,t){var n=this;this.addKnockoutBindingHandlers();var r=this.viewModels[e]=JSON.parse(document.getElementById("__dot_viewmodel_"+e).value);if(r.resources)for(var o in r.resources)this.resourceSigns[o]=!0;r.renderedResources&&r.renderedResources.forEach(function(e){return n.resourceSigns[e]=!0});var i=r.resultIdFragment,a=r.viewModel=this.serialization.deserialize(this.viewModels[e].viewModel,{},!0);this.culture=t,this.validation=new DotvvmValidation(this),this.viewModelObservables[e]=ko.observable(a),ko.applyBindings(this.viewModelObservables[e],document.documentElement),this.events.init.trigger({viewModel:a});var s=this.getSpaPlaceHolder();if(null!=s&&(this.domUtils.attachEvent(window,"hashchange",function(){return n.handleHashChange(e,s,!1)}),this.handleHashChange(e,s,!0)),this.isViewModelUpdating=!1,i)if(s){var l=document.getElementById(i);l&&"function"==typeof l.scrollIntoView&&l.scrollIntoView(!0)}else location.hash=i;this.domUtils.attachEvent(window,"beforeunload",function(t){n.persistViewModel(e)})},e.prototype.handleHashChange=function(e,t,n){if(0===document.location.hash.indexOf("#!/"))this.navigateCore(e,document.location.hash.substring(2));else{var r=t.getAttribute("data-dotvvm-spacontentplaceholder-defaultroute");if(r)r="#!/"+r,r=this.fixSpaUrlPrefix(r),this.performRedirect(r,n);else if(n)this.isSpaReady(!0),t.style.display="";else{var o=(r=document.location.toString()).indexOf("/","https://".length);r=o>0?r.substring(o):"/",this.navigateCore(e,r)}}},e.prototype.persistViewModel=function(e){var t=this.viewModels[e],n={};for(var r in t)t.hasOwnProperty(r)&&(n[r]=t[r]);n.viewModel=this.serialization.serialize(n.viewModel,{serializeAll:!0}),document.getElementById("__dot_viewmodel_"+e).value=JSON.stringify(n)},e.prototype.backUpPostBackConter=function(){return++this.postBackCounter},e.prototype.isPostBackStillActive=function(e){return this.postBackCounter===e},e.prototype.staticCommandPostback=function(e,t,n,r,o,i){var a=this;if(void 0===o&&(o=function(e){}),void 0===i&&(i=function(e,t){}),!this.isPostBackProhibited(t)){var s=this.serialization.serialize({args:r,command:n,$csrfToken:this.viewModels[e].viewModel.$csrfToken});dotvvm.events.staticCommandMethodInvoking.trigger(s),this.postJSON(this.viewModels[e].url,"POST",ko.toJSON(s),function(e){try{a.isViewModelUpdating=!0;var t=JSON.parse(e.responseText);dotvvm.events.staticCommandMethodInvoked.trigger(__assign({},s,{result:t})),o(t)}catch(t){dotvvm.events.staticCommandMethodFailed.trigger(__assign({},s,{xhr:e,error:t})),i(e,t)}finally{a.isViewModelUpdating=!1}},function(e){console.warn("StaticCommand postback failed: "+e.status+" - "+e.statusText,e),i(e),dotvvm.events.staticCommandMethodFailed.trigger(__assign({},s,{xhr:e}))},function(e){e.setRequestHeader("X-PostbackType","StaticCommand")})}},e.prototype.processPassedId=function(e,t){if("string"==typeof e||null==e)return e;if("object"==typeof e&&e.expr)return this.evaluator.evaluateOnViewModel(t,e.expr);throw new Error("invalid argument")},e.prototype.getPostbackHandler=function(e){var t=this.postbackHandlers[e];if(t)return t;throw new Error("Could not find postback handler of name '"+e+"'")},e.prototype.isPostbackHandler=function(e){return e&&"function"==typeof e.execute},e.prototype.findPostbackHandlers=function(e,t){var n=this,r=function(e,t){return!1===t.enabled?null:n.getPostbackHandler(e)(t)};return t.map(function(t){return"string"==typeof t?r(t,{}):n.isPostbackHandler(t)?t:t instanceof Array?function(){var n=t[0],o=t[1];return r(n,"function"==typeof o?o(e,e.$data):o)}():r(t.name,t.options&&t.options(e))}).filter(function(e){return null!=e})},e.prototype.sortHandlers=function(e){for(var t=function(){for(var t={},n=0,r=e;n0?{action:"redirect",url:o}:JSON.parse(t.responseText);!i.viewModel&&i.viewModelDiff&&(i.viewModel=a.patch(v.viewModel,i.viewModelDiff)),a.loadResourceList(i.resources,function(){var o=!1;if("successfulCommand"===i.action){try{a.isViewModelUpdating=!0;var s=a.cleanUpdatedControls(i);i.viewModel&&(ko.delaySync.pause(),a.serialization.deserialize(i.viewModel,a.viewModels[u].viewModel),ko.delaySync.resume()),o=!0,a.cleanUpdatedControls(i,s),a.restoreUpdatedControls(i,s,!0)}finally{a.isViewModelUpdating=!1}dotvvm.events.postbackViewModelUpdated.trigger({})}else if("redirect"===i.action)return a.handleRedirect(i,u),n();var l=i.resultIdFragment;if(l)if(a.getSpaPlaceHolder()||location.hash=="#"+l){var v=document.getElementById(l);v&&"function"==typeof v.scrollIntoView&&v.scrollIntoView(!0)}else location.hash=l;if(o){var c=new DotvvmAfterPostBackEventArgs(e,i,i.commandResult,t);n(c)}else r(new DotvvmErrorEventArgs(e.sender,d,u,t,e.postbackId,i))})})})},function(t){l({type:"network",options:e,args:new DotvvmErrorEventArgs(e.sender,d,u,t,e.postbackId)})})})},e.prototype.postBack=function(e,t,n,r,o,i,a,s){var l=this;if(this.isPostBackProhibited(t))return new Promise(function(e,t){return t("rejected")});i=i||ko.contextFor(t);var u=this.findPostbackHandlers(i,this.globalPostbackHandlers.concat(a||[]).concat(this.globalLaterPostbackHandlers));0==u.filter(function(e){return e.name&&0==e.name.indexOf("concurrency-")}).length&&u.push(this.defaultConcurrencyPostbackHandler);var d=new PostbackOptions(this.backUpPostBackConter(),t,s,i.$data,e),v=this.applyPostbackHandlersCore(function(e){return l.postbackCore(e,n,r,o,i,s)},d,u).then(function(e){return e().then(function(e){return e},function(e){return Promise.reject({type:"commit",args:e})})},function(e){return Promise.reject(e)});return v.then(function(e){return e&&l.events.afterPostback.trigger(e)},function(e){var t=new DotvvmAfterPostBackEventArgs(d,"commit"==e.type&&e.args?e.args.serverResponseObject:null,d.postbackId);"handler"==e.type||"event"==e.type?(t.wasInterrupted=!0,l.events.postbackRejected.trigger({})):"network"==e.type&&l.events.error.trigger(e.args),l.events.afterPostback.trigger(t)}),v},e.prototype.loadResourceList=function(e,t){var n="";for(var r in e){if(!/^__noname_\d+$/.test(r)){if(this.resourceSigns[r])continue;this.resourceSigns[r]=!0}n+=e[r]+" "}if(""!==n.trim()){var o=document.createElement("div");o.innerHTML=n;for(var i=[],a=0;a=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(e,t){var n=this,r=this.viewModels[e].viewModel,o=this.backUpPostBackConter(),i=new DotvvmSpaNavigatingEventArgs(r,e,t);if(this.events.spaNavigating.trigger(i),!i.cancel){t="/___dotvvm-spa___"+this.addLeadingSlash(t);var a=this.addLeadingSlash(this.concatUrl(this.viewModels[e].virtualDirectory||"",t)),s=this.getSpaPlaceHolder();if(s){var l=s.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(a,"GET",l,function(t){if(n.isPostBackStillActive(o)){var i=JSON.parse(t.responseText);n.loadResourceList(i.resources,function(){var o=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void n.handleRedirect(i,e,!0)}else try{n.isViewModelUpdating=!0;var a=n.cleanUpdatedControls(i);n.viewModels[e]={};for(var s in i)i.hasOwnProperty(s)&&(n.viewModels[e][s]=i[s]);ko.delaySync.pause(),n.serialization.deserialize(i.viewModel,n.viewModels[e].viewModel),ko.delaySync.resume(),o=!0,n.viewModelObservables[e](n.viewModels[e].viewModel),n.restoreUpdatedControls(i,a,!0),n.isSpaReady(!0)}finally{n.isViewModelUpdating=!1}var l=new DotvvmSpaNavigatedEventArgs(r,e,i,t);if(n.events.spaNavigated.trigger(l),!o&&!l.isHandled)throw"Invalid response from server!"})}},function(t){if(n.isPostBackStillActive(o)){var i=new DotvvmErrorEventArgs(void 0,r,e,t,-1,void 0,!0);n.events.error.trigger(i),i.handled||alert(t.responseText)}})}else document.location.href=a}},e.prototype.handleRedirect=function(e,t,n){void 0===n&&(n=!1),null!=e.replace&&(n=e.replace);var r;this.getSpaPlaceHolder()&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),r=this.fixSpaUrlPrefix(r)):r=e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n)},e.prototype.performRedirect=function(e,t){if(t)location.replace(e);else{var n=this.fakeRedirectAnchor;n||((n=document.createElement("a")).style.display="none",n.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(n),this.fakeRedirectAnchor=n),n.href=e,n.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 e.length>0&&"/"!=e.substring(0,1)?"/"+e:e},e.prototype.concatUrl=function(e,t){return e.length>0&&"/"==e.substring(e.length-1)&&(e=e.substring(0,e.length-1)),e+this.addLeadingSlash(t)},e.prototype.patch=function(e,t){var n=this;if(e instanceof Array&&t instanceof Array)return t.map(function(t,r){return n.patch(e[r],t)});if(e instanceof Array||t instanceof Array)return t;if("object"!=typeof e||"object"!=typeof t)return t;for(var r in t)null==t[r]?e[r]=null:null==e[r]?e[r]=t[r]:e[r]=this.patch(e[r],t[r]);return e},e.prototype.updateDynamicPathFragments=function(e,t){for(var n=t.length-1;n>=0;n--)t[n].indexOf("[$index]")>=0&&(t[n]=t[n].replace("[$index]","["+e.$index()+"]")),e=e.$parentContext},e.prototype.postJSON=function(e,t,n,r,o,i){void 0===i&&(i=function(e){});var a=this.getXHR();a.open(t,e,!0),a.setRequestHeader("Content-Type","application/json"),a.setRequestHeader("X-DotVVM-PostBack","true"),a.setRequestHeader("X-Requested-With","XMLHttpRequest"),i(a),a.onreadystatechange=function(){a.readyState===XMLHttpRequest.DONE&&(a.status<400?r(a):o(a))},a.send(n)},e.prototype.getJSON=function(e,t,n,r,o){var i=this.getXHR();i.open(t,e,!0),i.setRequestHeader("Content-Type","application/json"),i.setRequestHeader("X-DotVVM-SpaContentPlaceHolder",n),i.onreadystatechange=function(){i.readyState===XMLHttpRequest.DONE&&(i.status<400?r(i):o(i))},i.send()},e.prototype.getXHR=function(){return XMLHttpRequest?new XMLHttpRequest:new window.ActiveXObject("Microsoft.XMLHTTP")},e.prototype.cleanUpdatedControls=function(e,t){void 0===t&&(t={});for(var n in e.updatedControls)if(e.updatedControls.hasOwnProperty(n)){var r=document.getElementByDotvvmId(n);if(r){var o=ko.contextFor(r),i=r.nextSibling,a=r.parentNode;ko.removeNode(r),t[n]={control:r,nextSibling:i,parent:a,dataContext:o}}}return t},e.prototype.restoreUpdatedControls=function(e,t,n){var r=this;for(var o in e.updatedControls)if(e.updatedControls.hasOwnProperty(o)){var i=t[o];if(i){var a=document.createElement(t[o].parent.tagName||"div");if(a.innerHTML=e.updatedControls[o],a.childElementCount>1)throw new Error("Postback.Update control can not render more than one element");var s=a.firstElementChild;if(null==s.id)throw new Error("Postback.Update control always has to render id attribute.");s.id!==t[o].control.id&&console.log("Postback.Update control changed id from '"+t[o].control.id+"' to '"+s.id+"'"),a.removeChild(s),i.nextSibling?i.parent.insertBefore(s,i.nextSibling):i.parent.appendChild(s)}}n&&window.setTimeout(function(){try{r.isViewModelUpdating=!0;for(var n in e.updatedControls){var o=document.getElementByDotvvmId(n);o&&ko.applyBindings(t[n].dataContext,o)}}finally{r.isViewModelUpdating=!1}},0)},e.prototype.unwrapArrayExtension=function(e){return ko.unwrap(ko.unwrap(e))},e.prototype.buildRouteUrl=function(e,t){var n=e.replace(/\{([^\}]+?)\??(:(.+?))?\}/g,function(e,n,r,o){if(!n)return"";var i=ko.unwrap(t[n.toLowerCase()]);return null==i?"":i});return 0===n.indexOf("/")?n.substring(1):n},e.prototype.buildUrlSuffix=function(e,t){var n,r;-1!==e.indexOf("#")?(n=e.substring(0,e.indexOf("#")),r=e.substring(e.indexOf("#"))):(n=e,r="");for(var o in t)if(t.hasOwnProperty(o)){if(!o)continue;var i=ko.unwrap(t[o]);if(null==i)continue;n=n.concat(-1!==n.indexOf("?")?"&"+o+"="+i:"?"+o+"="+i)}return n.concat(r)},e.prototype.isPostBackProhibited=function(e){return!!(e&&e.tagName&&"a"===e.tagName.toLowerCase()&&e.getAttribute("disabled"))},e.prototype.addKnockoutBindingHandlers=function(){function e(e,t){void 0===t&&(t=null);var n=ko.pureComputed({read:function(){var t=e();return ko.unwrap(t)},write:function(n){var r=e();ko.isObservable(r)?r(n):console.warn("Attempted to write to readonly property"+(null==t?"":" "+t)+".")}});return n.wrappedProperty=e,n}ko.virtualElements.allowedBindings.dotvvm_withControlProperties=!0,ko.bindingHandlers.dotvvm_withControlProperties={init:function(t,n,r,o,i){if(!i)throw new Error;var a=n();for(var s in a)a[s]=e(function(){return n()[this.prop]}.bind({prop:s}),"'"+s+"' at '"+n.toString()+"'");var l=i.extend({$control:a});return t.innerBindingContext=l,ko.applyBindingsToDescendants(l,t),{controlsDescendantBindings:!0}},update:function(e,t,n,r,o){}},ko.virtualElements.allowedBindings.dotvvm_introduceAlias=!0,ko.bindingHandlers.dotvvm_introduceAlias={init:function(t,n,r,o,i){if(!i)throw new Error;var a=n(),s={};for(var l in a){for(var u=l.split("."),d=s;0=n&&t<=r},t}(DotvvmValidatorBase),DotvvmEnforceClientFormatValidator=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return __extends(t,e),t.prototype.isValid=function(e,t){var n=!0;e.parameters[0]||null!=e.valueToValidate||(n=!1),e.parameters[1]||0!==e.valueToValidate.length||(n=!1),!e.parameters[2]&&this.isEmpty(e.valueToValidate)&&(n=!1);var r=this.getValidationMetadata(t);if(r&&r.elementsMetadata)for(var o=0,i=r.elementsMetadata;o=r&&n<=o},t}(DotvvmValidatorBase),DotvvmNotNullValidator=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return __extends(t,e),t.prototype.isValid=function(e){return null!==e.valueToValidate&&void 0!==e.valueToValidate},t}(DotvvmValidatorBase),ValidationError=function(){function e(e,t){this.validatedObservable=e,this.errorMessage=t}return e.getOrCreate=function(e){if(e.wrappedProperty){var t=e.wrappedProperty();ko.isObservable(t)&&(e=t)}return e.validationErrors||(e.validationErrors=ko.observableArray()),e.validationErrors},e.isValid=function(e){return!e.validationErrors||0===e.validationErrors().length},e.prototype.clear=function(e){this.validatedObservable.validationErrors.remove(this),e.errors.remove(this)},e}(),DotvvmValidation=function(){function e(e){var t=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={hideWhenValid:function(e,t,n){t.length>0?e.style.display="":e.style.display="none"},invalidCssClass:function(e,t,n){t.length>0?e.className+=" "+n:e.className=e.className.split(" ").filter(function(e){return e!=n}).join(" ")},setToolTipText:function(e,t,n){t.length>0?e.title=t.join(", "):e.title=""},showErrorMessageText:function(e,t,n){e[e.innerText?"innerText":"textContent"]=t.join(", ")}};var n=function(n){return{execute:function(r,o){if(n){o.additionalPostbackData.validationTargetPath=n;var i=ko.contextFor(o.sender),a=e.evaluator.evaluateOnViewModel(i,n);if(t.clearValidationErrors(e.viewModelObservables[o.viewModelName||"root"]),t.validateViewModel(a),t.errors().length>0)return console.log("Validation failed: postback aborted; errors: ",t.errors()),Promise.reject({type:"handler",handler:t,message:"Validation failed"});t.events.validationErrorsChanged.trigger({viewModel:o.viewModel})}return r()}}};e.postbackHandlers.validate=function(e){return n(e.path)},e.postbackHandlers["validate-root"]=function(){return n("dotvvm.viewModelObservables['root']")},e.postbackHandlers["validate-this"]=function(){return n("$data")},e.events.afterPostback.subscribe(function(e){!e.wasInterrupted&&e.serverResponseObject&&("successfulCommand"===e.serverResponseObject.action?(t.mergeValidationRules(e),e.isHandled=!0):"validationErrors"===e.serverResponseObject.action&&(t.showValidationErrorsFromServer(e),e.isHandled=!0)),t.events.validationErrorsChanged.trigger(e)}),e.events.spaNavigating.subscribe(function(n){t.clearValidationErrors(e.viewModelObservables[n.viewModelName])}),ko.bindingHandlers.dotvvmValidation={init:function(e,n,r,o,i){var a=n();if(ko.isObservable(a)){var s=r.get("dotvvmValidationOptions"),l=function(e,n){for(var r in s)s.hasOwnProperty(r)&&t.elementUpdateFunctions[r](e,n.map(function(e){return e.errorMessage}),s[r])},u=ValidationError.getOrCreate(a);u.subscribe(function(t){return l(e,t)}),l(e,u())}}}}return e.prototype.validateViewModel=function(e){if(ko.isObservable(e)&&(e=ko.unwrap(e)),e&&dotvvm.viewModels.root.validationRules){var t=ko.unwrap(e.$type);if(t){var n=dotvvm.viewModels.root.validationRules[t]||{};for(var r in e)if(e.hasOwnProperty(r)&&0!==r.indexOf("$")){var o=e[r];if(o&&ko.isObservable(o)){var i=e[r]();n.hasOwnProperty(r)&&this.validateProperty(e,o,i,n[r]);var a=e[r+"$options"];if(a&&a.type&&ValidationError.isValid(o)&&!dotvvm.serialization.validateType(i,a.type)){var s=new ValidationError(o,"The value of property "+r+" ("+i+") is invalid value for type "+a.type+".");this.addValidationError(o,s)}if(i)if(Array.isArray(i))for(var l=0,u=i;l=0&&(this.handlers=this.handlers.splice(t,1))},e.prototype.trigger=function(e){for(var t=0;t7?parseInt(t[7].substring(1,4)):0):null},e.prototype.parseNumber=function(e){return dotvvm_Globalize.parseFloat(e,10,dotvvm.culture)},e.prototype.parseDate=function(e,t,n){return dotvvm_Globalize.parseDate(e,t,dotvvm.culture,n)},e.prototype.bindingDateToString=function(e,t){var n=this;if(void 0===t&&(t="G"),!e)return"";var r=function(){var t=ko.unwrap(e);return"string"==typeof t?n.parseDotvvmDate(t):t},o=function(){var e=r();return null!=e?dotvvm_Globalize.format(e,t,dotvvm.culture):""};if(ko.isWriteableObservable(e)){var i="string"==typeof r()?function(t){return e(null==t?null:dotvvm.serialization.serializeDate(t,!1))}:e;return ko.pureComputed({read:function(){return o()},write:function(e){return i(dotvvm_Globalize.parseDate(e,t,dotvvm.culture))}})}return ko.pureComputed(function(){return o()})},e.prototype.bindingNumberToString=function(e,t){var n=this;if(void 0===t&&(t="G"),!e)return"";var r=function(){var r=function(){var t=ko.unwrap(e);return"string"==typeof t?n.parseNumber(t):t}();return null!=r?dotvvm_Globalize.format(r,t,dotvvm.culture):""};return ko.isWriteableObservable(e)?ko.pureComputed({read:function(){return r()},write:function(t){var n=dotvvm_Globalize.parseFloat(t,10,dotvvm.culture),r=null==t||null!=n&&!isNaN(n);e(r?n:null)}}):ko.pureComputed(function(){return r()})},e}(),PostbackOptions=function(){return function(e,t,n,r,o){void 0===n&&(n=[]),this.postbackId=e,this.sender=t,this.args=n,this.viewModel=r,this.viewModelName=o,this.additionalPostbackData={}}}(),ConfirmPostBackHandler=function(){function e(e){this.message=e}return e.prototype.execute=function(e,t){var n=this;return new Promise(function(t,r){confirm(n.message)?e().then(t,r):r({type:"handler",handler:n,message:"The postback was not confirmed"})})},e}(),DotvvmSerialization=function(){function e(){}return e.prototype.deserialize=function(e,t,n){if(void 0===n&&(n=!1),void 0===e||null==e)return ko.isObservable(t)&&t(e),e;if("string"==typeof e||"number"==typeof e||"boolean"==typeof e)return ko.isObservable(t)&&t(e),e;if(e instanceof Date)return e=dotvvm.serialization.serializeDate(e),ko.isObservable(t)&&t(e),e;if(e instanceof Array){if(ko.isObservable(t)&&"removeAll"in t&&null!=t()&&t().length===e.length)for(var r=t(),o=0;o=a&&l<=s&&l===parseFloat(e)}return"number"!==t&&"single"!==t&&"double"!==t&&"decimal"!==t||(+e===e||!isNaN(+e)&&"string"==typeof e)},e.prototype.findObject=function(e,t){if(t(e))return[];if(e=ko.unwrap(e),t(e))return[];if("object"!=typeof e)return null;for(var n in e)if(e.hasOwnProperty(n)){var r=this.findObject(e[n],t);if(r)return r.push(n),r}return null},e.prototype.flatSerialize=function(e){return this.serialize(e,{ignoreSpecialProperties:!0,oneLevel:!0,serializeAll:!0})},e.prototype.getPureObject=function(e){if((e=ko.unwrap(e))instanceof Array)return e.map(this.getPureObject.bind(this));var t={};for(var n in e)"$"!=n[0]&&(t[n]=e[n]);return t},e.prototype.pad=function(e,t){for(;e.length0?Promise.reject({type:"handler",handler:this,message:"An postback is already running"}):dotvvm.commonConcurrencyHandler(t(),n,r)}}},"concurrency-queue":function(e){return{name:"concurrency-queue",before:["setIsPostackRunning"],execute:function(t,n){var r=e.q||"default";return dotvvm.getPostbackQueue(r).noRunning>0?new Promise(function(e){dotvvm.getPostbackQueue(r).queue.push(function(){return e(t())})}):dotvvm.commonConcurrencyHandler(t(),n,r)}}},suppressOnUpdating:function(e){return{name:"suppressOnUpdating",before:["setIsPostackRunning","concurrency-none","concurrency-queue","concurrency-deny"],execute:function(e,t){return dotvvm.isViewModelUpdating?Promise.reject({type:"handler",handler:this,message:"ViewModel is updating, so it's probably false onchange event"}):e()}}}},this.beforePostbackEventPostbackHandler={execute:function(t,n){var r=new DotvvmBeforePostBackEventArgs(n.sender,n.viewModel,n.viewModelName,n.postbackId);return e.events.beforePostback.trigger(r),r.cancel?Promise.reject({type:"event",options:n}):t()}},this.isPostBackRunningHandler=function(){var t=0;return{name:"setIsPostbackRunning",before:["eventInvoke-postbackHandlersStarted"],execute:function(n,r){e.isPostbackRunning(!0),t++;var o=n();return o.then(function(){return e.isPostbackRunning(!!--t)},function(){return e.isPostbackRunning(!!--t)}),o}}}(),this.windowSetTimeoutHandler=this.createWindowSetTimeoutHandler(0),this.commonConcurrencyHandler=function(t,n,r){var o=e.getPostbackQueue(r);o.noRunning++;var i=function(){if(o.noRunning--,o.queue.length>0){var e=o.queue.shift();window.setTimeout(e,0)}};return t.then(function(t){var r=e.lastStartedPostack==n.postbackId?t:function(){return Promise.reject(null)};return function(){var e=r();return e.then(i,i),e}},function(e){return i(),Promise.reject(e)})},this.defaultConcurrencyPostbackHandler=this.postbackHandlers["concurrency-none"]({}),this.postbackQueues={},this.postbackHandlersStartedEventHandler={name:"eventInvoke-postbackHandlersStarted",execute:function(e,t){return dotvvm.events.postbackHandlersStarted.trigger(t),e()}},this.postbackHandlersCompletedEventHandler={name:"eventInvoke-postbackHandlersCompleted",after:["eventInvoke-postbackHandlersStarted"],execute:function(e,t){return dotvvm.events.postbackHandlersCompleted.trigger(t),e()}},this.globalPostbackHandlers=[this.isPostBackRunningHandler,this.postbackHandlersStartedEventHandler],this.globalLaterPostbackHandlers=[this.postbackHandlersCompletedEventHandler,this.beforePostbackEventPostbackHandler],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(!1)}return e.prototype.createWindowSetTimeoutHandler=function(e){return{name:"timeout",before:["eventInvoke-postbackHandlersStarted","setIsPostbackRunning"],execute:function(t,n){return new Promise(function(t,n){return window.setTimeout(t,e)}).then(function(){return t()})}}},e.prototype.getPostbackQueue=function(e){return void 0===e&&(e="default"),this.postbackQueues[e]||(this.postbackQueues[e]={queue:[],noRunning:0}),this.postbackQueues[e]},e.prototype.init=function(e,t){var n=this;this.addKnockoutBindingHandlers();var r=this.viewModels[e]=JSON.parse(document.getElementById("__dot_viewmodel_"+e).value);if(r.resources)for(var o in r.resources)this.resourceSigns[o]=!0;r.renderedResources&&r.renderedResources.forEach(function(e){return n.resourceSigns[e]=!0});var i=r.resultIdFragment,a=r.viewModel=this.serialization.deserialize(this.viewModels[e].viewModel,{},!0);this.culture=t,this.validation=new DotvvmValidation(this),this.viewModelObservables[e]=ko.observable(a),ko.applyBindings(this.viewModelObservables[e],document.documentElement),this.events.init.trigger({viewModel:a});var s=this.getSpaPlaceHolder();if(null!=s&&(this.domUtils.attachEvent(window,"hashchange",function(){return n.handleHashChange(e,s,!1)}),this.handleHashChange(e,s,!0)),this.isViewModelUpdating=!1,i)if(s){var l=document.getElementById(i);l&&"function"==typeof l.scrollIntoView&&l.scrollIntoView(!0)}else location.hash=i;this.domUtils.attachEvent(window,"beforeunload",function(t){n.persistViewModel(e)})},e.prototype.handleHashChange=function(e,t,n){if(0===document.location.hash.indexOf("#!/"))this.navigateCore(e,document.location.hash.substring(2));else{var r=t.getAttribute("data-dotvvm-spacontentplaceholder-defaultroute");if(r)r="#!/"+r,r=this.fixSpaUrlPrefix(r),this.performRedirect(r,n);else if(n)this.isSpaReady(!0),t.style.display="";else{var o=(r=document.location.toString()).indexOf("/","https://".length);r=o>0?r.substring(o):"/",this.navigateCore(e,r)}}},e.prototype.persistViewModel=function(e){var t=this.viewModels[e],n={};for(var r in t)t.hasOwnProperty(r)&&(n[r]=t[r]);n.viewModel=this.serialization.serialize(n.viewModel,{serializeAll:!0}),document.getElementById("__dot_viewmodel_"+e).value=JSON.stringify(n)},e.prototype.backUpPostBackConter=function(){return++this.postBackCounter},e.prototype.isPostBackStillActive=function(e){return this.postBackCounter===e},e.prototype.staticCommandPostback=function(e,t,n,r,o,i){var a=this;if(void 0===o&&(o=function(e){}),void 0===i&&(i=function(e,t){}),!this.isPostBackProhibited(t)){var s=this.serialization.serialize({args:r,command:n,$csrfToken:this.viewModels[e].viewModel.$csrfToken});dotvvm.events.staticCommandMethodInvoking.trigger(s),this.postJSON(this.viewModels[e].url,"POST",ko.toJSON(s),function(e){try{a.isViewModelUpdating=!0;var t=JSON.parse(e.responseText);dotvvm.events.staticCommandMethodInvoked.trigger(__assign({},s,{result:t})),o(t)}catch(t){dotvvm.events.staticCommandMethodFailed.trigger(__assign({},s,{xhr:e,error:t})),i(e,t)}finally{a.isViewModelUpdating=!1}},function(e){console.warn("StaticCommand postback failed: "+e.status+" - "+e.statusText,e),i(e),dotvvm.events.staticCommandMethodFailed.trigger(__assign({},s,{xhr:e}))},function(e){e.setRequestHeader("X-PostbackType","StaticCommand")})}},e.prototype.processPassedId=function(e,t){if("string"==typeof e||null==e)return e;if("object"==typeof e&&e.expr)return this.evaluator.evaluateOnViewModel(t,e.expr);throw new Error("invalid argument")},e.prototype.getPostbackHandler=function(e){var t=this.postbackHandlers[e];if(t)return t;throw new Error("Could not find postback handler of name '"+e+"'")},e.prototype.isPostbackHandler=function(e){return e&&"function"==typeof e.execute},e.prototype.findPostbackHandlers=function(e,t){var n=this,r=function(e,t){return!1===t.enabled?null:n.getPostbackHandler(e)(t)};return t.map(function(t){return"string"==typeof t?r(t,{}):n.isPostbackHandler(t)?t:t instanceof Array?function(){var n=t[0],o=t[1];return r(n,"function"==typeof o?o(e,e.$data):o)}():r(t.name,t.options&&t.options(e))}).filter(function(e){return null!=e})},e.prototype.sortHandlers=function(e){for(var t=function(){for(var t={},n=0,r=e;n0?{action:"redirect",url:o}:JSON.parse(t.responseText);!i.viewModel&&i.viewModelDiff&&(i.viewModel=a.patch(v.viewModel,i.viewModelDiff)),a.loadResourceList(i.resources,function(){var o=!1;if("successfulCommand"===i.action){try{a.isViewModelUpdating=!0;var s=a.cleanUpdatedControls(i);i.viewModel&&(ko.delaySync.pause(),a.serialization.deserialize(i.viewModel,a.viewModels[u].viewModel),ko.delaySync.resume()),o=!0,a.cleanUpdatedControls(i,s),a.restoreUpdatedControls(i,s,!0)}finally{a.isViewModelUpdating=!1}dotvvm.events.postbackViewModelUpdated.trigger({})}else if("redirect"===i.action)return a.handleRedirect(i,u),n();var l=i.resultIdFragment;if(l)if(a.getSpaPlaceHolder()||location.hash=="#"+l){var v=document.getElementById(l);v&&"function"==typeof v.scrollIntoView&&v.scrollIntoView(!0)}else location.hash=l;if(o){var c=new DotvvmAfterPostBackEventArgs(e,i,i.commandResult,t);n(c)}else r(new DotvvmErrorEventArgs(e.sender,d,u,t,e.postbackId,i))})})})},function(t){l({type:"network",options:e,args:new DotvvmErrorEventArgs(e.sender,d,u,t,e.postbackId)})})})},e.prototype.postBack=function(e,t,n,r,o,i,a,s){var l=this;if(this.isPostBackProhibited(t))return new Promise(function(e,t){return t("rejected")});i=i||ko.contextFor(t);var u=this.findPostbackHandlers(i,this.globalPostbackHandlers.concat(a||[]).concat(this.globalLaterPostbackHandlers));0==u.filter(function(e){return e.name&&0==e.name.indexOf("concurrency-")}).length&&u.push(this.defaultConcurrencyPostbackHandler);var d=new PostbackOptions(this.backUpPostBackConter(),t,s,i.$data,e),v=this.applyPostbackHandlersCore(function(e){return l.postbackCore(e,n,r,o,i,s)},d,u).then(function(e){return e().then(function(e){return e},function(e){return Promise.reject({type:"commit",args:e})})},function(e){return Promise.reject(e)});return v.then(function(e){return e&&l.events.afterPostback.trigger(e)},function(e){var t=new DotvvmAfterPostBackEventArgs(d,"commit"==e.type&&e.args?e.args.serverResponseObject:null,d.postbackId);"handler"==e.type||"event"==e.type?(t.wasInterrupted=!0,l.events.postbackRejected.trigger({})):"network"==e.type&&l.events.error.trigger(e.args),l.events.afterPostback.trigger(t)}),v},e.prototype.loadResourceList=function(e,t){var n="";for(var r in e){if(!/^__noname_\d+$/.test(r)){if(this.resourceSigns[r])continue;this.resourceSigns[r]=!0}n+=e[r]+" "}if(""!==n.trim()){var o=document.createElement("div");o.innerHTML=n;for(var i=[],a=0;a=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(e,t){var n=this,r=this.viewModels[e].viewModel,o=this.backUpPostBackConter(),i=new DotvvmSpaNavigatingEventArgs(r,e,t);if(this.events.spaNavigating.trigger(i),!i.cancel){t="/___dotvvm-spa___"+this.addLeadingSlash(t);var a=this.addLeadingSlash(this.concatUrl(this.viewModels[e].virtualDirectory||"",t)),s=this.getSpaPlaceHolder();if(s){var l=s.attributes["data-dotvvm-spacontentplaceholder"].value;this.getJSON(a,"GET",l,function(t){if(n.isPostBackStillActive(o)){var i=JSON.parse(t.responseText);n.loadResourceList(i.resources,function(){var o=!1;if("successfulCommand"!==i.action&&i.action){if("redirect"===i.action)return void n.handleRedirect(i,e,!0)}else try{n.isViewModelUpdating=!0;var a=n.cleanUpdatedControls(i);n.viewModels[e]={};for(var s in i)i.hasOwnProperty(s)&&(n.viewModels[e][s]=i[s]);ko.delaySync.pause(),n.serialization.deserialize(i.viewModel,n.viewModels[e].viewModel),ko.delaySync.resume(),o=!0,n.viewModelObservables[e](n.viewModels[e].viewModel),n.restoreUpdatedControls(i,a,!0),n.isSpaReady(!0)}finally{n.isViewModelUpdating=!1}var l=new DotvvmSpaNavigatedEventArgs(r,e,i,t);if(n.events.spaNavigated.trigger(l),!o&&!l.isHandled)throw"Invalid response from server!"})}},function(t){if(n.isPostBackStillActive(o)){var i=new DotvvmErrorEventArgs(void 0,r,e,t,-1,void 0,!0);n.events.error.trigger(i),i.handled||alert(t.responseText)}})}else document.location.href=a}},e.prototype.handleRedirect=function(e,t,n){void 0===n&&(n=!1),null!=e.replace&&(n=e.replace);var r;this.getSpaPlaceHolder()&&e.url.indexOf("//")<0&&e.allowSpa?("#!"===(r="#!"+this.removeVirtualDirectoryFromUrl(e.url,t))&&(r="#!/"),r=this.fixSpaUrlPrefix(r)):r=e.url;var o=new DotvvmRedirectEventArgs(dotvvm.viewModels[t],t,r,n);this.events.redirect.trigger(o),this.performRedirect(r,n)},e.prototype.performRedirect=function(e,t){if(t)location.replace(e);else{var n=this.fakeRedirectAnchor;n||((n=document.createElement("a")).style.display="none",n.setAttribute("data-dotvvm-fake-id","dotvvm_fake_redirect_anchor_87D7145D_8EA8_47BA_9941_82B75EE88CDB"),document.body.appendChild(n),this.fakeRedirectAnchor=n),n.href=e,n.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 e.length>0&&"/"!=e.substring(0,1)?"/"+e:e},e.prototype.concatUrl=function(e,t){return e.length>0&&"/"==e.substring(e.length-1)&&(e=e.substring(0,e.length-1)),e+this.addLeadingSlash(t)},e.prototype.patch=function(e,t){var n=this;if(e instanceof Array&&t instanceof Array)return t.map(function(t,r){return n.patch(e[r],t)});if(e instanceof Array||t instanceof Array)return t;if("object"!=typeof e||"object"!=typeof t)return t;for(var r in t)null==t[r]?e[r]=null:null==e[r]?e[r]=t[r]:e[r]=this.patch(e[r],t[r]);return e},e.prototype.updateDynamicPathFragments=function(e,t){for(var n=t.length-1;n>=0;n--)t[n].indexOf("[$index]")>=0&&(t[n]=t[n].replace("[$index]","["+e.$index()+"]")),e=e.$parentContext},e.prototype.postJSON=function(e,t,n,r,o,i){void 0===i&&(i=function(e){});var a=this.getXHR();a.open(t,e,!0),a.setRequestHeader("Content-Type","application/json"),a.setRequestHeader("X-DotVVM-PostBack","true"),a.setRequestHeader("X-Requested-With","XMLHttpRequest"),i(a),a.onreadystatechange=function(){a.readyState===XMLHttpRequest.DONE&&(a.status<400?r(a):o(a))},a.send(n)},e.prototype.getJSON=function(e,t,n,r,o){var i=this.getXHR();i.open(t,e,!0),i.setRequestHeader("Content-Type","application/json"),i.setRequestHeader("X-DotVVM-SpaContentPlaceHolder",n),i.onreadystatechange=function(){i.readyState===XMLHttpRequest.DONE&&(i.status<400?r(i):o(i))},i.send()},e.prototype.getXHR=function(){return XMLHttpRequest?new XMLHttpRequest:new window.ActiveXObject("Microsoft.XMLHTTP")},e.prototype.cleanUpdatedControls=function(e,t){void 0===t&&(t={});for(var n in e.updatedControls)if(e.updatedControls.hasOwnProperty(n)){var r=document.getElementByDotvvmId(n);if(r){var o=ko.contextFor(r),i=r.nextSibling,a=r.parentNode;ko.removeNode(r),t[n]={control:r,nextSibling:i,parent:a,dataContext:o}}}return t},e.prototype.restoreUpdatedControls=function(e,t,n){var r=this;for(var o in e.updatedControls)if(e.updatedControls.hasOwnProperty(o)){var i=t[o];if(i){var a=document.createElement(t[o].parent.tagName||"div");if(a.innerHTML=e.updatedControls[o],a.childElementCount>1)throw new Error("Postback.Update control can not render more than one element");var s=a.firstElementChild;if(null==s.id)throw new Error("Postback.Update control always has to render id attribute.");s.id!==t[o].control.id&&console.log("Postback.Update control changed id from '"+t[o].control.id+"' to '"+s.id+"'"),a.removeChild(s),i.nextSibling?i.parent.insertBefore(s,i.nextSibling):i.parent.appendChild(s)}}n&&window.setTimeout(function(){try{r.isViewModelUpdating=!0;for(var n in e.updatedControls){var o=document.getElementByDotvvmId(n);o&&ko.applyBindings(t[n].dataContext,o)}}finally{r.isViewModelUpdating=!1}},0)},e.prototype.unwrapArrayExtension=function(e){return ko.unwrap(ko.unwrap(e))},e.prototype.buildRouteUrl=function(e,t){var n=(e="/"+e).replace(/(\/[^\/]*?)\{([^\}]+?)\??(:(.+?))?\}/g,function(e,n,r,o,i){if(!r)return"";var a=ko.unwrap(t[r.toLowerCase()]);return null==a?"":n+a});return 0===n.indexOf("/")?n.substring(1):n},e.prototype.buildUrlSuffix=function(e,t){var n,r;-1!==e.indexOf("#")?(n=e.substring(0,e.indexOf("#")),r=e.substring(e.indexOf("#"))):(n=e,r="");for(var o in t)if(t.hasOwnProperty(o)){if(!o)continue;var i=ko.unwrap(t[o]);if(null==i)continue;n=n.concat(-1!==n.indexOf("?")?"&"+o+"="+i:"?"+o+"="+i)}return n.concat(r)},e.prototype.isPostBackProhibited=function(e){return!!(e&&e.tagName&&"a"===e.tagName.toLowerCase()&&e.getAttribute("disabled"))},e.prototype.addKnockoutBindingHandlers=function(){function e(e,t){void 0===t&&(t=null);var n=ko.pureComputed({read:function(){var t=e();return ko.unwrap(t)},write:function(n){var r=e();ko.isObservable(r)?r(n):console.warn("Attempted to write to readonly property"+(null==t?"":" "+t)+".")}});return n.wrappedProperty=e,n}ko.virtualElements.allowedBindings.dotvvm_withControlProperties=!0,ko.bindingHandlers.dotvvm_withControlProperties={init:function(t,n,r,o,i){if(!i)throw new Error;var a=n();for(var s in a)a[s]=e(function(){return n()[this.prop]}.bind({prop:s}),"'"+s+"' at '"+n.toString()+"'");var l=i.extend({$control:a});return t.innerBindingContext=l,ko.applyBindingsToDescendants(l,t),{controlsDescendantBindings:!0}},update:function(e,t,n,r,o){}},ko.virtualElements.allowedBindings.dotvvm_introduceAlias=!0,ko.bindingHandlers.dotvvm_introduceAlias={init:function(t,n,r,o,i){if(!i)throw new Error;var a=n(),s={};for(var l in a){for(var u=l.split("."),d=s;0=n&&t<=r},t}(DotvvmValidatorBase),DotvvmEnforceClientFormatValidator=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return __extends(t,e),t.prototype.isValid=function(e,t){var n=!0;e.parameters[0]||null!=e.valueToValidate||(n=!1),e.parameters[1]||0!==e.valueToValidate.length||(n=!1),!e.parameters[2]&&this.isEmpty(e.valueToValidate)&&(n=!1);var r=this.getValidationMetadata(t);if(r&&r.elementsMetadata)for(var o=0,i=r.elementsMetadata;o=r&&n<=o},t}(DotvvmValidatorBase),DotvvmNotNullValidator=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return __extends(t,e),t.prototype.isValid=function(e){return null!==e.valueToValidate&&void 0!==e.valueToValidate},t}(DotvvmValidatorBase),ValidationError=function(){function e(e,t){this.validatedObservable=e,this.errorMessage=t}return e.getOrCreate=function(e){if(e.wrappedProperty){var t=e.wrappedProperty();ko.isObservable(t)&&(e=t)}return e.validationErrors||(e.validationErrors=ko.observableArray()),e.validationErrors},e.isValid=function(e){return!e.validationErrors||0===e.validationErrors().length},e.prototype.clear=function(e){this.validatedObservable.validationErrors.remove(this),e.errors.remove(this)},e}(),DotvvmValidation=function(){function e(e){var t=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={hideWhenValid:function(e,t,n){t.length>0?e.style.display="":e.style.display="none"},invalidCssClass:function(e,t,n){t.length>0?e.className+=" "+n:e.className=e.className.split(" ").filter(function(e){return e!=n}).join(" ")},setToolTipText:function(e,t,n){t.length>0?e.title=t.join(", "):e.title=""},showErrorMessageText:function(e,t,n){e[e.innerText?"innerText":"textContent"]=t.join(", ")}};var n=function(n){return{execute:function(r,o){if(n){o.additionalPostbackData.validationTargetPath=n;var i=ko.contextFor(o.sender),a=e.evaluator.evaluateOnViewModel(i,n);if(t.clearValidationErrors(e.viewModelObservables[o.viewModelName||"root"]),t.validateViewModel(a),t.errors().length>0)return console.log("Validation failed: postback aborted; errors: ",t.errors()),Promise.reject({type:"handler",handler:t,message:"Validation failed"});t.events.validationErrorsChanged.trigger({viewModel:o.viewModel})}return r()}}};e.postbackHandlers.validate=function(e){return n(e.path)},e.postbackHandlers["validate-root"]=function(){return n("dotvvm.viewModelObservables['root']")},e.postbackHandlers["validate-this"]=function(){return n("$data")},e.events.afterPostback.subscribe(function(e){!e.wasInterrupted&&e.serverResponseObject&&("successfulCommand"===e.serverResponseObject.action?(t.mergeValidationRules(e),e.isHandled=!0):"validationErrors"===e.serverResponseObject.action&&(t.showValidationErrorsFromServer(e),e.isHandled=!0)),t.events.validationErrorsChanged.trigger(e)}),e.events.spaNavigating.subscribe(function(n){t.clearValidationErrors(e.viewModelObservables[n.viewModelName])}),ko.bindingHandlers.dotvvmValidation={init:function(e,n,r,o,i){var a=n();if(ko.isObservable(a)){var s=r.get("dotvvmValidationOptions"),l=function(e,n){for(var r in s)s.hasOwnProperty(r)&&t.elementUpdateFunctions[r](e,n.map(function(e){return e.errorMessage}),s[r])},u=ValidationError.getOrCreate(a);u.subscribe(function(t){return l(e,t)}),l(e,u())}}}}return e.prototype.validateViewModel=function(e){if(ko.isObservable(e)&&(e=ko.unwrap(e)),e&&dotvvm.viewModels.root.validationRules){var t=ko.unwrap(e.$type);if(t){var n=dotvvm.viewModels.root.validationRules[t]||{};for(var r in e)if(e.hasOwnProperty(r)&&0!==r.indexOf("$")){var o=e[r];if(o&&ko.isObservable(o)){var i=e[r]();n.hasOwnProperty(r)&&this.validateProperty(e,o,i,n[r]);var a=e[r+"$options"];if(a&&a.type&&ValidationError.isValid(o)&&!dotvvm.serialization.validateType(i,a.type)){var s=new ValidationError(o,"The value of property "+r+" ("+i+") is invalid value for type "+a.type+".");this.addValidationError(o,s)}if(i)if(Array.isArray(i))for(var l=0,u=i;l { + // prepend url with backslash to correctly handle optional parameters at start + routePath = '/' + routePath; + + var url = routePath.replace(/(\/[^\/]*?)\{([^\}]+?)\??(:(.+?))?\}/g, (s, prefix, paramName, _, type) => { if (!paramName) return ""; const x = ko.unwrap(params[paramName.toLowerCase()]) - return x == null ? "" : x; + return x == null ? "" : prefix + x; }); if (url.indexOf('/') === 0) { diff --git a/src/DotVVM.Samples.Common/DotvvmStartup.cs b/src/DotVVM.Samples.Common/DotvvmStartup.cs index cf45ec25d7..616fb74cd0 100644 --- a/src/DotVVM.Samples.Common/DotvvmStartup.cs +++ b/src/DotVVM.Samples.Common/DotvvmStartup.cs @@ -82,6 +82,9 @@ private static void AddRoutes(DotvvmConfiguration config) config.RouteTable.Add("FeatureSamples_ParameterBinding_OptionalParameterBinding2", "FeatureSamples/ParameterBinding/OptionalParameterBinding2/{Id?}", "Views/FeatureSamples/ParameterBinding/OptionalParameterBinding.dothtml", new { Id = 300 }); config.RouteTable.AutoDiscoverRoutes(new DefaultRouteStrategy(config)); config.RouteTable.Add("RepeaterRouteLink-PageDetail", "ControlSamples/Repeater/RouteLink/{Id}", "Views/ControlSamples/Repeater/RouteLink.dothtml", new { Id = 0 }); + config.RouteTable.Add("RepeaterRouteLink-PageDetail_IdOptional", "ControlSamples/Repeater/RouteLink/{Id?}", "Views/ControlSamples/Repeater/RouteLink.dothtml"); + config.RouteTable.Add("RepeaterRouteLink-PageDetail_IdOptionalPrefixed", "ControlSamples/Repeater/RouteLink/id-{Id?}", "Views/ControlSamples/Repeater/RouteLink.dothtml"); + config.RouteTable.Add("RepeaterRouteLink-PageDetail_IdOptionalAtStart", "id-{Id?}/ControlSamples/Repeater/RouteLink", "Views/ControlSamples/Repeater/RouteLink.dothtml"); config.RouteTable.Add("RepeaterRouteLinkUrlSuffix-PageDetail", "ControlSamples/Repeater/RouteLinkUrlSuffix/{Id}", "Views/ControlSamples/Repeater/RouteLink.dothtml", new { Id = 0 }); config.RouteTable.Add("FeatureSamples_Redirect_RedirectFromPresenter", "FeatureSamples/Redirect/RedirectFromPresenter", provider => new RedirectingPresenter()); config.RouteTable.Add("FeatureSamples_Validation_ClientSideValidationDisabling2", "FeatureSamples/Validation/ClientSideValidationDisabling/{ClientSideValidationEnabled}", "Views/FeatureSamples/Validation/ClientSideValidationDisabling.dothtml", new { ClientSideValidationEnabled = false }); diff --git a/src/DotVVM.Samples.Common/ViewModels/ControlSamples/RouteLink/RouteLinkUrlGenViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/ControlSamples/RouteLink/RouteLinkUrlGenViewModel.cs new file mode 100644 index 0000000000..2ee51bddae --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/ControlSamples/RouteLink/RouteLinkUrlGenViewModel.cs @@ -0,0 +1,7 @@ +namespace DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.RouteLink +{ + public class RouteLinkUrlGenViewModel + { + public int RouteParameter { get; set; } = 1; + } +} diff --git a/src/DotVVM.Samples.Common/Views/ControlSamples/RouteLink/RouteLinkEnabled.dothtml b/src/DotVVM.Samples.Common/Views/ControlSamples/RouteLink/RouteLinkEnabled.dothtml index ddbaf74e83..338a25deff 100644 --- a/src/DotVVM.Samples.Common/Views/ControlSamples/RouteLink/RouteLinkEnabled.dothtml +++ b/src/DotVVM.Samples.Common/Views/ControlSamples/RouteLink/RouteLinkEnabled.dothtml @@ -15,17 +15,15 @@

RouteLink enabled property demo

-

- -

-

- -

- -

- -

- +

+ +

+

+ +

+

+ +

diff --git a/src/DotVVM.Samples.Common/Views/ControlSamples/RouteLink/RouteLinkUrlGen.dothtml b/src/DotVVM.Samples.Common/Views/ControlSamples/RouteLink/RouteLinkUrlGen.dothtml new file mode 100644 index 0000000000..a374f3988f --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/ControlSamples/RouteLink/RouteLinkUrlGen.dothtml @@ -0,0 +1,62 @@ +@viewModel DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.RouteLink.RouteLinkUrlGenViewModel + + + + + Hello from DotVVM! + + + + +
+

RouteLink url generation demo

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+
+ + diff --git a/src/DotVVM.Samples.Tests/Control/RouteLinkTests.cs b/src/DotVVM.Samples.Tests/Control/RouteLinkTests.cs index deacba32d3..22fb8705bf 100644 --- a/src/DotVVM.Samples.Tests/Control/RouteLinkTests.cs +++ b/src/DotVVM.Samples.Tests/Control/RouteLinkTests.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using DotVVM.Testing.Abstractions; using Microsoft.VisualStudio.TestTools.UnitTesting; - +using Riganti.Selenium.Core.Abstractions; namespace DotVVM.Samples.Tests.Control { @@ -16,8 +12,7 @@ public class RouteLinkTests : AppSeleniumTest [SampleReference(nameof(SamplesRouteUrls.ControlSamples_RouteLink_TestRoute))] public void Control_RouteLink_RouteLinkEnabled() { - RunInAllBrowsers(browser => - { + RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_RouteLink_RouteLinkEnabled); browser.Single("body > div.container > p:nth-child(2) > label > input[type=\"checkbox\"]") .CheckIfIsNotChecked(); @@ -25,7 +20,41 @@ public void Control_RouteLink_RouteLinkEnabled() browser.Single("body > div.container > p:nth-child(2) > label > input[type=\"checkbox\"]").Click(); browser.Single("body > div.container > p:nth-child(3) > a").Click(); - browser.CompareUrl("http://localhost:60320/ControlSamples/Repeater/RouteLink/0"); + browser.CheckUrl("/ControlSamples/Repeater/RouteLink/0", UrlKind.Relative, UriComponents.PathAndQuery); + browser.NavigateBack(); + }); + } + + [TestMethod] + [SampleReference(nameof(SamplesRouteUrls.ControlSamples_RouteLink_TestRoute))] + public void Control_RouteLink_RouteLinkUrlGeneration() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_RouteLink_RouteLinkUrlGen); + + void checkNavigatedUrl(string selector, string relativeUrl) + { + browser.Single(selector).Click(); + browser.CheckUrl(relativeUrl, UrlKind.Relative, UriComponents.PathAndQuery); + browser.NavigateBack(); + } + + checkNavigatedUrl("a[data-ui='optional-parameter-client']", "/ControlSamples/Repeater/RouteLink"); + checkNavigatedUrl("a[data-ui='optional-parameter-server']", "/ControlSamples/Repeater/RouteLink"); + + checkNavigatedUrl("a[data-ui='0-parameters']", "/"); + + checkNavigatedUrl("a[data-ui='optional-parameter-prefixed-client']", "/ControlSamples/Repeater/RouteLink"); + checkNavigatedUrl("a[data-ui='optional-parameter-prefixed-server']", "/ControlSamples/Repeater/RouteLink"); + + checkNavigatedUrl("a[data-ui='parameter-prefixed-client']", "/ControlSamples/Repeater/RouteLink/id-1"); + checkNavigatedUrl("a[data-ui='parameter-prefixed-server']", "/ControlSamples/Repeater/RouteLink/id-1"); + + checkNavigatedUrl("a[data-ui='optional-parameter-at-start-client']", "/ControlSamples/Repeater/RouteLink"); + checkNavigatedUrl("a[data-ui='optional-parameter-at-start-server']", "/ControlSamples/Repeater/RouteLink"); + + checkNavigatedUrl("a[data-ui='optional-prefixed-parameter-at-start-client']", "/id-1/ControlSamples/Repeater/RouteLink"); + checkNavigatedUrl("a[data-ui='optional-prefixed-parameter-at-start-client']", "/id-1/ControlSamples/Repeater/RouteLink"); }); } @@ -33,17 +62,16 @@ public void Control_RouteLink_RouteLinkEnabled() [SampleReference(nameof(SamplesRouteUrls.ControlSamples_RouteLink_TestRoute))] public void Control_RouteLink_RouteLinkEnabledFalse() { - RunInAllBrowsers(browser => - { + RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_RouteLink_RouteLinkEnabledFalse); //this RouteLink does not contain a binding ( "ControlSamples/RouteLink/RouteLinkEnabledFalse"; + public static string ControlSamples_RouteLink_RouteLinkUrlGen => "ControlSamples/RouteLink/RouteLinkUrlGen"; + public static string ControlSamples_RouteLink_TestRoute => "ControlSamples/RouteLink/TestRoute"; public static string ControlSamples_SpaContentPlaceHolder_Default => "ControlSamples/SpaContentPlaceHolder/Default";