2019-06-08 08:52:38 +12:00
2019-08-13 02:19:54 +12:00
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 ( ) ] ; }
2019-08-21 18:55:51 +12:00
let shift = path . shift ( ) ; if ( value !== null && value !== undefined && object && shift && object [ shift ] ) { object [ shift ] = value ; return true ; }
2019-08-13 02:19:54 +12:00
if ( ! object ) { return null ; }
2019-08-21 18:55:51 +12:00
if ( ! shift ) { result = object ; }
2019-08-13 02:19:54 +12:00
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 ; }
2020-02-03 11:17:24 +13:00
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 ) ; container . resolve ( view . controller ) ; if ( true !== view . repeat ) { node . removeAttribute ( view . selector ) ; } } ; let parse = function ( node , skip , callback ) { if ( node . tagName === 'SCRIPT' ) { return ; }
2019-08-13 02:19:54 +12:00
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 -- ; }
2020-02-03 11:17:24 +13:00
if ( callback ) { callback ( ) ; }
2019-08-13 02:19:54 +12:00
continue ; }
2020-02-03 11:17:24 +13:00
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 ) ; if ( callback ) { callback ( ) ; } } } ( node , comp ) , function ( error ) { throw new Error ( 'Failed to load comp template: ' + error . message ) ; } ) ; } } }
2019-08-13 02:19:54 +12:00
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' ) ; }
2020-02-10 20:58:27 +13:00
stock [ object . selector ] = object ; return this ; } , render : function ( element , callback ) { parse ( element , false , callback ) ; 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 . search . indexOf ( '?' ) ; if ( pos === - 1 ) return [ ] ; query = location . search . substr ( pos + 1 ) ; } else { query = location . search . substr ( 1 ) ; }
2019-08-13 02:19:54 +12:00
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' ) ; }
2019-08-25 19:42:10 +12:00
states [ states . length ++ ] = { path : path , view : view } ; return this ; } ; let match = function ( location ) { let url = location . pathname ; 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 ; }
2019-08-13 02:19:54 +12:00
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 ) ; } ; 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 < 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 + '\'' ; }
2019-08-20 22:10:32 +12:00
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 ) => { if ( typeof $value !== 'string' ) { return $value ; }
return $value . toUpperCase ( ) ; } ) ; add ( 'lowercase' , ( $value ) => { if ( typeof $value !== 'string' ) { return $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 ; }
2019-08-13 02:19:54 +12:00
return value . replace ( /&/g , '&' ) . 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 ) ; }
2019-08-25 19:42:10 +12:00
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 ) { 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 ) ; }
2019-08-13 02:19:54 +12:00
router . reset ( ) ; if ( null === route ) { return ; }
2020-02-06 08:56:50 +13:00
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 = '' ; document . dispatchEvent ( new CustomEvent ( 'state-changed' ) ) ; }
2020-02-03 11:17:24 +13:00
else if ( count === 1 ) { view . render ( element , function ( ) { document . dispatchEvent ( new CustomEvent ( 'state-changed' ) ) ; } ) ; }
else if ( null !== router . getPrevious ( ) ) { view . render ( element , function ( ) { document . dispatchEvent ( new CustomEvent ( 'state-changed' ) ) ; } ) ; } } ; let findParent = function ( tagName , el ) { if ( ( el . nodeName || el . tagName ) . toLowerCase ( ) === tagName . toLowerCase ( ) ) { return el ; }
2019-08-13 02:19:54 +12:00
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 ) ; }
2019-08-14 09:07:41 +12:00
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 debug = element . getAttribute ( 'data-debug' ) || false ; let check = ( ) => { container . set ( 'element' , element , true , false ) ; if ( debug ) { console . info ( 'debug-ls-attrs attributes:' , attrs ) ; }
for ( let i = 0 ; i < attrs . length ; i ++ ) { let attr = attrs [ i ] ; let key = expression . parse ( ( attr . substring ( 0 , attr . indexOf ( '=' ) ) || attr ) , null , $as , $prefix ) ; paths = paths . concat ( expression . getPaths ( ) ) ; let value = '' ; if ( attr . indexOf ( '=' ) > - 1 ) { value = expression . parse ( attr . substring ( attr . indexOf ( '=' ) + 1 ) , null , $as , $prefix ) || '' ; paths = paths . concat ( expression . getPaths ( ) ) ; }
if ( ! key ) { return null ; }
element . setAttribute ( key , value ) ; } } ; check ( ) ; for ( let i = 0 ; i < paths . length ; i ++ ) { let path = paths [ i ] . split ( '.' ) ; if ( debug ) { console . info ( 'debug-ls-attrs listen to:' , path . join ( '.' ) ) ; }
2019-08-20 22:10:32 +12:00
while ( path . length ) { container . bind ( element , path . join ( '.' ) , check , $as , $prefix ) ; path . pop ( ) ; } } } } ) ; 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' ) ; }
2019-08-13 02:19:54 +12:00
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 ; }
2019-08-21 23:00:32 +12:00
if ( element . value !== value ) { element . value = value ; element . dispatchEvent ( new Event ( 'change' ) ) ; }
2019-08-13 02:19:54 +12:00
if ( bind ) { element . addEventListener ( 'input' , sync ) ; element . addEventListener ( 'change' , sync ) ; } }
2019-08-28 12:13:26 +12:00
else { if ( element . textContent != value ) { element . innerHTML = value ; } } } ; let sync = ( ( as , prefix ) => { return ( ) => { for ( let i = 0 ; i < paths . length ; i ++ ) { if ( '{{' + paths [ i ] + '}}' !== syntax ) { continue ; }
container . path ( paths [ i ] , element . value , as , prefix ) ; } } } ) ( $as , $prefix ) ; let syntax = element . getAttribute ( 'data-ls-bind' ) ; let unsync = ( ! ! element . getAttribute ( 'data-unsync' ) ) || false ; let result = expression . parse ( syntax , null , $as , $prefix ) ; let paths = expression . getPaths ( ) ; echo ( result , ! unsync ) ; element . addEventListener ( 'looped' , function ( ) { echo ( expression . parse ( syntax , null , $as , $prefix ) , false ) ; } ) ; for ( let i = 0 ; i < paths . length ; i ++ ) { let path = paths [ i ] . split ( '.' ) ; while ( path . length ) { container . bind ( element , path . join ( '.' ) , ( ) => { echo ( expression . parse ( syntax , null , $as , $prefix ) , false ) ; } ) ; path . pop ( ) ; } } } } ) ; 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 ) ) ; }
2019-08-13 02:19:54 +12:00
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' ) ; }
2019-08-20 22:10:32 +12:00
if ( prv === true && element . $lsSkip === false ) { view . render ( element ) } } ; check ( ) ; for ( let i = 0 ; i < paths . length ; i ++ ) { let path = paths [ i ] . split ( '.' ) ; while ( path . length ) { container . bind ( element , path . join ( '.' ) , check , $as , $prefix ) ; path . pop ( ) ; } } } } ) ; 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 ; }
2019-08-13 02:19:54 +12:00
if ( array instanceof Array && typeof array !== 'object' ) { throw new Error ( 'Reference value must be array or object. ' + ( typeof array ) + ' given' ) ; }
2019-08-24 17:25:11 +12:00
let children = [ ] ; let originalIndex = container . get ( '$index' ) || null ; let originalPrefix = container . get ( '$prefix' ) || null ; let originalAs = container . get ( '$as' ) || null ; element . $lsSkip = true ; element . style . visibility = ( 0 === array . length ) ? 'hidden' : 'visible' ; for ( let prop in array ) { if ( ! array . hasOwnProperty ( prop ) ) { continue ; }
2019-08-13 02:19:54 +12:00
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 ) ; }
2019-08-24 17:25:11 +12:00
container . set ( '$index' , originalIndex , true , false ) ; container . set ( '$prefix' , originalPrefix , true , false ) ; container . set ( '$as' , originalAs , true , 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 ) ; let path = ( expr + '.length' ) . split ( '.' ) ; while ( path . length ) { container . bind ( element , path . join ( '.' ) , echo ) ; path . pop ( ) ; } } } ) ; 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 } ) ) ; }
2019-08-13 02:19:54 +12:00
else { element . innerHTML = '<span style="color: red">Missing template "' + template + '"</span>' ; }
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' ) ; } ) ; } } ) ;