From 657b058a91be9c1d42463db6c5dd760241c8a478 Mon Sep 17 00:00:00 2001 From: eldadfux Date: Mon, 12 Aug 2019 17:19:54 +0300 Subject: [PATCH] Fixed duplicated history states --- app/views/console/users/index.phtml | 16 +- public/dist/scripts/app-all.js | 31 +- public/dist/scripts/app.js | 31 +- public/scripts/dependencies/litespeed.js | 392 +++++++---------------- public/scripts/filters.js | 4 + public/scripts/views/service.js | 23 +- public/scripts/views/ui/phases.js | 4 +- 7 files changed, 181 insertions(+), 320 deletions(-) diff --git a/app/views/console/users/index.phtml b/app/views/console/users/index.phtml index f2bc9a737..d57e1d717 100644 --- a/app/views/console/users/index.phtml +++ b/app/views/console/users/index.phtml @@ -20,13 +20,12 @@ $limit = 5; data-event="submit" data-param-search="{{router.params.search}}" data-param-limit="" - data-param-offset="{{router.params.offset}}" + data-param-offset="0" data-param-order-type="ASC" data-scope="sdk" data-name="project-users" data-success="state" data-success-param-state-keys="search,offset"> -
@@ -97,6 +96,19 @@ $limit = 5;
+
+ +
+ diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index 1bd2dd009..5e8c14c41 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -2146,19 +2146,19 @@ let defaults={'selector':'','controller':function(){},'template':'','repeat':fal if(prop in object){continue;} object[prop]=defaults[prop];} if(!object.selector){throw new Error('View component is missing a selector attribute');} -stock[object.selector]=object;return this;},render:function(element){parse(element);element.dispatchEvent(new window.Event('rendered',{bubbles:false}));}}},true,false);window.ls.container.set('router',function(window){let states=[];let current=null;let previous=null;let getPrevious=()=>previous;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){state.params[key]=value;return this;};let getParam=function(key,def){if(key in state.params){return state.params[key];} -return def;};let getParams=function(){return state.params;};let getURL=function(){return window.location.href;};let reset=function(){state.params=getJsonFromUrl(window.location.search);state.hash=window.location.hash;};let add=function(path,view){if(typeof path!=='string'){throw new Error('path must be of type string');} +stock[object.selector]=object;return this;},render:function(element){parse(element);element.dispatchEvent(new window.Event('rendered',{bubbles:false}));}}},true,false);window.ls.container.set('router',function(window){let getJsonFromUrl=function(URL){let query;if(URL){let pos=location.href.indexOf('?');if(pos===-1)return[];query=location.href.substr(pos+1);}else{query=location.search.substr(1);} +let result={};query.split('&').forEach(function(part){if(!part){return;} +part=part.split('+').join(' ');let eq=part.indexOf('=');let key=eq>-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;} +else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];} +if(!index){result[key].push(val);} +else{result[key][index]=val;}}});return result;};let states=[];let params=getJsonFromUrl(window.location.search);let hash=window.location.hash;let current=null;let previous=null;let getPrevious=()=>previous;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){params[key]=value;return this;};let getParam=function(key,def){if(key in params){return params[key];} +return def;};let getParams=function(){return params;};let getURL=function(){return window.location.href;};let add=function(path,view){if(typeof path!=='string'){throw new Error('path must be of type string');} if(typeof view!=='object'){throw new Error('view must be of type object');} states[states.length++]={path:path,view:view};return this;};let match=function(location){let url=location.pathname+((location.hash)?location.hash:'');states.sort(function(a,b){return b.path.length-a.path.length;});states.sort(function(a,b){let n=b.path.split('/').length-a.path.split('/').length;if(n!==0){return n;} return b.path.length-a.path.length;});for(let i=0;i-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;} -else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];} -if(!index){result[key].push(val);} -else{result[key][index]=val;}}});return result;};let state={setParam:setParam,getParam:getParam,getParams:getParams,getURL:getURL,add:add,change:change,reload:reload,reset:reset,match:match,getCurrent:getCurrent,setCurrent:setCurrent,getPrevious:getPrevious,setPrevious:setPrevious,params:getJsonFromUrl(window.location.search),hash:window.location.hash};return state;},true,true);window.ls.container.set('expression',function(container,filter){let paths=[];return{regex:/(\{{.*?\}})/gi,parse:function(string,def,as,prefix,cast=false){def=def||'';paths=[];return string.replace(this.regex,match=>{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=(reference[0]||'');let result=container.path(path,undefined,as,prefix);path=(path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix);if(!paths.includes(path)){paths.push(path);} +window.dispatchEvent(new PopStateEvent('popstate',{}));return this;};let reload=function(){return change(window.location.href);};return{setParam:setParam,getParam:getParam,getParams:getParams,getURL:getURL,add:add,change:change,reload:reload,match:match,getCurrent:getCurrent,setCurrent:setCurrent,getPrevious:getPrevious,setPrevious:setPrevious,params:params,hash:hash,reset:function(){this.params=getJsonFromUrl(window.location.search);this.hash=window.location.hash;}};},true,true);window.ls.container.set('expression',function(container,filter){let paths=[];return{regex:/(\{{.*?\}})/gi,parse:function(string,def,as,prefix,cast=false){def=def||'';paths=[];return string.replace(this.regex,match=>{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=(reference[0]||'');let result=container.path(path,undefined,as,prefix);path=(path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix);if(!paths.includes(path)){paths.push(path);} if(reference.length>=2){for(let i=1;i=thresh&&u'+units[u]+'';}).add('statsTotal',function($value){if(!$value){return 0;} $value=abbreviate($value,1,false,false);return($value==='0')?'N/A':$value;});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);} @@ -2365,15 +2365,14 @@ window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendC document.body.classList.add(prefix+'-'+value);cookie.set('language-'+prefix,value,365);document.dispatchEvent(new CustomEvent('updated-language-'+prefix));};document.addEventListener('updated-language-'+prefix,function(){element.value=cookie.get('language-'+prefix);});let def=cookie.get('language-'+prefix)||element.options[0].value;select(def);element.value=def;}}).add({selector:'data-ls-ui-chart',repeat:false,controller:function(element,container,date,document){let child=document.createElement('canvas');child.width=500;child.height=175;let stats=container.get('usage');if(!stats||!stats['requests']||!stats['requests']['data']){return;} let config={type:'line',data:{labels:[],datasets:[{label:'Requests',backgroundColor:'rgba(230, 248, 253, 0.3)',borderColor:'#29b5d9',borderWidth:2,data:[0,0,0,0,0,0,0],fill:true}]},options:{responsive:true,title:{display:false,text:'Stats'},legend:{display:false},tooltips:{mode:'index',intersect:false,caretPadding:0},hover:{mode:'nearest',intersect:true},scales:{xAxes:[{display:false}],yAxes:[{display:false,}]}}};for(let i=0;ielement.trim()):[];failure=(failure&&failure!='')?failure.split(',').map(element=>element.trim()):[];if(debug)console.log('%c[service init]: '+action+' ('+service+')','color:red');let callbacks={'reset':function(){return function(){if('FORM'===element.tagName){return element.reset();} +element.innerHTML='';element.appendChild(child);container.set('chart',new Chart(child.getContext('2d'),config),true);element.dataset['canvas']=true;}});(function(window){"use strict";window.ls.container.get('view').add({selector:'data-service',repeat:false,controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset['service'];let service=element.dataset['name']||action;let event=element.dataset['event'];let confirm=element.dataset['confirm']||'';let loading=element.dataset['loading']||'';let loaderId=null;let scope=element.dataset['scope']||'sdk';let debug=!!(element.dataset['debug']);let success=(element.dataset['success']||'');let failure=(element.dataset['failure']||'');success=(success&&success!='')?success.split(',').map(element=>element.trim()):[];failure=(failure&&failure!='')?failure.split(',').map(element=>element.trim()):[];if(debug)console.log('%c[service init]: '+action+' ('+service+')','color:red');let callbacks={'reset':function(){return function(){if('FORM'===element.tagName){return element.reset();} throw new Error('This callback is only valid for forms');}},'alert':function(text,classname){return function(alerts){alerts.add({text:text,class:(classname||'success')},3000);}},'redirect':function(url){return function(router){router.change(url||'/');}},'reload':function(){return function(router){router.reload();}},'state':function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=='undefined'&&value!==null){return url.replace(re,'$1'+key+"="+value+'$2$3');} else{hash=url.split('#');url=hash[0].replace(re,'$1$3').replace(/(&|\?)$/,'');if(typeof hash[1]!=='undefined'&&hash[1]!==null){url+='#'+hash[1];} return url;}} else{if(typeof value!=='undefined'&&value!==null){var separator=url.indexOf('?')!==-1?'&':'?';hash=url.split('#');url=hash[0]+separator+key+'='+value;if(typeof hash[1]!=='undefined'&&hash[1]!==null){url+='#'+hash[1];} return url;} else{return url;}}} -keys=keys.split(',').map(element=>element.trim());return function(router,serviceData){let url=window.location.href;keys.map(key=>{let value=getValue(key,'param',serviceData);if(!value){} -url=updateQueryString(key,(value?value:null),url)});console.log(url);window.history.replaceState({},'',url);}},'trigger':function(events){return function(document){events=events.trim().split(',');for(let i=0;ielement.trim());return function(serviceForm,router,window){let url=window.location.href;keys.map(key=>{let value=getValue(key,'param',serviceForm);url=updateQueryString(key,(value?value:null),url)});if(url!==window.location.href){window.history.pushState({},'',url);router.reset();}}},'trigger':function(events){return function(document){events=events.trim().split(',');for(let i=0;i)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,'');functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==='('){functionAsString=functionAsString.slice(1,-1);} while(match=REGEX_PARAMETERS_VALUES.exec(functionAsString)){params.push(match[1]);} return params;} @@ -2388,11 +2387,11 @@ if(event){event.preventDefault();} if(confirm){if(window.confirm(confirm)!==true){return false;}} if(loading){loaderId=alerts.add({text:loading,class:''},0);} let method=container.path(scope+'.'+action);if(!method){throw new Error('Method "'+scope+'.'+action+'" not found');} -let result=resolve(method,'param',('FORM'===element.tagName)?form.toJson(element):{});if(!result){return;} +let formData=('FORM'===element.tagName)?form.toJson(element):{};let result=resolve(method,'param',formData);if(!result){return;} result.then(function(data){if(loaderId!==null){alerts.remove(loaderId);} if(!element){return;} -container.set(service.replace('.','-'),data,true,true);container.set('serviceData',data,true,true);if(debug)console.log('%cservice ready: "'+service.replace('.','-')+'"','color:green');if(debug)console.log('%cservice:','color:blue',container.get(service.replace('.','-')));for(let i=0;i0)?current+' / '+total:'';}});})(window);(function(window){window.ls.container.get('view').add({selector:'data-ui-phases',controller:function(element,window,document,expression){var tabs=document.createElement('ul');var container=document.createElement('div');var titles=Array.prototype.slice.call(element.getElementsByTagName('h2'));var next=Array.prototype.slice.call(element.querySelectorAll('[data-next]'));var previous=Array.prototype.slice.call(element.querySelectorAll('[data-previous]'));var position=0;for(var i=0;iprevious;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){state.params[key]=value;return this;};let getParam=function(key,def){if(key in state.params){return state.params[key];} -return def;};let getParams=function(){return state.params;};let getURL=function(){return window.location.href;};let reset=function(){state.params=getJsonFromUrl(window.location.search);state.hash=window.location.hash;};let add=function(path,view){if(typeof path!=='string'){throw new Error('path must be of type string');} +stock[object.selector]=object;return this;},render:function(element){parse(element);element.dispatchEvent(new window.Event('rendered',{bubbles:false}));}}},true,false);window.ls.container.set('router',function(window){let getJsonFromUrl=function(URL){let query;if(URL){let pos=location.href.indexOf('?');if(pos===-1)return[];query=location.href.substr(pos+1);}else{query=location.search.substr(1);} +let result={};query.split('&').forEach(function(part){if(!part){return;} +part=part.split('+').join(' ');let eq=part.indexOf('=');let key=eq>-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;} +else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];} +if(!index){result[key].push(val);} +else{result[key][index]=val;}}});return result;};let states=[];let params=getJsonFromUrl(window.location.search);let hash=window.location.hash;let current=null;let previous=null;let getPrevious=()=>previous;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){params[key]=value;return this;};let getParam=function(key,def){if(key in params){return params[key];} +return def;};let getParams=function(){return params;};let getURL=function(){return window.location.href;};let add=function(path,view){if(typeof path!=='string'){throw new Error('path must be of type string');} if(typeof view!=='object'){throw new Error('view must be of type object');} states[states.length++]={path:path,view:view};return this;};let match=function(location){let url=location.pathname+((location.hash)?location.hash:'');states.sort(function(a,b){return b.path.length-a.path.length;});states.sort(function(a,b){let n=b.path.split('/').length-a.path.split('/').length;if(n!==0){return n;} return b.path.length-a.path.length;});for(let i=0;i-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;} -else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];} -if(!index){result[key].push(val);} -else{result[key][index]=val;}}});return result;};let state={setParam:setParam,getParam:getParam,getParams:getParams,getURL:getURL,add:add,change:change,reload:reload,reset:reset,match:match,getCurrent:getCurrent,setCurrent:setCurrent,getPrevious:getPrevious,setPrevious:setPrevious,params:getJsonFromUrl(window.location.search),hash:window.location.hash};return state;},true,true);window.ls.container.set('expression',function(container,filter){let paths=[];return{regex:/(\{{.*?\}})/gi,parse:function(string,def,as,prefix,cast=false){def=def||'';paths=[];return string.replace(this.regex,match=>{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=(reference[0]||'');let result=container.path(path,undefined,as,prefix);path=(path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix);if(!paths.includes(path)){paths.push(path);} +window.dispatchEvent(new PopStateEvent('popstate',{}));return this;};let reload=function(){return change(window.location.href);};return{setParam:setParam,getParam:getParam,getParams:getParams,getURL:getURL,add:add,change:change,reload:reload,match:match,getCurrent:getCurrent,setCurrent:setCurrent,getPrevious:getPrevious,setPrevious:setPrevious,params:params,hash:hash,reset:function(){this.params=getJsonFromUrl(window.location.search);this.hash=window.location.hash;}};},true,true);window.ls.container.set('expression',function(container,filter){let paths=[];return{regex:/(\{{.*?\}})/gi,parse:function(string,def,as,prefix,cast=false){def=def||'';paths=[];return string.replace(this.regex,match=>{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=(reference[0]||'');let result=container.path(path,undefined,as,prefix);path=(path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix);if(!paths.includes(path)){paths.push(path);} if(reference.length>=2){for(let i=1;i=thresh&&u'+units[u]+'';}).add('statsTotal',function($value){if(!$value){return 0;} $value=abbreviate($value,1,false,false);return($value==='0')?'N/A':$value;});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);} @@ -272,15 +272,14 @@ window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendC document.body.classList.add(prefix+'-'+value);cookie.set('language-'+prefix,value,365);document.dispatchEvent(new CustomEvent('updated-language-'+prefix));};document.addEventListener('updated-language-'+prefix,function(){element.value=cookie.get('language-'+prefix);});let def=cookie.get('language-'+prefix)||element.options[0].value;select(def);element.value=def;}}).add({selector:'data-ls-ui-chart',repeat:false,controller:function(element,container,date,document){let child=document.createElement('canvas');child.width=500;child.height=175;let stats=container.get('usage');if(!stats||!stats['requests']||!stats['requests']['data']){return;} let config={type:'line',data:{labels:[],datasets:[{label:'Requests',backgroundColor:'rgba(230, 248, 253, 0.3)',borderColor:'#29b5d9',borderWidth:2,data:[0,0,0,0,0,0,0],fill:true}]},options:{responsive:true,title:{display:false,text:'Stats'},legend:{display:false},tooltips:{mode:'index',intersect:false,caretPadding:0},hover:{mode:'nearest',intersect:true},scales:{xAxes:[{display:false}],yAxes:[{display:false,}]}}};for(let i=0;ielement.trim()):[];failure=(failure&&failure!='')?failure.split(',').map(element=>element.trim()):[];if(debug)console.log('%c[service init]: '+action+' ('+service+')','color:red');let callbacks={'reset':function(){return function(){if('FORM'===element.tagName){return element.reset();} +element.innerHTML='';element.appendChild(child);container.set('chart',new Chart(child.getContext('2d'),config),true);element.dataset['canvas']=true;}});(function(window){"use strict";window.ls.container.get('view').add({selector:'data-service',repeat:false,controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset['service'];let service=element.dataset['name']||action;let event=element.dataset['event'];let confirm=element.dataset['confirm']||'';let loading=element.dataset['loading']||'';let loaderId=null;let scope=element.dataset['scope']||'sdk';let debug=!!(element.dataset['debug']);let success=(element.dataset['success']||'');let failure=(element.dataset['failure']||'');success=(success&&success!='')?success.split(',').map(element=>element.trim()):[];failure=(failure&&failure!='')?failure.split(',').map(element=>element.trim()):[];if(debug)console.log('%c[service init]: '+action+' ('+service+')','color:red');let callbacks={'reset':function(){return function(){if('FORM'===element.tagName){return element.reset();} throw new Error('This callback is only valid for forms');}},'alert':function(text,classname){return function(alerts){alerts.add({text:text,class:(classname||'success')},3000);}},'redirect':function(url){return function(router){router.change(url||'/');}},'reload':function(){return function(router){router.reload();}},'state':function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=='undefined'&&value!==null){return url.replace(re,'$1'+key+"="+value+'$2$3');} else{hash=url.split('#');url=hash[0].replace(re,'$1$3').replace(/(&|\?)$/,'');if(typeof hash[1]!=='undefined'&&hash[1]!==null){url+='#'+hash[1];} return url;}} else{if(typeof value!=='undefined'&&value!==null){var separator=url.indexOf('?')!==-1?'&':'?';hash=url.split('#');url=hash[0]+separator+key+'='+value;if(typeof hash[1]!=='undefined'&&hash[1]!==null){url+='#'+hash[1];} return url;} else{return url;}}} -keys=keys.split(',').map(element=>element.trim());return function(router,serviceData){let url=window.location.href;keys.map(key=>{let value=getValue(key,'param',serviceData);if(!value){} -url=updateQueryString(key,(value?value:null),url)});console.log(url);window.history.replaceState({},'',url);}},'trigger':function(events){return function(document){events=events.trim().split(',');for(let i=0;ielement.trim());return function(serviceForm,router,window){let url=window.location.href;keys.map(key=>{let value=getValue(key,'param',serviceForm);url=updateQueryString(key,(value?value:null),url)});if(url!==window.location.href){window.history.pushState({},'',url);router.reset();}}},'trigger':function(events){return function(document){events=events.trim().split(',');for(let i=0;i)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,'');functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==='('){functionAsString=functionAsString.slice(1,-1);} while(match=REGEX_PARAMETERS_VALUES.exec(functionAsString)){params.push(match[1]);} return params;} @@ -295,11 +294,11 @@ if(event){event.preventDefault();} if(confirm){if(window.confirm(confirm)!==true){return false;}} if(loading){loaderId=alerts.add({text:loading,class:''},0);} let method=container.path(scope+'.'+action);if(!method){throw new Error('Method "'+scope+'.'+action+'" not found');} -let result=resolve(method,'param',('FORM'===element.tagName)?form.toJson(element):{});if(!result){return;} +let formData=('FORM'===element.tagName)?form.toJson(element):{};let result=resolve(method,'param',formData);if(!result){return;} result.then(function(data){if(loaderId!==null){alerts.remove(loaderId);} if(!element){return;} -container.set(service.replace('.','-'),data,true,true);container.set('serviceData',data,true,true);if(debug)console.log('%cservice ready: "'+service.replace('.','-')+'"','color:green');if(debug)console.log('%cservice:','color:blue',container.get(service.replace('.','-')));for(let i=0;i0)?current+' / '+total:'';}});})(window);(function(window){window.ls.container.get('view').add({selector:'data-ui-phases',controller:function(element,window,document,expression){var tabs=document.createElement('ul');var container=document.createElement('div');var titles=Array.prototype.slice.call(element.getElementsByTagName('h2'));var next=Array.prototype.slice.call(element.querySelectorAll('[data-next]'));var previous=Array.prototype.slice.call(element.querySelectorAll('[data-previous]'));var position=0;for(var i=0;i { }; } - let self = this; const REGEX_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; const REGEX_FUNCTION_PARAMS = /(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m; const REGEX_PARAMETERS_VALUES = /\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm; function getParams(func) { - let functionAsString = func.toString(); let params = []; let match; functionAsString = functionAsString.replace(REGEX_COMMENTS, ''); functionAsString = functionAsString.match(REGEX_FUNCTION_PARAMS)[1]; if (functionAsString.charAt(0) === '(') { functionAsString = functionAsString.slice(1, -1); } - while (match = REGEX_PARAMETERS_VALUES.exec(functionAsString)) { params.push(match[1]); } - return params; - } - let args = getParams(target); return target.apply(target, args.map(function (value) { return self.get(value.trim()); })); - }; let path = function (path, value, as, prefix) { - as = (as) ? as : container.get('$as'); prefix = (prefix) ? prefix : container.get('$prefix'); path = ((path.indexOf('.') > -1) ? path.replace(as + '.', prefix + '.') : path.replace(as, prefix)).split('.'); let name = path.shift(); let object = this.get(name); let result = null; while (path.length > 1) { - if (!object) { return null; } - object = object[path.shift()]; - } - if (value !== null && value !== undefined) { object[path.shift()] = value; return true; } - if (!object) { return null; } - let shift = path.shift(); if (!shift) { result = object; } - else { return object[shift]; } - return result; - }; let bind = function (element, path, callback, as, prefix) { - as = (as) ? as : container.get('$as'); prefix = (prefix) ? prefix : container.get('$prefix'); let event = ((path.indexOf('.') > -1) ? path.replace(as + '.', prefix + '.') : path.replace(as, prefix)) + '.changed'; let service = event.split('.').slice(0, 1).pop(); listeners[service] = listeners[service] || {}; listeners[service][event] = true; let printer = () => { - if (!document.body.contains(element)) { element = null; document.removeEventListener(event, printer, false); return false; } - callback(); - }; document.addEventListener(event, printer); - }; let container = { set: set, get: get, resolve: resolve, path: path, bind: bind, stock: stock, listeners: listeners, }; set('container', container, true, false); return container; -}(); window.ls.container.set('http', function (document) { - let globalParams = [], globalHeaders = []; let addParam = function (url, param, value) { param = encodeURIComponent(param); let a = document.createElement('a'); param += (value ? "=" + encodeURIComponent(value) : ""); a.href = url; a.search += (a.search ? "&" : "") + param; return a.href; }; let request = function (method, url, headers, payload, progress) { - let i; if (-1 === ['GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'HEAD', 'OPTIONS', 'CONNECT', 'PATCH'].indexOf(method)) { throw new Error('var method must contain a valid HTTP method name'); } - if (typeof url !== 'string') { throw new Error('var url must be of type string'); } - if (typeof headers !== 'object') { throw new Error('var headers must be of type object'); } - if (typeof url !== 'string') { throw new Error('var url must be of type string'); } - for (i = 0; i < globalParams.length; i++) { url = addParam(url, globalParams[i].key, globalParams[i].value); } - return new Promise(function (resolve, reject) { - let xmlhttp = new XMLHttpRequest(); xmlhttp.open(method, url, true); xmlhttp.setRequestHeader('Ajax', '1'); for (i = 0; i < globalHeaders.length; i++) { xmlhttp.setRequestHeader(globalHeaders[i].key, globalHeaders[i].value); } - for (let key in headers) { if (headers.hasOwnProperty(key)) { xmlhttp.setRequestHeader(key, headers[key]); } } - xmlhttp.onload = function () { - if (4 === xmlhttp.readyState && 200 === xmlhttp.status) { resolve(xmlhttp.response); } - else { document.dispatchEvent(new CustomEvent('http-' + method.toLowerCase() + '-' + xmlhttp.status)); reject(new Error(xmlhttp.statusText)); } - }; if (progress) { xmlhttp.addEventListener('progress', progress); xmlhttp.upload.addEventListener('progress', progress, false); } - xmlhttp.onerror = function () { reject(new Error("Network Error")); }; xmlhttp.send(payload); - }) - }; return { 'get': function (url) { return request('GET', url, {}, '') }, 'post': function (url, headers, payload) { return request('POST', url, headers, payload) }, 'put': function (url, headers, payload) { return request('PUT', url, headers, payload) }, 'patch': function (url, headers, payload) { return request('PATCH', url, headers, payload) }, 'delete': function (url) { return request('DELETE', url, {}, '') }, 'addGlobalParam': function (key, value) { globalParams.push({ key: key, value: value }); }, 'addGlobalHeader': function (key, value) { globalHeaders.push({ key: key, value: value }); } } -}, true, false); window.ls.container.set('cookie', function (document) { - function get(name) { - let value = "; " + document.cookie, parts = value.split("; " + name + "="); if (parts.length === 2) { return parts.pop().split(";").shift(); } - return null; - } - function set(name, value, days) { let date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); let expires = (0 < days) ? 'expires=' + date.toUTCString() : 'expires=0'; document.cookie = name + "=" + value + ";" + expires + ";path=/"; return this; } - return { 'get': get, 'set': set } -}, true, false); window.ls.container.set('view', function (http, container) { - let stock = {}; let execute = function (view, node, container) { container.set('element', node, true, false, false); container.resolve(view.controller); if (true !== view.repeat) { node.removeAttribute(view.selector); } }; let parse = function (node, skip) { - if (node.tagName === 'SCRIPT') { return; } - if (node.attributes && skip !== true) { - let attrs = []; let attrsLen = node.attributes.length; for (let x = 0; x < attrsLen; x++) { attrs.push(node.attributes[x].nodeName); } - if (1 !== node.nodeType) { return; } - if (attrs && attrsLen) { - for (let x = 0; x < attrsLen; x++) { - if (node.$lsSkip === true) { break; } - let pointer = (!/Edge/.test(navigator.userAgent)) ? x : (attrsLen - 1) - x; let length = attrsLen; let attr = attrs[pointer]; if (!stock[attr]) { continue; } - let comp = stock[attr]; if (typeof comp.template === "function") { comp.template = container.resolve(comp.template); } - if (!comp.template) { - (function (comp, node, container) { execute(comp, node, container); })(comp, node, container); if (length !== attrsLen) { x--; } - continue; - } - node.classList.remove('load-end'); node.classList.add('load-start'); node.$lsSkip = true; http.get(comp.template).then(function (node, comp) { return function (data) { node.$lsSkip = false; node.innerHTML = data; node.classList.remove('load-start'); node.classList.add('load-end'); (function (comp, node, container) { execute(comp, node, container); })(comp, node, container); parse(node, true); } }(node, comp), function (error) { throw new Error('Failed to load comp template: ' + error.message); }); - } - } - } - if (true === node.$lsSkip) { return; } - let list = (node) ? node.childNodes : []; if (node.$lsSkip === true) { list = []; } - for (let i = 0; i < list.length; i++) { let child = list[i]; parse(child); } - }; return { - stock: stock, add: function (object) { - if (typeof object !== 'object') { throw new Error('object must be of type object'); } - let defaults = { 'selector': '', 'controller': function () { }, 'template': '', 'repeat': false, 'protected': false }; for (let prop in defaults) { - if (!defaults.hasOwnProperty(prop)) { continue; } - if (prop in object) { continue; } - object[prop] = defaults[prop]; - } - if (!object.selector) { throw new Error('View component is missing a selector attribute'); } - stock[object.selector] = object; return this; - }, render: function (element) { parse(element); element.dispatchEvent(new window.Event('rendered', { bubbles: false })); } - } -}, true, false); window.ls.container.set('router', function (window) { - let states = []; let current = null; let previous = null; let getPrevious = () => previous; let getCurrent = () => current; let setPrevious = (value) => { previous = value; return this; }; let setCurrent = (value) => { current = value; return this; }; let setParam = function (key, value) { state.params[key] = value; return this; }; let getParam = function (key, def) { - if (key in state.params) { return state.params[key]; } - return def; - }; let getParams = function () { return state.params; }; let getURL = function () { return window.location.href; }; let reset = function () { state.params = getJsonFromUrl(window.location.search); state.hash = window.location.hash; }; let add = function (path, view) { - if (typeof path !== 'string') { throw new Error('path must be of type string'); } - if (typeof view !== 'object') { throw new Error('view must be of type object'); } - states[states.length++] = { path: path, view: view }; return this; - }; let match = function (location) { - let url = location.pathname + ((location.hash) ? location.hash : ''); states.sort(function (a, b) { return b.path.length - a.path.length; }); states.sort(function (a, b) { - let n = b.path.split('/').length - a.path.split('/').length; if (n !== 0) { return n; } - return b.path.length - a.path.length; - }); for (let i = 0; i < states.length; i++) { let value = states[i]; value.path = (value.path.substring(0, 1) !== '/') ? location.pathname + value.path : value.path; let match = new RegExp("^" + value.path.replace(/:[^\s/]+/g, '([\\w-]+)') + "$"); let found = url.match(match); if (found) { previous = current; current = value; return value; } } - return null - }; let change = function (URL, replace) { - if (!replace) { window.history.pushState({}, '', URL); } - else { window.history.replaceState({}, '', URL); } - window.dispatchEvent(new PopStateEvent('popstate', {})); return this; - }; let reload = function () { return change(window.location.href); }; let getJsonFromUrl = function (URL) { - let query; if (URL) { let pos = location.href.indexOf('?'); if (pos === -1) return []; query = location.href.substr(pos + 1); } else { query = location.search.substr(1); } - let result = {}; query.split('&').forEach(function (part) { - if (!part) { return; } - part = part.split('+').join(' '); let eq = part.indexOf('='); let key = eq > -1 ? part.substr(0, eq) : part; let val = eq > -1 ? decodeURIComponent(part.substr(eq + 1)) : ''; let from = key.indexOf('['); if (from === -1) { result[decodeURIComponent(key)] = val; } - else { - let to = key.indexOf(']'); let index = decodeURIComponent(key.substring(from + 1, to)); key = decodeURIComponent(key.substring(0, from)); if (!result[key]) { result[key] = []; } - if (!index) { result[key].push(val); } - else { result[key][index] = val; } - } - }); return result; - }; let state = { setParam: setParam, getParam: getParam, getParams: getParams, getURL: getURL, add: add, change: change, reload: reload, reset: reset, match: match, getCurrent: getCurrent, setCurrent: setCurrent, getPrevious: getPrevious, setPrevious: setPrevious, params: getJsonFromUrl(window.location.search), hash: window.location.hash }; return state; -}, true, true); window.ls.container.set('expression', function (container, filter) { - let paths = []; return { - regex: /(\{{.*?\}})/gi, parse: function (string, def, as, prefix, cast = false) { - def = def || ''; paths = []; return string.replace(this.regex, match => { - let reference = match.substring(2, match.length - 2).replace('[\'', '.').replace('\']', '').trim(); reference = reference.split('|'); let path = (reference[0] || ''); let result = container.path(path, undefined, as, prefix); path = (path.indexOf('.') > -1) ? path.replace(as + '.', prefix + '.') : path.replace(as, prefix); if (!paths.includes(path)) { paths.push(path); } - if (reference.length >= 2) { for (let i = 1; i < reference.length; i++) { result = filter.apply(reference[i], result); } } - if (null === result || undefined === result) { result = def; } - else if (typeof result === 'object') { result = JSON.stringify(result, null, 4); } - else if (((typeof result === 'object') || (typeof result === 'string')) && cast) { result = '\'' + result + '\''; } - return result; - }); - }, getPaths: () => paths, - } -}, true, false); window.ls.container.set('filter', function (container) { let filters = {}; let add = function (name, callback) { filters[name] = callback; return this; }; let apply = function (name, value) { container.set('$value', value, true, false); return container.resolve(filters[name]); }; add('uppercase', ($value) => { return $value.toUpperCase(); }); add('lowercase', ($value) => { return $value.toLowerCase(); }); return { add: add, apply: apply } }, true, false); window.ls.container.get('filter').add('escape', value => { - if (typeof value !== 'string') { return value; } - return value.replace(/&/g, '&').replace(//g, '>').replace(/\"/g, '"').replace(/\'/g, ''').replace(/\//g, '/'); -}); window.ls = window.ls || {}; window.ls.container.set('window', window, true, false).set('document', window.document, true, false).set('element', window.document, true, false); window.ls.run = function (window) { - try { this.view.render(window.document); } - catch (error) { let handler = window.ls.container.resolve(this.error); handler(error); } -}; window.ls.error = () => { return error => { console.error('ls-error', error.message, error.stack, error.toString()); } }; window.ls.router = window.ls.container.get('router'); window.ls.view = window.ls.container.get('view'); window.ls.filter = window.ls.container.get('filter'); window.ls.container.get('view').add({ - selector: 'data-ls-router', repeat: false, controller: function (element, window, document, view, router, tasks) { - let firstFromServer = (element.getAttribute('data-first-from-server') === 'true'); let scope = { selector: 'data-ls-scope', template: false, repeat: true, controller: function () { }, }; let init = function (route) { - let count = parseInt(element.getAttribute('data-ls-scope-count') || 0); element.setAttribute('data-ls-scope-count', count + 1); window.scrollTo(0, 0); if (window.document.body.scrollTo) { window.document.body.scrollTo(0, 0); } - router.reset(); if (null === route) { return; } - scope.template = (undefined !== route.view.template) ? route.view.template : null; scope.controller = (undefined !== route.view.controller) ? route.view.controller : function () { }; document.dispatchEvent(new CustomEvent('state-change')); if (firstFromServer && null === router.getPrevious()) { scope.template = ''; } - else if (count === 1) { view.render(element); } - else if (null !== router.getPrevious()) { view.render(element); } - document.dispatchEvent(new CustomEvent('state-changed')); - }; let findParent = function (tagName, el) { - if ((el.nodeName || el.tagName).toLowerCase() === tagName.toLowerCase()) { return el; } - while (el = el.parentNode) { if ((el.nodeName || el.tagName).toLowerCase() === tagName.toLowerCase()) { return el; } } - return null; - }; element.removeAttribute('data-ls-router'); element.setAttribute('data-ls-scope', ''); element.setAttribute('data-ls-scope-count', 1); view.add(scope); document.addEventListener('click', function (event) { - let target = findParent('a', event.target); if (!target) { return false; } - if (!target.href) { return false; } - if ((event.metaKey)) { return false; } - if ((target.hasAttribute('target')) && ('_blank' === target.getAttribute('target'))) { return false; } - if (target.hostname !== window.location.hostname) { return false; } - let route = router.match(target); if (null === route) { return false; } - event.preventDefault(); if (window.location === target.href) { return false; } - route.view.state = (undefined === route.view.state) ? true : route.view.state; if (true === route.view.state) { - if (router.getPrevious() && router.getPrevious().view && (router.getPrevious().view.scope !== route.view.scope)) { window.location.href = target.href; return false; } - window.history.pushState({}, 'Unknown', target.href); - } - init(route); return true; - }); window.addEventListener('popstate', function () { init(router.match(window.location)); }); window.addEventListener('hashchange', function () { init(router.match(window.location)); }); init(router.match(window.location)); - } -}); window.ls.container.get('view').add({ - selector: 'data-ls-attrs', controller: function (element, expression, container, $as, $prefix) { - let attrs = element.getAttribute('data-ls-attrs').trim().split(','); let paths = []; let check = () => { - for (let i = 0; i < attrs.length; i++) { - let attr = attrs[i]; let key = expression.parse(attr.substring(0, attr.indexOf('=')), null, $as, $prefix) || null; paths = paths.concat(expression.getPaths()); let value = expression.parse(attr.substring(attr.indexOf('=') + 1), null, $as, $prefix) || null; paths = paths.concat(expression.getPaths()); if (!key) { return null; } - element.setAttribute(key, value); - } - }; check(); for (let i = 0; i < paths.length; i++) { container.bind(element, paths[i], check); } - } -}); window.ls.container.get('view').add({ - selector: 'data-ls-bind', controller: function (element, expression, container, $prefix, $as) { - let echo = function (value, bind = true) { - if (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'BUTTON' || element.tagName === 'TEXTAREA') { - let type = element.getAttribute('type'); if ('radio' === type) { - if (value.toString() === element.value) { element.setAttribute('checked', 'checked'); } - else { element.removeAttribute('checked'); } - if (bind) { - element.addEventListener('change', () => { - for (let i = 0; i < paths.length; i++) { - if (element.checked) { value = element.value; } - container.path(paths[i], value, $as, $prefix); - } - }); - } - return; - } - if ('checkbox' === type) { - if (typeof value === 'boolean' || value === 'true' || value === 'false') { - if (value === true || value === 'true') { element.setAttribute('checked', 'checked'); element.checked = true; } - else { element.removeAttribute('checked'); element.checked = false; } - } - else { - try { value = JSON.parse(value); element.checked = (Array.isArray(value) && (value.indexOf(element.value) > -1)); value = element.value; } - catch{ return null; } - } - if (bind) { - element.addEventListener('change', () => { - for (let i = 0; i < paths.length; i++) { - let value = container.path(paths[i], undefined, $as, $prefix); let index = value.indexOf(element.value); if (element.checked && index < 0) { value.push(element.value); } - if (!element.checked && index > -1) { value.splice(index, 1); } - container.path(paths[i], value, $as, $prefix); - } - }); - } - return; - } - if (element.value !== value) { element.value = value; } - if (bind) { element.addEventListener('input', sync); element.addEventListener('change', sync); } - } - else { if (element.innerText !== value) { element.innerHTML = value; } } - }; let sync = ((as, prefix) => { return () => { for (let i = 0; i < paths.length; i++) { container.path(paths[i], element.value, as, prefix); } } })($as, $prefix); let syntax = element.getAttribute('data-ls-bind'); let result = expression.parse(syntax, null, $as, $prefix); let paths = expression.getPaths(); echo(result, true); element.addEventListener('looped', function () { echo(expression.parse(syntax, null, $as, $prefix), false); }); for (let i = 0; i < paths.length; i++) { container.bind(element, paths[i], () => { echo(expression.parse(syntax, null, $as, $prefix), false); }); } - } -}); window.ls.container.get('view').add({ - selector: 'data-ls-if', controller: function (element, expression, container, view, $as, $prefix) { - let result = ''; let syntax = element.getAttribute('data-ls-if') || ''; let debug = element.getAttribute('data-debug') || false; let paths = []; let check = () => { - if (debug) { console.info('debug-ls-if', expression.parse(syntax.replace(/(\r\n|\n|\r)/gm, ' '), 'undefined', $as, $prefix, true)); } - try { result = (eval(expression.parse(syntax.replace(/(\r\n|\n|\r)/gm, ' '), 'undefined', $as, $prefix, true))); } - catch (error) { throw new Error('Failed to evaluate expression "' + syntax + ' (resulted with: "' + result + '")": ' + error); } - if (debug) { console.info('debug-ls-if result:', result); } - paths = expression.getPaths(); let prv = element.$lsSkip; element.$lsSkip = !result; if (!result) { element.style.visibility = 'hidden'; element.style.display = 'none'; } - else { element.style.removeProperty('display'); element.style.removeProperty('visibility'); } - if (prv === true && element.$lsSkip === false) { view.render(element) } - }; check(); for (let i = 0; i < paths.length; i++) { container.bind(element, paths[i], check); } - } -}); window.ls.container.get('view').add({ - selector: 'data-ls-loop', template: false, repeat: false, nested: false, controller: function (element, view, container, window) { - let expr = element.getAttribute('data-ls-loop'); let as = element.getAttribute('data-ls-as'); let echo = function () { - let array = container.path(expr); array = (!array) ? [] : array; let watch = !!(array && array.__proxy); while (element.hasChildNodes()) { element.removeChild(element.lastChild); element.lastChild = null; } - if (array instanceof Array && typeof array !== 'object') { throw new Error('Reference value must be array or object. ' + (typeof array) + ' given'); } - let children = []; element.$lsSkip = true; element.style.visibility = (0 === array.length) ? 'hidden' : 'visible'; for (let prop in array) { - if (!array.hasOwnProperty(prop)) { continue; } - children[prop] = template.cloneNode(true); element.appendChild(children[prop]); (index => { let context = expr + '.' + index; container.set(as, container.path(context), true, watch); container.set('$index', index, true, false); container.set('$prefix', context, true, false); container.set('$as', as, true, false); view.render(children[prop]); })(prop); - } - container.set('$index', null, true, false, false); container.set('$prefix', '', true, false, false); container.set('$as', '', true, false, false); element.dispatchEvent(new Event('looped')); - }; let template = (element.children.length === 1) ? element.children[0] : window.document.createElement('li'); echo(); container.bind(element, expr + '.length', echo); - } -}); window.ls.container.get('view').add({ - selector: 'data-ls-template', template: false, repeat: true, controller: function (element, view, http, expression, document) { - let template = expression.parse(element.getAttribute('data-ls-template')); let type = element.getAttribute('data-type') || 'url'; element.innerHTML = ''; if ('script' === type) { - let inlineTemplate = document.getElementById(template); if (inlineTemplate && inlineTemplate.innerHTML) { element.innerHTML = inlineTemplate.innerHTML; element.dispatchEvent(new CustomEvent('template-loaded', { bubbles: true, cancelable: false })); } - else { element.innerHTML = 'Missing template "' + template + '"'; } - return; - } - http.get(template).then(function (element) { return function (data) { element.innerHTML = data; view.render(element); element.dispatchEvent(new CustomEvent('template-loaded', { bubbles: true, cancelable: false })); } }(element), function () { throw new Error('Failed loading template'); }); - } -}); \ No newline at end of file +window.ls=window.ls||{};window.ls.container=function(){let stock={};let listeners={};let set=function(name,object,singleton,watch=true){if(typeof name!=='string'){throw new Error('var name must be of type string');} +if(typeof singleton!=='boolean'){throw new Error('var singleton "'+singleton+'" of service "'+name+'" must be of type boolean');} +stock[name]={name:name,object:object,singleton:singleton,instance:null,watch:watch,};if(!watch){return this;} +let binds=listeners[name]||{};for(let key in binds){if(binds.hasOwnProperty(key)){document.dispatchEvent(new CustomEvent(key));}} +return this;};let get=function(name){let service=(undefined!==stock[name])?stock[name]:null;if(null==service){return null;} +if(service.instance){return service.instance;} +let instance=(typeof service.object==='function')?this.resolve(service.object):service.object;let skip=false;if(service.watch&&name!=='window'&&name!=='document'&&name!=='element'&&typeof instance==='object'&&instance!==null){let handler={name:service.name,watch:function(){},get:function(target,key){if(key==="__name"){return this.name;} +if(key==="__watch"){return this.watch;} +if(key==="__proxy"){return true;} +if(typeof target[key]==='object'&&target[key]!==null&&!target[key].__proxy){let handler=Object.assign({},this);handler.name=handler.name+'.'+key;return new Proxy(target[key],handler)} +else{return target[key];}},set:function(target,key,value,receiver){if(key==="__name"){return this.name=value;} +if(key==="__watch"){return this.watch=value;} +target[key]=value;let path=receiver.__name+'.'+key;document.dispatchEvent(new CustomEvent(path+'.changed'));if(skip){return true;} +skip=true;container.set('$prop',key,true);container.set('$value',value,true);container.resolve(this.watch);container.set('$key',null,true);container.set('$value',null,true);skip=false;return true;},};instance=new Proxy(instance,handler);} +if(service.singleton){service.instance=instance;} +return instance;};let resolve=function(target){if(!target){return()=>{};} +let self=this;const REGEX_COMMENTS=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;const REGEX_FUNCTION_PARAMS=/(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m;const REGEX_PARAMETERS_VALUES=/\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm;function getParams(func){let functionAsString=func.toString();let params=[];let match;functionAsString=functionAsString.replace(REGEX_COMMENTS,'');functionAsString=functionAsString.match(REGEX_FUNCTION_PARAMS)[1];if(functionAsString.charAt(0)==='('){functionAsString=functionAsString.slice(1,-1);} +while(match=REGEX_PARAMETERS_VALUES.exec(functionAsString)){params.push(match[1]);} +return params;} +let args=getParams(target);return target.apply(target,args.map(function(value){return self.get(value.trim());}));};let path=function(path,value,as,prefix){as=(as)?as:container.get('$as');prefix=(prefix)?prefix:container.get('$prefix');path=((path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix)).split('.');let name=path.shift();let object=this.get(name);let result=null;while(path.length>1){if(!object){return null;} +object=object[path.shift()];} +if(value!==null&&value!==undefined){object[path.shift()]=value;return true;} +if(!object){return null;} +let shift=path.shift();if(!shift){result=object;} +else{return object[shift];} +return result;};let bind=function(element,path,callback,as,prefix){as=(as)?as:container.get('$as');prefix=(prefix)?prefix:container.get('$prefix');let event=((path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix))+'.changed';let service=event.split('.').slice(0,1).pop();listeners[service]=listeners[service]||{};listeners[service][event]=true;let printer=()=>{if(!document.body.contains(element)){element=null;document.removeEventListener(event,printer,false);return false;} +callback();};document.addEventListener(event,printer);};let container={set:set,get:get,resolve:resolve,path:path,bind:bind,stock:stock,listeners:listeners,};set('container',container,true,false);return container;}();window.ls.container.set('http',function(document){let globalParams=[],globalHeaders=[];let addParam=function(url,param,value){param=encodeURIComponent(param);let a=document.createElement('a');param+=(value?"="+encodeURIComponent(value):"");a.href=url;a.search+=(a.search?"&":"")+param;return a.href;};let request=function(method,url,headers,payload,progress){let i;if(-1===['GET','POST','PUT','DELETE','TRACE','HEAD','OPTIONS','CONNECT','PATCH'].indexOf(method)){throw new Error('var method must contain a valid HTTP method name');} +if(typeof url!=='string'){throw new Error('var url must be of type string');} +if(typeof headers!=='object'){throw new Error('var headers must be of type object');} +if(typeof url!=='string'){throw new Error('var url must be of type string');} +for(i=0;i-1?part.substr(0,eq):part;let val=eq>-1?decodeURIComponent(part.substr(eq+1)):'';let from=key.indexOf('[');if(from===-1){result[decodeURIComponent(key)]=val;} +else{let to=key.indexOf(']');let index=decodeURIComponent(key.substring(from+1,to));key=decodeURIComponent(key.substring(0,from));if(!result[key]){result[key]=[];} +if(!index){result[key].push(val);} +else{result[key][index]=val;}}});return result;};let states=[];let params=getJsonFromUrl(window.location.search);let hash=window.location.hash;let current=null;let previous=null;let getPrevious=()=>previous;let getCurrent=()=>current;let setPrevious=(value)=>{previous=value;return this;};let setCurrent=(value)=>{current=value;return this;};let setParam=function(key,value){params[key]=value;return this;};let getParam=function(key,def){if(key in params){return params[key];} +return def;};let getParams=function(){return params;};let getURL=function(){return window.location.href;};let add=function(path,view){if(typeof path!=='string'){throw new Error('path must be of type string');} +if(typeof view!=='object'){throw new Error('view must be of type object');} +states[states.length++]={path:path,view:view};return this;};let match=function(location){let url=location.pathname+((location.hash)?location.hash:'');states.sort(function(a,b){return b.path.length-a.path.length;});states.sort(function(a,b){let n=b.path.split('/').length-a.path.split('/').length;if(n!==0){return n;} +return b.path.length-a.path.length;});for(let i=0;i{let reference=match.substring(2,match.length-2).replace('[\'','.').replace('\']','').trim();reference=reference.split('|');let path=(reference[0]||'');let result=container.path(path,undefined,as,prefix);path=(path.indexOf('.')>-1)?path.replace(as+'.',prefix+'.'):path.replace(as,prefix);if(!paths.includes(path)){paths.push(path);} +if(reference.length>=2){for(let i=1;ipaths,}},true,false);window.ls.container.set('filter',function(container){let filters={};let add=function(name,callback){filters[name]=callback;return this;};let apply=function(name,value){container.set('$value',value,true,false);return container.resolve(filters[name]);};add('uppercase',($value)=>{return $value.toUpperCase();});add('lowercase',($value)=>{return $value.toLowerCase();});return{add:add,apply:apply}},true,false);window.ls.container.get('filter').add('escape',value=>{if(typeof value!=='string'){return value;} +return value.replace(/&/g,'&').replace(//g,'>').replace(/\"/g,'"').replace(/\'/g,''').replace(/\//g,'/');});window.ls=window.ls||{};window.ls.container.set('window',window,true,false).set('document',window.document,true,false).set('element',window.document,true,false);window.ls.run=function(window){try{this.view.render(window.document);} +catch(error){let handler=window.ls.container.resolve(this.error);handler(error);}};window.ls.error=()=>{return error=>{console.error('ls-error',error.message,error.stack,error.toString());}};window.ls.router=window.ls.container.get('router');window.ls.view=window.ls.container.get('view');window.ls.filter=window.ls.container.get('filter');window.ls.container.get('view').add({selector:'data-ls-router',repeat:false,controller:function(element,window,document,view,router,tasks){let firstFromServer=(element.getAttribute('data-first-from-server')==='true');let scope={selector:'data-ls-scope',template:false,repeat:true,controller:function(){},};let init=function(route){let count=parseInt(element.getAttribute('data-ls-scope-count')||0);element.setAttribute('data-ls-scope-count',count+1);window.scrollTo(0,0);if(window.document.body.scrollTo){window.document.body.scrollTo(0,0);} +router.reset();if(null===route){return;} +scope.template=(undefined!==route.view.template)?route.view.template:null;scope.controller=(undefined!==route.view.controller)?route.view.controller:function(){};document.dispatchEvent(new CustomEvent('state-change'));if(firstFromServer&&null===router.getPrevious()){scope.template='';} +else if(count===1){view.render(element);} +else if(null!==router.getPrevious()){view.render(element);} +document.dispatchEvent(new CustomEvent('state-changed'));};let findParent=function(tagName,el){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;} +while(el=el.parentNode){if((el.nodeName||el.tagName).toLowerCase()===tagName.toLowerCase()){return el;}} +return null;};element.removeAttribute('data-ls-router');element.setAttribute('data-ls-scope','');element.setAttribute('data-ls-scope-count',1);view.add(scope);document.addEventListener('click',function(event){let target=findParent('a',event.target);if(!target){return false;} +if(!target.href){return false;} +if((event.metaKey)){return false;} +if((target.hasAttribute('target'))&&('_blank'===target.getAttribute('target'))){return false;} +if(target.hostname!==window.location.hostname){return false;} +let route=router.match(target);if(null===route){return false;} +event.preventDefault();if(window.location===target.href){return false;} +route.view.state=(undefined===route.view.state)?true:route.view.state;if(true===route.view.state){if(router.getPrevious()&&router.getPrevious().view&&(router.getPrevious().view.scope!==route.view.scope)){window.location.href=target.href;return false;} +window.history.pushState({},'Unknown',target.href);} +init(route);return true;});window.addEventListener('popstate',function(){init(router.match(window.location));});window.addEventListener('hashchange',function(){init(router.match(window.location));});init(router.match(window.location));}});window.ls.container.get('view').add({selector:'data-ls-attrs',controller:function(element,expression,container,$as,$prefix){let attrs=element.getAttribute('data-ls-attrs').trim().split(',');let paths=[];let check=()=>{for(let i=0;i{for(let i=0;i-1));value=element.value;} +catch{return null;}} +if(bind){element.addEventListener('change',()=>{for(let i=0;i-1){value.splice(index,1);} +container.path(paths[i],value,$as,$prefix);}});} +return;} +if(element.value!==value){element.value=value;} +if(bind){element.addEventListener('input',sync);element.addEventListener('change',sync);}} +else{if(element.innerText!==value){element.innerHTML=value;}}};let sync=((as,prefix)=>{return()=>{for(let i=0;i{echo(expression.parse(syntax,null,$as,$prefix),false);});}}});window.ls.container.get('view').add({selector:'data-ls-if',controller:function(element,expression,container,view,$as,$prefix){let result='';let syntax=element.getAttribute('data-ls-if')||'';let debug=element.getAttribute('data-debug')||false;let paths=[];let check=()=>{if(debug){console.info('debug-ls-if',expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',$as,$prefix,true));} +try{result=(eval(expression.parse(syntax.replace(/(\r\n|\n|\r)/gm,' '),'undefined',$as,$prefix,true)));} +catch(error){throw new Error('Failed to evaluate expression "'+syntax+' (resulted with: "'+result+'")": '+error);} +if(debug){console.info('debug-ls-if result:',result);} +paths=expression.getPaths();let prv=element.$lsSkip;element.$lsSkip=!result;if(!result){element.style.visibility='hidden';element.style.display='none';} +else{element.style.removeProperty('display');element.style.removeProperty('visibility');} +if(prv===true&&element.$lsSkip===false){view.render(element)}};check();for(let i=0;i{let context=expr+'.'+index;container.set(as,container.path(context),true,watch);container.set('$index',index,true,false);container.set('$prefix',context,true,false);container.set('$as',as,true,false);view.render(children[prop]);})(prop);} +container.set('$index',null,true,false,false);container.set('$prefix','',true,false,false);container.set('$as','',true,false,false);element.dispatchEvent(new Event('looped'));};let template=(element.children.length===1)?element.children[0]:window.document.createElement('li');echo();container.bind(element,expr+'.length',echo);}});window.ls.container.get('view').add({selector:'data-ls-template',template:false,repeat:true,controller:function(element,view,http,expression,document){let template=expression.parse(element.getAttribute('data-ls-template'));let type=element.getAttribute('data-type')||'url';element.innerHTML='';if('script'===type){let inlineTemplate=document.getElementById(template);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));} +else{element.innerHTML='Missing template "'+template+'"';} +return;} +http.get(template).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});}}); \ No newline at end of file diff --git a/public/scripts/filters.js b/public/scripts/filters.js index 23f39b7fb..b77b67a5b 100644 --- a/public/scripts/filters.js +++ b/public/scripts/filters.js @@ -69,6 +69,10 @@ window.ls.filter .add('markdown', function ($value, markdown) { return markdown.render($value); }) + .add('pageNext', function ($value, element) { + $value = parseInt($value || 0); + return $value + 5; + }) .add('humanFileSize', function ($value) { if (!$value) { return 0; diff --git a/public/scripts/views/service.js b/public/scripts/views/service.js index 267d592e1..95cb266d9 100644 --- a/public/scripts/views/service.js +++ b/public/scripts/views/service.js @@ -5,7 +5,7 @@ { selector: 'data-service', repeat: false, - controller: function(element, view, container, form, alerts, expression, window, router) { + controller: function(element, view, container, form, alerts, expression, window) { let action = element.dataset['service']; let service = element.dataset['name'] || action; let event = element.dataset['event']; // load, click, change, submit @@ -87,22 +87,18 @@ keys = keys.split(',').map(element => element.trim()); - return function (router, serviceData) { + return function (serviceForm, router, window) { let url = window.location.href; keys.map(key => { - let value = getValue(key, 'param', serviceData); - - if(!value) { - // return; - } - + let value = getValue(key, 'param', serviceForm); url = updateQueryString(key, (value ? value : null), url) }); - console.log(url); - //router.change(url, true); - window.history.replaceState({}, '', url); + if(url !== window.location.href) { + window.history.pushState({}, '', url); + router.reset(); + } } }, @@ -238,7 +234,8 @@ throw new Error('Method "' + scope + '.' + action + '" not found'); } - let result = resolve(method, 'param', ('FORM' === element.tagName) ? form.toJson(element) : {}); + let formData = ('FORM' === element.tagName) ? form.toJson(element) : {}; + let result = resolve(method, 'param', formData); if(!result) { return; @@ -256,6 +253,7 @@ container.set(service.replace('.', '-'), data, true, true); container.set('serviceData', data, true, true); + container.set('serviceForm', formData, true, true); if (debug) console.log('%cservice ready: "' + service.replace('.', '-') + '"', 'color:green'); if (debug) console.log('%cservice:', 'color:blue', container.get(service.replace('.', '-'))); @@ -265,6 +263,7 @@ } container.set('serviceData', null, true, true); + container.set('serviceForm', null, true, true); element.$lsSkip = false; diff --git a/public/scripts/views/ui/phases.js b/public/scripts/views/ui/phases.js index 15a83e0f2..da4bb1058 100644 --- a/public/scripts/views/ui/phases.js +++ b/public/scripts/views/ui/phases.js @@ -25,7 +25,9 @@ var parser = document.createElement('a'); parser.href = tabState; - window.history.pushState({}, '', parser.pathname + window.location.search); + if(position != index) { // When tab has changed add state to history + window.history.pushState({}, '', parser.pathname + window.location.search); + } } element.children[position].classList.remove('selected');