2022-04-06 00:04:46 +02:00
/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */
! function ( a , b ) { "object" == typeof module && "object" == typeof module . exports ? module . exports = a . document ? b ( a , ! 0 ) : function ( a ) { if ( ! a . document ) throw new Error ( "jQuery requires a window with a document" ) ; return b ( a ) } : b ( a ) } ( "undefined" != typeof window ? window : this , function ( a , b ) { var c = [ ] , d = a . document , e = c . slice , f = c . concat , g = c . push , h = c . indexOf , i = { } , j = i . toString , k = i . hasOwnProperty , l = { } , m = "1.12.4" , n = function ( a , b ) { return new n . fn . init ( a , b ) } , o = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g , p = /^-ms-/ , q = /-([\da-z])/gi , r = function ( a , b ) { return b . toUpperCase ( ) } ; n . fn = n . prototype = { jquery : m , constructor : n , selector : "" , length : 0 , toArray : function ( ) { return e . call ( this ) } , get : function ( a ) { return null != a ? 0 > a ? this [ a + this . length ] : this [ a ] : e . call ( this ) } , pushStack : function ( a ) { var b = n . merge ( this . constructor ( ) , a ) ; return b . prevObject = this , b . context = this . context , b } , each : function ( a ) { return n . each ( this , a ) } , map : function ( a ) { return this . pushStack ( n . map ( this , function ( b , c ) { return a . call ( b , c , b ) } ) ) } , slice : function ( ) { return this . pushStack ( e . apply ( this , arguments ) ) } , first : function ( ) { return this . eq ( 0 ) } , last : function ( ) { return this . eq ( - 1 ) } , eq : function ( a ) { var b = this . length , c = + a + ( 0 > a ? b : 0 ) ; return this . pushStack ( c >= 0 && b > c ? [ this [ c ] ] : [ ] ) } , end : function ( ) { return this . prevObject || this . constructor ( ) } , push : g , sort : c . sort , splice : c . splice } , n . extend = n . fn . extend = function ( ) { var a , b , c , d , e , f , g = arguments [ 0 ] || { } , h = 1 , i = arguments . length , j = ! 1 ; for ( "boolean" == typeof g && ( j = g , g = arguments [ h ] || { } , h ++ ) , "object" == typeof g || n . isFunction ( g ) || ( g = { } ) , h === i && ( g = this , h -- ) ; i > h ; h ++ ) if ( null != ( e = arguments [ h ] ) ) for ( d in e ) a = g [ d ] , c = e [ d ] , g !== c && ( j && c && ( n . isPlainObject ( c ) || ( b = n . isArray ( c ) ) ) ? ( b ? ( b = ! 1 , f = a && n . isArray ( a ) ? a : [ ] ) : f = a && n . isPlainObject ( a ) ? a : { } , g [ d ] = n . extend ( j , f , c ) ) : void 0 !== c && ( g [ d ] = c ) ) ; return g } , n . extend ( { expando : "jQuery" + ( m + Math . random ( ) ) . replace ( /\D/g , "" ) , isReady : ! 0 , error : function ( a ) { throw new Error ( a ) } , noop : function ( ) { } , isFunction : function ( a ) { return "function" === n . type ( a ) } , isArray : Array . isArray || function ( a ) { return "array" === n . type ( a ) } , isWindow : function ( a ) { return null != a && a == a . window } , isNumeric : function ( a ) { var b = a && a . toString ( ) ; return ! n . isArray ( a ) && b - parseFloat ( b ) + 1 >= 0 } , isEmptyObject : function ( a ) { var b ; for ( b in a ) return ! 1 ; return ! 0 } , isPlainObject : function ( a ) { var b ; if ( ! a || "object" !== n . type ( a ) || a . nodeType || n . isWindow ( a ) ) return ! 1 ; try { if ( a . constructor && ! k . call ( a , "constructor" ) && ! k . call ( a . constructor . prototype , "isPrototypeOf" ) ) return ! 1 } catch ( c ) { return ! 1 } if ( ! l . ownFirst ) for ( b in a ) return k . call ( a , b ) ; for ( b in a ) ; return void 0 === b || k . call ( a , b ) } , type : function ( a ) { return null == a ? a + "" : "object" == typeof a || "function" == typeof a ? i [ j . call ( a ) ] || "object" : typeof a } , globalEval : function ( b ) { b && n . trim ( b ) && ( a . execScript || function ( b ) { a . eval . call ( a , b ) } ) ( b ) } , camelCase : function ( a ) { return a . replace ( p , "ms-" ) . replace ( q , r ) } , nodeName : function ( a , b ) { return a . nodeName && a . nodeName . toLowerCase ( ) === b . toLowerCase ( ) } , each : function ( a , b ) { var c , d = 0 ; if ( s ( a ) ) { for ( c = a . length ; c > d ; d ++ ) if ( b . call ( a [ d ] , d , a [ d ] ) === ! 1 ) break } else for ( d in a ) if ( b . call ( a [ d ] , d , a [ d ] ) === ! 1 ) break ; return a } , trim : function ( a ) { return null == a ? "" : ( a + "" ) . replace ( o , "" ) } , makeArray : function ( a , b ) { var c = b || [ ] ; return null != a && ( s ( Object ( a ) ) ? n . merge ( c , "string" == typeof a ? [ a ] : a ) : g . call ( c , a ) ) , c } , inArray : function ( a , b , c ) { var d ; if ( b ) { if ( h ) return h . call ( b , a , c ) ; for ( d = b . length , c = c ? 0 > c ? Math . max ( 0 , d + c ) : c : 0 ; d > c ; c ++ ) if ( c in b && b [ c ] === a ) return c } return - 1 } , merge : function ( a , b ) { var c = + b . length , d = 0 , e = a . length ; while ( c > d ) a [ e ++ ] = b [ d ++ ] ; if ( c !== c ) while ( void 0 !== b [ d ] ) a [ e ++ ] = b [ d ++ ] ; return a . length = e , a } , grep : function ( a , b , c ) { for ( var d , e = [ ] , f = 0 , g = a . length , h = ! c ; g > f ; f ++ ) d = ! b ( a [ f ] , f ) , d !== h && e . push ( a [ f ] ) ; return e } , map : function ( a , b , c ) { var d , e , g = 0 , h = [ ] ; if ( s ( a ) ) for ( d = a . length ; d > g ; g ++ ) e = b ( a [ g ] , g , c ) , null != e && h . push ( e ) ; else for ( g in a ) e = b ( a [ g ] , g , c ) , null != e && h . push ( e ) ; return f . apply ( [ ] , h ) } , guid : 1 , proxy : function ( a , b ) { var c , d , f ; return "string" == typeof b && ( f = a [ b ] , b = a , a = f ) , n . isFunction ( a ) ? ( c = e . call ( arguments , 2 ) , d = function ( ) { return a . apply ( b || this , c . concat ( e . call ( arguments ) ) ) } , d . guid = a . guid = a . guid || n . guid ++ , d ) : void 0 } , now : function ( ) { return + new Date } , support : l } ) , "function" == typeof Symbol && ( n . fn [ Symbol . iterator ] = c [ Symbol . iterator ] ) , n . each ( " Boolean Number String Function A
} return c } function Q ( a ) { var b ; for ( b in a ) if ( ( "data" !== b || ! n . isEmptyObject ( a [ b ] ) ) && "toJSON" !== b ) return ! 1 ; return ! 0 } function R ( a , b , d , e ) { if ( M ( a ) ) { var f , g , h = n . expando , i = a . nodeType , j = i ? n . cache : a , k = i ? a [ h ] : a [ h ] && h ; if ( k && j [ k ] && ( e || j [ k ] . data ) || void 0 !== d || "string" != typeof b ) return k || ( k = i ? a [ h ] = c . pop ( ) || n . guid ++ : h ) , j [ k ] || ( j [ k ] = i ? { } : { toJSON : n . noop } ) , "object" != typeof b && "function" != typeof b || ( e ? j [ k ] = n . extend ( j [ k ] , b ) : j [ k ] . data = n . extend ( j [ k ] . data , b ) ) , g = j [ k ] , e || ( g . data || ( g . data = { } ) , g = g . data ) , void 0 !== d && ( g [ n . camelCase ( b ) ] = d ) , "string" == typeof b ? ( f = g [ b ] , null == f && ( f = g [ n . camelCase ( b ) ] ) ) : f = g , f } } function S ( a , b , c ) { if ( M ( a ) ) { var d , e , f = a . nodeType , g = f ? n . cache : a , h = f ? a [ n . expando ] : n . expando ; if ( g [ h ] ) { if ( b && ( d = c ? g [ h ] : g [ h ] . data ) ) { n . isArray ( b ) ? b = b . concat ( n . map ( b , n . camelCase ) ) : b in d ? b = [ b ] : ( b = n . camelCase ( b ) , b = b in d ? [ b ] : b . split ( " " ) ) , e = b . length ; while ( e -- ) delete d [ b [ e ] ] ; if ( c ? ! Q ( d ) : ! n . isEmptyObject ( d ) ) return } ( c || ( delete g [ h ] . data , Q ( g [ h ] ) ) ) && ( f ? n . cleanData ( [ a ] , ! 0 ) : l . deleteExpando || g != g . window ? delete g [ h ] : g [ h ] = void 0 ) } } } n . extend ( { cache : { } , noData : { "applet " : ! 0 , "embed " : ! 0 , "object " : "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" } , hasData : function ( a ) { return a = a . nodeType ? n . cache [ a [ n . expando ] ] : a [ n . expando ] , ! ! a && ! Q ( a ) } , data : function ( a , b , c ) { return R ( a , b , c ) } , removeData : function ( a , b ) { return S ( a , b ) } , _data : function ( a , b , c ) { return R ( a , b , c , ! 0 ) } , _removeData : function ( a , b ) { return S ( a , b , ! 0 ) } } ) , n . fn . extend ( { data : function ( a , b ) { var c , d , e , f = this [ 0 ] , g = f && f . attributes ; if ( void 0 === a ) { if ( this . length && ( e = n . data ( f ) , 1 === f . nodeType && ! n . _data ( f , "parsedAttrs" ) ) ) { c = g . length ; while ( c -- ) g [ c ] && ( d = g [ c ] . name , 0 === d . indexOf ( "data-" ) && ( d = n . camelCase ( d . slice ( 5 ) ) , P ( f , d , e [ d ] ) ) ) ; n . _data ( f , "parsedAttrs" , ! 0 ) } return e } return "object" == typeof a ? this . each ( function ( ) { n . data ( this , a ) } ) : arguments . length > 1 ? this . each ( function ( ) { n . data ( this , a , b ) } ) : f ? P ( f , a , n . data ( f , a ) ) : void 0 } , removeData : function ( a ) { return this . each ( function ( ) { n . removeData ( this , a ) } ) } } ) , n . extend ( { queue : function ( a , b , c ) { var d ; return a ? ( b = ( b || "fx" ) + "queue" , d = n . _data ( a , b ) , c && ( ! d || n . isArray ( c ) ? d = n . _data ( a , b , n . makeArray ( c ) ) : d . push ( c ) ) , d || [ ] ) : void 0 } , dequeue : function ( a , b ) { b = b || "fx" ; var c = n . queue ( a , b ) , d = c . length , e = c . shift ( ) , f = n . _queueHooks ( a , b ) , g = function ( ) { n . dequeue ( a , b ) } ; "inprogress" === e && ( e = c . shift ( ) , d -- ) , e && ( "fx" === b && c . unshift ( "inprogress" ) , delete f . stop , e . call ( a , g , f ) ) , ! d && f && f . empty . fire ( ) } , _queueHooks : function ( a , b ) { var c = b + "queueHooks" ; return n . _data ( a , c ) || n . _data ( a , c , { empty : n . Callbacks ( "once memory" ) . add ( function ( ) { n . _removeData ( a , b + "queue" ) , n . _removeData ( a , c ) } ) } ) } } ) , n . fn . extend ( { queue : function ( a , b ) { var c = 2 ; return "string" != typeof a && ( b = a , a = "fx" , c -- ) , arguments . length < c ? n . queue ( this [ 0 ] , a ) : void 0 === b ? this : this . each ( function ( ) { var c = n . queue ( this , a , b ) ; n . _queueHooks ( this , a ) , "fx" === a && "inprogress" !== c [ 0 ] && n . dequeue ( this , a ) } ) } , dequeue : function ( a ) { return this . each ( function ( ) { n . dequeue ( this , a ) } ) } , clearQueue : function ( a ) { return this . queue ( a || "fx" , [ ] ) } , promise : function ( a , b ) { var c , d = 1 , e = n . Deferred ( ) , f = this , g = this . length , h = function ( ) { -- d || e . resolveWith ( f , [ f ] ) } ; "string" != typeof a && ( b = a , a = void 0 ) , a = a || "fx" ; while ( g -- ) c = n . _data ( f [ g ] , a + "queueHooks" ) , c && c . empty && ( d ++ , c . empty . add ( h ) ) ; return h ( ) , e . promise ( b ) } } ) , function ( ) { var a ; l . shrinkWrapBlocks = function ( ) { if ( null != a ) return a ; a = ! 1 ; var b , c , e ; return c = d . getElementsByTagName ( "body" ) [ 0 ] , c && c . style ? ( b = d . createElement ( "div" ) , e = d . createElement ( "div" ) , e . style . cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px" , c . appendChild ( e ) . appendChild ( b ) , "undefined" != typeof b . style . zoom && ( b . style . cssText = "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1" , b . appendChild ( d . createElement ( "div" ) ) . style . width = "5px" , a = 3 !== b . offsetWidth ) , c . removeChild ( e ) , a ) : void 0 } } ( ) ; var T = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ . source , U = new RegExp ( "^(?:([+-])=|)(" + T + ")([a-z%]*)$" , "i" ) , V = [ "Top" , "Right" , "Bottom" , "Left" ] , W = function ( a , b ) { return a = b || a , "none" === n . css ( a , "display" ) || ! n . contains ( a . ownerDocument , a ) } ; function X ( a , b , c , d ) { var e , f = 1 , g = 20 , h = d ? function ( ) { return d . cur ( ) } : function ( ) { return n . css ( a , b , "" ) } , i = h ( ) , j = c && c [ 3 ] || ( n . cssNumber [ b ] ? "" : "px" ) , k = ( n . cssNum
marginLeft : 0 } , function ( ) { return a . getBoundingClientRect ( ) . left } ) : 0 ) ) + "px" : void 0 } ) , n . each ( { margin : "" , padding : "" , border : "Width" } , function ( a , b ) { n . cssHooks [ a + b ] = { expand : function ( c ) { for ( var d = 0 , e = { } , f = "string" == typeof c ? c . split ( " " ) : [ c ] ; 4 > d ; d ++ ) e [ a + V [ d ] + b ] = f [ d ] || f [ d - 2 ] || f [ 0 ] ; return e } } , Na . test ( a ) || ( n . cssHooks [ a + b ] . set = db ) } ) , n . fn . extend ( { css : function ( a , b ) { return Y ( this , function ( a , b , c ) { var d , e , f = { } , g = 0 ; if ( n . isArray ( b ) ) { for ( d = Ra ( a ) , e = b . length ; e > g ; g ++ ) f [ b [ g ] ] = n . css ( a , b [ g ] , ! 1 , d ) ; return f } return void 0 !== c ? n . style ( a , b , c ) : n . css ( a , b ) } , a , b , arguments . length > 1 ) } , show : function ( ) { return cb ( this , ! 0 ) } , hide : function ( ) { return cb ( this ) } , toggle : function ( a ) { return "boolean" == typeof a ? a ? this . show ( ) : this . hide ( ) : this . each ( function ( ) { W ( this ) ? n ( this ) . show ( ) : n ( this ) . hide ( ) } ) } } ) ; function gb ( a , b , c , d , e ) { return new gb . prototype . init ( a , b , c , d , e ) } n . Tween = gb , gb . prototype = { constructor : gb , init : function ( a , b , c , d , e , f ) { this . elem = a , this . prop = c , this . easing = e || n . easing . _default , this . options = b , this . start = this . now = this . cur ( ) , this . end = d , this . unit = f || ( n . cssNumber [ c ] ? "" : "px" ) } , cur : function ( ) { var a = gb . propHooks [ this . prop ] ; return a && a . get ? a . get ( this ) : gb . propHooks . _default . get ( this ) } , run : function ( a ) { var b , c = gb . propHooks [ this . prop ] ; return this . options . duration ? this . pos = b = n . easing [ this . easing ] ( a , this . options . duration * a , 0 , 1 , this . options . duration ) : this . pos = b = a , this . now = ( this . end - this . start ) * b + this . start , this . options . step && this . options . step . call ( this . elem , this . now , this ) , c && c . set ? c . set ( this ) : gb . propHooks . _default . set ( this ) , this } } , gb . prototype . init . prototype = gb . prototype , gb . propHooks = { _default : { get : function ( a ) { var b ; return 1 !== a . elem . nodeType || null != a . elem [ a . prop ] && null == a . elem . style [ a . prop ] ? a . elem [ a . prop ] : ( b = n . css ( a . elem , a . prop , "" ) , b && "auto" !== b ? b : 0 ) } , set : function ( a ) { n . fx . step [ a . prop ] ? n . fx . step [ a . prop ] ( a ) : 1 !== a . elem . nodeType || null == a . elem . style [ n . cssProps [ a . prop ] ] && ! n . cssHooks [ a . prop ] ? a . elem [ a . prop ] = a . now : n . style ( a . elem , a . prop , a . now + a . unit ) } } } , gb . propHooks . scrollTop = gb . propHooks . scrollLeft = { set : function ( a ) { a . elem . nodeType && a . elem . parentNode && ( a . elem [ a . prop ] = a . now ) } } , n . easing = { linear : function ( a ) { return a } , swing : function ( a ) { return . 5 - Math . cos ( a * Math . PI ) / 2 } , _default : "swing" } , n . fx = gb . prototype . init , n . fx . step = { } ; var hb , ib , jb = /^(?:toggle|show|hide)$/ , kb = /queueHooks$/ ; function lb ( ) { return a . setTimeout ( function ( ) { hb = void 0 } ) , hb = n . now ( ) } function mb ( a , b ) { var c , d = { height : a } , e = 0 ; for ( b = b ? 1 : 0 ; 4 > e ; e += 2 - b ) c = V [ e ] , d [ "margin" + c ] = d [ "padding" + c ] = a ; return b && ( d . opacity = d . width = a ) , d } function nb ( a , b , c ) { for ( var d , e = ( qb . tweeners [ b ] || [ ] ) . concat ( qb . tweeners [ "*" ] ) , f = 0 , g = e . length ; g > f ; f ++ ) if ( d = e [ f ] . call ( c , b , a ) ) return d } function ob ( a , b , c ) { var d , e , f , g , h , i , j , k , m = this , o = { } , p = a . style , q = a . nodeType && W ( a ) , r = n . _data ( a , "fxshow" ) ; c . queue || ( h = n . _queueHooks ( a , "fx" ) , null == h . unqueued && ( h . unqueued = 0 , i = h . empty . fire , h . empty . fire = function ( ) { h . unqueued || i ( ) } ) , h . unqueued ++ , m . always ( function ( ) { m . always ( function ( ) { h . unqueued -- , n . queue ( a , "fx" ) . length || h . empty . fire ( ) } ) } ) ) , 1 === a . nodeType && ( "height" in b || "width" in b ) && ( c . overflow = [ p . overflow , p . overflowX , p . overflowY ] , j = n . css ( a , "display" ) , k = "none" === j ? n . _data ( a , "olddisplay" ) || Ma ( a . nodeName ) : j , "inline" === k && "none" === n . css ( a , "float" ) && ( l . inlineBlockNeedsLayout && "inline" !== Ma ( a . nodeName ) ? p . zoom = 1 : p . display = "inline-block" ) ) , c . overflow && ( p . overflow = "hidden" , l . shrinkWrapBlocks ( ) || m . always ( function ( ) { p . overflow = c . overflow [ 0 ] , p . overflowX = c . overflow [ 1 ] , p . overflowY = c . overflow [ 2 ] } ) ) ; for ( d in b ) if ( e = b [ d ] , jb . exec ( e ) ) { if ( delete b [ d ] , f = f || "toggle" === e , e === ( q ? "hide" : "show" ) ) { if ( "show" !== e || ! r || void 0 === r [ d ] ) continue ; q = ! 0 } o [ d ] = r && r [ d ] || n . style ( a , d ) } else j = void 0 ; if ( n . isEmptyObject ( o ) ) "inline" === ( "none" === j ? Ma ( a . nodeName ) : j ) && ( p . display = j ) ; else { r ? "hidden" in r && ( q = r . hidden ) : r = n . _data ( a , "fxshow" , { } ) , f && ( r . hidden = ! q ) , q ? n ( a ) . show ( ) : m . done ( function ( ) { n ( a ) . hide ( ) } ) , m . done ( function ( ) { var b ; n . _removeData ( a , "fxshow" ) ; for ( b in o ) n . style ( a , b , o [ b ] ) } ) ; for ( d in o ) g = nb ( q ? r [ d ] : 0 , d , m ) , d in r || ( r [ d ] = g . start , q && ( g . end = g . start , g . start = "width" === d || "height" === d ? 1 : 0 ) ) } } function pb ( a , b ) { var c , d , e , f , g ; for ( c in a ) if ( d = n . camelCase ( c ) , e = b [ d ] , f = a [ c ] , n . isArray ( f ) && ( e = f [ 1 ] , f = a [ c ] = f [ 0 ] ) , c !== d && ( a [ d ] = f , delete a [ c ] ) , g = n . cssHooks [ d ] , g && " expa
padding : "inner" + a , content : b , "" : "outer" + a } , function ( c , d ) { n . fn [ d ] = function ( d , e ) { var f = arguments . length && ( c || "boolean" != typeof d ) , g = c || ( d === ! 0 || e === ! 0 ? "margin" : "border" ) ; return Y ( this , function ( b , c , d ) { var e ; return n . isWindow ( b ) ? b . document . documentElement [ "client" + a ] : 9 === b . nodeType ? ( e = b . documentElement , Math . max ( b . body [ "scroll" + a ] , e [ "scroll" + a ] , b . body [ "offset" + a ] , e [ "offset" + a ] , e [ "client" + a ] ) ) : void 0 === d ? n . css ( b , c , g ) : n . style ( b , c , d , g ) } , b , f ? d : void 0 , f , null ) } } ) } ) , n . fn . extend ( { bind : function ( a , b , c ) { return this . on ( a , null , b , c ) } , unbind : function ( a , b ) { return this . off ( a , null , b ) } , delegate : function ( a , b , c , d ) { return this . on ( b , a , c , d ) } , undelegate : function ( a , b , c ) { return 1 === arguments . length ? this . off ( a , "**" ) : this . off ( b , a || "**" , c ) } } ) , n . fn . size = function ( ) { return this . length } , n . fn . andSelf = n . fn . addBack , "function" == typeof define && define . amd && define ( "jquery" , [ ] , function ( ) { return n } ) ; var nc = a . jQuery , oc = a . $ ; return n . noConflict = function ( b ) { return a . $ === n && ( a . $ = oc ) , b && a . jQuery === n && ( a . jQuery = nc ) , n } , b || ( a . jQuery = a . $ = n ) , n } ) ;
/ * !
* Bootstrap v3 . 3.7 ( http : //getbootstrap.com)
* Copyright 2011 - 2016 Twitter , Inc .
* Licensed under the MIT license
* /
if ( "undefined" == typeof jQuery ) throw new Error ( "Bootstrap's JavaScript requires jQuery" ) ; + function ( a ) { "use strict" ; var b = a . fn . jquery . split ( " " ) [ 0 ] . split ( "." ) ; if ( b [ 0 ] < 2 && b [ 1 ] < 9 || 1 == b [ 0 ] && 9 == b [ 1 ] && b [ 2 ] < 1 || b [ 0 ] > 3 ) throw new Error ( "Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4" ) } ( jQuery ) , + function ( a ) { "use strict" ; function b ( ) { var a = document . createElement ( "bootstrap" ) , b = { WebkitTransition : "webkitTransitionEnd" , MozTransition : "transitionend" , OTransition : "oTransitionEnd otransitionend" , transition : "transitionend" } ; for ( var c in b ) if ( void 0 !== a . style [ c ] ) return { end : b [ c ] } ; return ! 1 } a . fn . emulateTransitionEnd = function ( b ) { var c = ! 1 , d = this ; a ( this ) . one ( "bsTransitionEnd" , function ( ) { c = ! 0 } ) ; var e = function ( ) { c || a ( d ) . trigger ( a . support . transition . end ) } ; return setTimeout ( e , b ) , this } , a ( function ( ) { a . support . transition = b ( ) , a . support . transition && ( a . event . special . bsTransitionEnd = { bindType : a . support . transition . end , delegateType : a . support . transition . end , handle : function ( b ) { if ( a ( b . target ) . is ( this ) ) return b . handleObj . handler . apply ( this , arguments ) } } ) } ) } ( jQuery ) , + function ( a ) { "use strict" ; function b ( b ) { return this . each ( function ( ) { var c = a ( this ) , e = c . data ( "bs.alert" ) ; e || c . data ( "bs.alert" , e = new d ( this ) ) , "string" == typeof b && e [ b ] . call ( c ) } ) } var c = '[data-dismiss="alert"]' , d = function ( b ) { a ( b ) . on ( "click" , c , this . close ) } ; d . VERSION = "3.3.7" , d . TRANSITION _DURATION = 150 , d . prototype . close = function ( b ) { function c ( ) { g . detach ( ) . trigger ( "closed.bs.alert" ) . remove ( ) } var e = a ( this ) , f = e . attr ( "data-target" ) ; f || ( f = e . attr ( "href" ) , f = f && f . replace ( /.*(?=#[^\s]*$)/ , "" ) ) ; var g = a ( "#" === f ? [ ] : f ) ; b && b . preventDefault ( ) , g . length || ( g = e . closest ( ".alert" ) ) , g . trigger ( b = a . Event ( "close.bs.alert" ) ) , b . isDefaultPrevented ( ) || ( g . removeClass ( "in" ) , a . support . transition && g . hasClass ( "fade" ) ? g . one ( "bsTransitionEnd" , c ) . emulateTransitionEnd ( d . TRANSITION _DURATION ) : c ( ) ) } ; var e = a . fn . alert ; a . fn . alert = b , a . fn . alert . Constructor = d , a . fn . alert . noConflict = function ( ) { return a . fn . alert = e , this } , a ( document ) . on ( "click.bs.alert.data-api" , c , d . prototype . close ) } ( jQuery ) , + function ( a ) { "use strict" ; function b ( b ) { return this . each ( function ( ) { var d = a ( this ) , e = d . data ( "bs.button" ) , f = "object" == typeof b && b ; e || d . data ( "bs.button" , e = new c ( this , f ) ) , "toggle" == b ? e . toggle ( ) : b && e . setState ( b ) } ) } var c = function ( b , d ) { this . $element = a ( b ) , this . options = a . extend ( { } , c . DEFAULTS , d ) , this . isLoading = ! 1 } ; c . VERSION = "3.3.7" , c . DEFAULTS = { loadingText : "loading..." } , c . prototype . setState = function ( b ) { var c = "disabled" , d = this . $element , e = d . is ( "input" ) ? "val" : "html" , f = d . data ( ) ; b += "Text" , null == f . resetText && d . data ( "resetText" , d [ e ] ( ) ) , setTimeout ( a . proxy ( function ( ) { d [ e ] ( null == f [ b ] ? this . options [ b ] : f [ b ] ) , "loadingText" == b ? ( this . isLoading = ! 0 , d . addClass ( c ) . attr ( c , c ) . prop ( c , ! 0 ) ) : this . isLoading && ( this . isLoading = ! 1 , d . removeClass ( c ) . removeAttr ( c ) . prop ( c , ! 1 ) ) } , this ) , 0 ) } , c . prototype . toggle = function ( ) { var a = ! 0 , b = this . $element . closest ( '[data-toggle="buttons"]' ) ; if ( b . length ) { var c = this . $element . find ( "input" ) ; "radio" == c . prop ( "type" ) ? ( c . prop ( "checked" ) && ( a = ! 1 ) , b . find ( ".active" ) . removeClass ( "active" ) , this . $element . addClass ( "active" ) ) : "checkbox" == c . prop ( "type" ) && ( c . prop ( "checked" ) !== this . $element . hasClass ( "active" ) && ( a = ! 1 ) , this . $element . toggleClass ( "active" ) ) , c . prop ( "checked" , this . $element . hasClass ( "active" ) ) , a && c . trigger ( "change" ) } else this . $element . attr ( "aria-pressed" , ! this . $element . hasClass ( "active" ) ) , this . $element . toggleClass ( "active" ) } ; var d = a . fn . button ; a . fn . button = b , a . fn . button . Constructor = c , a . fn . button . noConflict = function ( ) { return a . fn . button = d , this } , a ( document ) . on ( "click.bs.button.data-api" , '[data-toggle^="button"]' , function ( c ) { var d = a ( c . target ) . closest ( ".btn" ) ; b . call ( d , "toggle" ) , a ( c . target ) . is ( 'input[type="radio"], input[type="checkbox"]' ) || ( c . preventDefault ( ) , d . is ( "input,button" ) ? d . trigger ( "focus" ) : d . find ( "input:visible,button:visible" ) . first ( ) . trigger ( "focus" ) ) } ) . on ( "focus.bs.button.data-api blur.bs.button.data-api" , '[data-toggle^="button"]' , function ( b ) { a ( b . target ) . closest ( ".btn" ) . toggleClass ( "focus" , /^focus(in)?$/ . test ( b . type ) ) } ) } ( jQuery ) , + function ( a ) { "use strict" ; function b ( b ) { return this . each ( function ( ) { var d = a ( this ) , e = d . data ( "bs.carousel" ) , f = a . extend ( { } , c . D
this . activeTarget = b , this . clear ( ) ; var c = this . selector + '[data-target="' + b + '"],' + this . selector + '[href="' + b + '"]' , d = a ( c ) . parents ( "li" ) . addClass ( "active" ) ; d . parent ( ".dropdown-menu" ) . length && ( d = d . closest ( "li.dropdown" ) . addClass ( "active" ) ) , d . trigger ( "activate.bs.scrollspy" ) } , b . prototype . clear = function ( ) { a ( this . selector ) . parentsUntil ( this . options . target , ".active" ) . removeClass ( "active" ) } ; var d = a . fn . scrollspy ; a . fn . scrollspy = c , a . fn . scrollspy . Constructor = b , a . fn . scrollspy . noConflict = function ( ) { return a . fn . scrollspy = d , this } , a ( window ) . on ( "load.bs.scrollspy.data-api" , function ( ) { a ( '[data-spy="scroll"]' ) . each ( function ( ) { var b = a ( this ) ; c . call ( b , b . data ( ) ) } ) } ) } ( jQuery ) , + function ( a ) { "use strict" ; function b ( b ) { return this . each ( function ( ) { var d = a ( this ) , e = d . data ( "bs.tab" ) ; e || d . data ( "bs.tab" , e = new c ( this ) ) , "string" == typeof b && e [ b ] ( ) } ) } var c = function ( b ) { this . element = a ( b ) } ; c . VERSION = "3.3.7" , c . TRANSITION _DURATION = 150 , c . prototype . show = function ( ) { var b = this . element , c = b . closest ( "ul:not(.dropdown-menu)" ) , d = b . data ( "target" ) ; if ( d || ( d = b . attr ( "href" ) , d = d && d . replace ( /.*(?=#[^\s]*$)/ , "" ) ) , ! b . parent ( "li" ) . hasClass ( "active" ) ) { var e = c . find ( ".active:last a" ) , f = a . Event ( "hide.bs.tab" , { relatedTarget : b [ 0 ] } ) , g = a . Event ( "show.bs.tab" , { relatedTarget : e [ 0 ] } ) ; if ( e . trigger ( f ) , b . trigger ( g ) , ! g . isDefaultPrevented ( ) && ! f . isDefaultPrevented ( ) ) { var h = a ( d ) ; this . activate ( b . closest ( "li" ) , c ) , this . activate ( h , h . parent ( ) , function ( ) { e . trigger ( { type : "hidden.bs.tab" , relatedTarget : b [ 0 ] } ) , b . trigger ( { type : "shown.bs.tab" , relatedTarget : e [ 0 ] } ) } ) } } } , c . prototype . activate = function ( b , d , e ) { function f ( ) { g . removeClass ( "active" ) . find ( "> .dropdown-menu > .active" ) . removeClass ( "active" ) . end ( ) . find ( '[data-toggle="tab"]' ) . attr ( "aria-expanded" , ! 1 ) , b . addClass ( "active" ) . find ( '[data-toggle="tab"]' ) . attr ( "aria-expanded" , ! 0 ) , h ? ( b [ 0 ] . offsetWidth , b . addClass ( "in" ) ) : b . removeClass ( "fade" ) , b . parent ( ".dropdown-menu" ) . length && b . closest ( "li.dropdown" ) . addClass ( "active" ) . end ( ) . find ( '[data-toggle="tab"]' ) . attr ( "aria-expanded" , ! 0 ) , e && e ( ) } var g = d . find ( "> .active" ) , h = e && a . support . transition && ( g . length && g . hasClass ( "fade" ) || ! ! d . find ( "> .fade" ) . length ) ; g . length && h ? g . one ( "bsTransitionEnd" , f ) . emulateTransitionEnd ( c . TRANSITION _DURATION ) : f ( ) , g . removeClass ( "in" ) } ; var d = a . fn . tab ; a . fn . tab = b , a . fn . tab . Constructor = c , a . fn . tab . noConflict = function ( ) { return a . fn . tab = d , this } ; var e = function ( c ) { c . preventDefault ( ) , b . call ( a ( this ) , "show" ) } ; a ( document ) . on ( "click.bs.tab.data-api" , '[data-toggle="tab"]' , e ) . on ( "click.bs.tab.data-api" , '[data-toggle="pill"]' , e ) } ( jQuery ) , + function ( a ) { "use strict" ; function b ( b ) { return this . each ( function ( ) { var d = a ( this ) , e = d . data ( "bs.affix" ) , f = "object" == typeof b && b ; e || d . data ( "bs.affix" , e = new c ( this , f ) ) , "string" == typeof b && e [ b ] ( ) } ) } var c = function ( b , d ) { this . options = a . extend ( { } , c . DEFAULTS , d ) , this . $target = a ( this . options . target ) . on ( "scroll.bs.affix.data-api" , a . proxy ( this . checkPosition , this ) ) . on ( "click.bs.affix.data-api" , a . proxy ( this . checkPositionWithEventLoop , this ) ) , this . $element = a ( b ) , this . affixed = null , this . unpin = null , this . pinnedOffset = null , this . checkPosition ( ) } ; c . VERSION = "3.3.7" , c . RESET = "affix affix-top affix-bottom" , c . DEFAULTS = { offset : 0 , target : window } , c . prototype . getState = function ( a , b , c , d ) { var e = this . $target . scrollTop ( ) , f = this . $element . offset ( ) , g = this . $target . height ( ) ; if ( null != c && "top" == this . affixed ) return e < c && "top" ; if ( "bottom" == this . affixed ) return null != c ? ! ( e + this . unpin <= f . top ) && "bottom" : ! ( e + g <= a - d ) && "bottom" ; var h = null == this . affixed , i = h ? e : f . top , j = h ? g : b ; return null != c && e <= c ? "top" : null != d && i + j >= a - d && "bottom" } , c . prototype . getPinnedOffset = function ( ) { if ( this . pinnedOffset ) return this . pinnedOffset ; this . $element . removeClass ( c . RESET ) . addClass ( "affix" ) ; var a = this . $target . scrollTop ( ) , b = this . $element . offset ( ) ; return this . pinnedOffset = b . top - a } , c . prototype . checkPositionWithEventLoop = function ( ) { setTimeout ( a . proxy ( this . checkPosition , this ) , 1 ) } , c . prototype . checkPosition = function ( ) { if ( this . $element . is ( ":visible" ) ) { var b = this . $element . height ( ) , d = this . options . offset , e = d . top , f = d . bottom , g = Math . max ( a ( document ) . height ( ) , a ( document . body ) . height ( ) ) ; "object" != typeof d && ( f = e = d ) , "function" == typeof e && ( e = d . top ( this . $element ) ) , "function" == typeof f && ( f = d . bottom (
* The MIT License ( MIT )
*
* Copyright ( c ) 2014 - 2016 Patrick Gansterer < paroga @ paroga . com >
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the "Software" ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
* /
( function ( global , undefined ) { "use strict" ;
var POW _2 _24 = 5.960464477539063 e - 8 ,
POW _2 _32 = 4294967296 ,
POW _2 _53 = 9007199254740992 ;
function encode ( value ) {
var data = new ArrayBuffer ( 256 ) ;
var dataView = new DataView ( data ) ;
var lastLength ;
var offset = 0 ;
function prepareWrite ( length ) {
var newByteLength = data . byteLength ;
var requiredLength = offset + length ;
while ( newByteLength < requiredLength )
newByteLength <<= 1 ;
if ( newByteLength !== data . byteLength ) {
var oldDataView = dataView ;
data = new ArrayBuffer ( newByteLength ) ;
dataView = new DataView ( data ) ;
var uint32count = ( offset + 3 ) >> 2 ;
for ( var i = 0 ; i < uint32count ; ++ i )
dataView . setUint32 ( i << 2 , oldDataView . getUint32 ( i << 2 ) ) ;
}
lastLength = length ;
return dataView ;
}
function commitWrite ( ) {
offset += lastLength ;
}
function writeFloat64 ( value ) {
commitWrite ( prepareWrite ( 8 ) . setFloat64 ( offset , value ) ) ;
}
function writeUint8 ( value ) {
commitWrite ( prepareWrite ( 1 ) . setUint8 ( offset , value ) ) ;
}
function writeUint8Array ( value ) {
var dataView = prepareWrite ( value . length ) ;
for ( var i = 0 ; i < value . length ; ++ i )
dataView . setUint8 ( offset + i , value [ i ] ) ;
commitWrite ( ) ;
}
function writeUint16 ( value ) {
commitWrite ( prepareWrite ( 2 ) . setUint16 ( offset , value ) ) ;
}
function writeUint32 ( value ) {
commitWrite ( prepareWrite ( 4 ) . setUint32 ( offset , value ) ) ;
}
function writeUint64 ( value ) {
var low = value % POW _2 _32 ;
var high = ( value - low ) / POW _2 _32 ;
var dataView = prepareWrite ( 8 ) ;
dataView . setUint32 ( offset , high ) ;
dataView . setUint32 ( offset + 4 , low ) ;
commitWrite ( ) ;
}
function writeTypeAndLength ( type , length ) {
if ( length < 24 ) {
writeUint8 ( type << 5 | length ) ;
} else if ( length < 0x100 ) {
writeUint8 ( type << 5 | 24 ) ;
writeUint8 ( length ) ;
} else if ( length < 0x10000 ) {
writeUint8 ( type << 5 | 25 ) ;
writeUint16 ( length ) ;
} else if ( length < 0x100000000 ) {
writeUint8 ( type << 5 | 26 ) ;
writeUint32 ( length ) ;
} else {
writeUint8 ( type << 5 | 27 ) ;
writeUint64 ( length ) ;
}
}
function encodeItem ( value ) {
var i ;
if ( value === false )
return writeUint8 ( 0xf4 ) ;
if ( value === true )
return writeUint8 ( 0xf5 ) ;
if ( value === null )
return writeUint8 ( 0xf6 ) ;
if ( value === undefined )
return writeUint8 ( 0xf7 ) ;
switch ( typeof value ) {
case "number" :
if ( Math . floor ( value ) === value ) {
if ( 0 <= value && value <= POW _2 _53 )
return writeTypeAndLength ( 0 , value ) ;
if ( - POW _2 _53 <= value && value < 0 )
return writeTypeAndLength ( 1 , - ( value + 1 ) ) ;
}
writeUint8 ( 0xfb ) ;
return writeFloat64 ( value ) ;
case "string" :
var utf8data = [ ] ;
for ( i = 0 ; i < value . length ; ++ i ) {
var charCode = value . charCodeAt ( i ) ;
if ( charCode < 0x80 ) {
utf8data . push ( charCode ) ;
} else if ( charCode < 0x800 ) {
utf8data . push ( 0xc0 | charCode >> 6 ) ;
utf8data . push ( 0x80 | charCode & 0x3f ) ;
} else if ( charCode < 0xd800 ) {
utf8data . push ( 0xe0 | charCode >> 12 ) ;
utf8data . push ( 0x80 | ( charCode >> 6 ) & 0x3f ) ;
utf8data . push ( 0x80 | charCode & 0x3f ) ;
} else {
charCode = ( charCode & 0x3ff ) << 10 ;
charCode |= value . charCodeAt ( ++ i ) & 0x3ff ;
charCode += 0x10000 ;
utf8data . push ( 0xf0 | charCode >> 18 ) ;
utf8data . push ( 0x80 | ( charCode >> 12 ) & 0x3f ) ;
utf8data . push ( 0x80 | ( charCode >> 6 ) & 0x3f ) ;
utf8data . push ( 0x80 | charCode & 0x3f ) ;
}
}
writeTypeAndLength ( 3 , utf8data . length ) ;
return writeUint8Array ( utf8data ) ;
default :
var length ;
if ( Array . isArray ( value ) ) {
length = value . length ;
writeTypeAndLength ( 4 , length ) ;
for ( i = 0 ; i < length ; ++ i )
encodeItem ( value [ i ] ) ;
} else if ( value instanceof Uint8Array ) {
writeTypeAndLength ( 2 , value . length ) ;
writeUint8Array ( value ) ;
} else {
var keys = Object . keys ( value ) ;
length = keys . length ;
writeTypeAndLength ( 5 , length ) ;
for ( i = 0 ; i < length ; ++ i ) {
var key = keys [ i ] ;
encodeItem ( key ) ;
encodeItem ( value [ key ] ) ;
}
}
}
}
encodeItem ( value ) ;
if ( "slice" in data )
return data . slice ( 0 , offset ) ;
var ret = new ArrayBuffer ( offset ) ;
var retView = new DataView ( ret ) ;
for ( var i = 0 ; i < offset ; ++ i )
retView . setUint8 ( i , dataView . getUint8 ( i ) ) ;
return ret ;
}
function decode ( data , tagger , simpleValue ) {
// OVMS extension: if data is passed as a string, assume 8 bit stream,
// to allow decoding in browser as simple as encoding in Duktape:
if ( typeof ( data ) == "string" ) {
var u8 = new Uint8Array ( data . length ) ;
for ( var i = 0 ; i < data . length ; i ++ )
u8 [ i ] = data . charCodeAt ( i ) & 0xff ;
data = u8 . buffer ;
}
var dataView = new DataView ( data ) ;
var offset = 0 ;
if ( typeof tagger !== "function" )
tagger = function ( value ) { return value ; } ;
if ( typeof simpleValue !== "function" )
simpleValue = function ( ) { return undefined ; } ;
function commitRead ( length , value ) {
offset += length ;
return value ;
}
function readArrayBuffer ( length ) {
return commitRead ( length , new Uint8Array ( data , offset , length ) ) ;
}
function readFloat16 ( ) {
var tempArrayBuffer = new ArrayBuffer ( 4 ) ;
var tempDataView = new DataView ( tempArrayBuffer ) ;
var value = readUint16 ( ) ;
var sign = value & 0x8000 ;
var exponent = value & 0x7c00 ;
var fraction = value & 0x03ff ;
if ( exponent === 0x7c00 )
exponent = 0xff << 10 ;
else if ( exponent !== 0 )
exponent += ( 127 - 15 ) << 10 ;
else if ( fraction !== 0 )
return ( sign ? - 1 : 1 ) * fraction * POW _2 _24 ;
tempDataView . setUint32 ( 0 , sign << 16 | exponent << 13 | fraction << 13 ) ;
return tempDataView . getFloat32 ( 0 ) ;
}
function readFloat32 ( ) {
return commitRead ( 4 , dataView . getFloat32 ( offset ) ) ;
}
function readFloat64 ( ) {
return commitRead ( 8 , dataView . getFloat64 ( offset ) ) ;
}
function readUint8 ( ) {
return commitRead ( 1 , dataView . getUint8 ( offset ) ) ;
}
function readUint16 ( ) {
return commitRead ( 2 , dataView . getUint16 ( offset ) ) ;
}
function readUint32 ( ) {
return commitRead ( 4 , dataView . getUint32 ( offset ) ) ;
}
function readUint64 ( ) {
return readUint32 ( ) * POW _2 _32 + readUint32 ( ) ;
}
function readBreak ( ) {
if ( dataView . getUint8 ( offset ) !== 0xff )
return false ;
offset += 1 ;
return true ;
}
function readLength ( additionalInformation ) {
if ( additionalInformation < 24 )
return additionalInformation ;
if ( additionalInformation === 24 )
return readUint8 ( ) ;
if ( additionalInformation === 25 )
return readUint16 ( ) ;
if ( additionalInformation === 26 )
return readUint32 ( ) ;
if ( additionalInformation === 27 )
return readUint64 ( ) ;
if ( additionalInformation === 31 )
return - 1 ;
throw "Invalid length encoding" ;
}
function readIndefiniteStringLength ( majorType ) {
var initialByte = readUint8 ( ) ;
if ( initialByte === 0xff )
return - 1 ;
var length = readLength ( initialByte & 0x1f ) ;
if ( length < 0 || ( initialByte >> 5 ) !== majorType )
throw "Invalid indefinite length element" ;
return length ;
}
function appendUtf16Data ( utf16data , length ) {
for ( var i = 0 ; i < length ; ++ i ) {
var value = readUint8 ( ) ;
if ( value & 0x80 ) {
if ( value < 0xe0 ) {
value = ( value & 0x1f ) << 6
| ( readUint8 ( ) & 0x3f ) ;
length -= 1 ;
} else if ( value < 0xf0 ) {
value = ( value & 0x0f ) << 12
| ( readUint8 ( ) & 0x3f ) << 6
| ( readUint8 ( ) & 0x3f ) ;
length -= 2 ;
} else {
value = ( value & 0x0f ) << 18
| ( readUint8 ( ) & 0x3f ) << 12
| ( readUint8 ( ) & 0x3f ) << 6
| ( readUint8 ( ) & 0x3f ) ;
length -= 3 ;
}
}
if ( value < 0x10000 ) {
utf16data . push ( value ) ;
} else {
value -= 0x10000 ;
utf16data . push ( 0xd800 | ( value >> 10 ) ) ;
utf16data . push ( 0xdc00 | ( value & 0x3ff ) ) ;
}
}
}
function decodeItem ( ) {
var initialByte = readUint8 ( ) ;
var majorType = initialByte >> 5 ;
var additionalInformation = initialByte & 0x1f ;
var i ;
var length ;
if ( majorType === 7 ) {
switch ( additionalInformation ) {
case 25 :
return readFloat16 ( ) ;
case 26 :
return readFloat32 ( ) ;
case 27 :
return readFloat64 ( ) ;
}
}
length = readLength ( additionalInformation ) ;
if ( length < 0 && ( majorType < 2 || 6 < majorType ) )
throw "Invalid length" ;
switch ( majorType ) {
case 0 :
return length ;
case 1 :
return - 1 - length ;
case 2 :
if ( length < 0 ) {
var elements = [ ] ;
var fullArrayLength = 0 ;
while ( ( length = readIndefiniteStringLength ( majorType ) ) >= 0 ) {
fullArrayLength += length ;
elements . push ( readArrayBuffer ( length ) ) ;
}
var fullArray = new Uint8Array ( fullArrayLength ) ;
var fullArrayOffset = 0 ;
for ( i = 0 ; i < elements . length ; ++ i ) {
fullArray . set ( elements [ i ] , fullArrayOffset ) ;
fullArrayOffset += elements [ i ] . length ;
}
return fullArray ;
}
return readArrayBuffer ( length ) ;
case 3 :
var utf16data = [ ] ;
if ( length < 0 ) {
while ( ( length = readIndefiniteStringLength ( majorType ) ) >= 0 )
appendUtf16Data ( utf16data , length ) ;
} else
appendUtf16Data ( utf16data , length ) ;
return String . fromCharCode . apply ( null , utf16data ) ;
case 4 :
var retArray ;
if ( length < 0 ) {
retArray = [ ] ;
while ( ! readBreak ( ) )
retArray . push ( decodeItem ( ) ) ;
} else {
retArray = new Array ( length ) ;
for ( i = 0 ; i < length ; ++ i )
retArray [ i ] = decodeItem ( ) ;
}
return retArray ;
case 5 :
var retObject = { } ;
for ( i = 0 ; i < length || length < 0 && ! readBreak ( ) ; ++ i ) {
var key = decodeItem ( ) ;
retObject [ key ] = decodeItem ( ) ;
}
return retObject ;
case 6 :
return tagger ( decodeItem ( ) , length ) ;
case 7 :
switch ( length ) {
case 20 :
return false ;
case 21 :
return true ;
case 22 :
return null ;
case 23 :
return undefined ;
default :
return simpleValue ( length ) ;
}
}
}
var ret = decodeItem ( ) ;
if ( offset !== data . byteLength )
throw "Remaining bytes" ;
return ret ;
}
var obj = { encode : encode , decode : decode } ;
if ( typeof define === "function" && define . amd )
define ( "cbor/cbor" , obj ) ;
else if ( typeof module !== "undefined" && module . exports )
module . exports = obj ;
else if ( ! global . CBOR )
global . CBOR = obj ;
} ) ( this ) ;
/* ovms.js | (c) Michael Balzer | https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3 */
const monthnames = [ 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' ] ;
const supportsTouch = 'ontouchstart' in window || navigator . msMaxTouchPoints ;
if ( window . loggedin == undefined )
window . loggedin = false ;
/ * *
* Utilities
* /
function after ( seconds , fn ) {
return window . setTimeout ( fn , seconds * 1000 ) ;
}
function now ( ) {
return Math . floor ( ( new Date ( ) ) . getTime ( ) / 1000 ) ;
}
function encode _html ( s ) {
return String ( s )
. replace ( /&/g , '&' )
. replace ( /'/g , ''' )
. replace ( /"/g , '"' )
. replace ( /</g , '<' )
. replace ( />/g , '>' ) ;
}
function unwrapLogLine ( s ) {
return String ( s )
. replace ( /(\S)\|+(.)/g , "$1\n……: $2" ) ;
}
function fix _minheight ( $el ) {
if ( $el . css ( "resize" ) != "none" ) return ;
var mh = parseInt ( $el . css ( "max-height" ) ) , h = $el . outerHeight ( ) ;
$el . css ( "min-height" , mh ? Math . min ( h , mh ) : h ) ;
}
function getPathURL ( path ) {
// TODO: use actual http.server config
if ( path . startsWith ( '/sd/' ) )
return path . substr ( 3 ) ;
else
return '' ;
}
/ * *
* Hexadecimal encoding & decoding
* based on : https : //stackoverflow.com/a/54099484 by Aaron Watters
* /
var HEX = {
to _hex _array : [ ] ,
to _byte _map : { } ,
// Init lookup tables:
init : function ( ) {
for ( var ord = 0 ; ord <= 0xff ; ord ++ ) {
var s = ord . toString ( 16 ) ;
if ( s . length < 2 ) {
s = "0" + s ;
}
this . to _hex _array . push ( s ) ;
this . to _byte _map [ s ] = ord ;
}
} ,
// Encode ArrayBuffer to hexadecimal string:
// Usage example: HEX.encode(CBOR.encode({a: 42})) = "a16161182a"
encode : function ( arraybuffer ) {
if ( ! this . to _hex _array . length ) this . init ( ) ;
var buffer = new Uint8Array ( arraybuffer ) ;
var hex _array = [ ] ;
for ( var i = 0 ; i < buffer . length ; i ++ ) {
hex _array . push ( this . to _hex _array [ buffer [ i ] ] ) ;
}
return hex _array . join ( '' ) ;
} ,
// Decode hexadecimal string into ArrayBuffer:
// Usage example: CBOR.decode(HEX.decode("a16161182a")) = {a: 42}
decode : function ( s ) {
if ( ! this . to _hex _array . length ) this . init ( ) ;
var length2 = s . length ;
if ( ( length2 % 2 ) != 0 ) {
console . error ( "HEX.decode: string must have length a multiple of 2" ) ;
return null ;
}
var length = length2 / 2 ;
var buffer = new Uint8Array ( length ) ;
for ( var i = 0 ; i < length ; i ++ ) {
var i2 = i * 2 ;
var b = s . substring ( i2 , i2 + 2 ) ;
buffer [ i ] = this . to _byte _map [ b ] ;
}
return buffer . buffer ;
}
} ;
/ * *
* AJAX Pages & Commands
* /
var page = {
uri : null ,
path : null ,
search : null ,
params : { }
} ;
function setPage ( uri ) {
page . uri = uri ;
var uriparts = uri . split ( "?" ) ;
page . path = uriparts [ 0 ] ;
page . search = uriparts [ 1 ] ;
page . params = { } ;
if ( page . search ) {
page . search . split ( "&" ) . map ( function ( kv ) {
var v = kv . split ( "=" ) ;
page . params [ decodeURIComponent ( v [ 0 ] ) ] = ( v [ 1 ] != null ) ? decodeURIComponent ( v [ 1 ] ) : true ;
} ) ;
}
if ( page . params [ "nm" ] == 1 ) $ ( "body" ) . addClass ( "night" ) ;
else if ( page . params [ "nm" ] == 0 ) $ ( "body" ) . removeClass ( "night" ) ;
}
function updateLocation ( reload ) {
page . search = '' ;
for ( v in page . params )
page . search += "&" + encodeURIComponent ( v ) + "=" + encodeURIComponent ( page . params [ v ] ) ;
page . search = page . search . slice ( 1 ) ;
var hash = "#" + page . path + ( page . search ? "?" + page . search : "" ) ;
if ( ! reload ) $ ( "#main" ) . data ( "uri" , hash . substr ( 1 ) ) ;
location . hash = hash ;
}
function readLocation ( ) {
var uri = location . hash . substr ( 1 ) ;
if ( ! uri . match ( "^/?[a-zA-Z0-9_]" ) )
uri = "/home" ;
return uri ;
}
function loadPage ( uri ) {
if ( typeof uri != "string" )
uri = readLocation ( ) ;
if ( $ ( "#main" ) . data ( "uri" ) != uri ) {
loaduri ( "#main" , "get" , uri , { } ) ;
}
}
function reloadpage ( ) {
var uri = readLocation ( ) ;
loaduri ( "#main" , "get" , uri , { } ) ;
}
function reloadmenu ( ) {
$ ( "#menu" ) . load ( "/menu" ) ;
}
function login ( dsturi ) {
if ( ! dsturi )
dsturi = page . uri || "/home" ;
loadPage ( "/login?uri=" + encodeURIComponent ( dsturi ) ) ;
}
function logout ( ) {
loadPage ( "/logout" ) ;
}
function xhrErrorInfo ( request , textStatus , errorThrown ) {
var txt = "" ;
if ( request . status == 401 || request . status == 403 )
txt = "Unauthorized. <a class=\"btn btn-sm btn-default\" href=\"javascript:login()\">Login</a>" ;
else if ( request . status >= 400 )
txt = "Error " + request . status + " " + request . statusText ;
else if ( textStatus )
txt = "Request " + textStatus + ", please retry" ;
else if ( errorThrown )
txt = errorThrown ;
return txt ;
}
function setcontent ( tgt , uri , text ) {
if ( ! tgt || ! tgt . length ) return ;
tgt . find ( ".receiver" ) . unsubscribe ( ) ;
tgt . chart ( "destroy" ) ;
tgt . table ( "destroy" ) ;
if ( tgt [ 0 ] . id == "main" ) {
$ ( "#nav .dropdown.open .dropdown-toggle" ) . dropdown ( "toggle" ) ;
$ ( "#nav .navbar-collapse" ) . collapse ( "hide" ) ;
$ ( "#nav li" ) . removeClass ( "active" ) ;
var mi = $ ( "#nav [href='" + uri + "']" ) ;
mi . parents ( "li" ) . addClass ( "active" ) ;
if ( tgt [ 0 ] . id == "main" )
window . scrollTo ( 0 , 0 ) ;
else
tgt [ 0 ] . scrollIntoView ( ) ;
tgt . html ( text ) ;
var $p = tgt . find ( ">.panel" ) ;
if ( $p . length == 1 ) $p . addClass ( "panel-single" ) ;
var moduleid = $ ( "title" ) . data ( "moduleid" ) || "OVMS" ;
if ( mi . length > 0 )
document . title = moduleid + " " + ( mi . attr ( "title" ) || mi . text ( ) ) ;
else
document . title = moduleid + " Console" ;
} else {
if ( tgt [ 0 ] . id == "main" )
window . scrollTo ( 0 , 0 ) ;
else
tgt [ 0 ] . scrollIntoView ( ) ;
tgt . html ( text ) ;
}
tgt . find ( ".get-window-resize" ) . trigger ( 'window-resize' ) ;
tgt . find ( ".receiver" ) . subscribe ( ) . trigger ( "msg:metrics" , metrics ) ;
tgt . trigger ( "load" ) ;
}
function loaduri ( target , method , uri , data ) {
var tgt = $ ( target ) , cont ;
if ( tgt . length != 1 )
return false ;
tgt . data ( "uri" , uri ) ;
if ( tgt [ 0 ] . id == "main" ) {
cont = $ ( "html" ) ;
location . hash = "#" + uri ;
setPage ( uri ) ;
} else {
cont = tgt . closest ( ".modal" ) || tgt . closest ( "form" ) || tgt ;
}
$ . ajax ( { "type" : method , "url" : uri , "data" : data ,
"timeout" : 15000 ,
"beforeSend" : function ( ) {
cont . addClass ( "loading" ) ;
} ,
"complete" : function ( ) {
cont . removeClass ( "loading" ) ;
} ,
"success" : function ( response ) {
setcontent ( tgt , uri , response ) ;
} ,
"error" : function ( response , xhrerror , httperror ) {
var text = response . responseText || httperror + "\n" || xhrerror + "\n" ;
if ( text . search ( "alert" ) == - 1 ) {
text = '<div id="alert" class="alert alert-danger alert-dismissable">'
+ '<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>'
+ '<strong>' + text + '</strong>'
+ '</div>' ;
}
setcontent ( tgt , uri , text ) ;
} ,
} ) ;
return true ;
}
$ . fn . loaduri = function ( method , uri , data ) {
return this . each ( function ( ) {
loaduri ( this , method , uri , data ) ;
} ) ;
} ;
function standardTextFilter ( msg ) {
if ( msg . error )
return '<div class="bg-danger">' + msg . error + '</div>' ;
else
return $ ( '<div/>' ) . text ( msg . text ) . html ( ) ;
}
function loadjs ( command , target , filter , timeout ) {
if ( ! command ) return null ;
var args = { } ;
if ( typeof command == "object" ) {
args = command ;
} else {
args . command = command ;
}
args . type = "js" ;
return loadcmd ( args , target , filter , timeout ) ;
}
function loadcmd ( command , target , filter , timeout ) {
var $output , outmode = "" , args = { } ;
if ( ! command ) return null ;
if ( typeof command == "object" ) {
args = command ;
} else {
args . command = command ;
}
if ( typeof filter == "number" ) {
timeout = filter ; filter = null ;
}
if ( typeof target == "function" ) {
filter = target ; target = null ;
}
if ( target == null ) {
$output = $ ( null ) ;
}
else if ( typeof target == "object" ) {
$output = target ;
}
else if ( target . startsWith ( "+" ) ) {
outmode = "+" ;
$output = $ ( target . substr ( 1 ) ) ;
} else {
$output = $ ( target ) ;
}
if ( ! filter )
filter = standardTextFilter ;
if ( ! timeout ) {
if ( args [ "type" ] != "js" && /^(test |ota |copen .* scan)/ . test ( args . command ) )
timeout = 300 ;
else
timeout = 20 ;
}
var lastlen = 0 , xhr , timeouthd ;
var checkabort = function ( ) {
if ( xhr . readyState != 4 )
xhr . abort ( "timeout" ) ;
} ;
var add _output = function ( addhtml ) {
if ( addhtml == null || ! $output . length ) {
outmode = "+" ;
return ;
}
if ( outmode == "" ) { $output . empty ( ) ; outmode = "+" ; }
var autoscroll = ( $output . get ( 0 ) . scrollTop + $output . innerHeight ( ) ) >= $output . get ( 0 ) . scrollHeight ;
$output . append ( addhtml ) ;
$output . closest ( '.get-window-resize' ) . trigger ( 'window-resize' ) ;
if ( autoscroll ) $output . scrollTop ( $output . get ( 0 ) . scrollHeight ) ;
} ;
xhr = $ . ajax ( { "type" : "post" , "url" : "/api/execute" , "data" : args ,
"timeout" : 0 ,
"beforeSend" : function ( ) {
if ( $output . length ) {
$output . addClass ( "loading" ) ;
fix _minheight ( $output ) ;
}
timeouthd = window . setTimeout ( checkabort , timeout * 1000 ) ;
} ,
"complete" : function ( ) {
window . clearTimeout ( timeouthd ) ;
if ( $output . length ) {
$output . removeClass ( "loading" ) ;
fix _minheight ( $output ) ;
}
} ,
"xhrFields" : {
onprogress : function ( ev ) {
var request = ev . currentTarget ;
if ( request . status != 200 ) return ;
var addtext = request . response . substring ( lastlen ) ;
lastlen = request . response . length ;
add _output ( filter ( { "request" : request , "text" : addtext } ) ) ;
window . clearTimeout ( timeouthd ) ;
timeouthd = window . setTimeout ( checkabort , timeout * 1000 ) ;
} ,
} ,
"success" : function ( response , textStatus , request ) {
var addtext = response . substring ( lastlen ) ;
add _output ( filter ( { "request" : request , "text" : addtext } ) ) ;
} ,
"error" : function ( request , textStatus , errorThrown ) {
var cmdinfo = ( args . command . length > 200 ) ? args . command . substr ( 0 , 200 ) + "[…]" : args . command ;
console . log ( "loadcmd '" + cmdinfo + "' ERROR: status=" + textStatus + ", httperror=" + errorThrown ) ;
var txt = xhrErrorInfo ( request , textStatus , errorThrown ) ;
add _output ( filter ( { "request" : request , "error" : txt } ) ) ;
} ,
} ) ;
return xhr ;
}
$ . fn . loadcmd = function ( command , filter , timeout ) {
return this . each ( function ( ) {
loadcmd ( command , $ ( this ) , filter , timeout ) ;
} ) ;
} ;
/ * *
* WebSocket Connection
* /
var monitorTimer , last _monotonic = 0 ;
var ws , ws _inhibit = 0 ;
var metrics = { } ;
2023-01-24 20:50:05 +01:00
var units = { metrics : { } , prefs : { } } ;
mi _to _km = function ( mi ) { return mi * 1.609347 ; }
km _to _mi = function ( km ) { return km * 0.6213700 ; }
pkm _to _pmi = function ( pkm ) { return pkm * 1.609347 ; }
pmi _to _pkm = function ( pmi ) { return pmi * 0.6213700 ; }
no _conversion = function ( value ) { return value ; }
x _to _kx = function ( value ) { return value / 1000 ; }
kx _to _x = function ( value ) { return value * 1000 ; }
const feet _per _mile = 5280 ;
var unit _conversions = {
"native" : no _conversion ,
"km>miles" : km _to _mi ,
"km>meters" : kx _to _x ,
"km>feet" : function ( value ) { return km _to _mi ( value ) * feet _per _mile ; } ,
"miles>km" : mi _to _km ,
"miles>meters" : function ( value ) { return ( mi _to _km ( value ) * 1000 ) ; } ,
"miles>feet" : function ( value ) { return value * feet _per _mile ; } ,
"meters>miles" : function ( value ) { return km _to _mi ( value / 1000 ) ; } ,
"meters>km" : x _to _kx ,
"meters>feet" : function ( value ) { return km _to _mi ( value / 1000 ) * feet _per _mile ; } ,
"feet>km" : function ( value ) { return mi _to _km ( value / feet _per _mile ) ; } ,
"feet>meters" : function ( value ) { return ( mi _to _km ( value / feet _per _mile ) * 1000 ) ; } ,
"feet>miles" : function ( value ) { return value / feet _per _mile ; } ,
"kmphps>miphps" : km _to _mi ,
"kmphps>mpss" : function ( value ) { return value / 3.6 ; } ,
"kmphps>ftpss" : function ( value ) { return km _to _mi ( value ) * feet _per _mile / 3600 ; } ,
"miphps>kmphps" : mi _to _km ,
"miphps>mpss" : function ( value ) { return mi _to _km ( value ) / 3.6 ; } ,
"miphps>ftpss" : function ( value ) { return value * feet _per _mile / 3600 ; } ,
"mpss>kmphps" : function ( value ) { return ( value * 3.6 ) ; } ,
"mpss>miphps" : function ( value ) { return km _to _mi ( value ) * 3.6 ; } ,
"mpss>ftpss" : function ( value ) { return km _to _mi ( value ) * feet _per _mile ; } ,
"ftpss>kmphps" : function ( value ) { return ( mi _to _km ( value / feet _per _mile ) * 3.6 ) ; } ,
"ftpss>miphps" : function ( value ) { return value * 3600 / feet _per _mile ; } ,
"ftpss>mpss" : function ( value ) { return mi _to _km ( value / feet _per _mile ) * 1000 ; } ,
"kw>watts" : kx _to _x ,
"watts>kw" : x _to _kx ,
"kwh>watthours" : kx _to _x ,
"watthours>kwh" : x _to _kx ,
"whpkm>whpmi" : pkm _to _pmi ,
"whpkm>kwhp100km" : function ( value ) { return value / 10 ; } ,
"whpkm>kmpkwh" : function ( value ) { return value ? 1000.0 / value : 0 ; } ,
"whpkm>mipkwh" : function ( value ) { return value ? ( km _to _mi ( 1000.0 / value ) ) : 0 ; } ,
"whpmi>whpkm" : pmi _to _pkm ,
"whpmi>kwhp100km" : function ( value ) { return pmi _to _pkm ( value ) / 10 ; } ,
"whpmi>kmpkwh" : function ( value ) { return value ? ( mi _to _km ( 1000.0 / value ) ) : 0 ; } ,
"whpmi>mipkwh" : function ( value ) { return value ? ( 1000.0 / value ) : 0 ; } ,
"kwhp100km>whpmi" : function ( value ) { return pkm _to _pmi ( value * 10 ) ; } ,
"kwhp100km>whpkm" : function ( value ) { return value * 10 ; } ,
"kwhp100km>kmpkwh" : function ( value ) { return value ? ( 100.0 / value ) : 0 ; } ,
"kwhp100km>mipkwh" : function ( value ) { return value ? km _to _mi ( 100.0 / value ) : 0 ; } ,
"kmpkwh>whpmi" : function ( value ) { return value ? ( 1000.0 / km _to _mi ( value ) ) : 0 ; } ,
"kmpkwh>whpkm" : function ( value ) { return value ? ( 1 / ( 1000.0 * value ) ) : 0 ; } ,
"kmpkwh>kwhp100km" : function ( value ) { return value ? ( 100.0 / value ) : 0 ; } ,
"kmpkwh>mipkwh" : km _to _mi ,
"mipkwh>whpmi" : function ( value ) { return value ? 1000 / value : 0 ; } ,
"mipkwh>whpkm" : function ( value ) { return value ? ( 1000 / mi _to _km ( value ) ) : 0 ; } ,
"mipkwh>kwhp100km" : function ( value ) { return value ? ( 100.0 / mi _to _km ( value ) ) : 0 ; } ,
"mipkwh>kmpkwh" : mi _to _km ,
"celcius>fahrenheit" : function ( value ) { return ( ( value * 9 ) / 5 ) + 32 ; } ,
"fahrenheit>celcius" : function ( value ) { return ( ( value - 32 ) * 5 ) / 9 ; } ,
"kpa>pa" : kx _to _x ,
"kpa>bar" : function ( value ) { return value / 100 ; } ,
"kpa>psi" : function ( value ) { return value * 0.14503773773020923 ; } ,
"pa>kpa" : x _to _kx ,
"pa>bar" : function ( value ) { return value / 100000 ; } ,
"pa>psi" : function ( value ) { return value * 0.00014503773773020923 ; } ,
"psi>kpa" : function ( value ) { return value * 6.894757293168361 ; } ,
"psi>pa" : function ( value ) { return value * 6894.757293168361 ; } ,
"psi>bar" : function ( value ) { return value * 0.06894757293168361 ; } ,
"bar>pa" : function ( value ) { return value * 100000 ; } ,
"bar>kpa" : function ( value ) { return value * 100 ; } ,
"bar>psi" : function ( value ) { return value * 14.503773773020923 ; } ,
"seconds>minutes" : function ( value ) { return value / 60 ; } ,
"seconds>hours" : function ( value ) { return value / 3600 ; } ,
"minutes>seconds" : function ( value ) { return value * 60 ; } ,
"minutes>hours" : function ( value ) { return value / 60 ; } ,
"hours>seconds" : function ( value ) { return value * 3600 ; } ,
"hours>minutes" : function ( value ) { return value * 60 ; } ,
"kmph>miph" : km _to _mi ,
"miph>kmph" : mi _to _km ,
"dbm>sq" : function ( value ) { return Math . round ( ( value <= - 51 ) ? ( ( value + 113 ) / 2 ) : 0 ) ; } ,
"sq>dbm" : function ( value ) { return Math . round ( ( value <= 31 ) ? ( - 113 + ( value * 2 ) ) : 0 ) ; } ,
"percent>permille" : function ( value ) { return value * 10.0 ; } ,
"permille>percent" : function ( value ) { return value * 0.10 ; }
}
convertUnitFunction = function ( from , to ) {
return unit _conversions [ from + ">" + to ] || no _conversion ;
}
convertUnits = function ( from , to , value ) {
return convertUnitFunction ( from , to ) ( value ) ;
}
units . convertMetricToUserUnits = function ( value , name ) {
if ( value == undefined )
return value
var unit _entry = this . metrics [ name ] ;
if ( unit _entry == undefined )
return value
var cnvfn = unit _entry . user _fn ;
if ( cnvfn == undefined ) {
cnvfn = convertUnitFunction ( unit _entry . native , unit _entry . code ) ;
this . metrics [ name ] . user _fn = cnvfn ;
}
return cnvfn ( value ) ;
}
units . userUnitLabelFromMetric = function ( name ) {
var unit _entry = this . metrics [ name ] ;
if ( unit _entry == undefined )
return "" ;
return unit _entry . label ;
}
units . unitLabelToUser = function ( unitType , defaultLabel ) {
var res = this . prefs [ unitType ] ;
return ( res && res . label ) ? res . label : defaultLabel
}
units . unitValueToUser = function ( unitType , value ) {
var entry = this . prefs [ unitType ] ;
if ( ! entry )
return value ;
return convertUnits ( value , unitType , entry . unit ) ;
}
// Works for units and metrics collection.
metricsProxyHas = function ( target , name ) {
return target [ name ] != undefined
}
var metrics _all = new Proxy ( metrics , {
get : function ( target , name ) {
if ( name == Symbol . toStringTag )
return 'metrics_all[]' ;
if ( ! ( typeof name === "string" || name instanceof String ) )
return undefined ;
var names = name . split ( '#' , 2 ) ;
var name = names [ 0 ] ;
var value _type = names [ 1 ]
if ( value _type === "unit" )
return units . userUnitLabelFromMetric ( name ) ;
var value = target [ name ] ;
if ( value _type === "label" )
value = units . convertMetricToUserUnits ( value , name )
return value ;
} ,
has : metricsProxyHas
} ) ;
var metrics _user = new Proxy ( metrics , {
get :
function ( target , name ) {
if ( name == Symbol . toStringTag )
return 'metrics_user[]' ;
return units . convertMetricToUserUnits ( target [ name ] , name )
} ,
has : metricsProxyHas
} ) ;
var metrics _label = new Proxy ( units . metrics , {
get :
function ( target , name ) {
if ( name == Symbol . toStringTag )
return 'metrics_label[]' ;
var unit _entry = target [ name ] ;
if ( unit _entry == undefined ) {
return "" ;
}
return unit _entry . label ;
} ,
has : metricsProxyHas
} ) ;
2022-04-06 00:04:46 +02:00
var shellhist = [ "" ] , shellhpos = 0 ;
var loghist = [ ] ;
const loghist _maxsize = 100 ;
function initSocketConnection ( ) {
if ( location . protocol == "https:" ) {
ws = new WebSocket ( 'wss://' + location . host + '/msg' ) ;
} else {
ws = new WebSocket ( 'ws://' + location . host + '/msg' ) ;
}
ws . onopen = function ( ev ) {
console . log ( "WebSocket OPENED" , ev ) ;
$ ( ".receiver" ) . subscribe ( ) ;
2023-01-24 20:50:05 +01:00
subscribeToTopic ( "units/#" ) ;
2022-04-06 00:04:46 +02:00
} ;
ws . onerror = function ( ev ) { console . log ( "WebSocket ERROR" , ev ) ; } ;
ws . onclose = function ( ev ) { console . log ( "WebSocket CLOSED" , ev ) ; } ;
ws . onmessage = function ( ev ) {
var msg ;
try {
msg = JSON . parse ( ev . data ) ;
} catch ( e ) {
console . error ( "WebSocket msg: " + e + ": " + ev . data ) ;
return ;
}
for ( msgtype in msg ) {
if ( msgtype == "event" ) {
$ ( ".receiver" ) . trigger ( "msg:event" , msg . event ) ;
$ ( ".monitor[data-events]" ) . each ( function ( ) {
var cmd = $ ( this ) . data ( "updcmd" ) ;
var js = $ ( this ) . data ( "updjs" ) ;
var evf = $ ( this ) . data ( "events" ) ;
if ( ( cmd || js ) && evf && msg . event . match ( evf ) ) {
$ ( this ) . data ( "updlast" , now ( ) ) ;
loadcmd ( { command : js ? js : cmd , type : js ? "js" : "cmd" } , $ ( this ) ) ;
}
} ) ;
}
else if ( msgtype == "metrics" ) {
$ . extend ( metrics , msg . metrics ) ;
$ ( ".receiver" ) . trigger ( "msg:metrics" , msg . metrics ) ;
}
2023-01-24 20:50:05 +01:00
else if ( msgtype == "units" ) {
for ( var subtype in msg . units ) {
if ( subtype == "metrics" ) {
$ . extend ( units . metrics , msg . units . metrics ) ;
$ ( ".receiver" ) . trigger ( "msg:units:metrics" , msg . units . metrics ) ;
var msgmetrics = { } ;
for ( metricname in msg . units . metrics )
msgmetrics [ metricname ] = metrics [ metricname ] ;
$ ( ".receiver" ) . trigger ( "msg:metrics" , msgmetrics ) ;
} else if ( subtype == "prefs" ) {
$ . extend ( units . prefs , msg . units . prefs ) ;
$ ( ".receiver" ) . trigger ( "msg:units:prefs" , msg . units . prefs ) ;
}
}
}
2022-04-06 00:04:46 +02:00
else if ( msgtype == "notify" ) {
processNotification ( msg . notify ) ;
$ ( ".receiver" ) . trigger ( "msg:notify" , msg . notify ) ;
}
else if ( msgtype == "log" ) {
loghist . push ( msg . log ) ;
if ( loghist . length > loghist _maxsize ) loghist . shift ( ) ;
$ ( ".receiver" ) . trigger ( "msg:log" , msg . log ) ;
}
}
} ;
}
function monitorInit ( force ) {
$ ( ".monitor" ) . each ( function ( ) {
var cmd = $ ( this ) . data ( "updcmd" ) ;
var js = $ ( this ) . data ( "updjs" ) ;
var txt = $ ( this ) . text ( ) ;
if ( ( cmd || js ) && ( force || ! txt ) ) {
$ ( this ) . data ( "updlast" , now ( ) ) ;
loadcmd ( { command : js ? js : cmd , type : js ? "js" : "cmd" } , $ ( this ) ) ;
}
} ) ;
}
function monitorUpdate ( ) {
if ( ! ws || ws . readyState == ws . CLOSED ) {
if ( ws _inhibit != 0 )
-- ws _inhibit ;
if ( ws _inhibit == 0 )
initSocketConnection ( ) ;
}
var new _monotonic = parseInt ( metrics [ "m.monotonic" ] ) || 0 ;
if ( new _monotonic < last _monotonic )
location . reload ( ) ;
else
last _monotonic = new _monotonic ;
$ ( ".monitor" ) . each ( function ( ) {
var cnt = $ ( this ) . data ( "updcnt" ) ;
var int = $ ( this ) . data ( "updint" ) ;
var last = $ ( this ) . data ( "updlast" ) ;
var cmd = $ ( this ) . data ( "updcmd" ) ;
var js = $ ( this ) . data ( "updjs" ) ;
if ( ! cnt || ( ! cmd && ! js ) || ( now ( ) - last ) < int )
return ;
$ ( this ) . data ( "updcnt" , cnt - 1 ) ;
$ ( this ) . data ( "updlast" , now ( ) ) ;
loadcmd ( { command : js ? js : cmd , type : js ? "js" : "cmd" } , $ ( this ) ) ;
} ) ;
}
function processNotification ( msg ) {
var opts = { timeout : 0 } ;
if ( msg . type == "info" ) {
opts . title = '<span class="lead text-info"><i>ⓘ</i> ' + msg . subtype + ' Info</span>' ;
opts . timeout = 60 ;
}
else if ( msg . type == "alert" ) {
opts . title = '<span class="lead text-danger"><i>⚠</i> ' + msg . subtype + ' Alert</span>' ;
}
else if ( msg . type == "error" ) {
opts . title = '<span class="lead text-warning"><i>⛍</i> ' + msg . subtype + ' Error</span>' ;
}
else
return ;
opts . body = '<pre>' + msg . value + '</pre>' ;
confirmdialog ( opts . title , opts . body , [ "OK" ] , opts . timeout ) ;
}
2023-01-24 20:50:05 +01:00
subscribeToTopic = function ( topic ) {
try {
console . debug ( "subscribe " + topic ) ;
if ( ws ) ws . send ( "subscribe " + topic ) ;
} catch ( e ) {
console . log ( e ) ;
}
}
2022-04-06 00:04:46 +02:00
$ . fn . subscribe = function ( topics ) {
return this . each ( function ( ) {
var subscriptions = $ ( this ) . data ( "subscriptions" ) ;
if ( ! topics ) {
// init from data attr:
topics = subscriptions ;
subscriptions = "" ;
}
var subs = subscriptions ? subscriptions . split ( ' ' ) : [ ] ;
var tops = topics ? topics . split ( ' ' ) : [ ] ;
for ( var i = 0 ; i < tops . length ; i ++ ) {
if ( tops [ i ] && ! subs . includes ( tops [ i ] ) ) {
2023-01-24 20:50:05 +01:00
subscribeToTopic ( tops [ i ] ) ;
2022-04-06 00:04:46 +02:00
}
}
$ ( this ) . data ( "subscriptions" , subs . join ( ' ' ) ) ;
} ) ;
} ;
$ . fn . unsubscribe = function ( topics ) {
return this . each ( function ( ) {
var subscriptions = $ ( this ) . data ( "subscriptions" ) ;
if ( ! topics ) {
// cleanup:
topics = subscriptions ;
}
var subs = subscriptions ? subscriptions . split ( ' ' ) : [ ] ;
var tops = topics ? topics . split ( ' ' ) : [ ] ;
var i , j ;
for ( i = 0 ; i < tops . length ; i ++ ) {
if ( tops [ i ] && ( j = subs . indexOf ( tops [ i ] ) ) >= 0 ) {
try {
console . log ( "unsubscribe " + tops [ i ] ) ;
if ( ws ) ws . send ( "unsubscribe " + tops [ i ] ) ;
subs . splice ( j , 1 ) ;
} catch ( e ) {
console . log ( e ) ;
}
}
}
$ ( this ) . data ( "subscriptions" , subs . join ( ' ' ) ) ;
} ) ;
} ;
$ . fn . reconnectTicker = function ( msg ) {
$ ( "html" ) . addClass ( "loading disabled" ) ;
ws _inhibit = 10 ;
if ( ws ) ws . close ( ) ;
window . setInterval ( function ( ) {
if ( ws && ws . readyState == ws . OPEN )
location . reload ( ) ;
else
$ ( "#reconnectTickerDots" ) . append ( "•" ) ;
} , 1000 ) ;
return this . append (
( msg ? msg : '<p class="lead">Rebooting now…</p>' ) +
'<p>The window will automatically reload when the browser reconnects to the module.</p>' +
'<p id="reconnectTickerDots">•</p>' ) ;
} ;
/ * *
* UI Widgets
* /
// Plugin Maker
// credits: https://www.bitovi.com/blog/writing-the-perfect-jquery-plugin
$ . pluginMaker = function ( plugin ) {
$ . fn [ plugin . prototype . cname ] = function ( options ) {
var args = $ . makeArray ( arguments ) , after = args . slice ( 1 ) ;
var ismethod = ( typeof options == "string" ) ;
var result ;
this . each ( function ( ) {
// see if we have an instance
var instance = $ . data ( this , plugin . prototype . cname ) ;
if ( instance ) {
if ( ismethod ) {
// call a method on the instance
if ( $ . isFunction ( instance [ options ] ) )
result = instance [ options ] . apply ( instance , after ) ;
else
throw "UndefinedMethod: " + plugin . prototype . cname + "." + options ;
} else if ( instance . update ) {
// call update on the instance
instance . update . apply ( instance , args ) ;
}
} else {
// create the plugin
new plugin ( this , options ) ;
}
} ) ;
return ( ismethod && result != undefined ) ? result : this ;
} ;
} ;
// OVMS namespace:
var ovms = { uid : 0 } ;
// Widget root class:
ovms . Widget = function ( el , options ) {
if ( el ) this . init ( el , options ) ;
}
$ . extend ( ovms . Widget . prototype , {
cname : "widget" ,
options : { } ,
init : function ( el , options ) {
this . uid = ++ ovms . uid ;
this . $el = $ ( el ) ;
this . $el . data ( this . cname , this ) ;
this . $el . addClass ( this . cname ) ;
var dataoptions = this . $el . data ( "options" ) ;
if ( dataoptions != null && typeof dataoptions != "object" ) {
console . error ( "Invalid JSON syntax: " + this . $el . data ( "options" ) ) ;
dataoptions = null ;
}
this . options = $ . extend ( { } , this . options , dataoptions , options ) ;
} ,
update : function ( options ) {
$ . extend ( this . options , options ) ;
} ,
} ) ;
// Dialog:
ovms . Dialog = function ( el , options ) {
if ( el ) this . init ( el , options ) ;
}
$ . extend ( ovms . Dialog . prototype , ovms . Widget . prototype , {
cname : "dialog" ,
options : {
title : '' ,
body : '' ,
show : false ,
remote : false ,
backdrop : true ,
keyboard : true ,
transition : 'fade' ,
size : '' ,
contentClass : '' ,
onShow : null ,
onHide : null ,
onShown : null ,
onHidden : null ,
onUpdate : null ,
buttons : [ { } ] ,
timeout : 0 ,
input : null ,
} ,
init : function ( el , options ) {
if ( $ ( el ) . parent ( ) . length == 0 ) {
options = $ . extend ( options , { show : true , isDynamic : true } ) ;
}
ovms . Widget . prototype . init . call ( this , el , options ) ;
this . input = options . input ? options . input : { } ;
this . data = { showing : false } ;
this . $buttons = [ ] ;
// convert element to modal if not predefined by user:
if ( this . $el . children ( ) . length == 0 ) {
this . $el . html ( '<div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button><h4 class="modal-title"></h4></div><div class="modal-body"></div><div class="modal-footer"></div></div></div></div>' ) ;
this . $el . addClass ( "modal" ) ;
}
this . $el . on ( 'show.bs.modal' , $ . proxy ( this . onShow , this ) ) ;
this . $el . on ( 'hide.bs.modal' , $ . proxy ( this . onHide , this ) ) ;
this . $el . on ( 'shown.bs.modal' , $ . proxy ( this . onShown , this ) ) ;
this . $el . on ( 'hidden.bs.modal' , $ . proxy ( this . onHidden , this ) ) ;
if ( this . options . isDynamic ) this . $el . appendTo ( 'body' ) ;
this . update ( this . options ) ;
} ,
update : function ( options ) {
$ . extend ( this . options , options ) ;
// configure modal:
this . $el . removeClass ( 'fade' ) . addClass ( this . options . transition ) ;
this . $el . find ( '.modal-dialog' ) . attr ( "class" , "modal-dialog " + ( this . options . size ? ( "modal-" + this . options . size ) : "" ) ) ;
this . $el . find ( '.modal-content' ) . attr ( "class" , "modal-content " + ( this . options . contentClass || "" ) ) ;
if ( options . title != null )
this . $el . find ( '.modal-title' ) . html ( options . title ) ;
if ( options . body != null )
this . $el . find ( '.modal-body' ) . html ( options . body ) ;
if ( options . buttons != null ) {
var footer = this . $el . find ( '.modal-footer' ) ;
footer . empty ( ) ;
this . $buttons = [ ] ;
for ( var i = 0 ; i < this . options . buttons . length ; i ++ ) {
var btn = $ . extend ( { label : 'Close' , btnClass : 'default' , autoHide : true , action : null } , this . options . buttons [ i ] , { index : i } ) ;
this . options . buttons [ i ] = btn ;
this . $buttons [ i ] = $ ( '<button type="button" class="btn btn-' + btn . btnClass + '" value="' + btn . index + '">' + btn . label + '</button>' )
. appendTo ( footer ) . on ( 'click' , $ . proxy ( this . onClick , this , btn ) ) ;
}
}
if ( this . options . onUpdate )
this . options . onUpdate . call ( this . $el , this . input ) ;
this . $el . modal ( this . options ) ;
} ,
show : function ( options ) {
if ( options ) this . update ( options ) ;
this . $el . modal ( 'show' ) ;
} ,
hide : function ( ) {
this . $el . modal ( 'hide' ) ;
} ,
onShow : function ( ) {
if ( this . options . onShow )
this . options . onShow . call ( this . $el , this . input ) ;
} ,
onHide : function ( ) {
if ( this . options . onHide )
this . options . onHide . call ( this . $el , this . input ) ;
} ,
onShown : function ( ) {
this . data . showing = true ;
this . input . button = null ;
if ( ! supportsTouch ) this . $el . find ( '.form-control, .btn' ) . first ( ) . focus ( ) ;
if ( this . options . onShown )
this . options . onShown . call ( this . $el , this . input ) ;
if ( this . options . timeout )
after ( this . options . timeout , $ . proxy ( this . hide , this ) ) ;
} ,
onHidden : function ( ) {
this . data . showing = false ;
if ( this . options . isDynamic )
this . $el . detach ( ) ;
if ( this . options . onHidden )
this . options . onHidden . call ( this . $el , this . input ) ;
if ( this . input . button != null && this . input . button . action ) {
this . input . button . action . call ( this . $el , this . input ) ;
}
} ,
isShowing : function ( ) {
return this . data . showing ;
} ,
triggerButton : function ( button ) {
var btn ;
if ( typeof button == "number" ) {
btn = this . $buttons [ button ] ;
}
else if ( typeof button == "string" ) {
for ( var i = 0 ; i < this . options . buttons . length ; i ++ ) {
if ( this . options . buttons [ i ] . label == button ) {
btn = this . $buttons [ i ] ;
break ;
}
}
}
if ( btn && ! btn . prop ( "disabled" ) ) {
btn . trigger ( 'click' ) ;
return true ;
}
return false ;
} ,
onClick : function ( button ) {
this . input . button = button ;
if ( button . autoHide )
this . hide ( ) ;
else if ( button . action ) {
button . action . call ( this . $el , this . input ) ;
this . input . button = null ;
}
} ,
} ) ;
$ . pluginMaker ( ovms . Dialog ) ;
// Dialog utility wrappers:
$ . fn . confirmdialog = function ( _title , _body , _buttons , _action , _timeout ) {
if ( typeof _action == "number" ) { _timeout = _action ; _action = null ; }
var options = { show : true , title : _title , body : _body , buttons : [ ] , timeout : _timeout } ;
if ( _buttons ) {
for ( var i = 0 ; i < _buttons . length ; i ++ ) {
options . buttons . push ( { label : _buttons [ i ] ,
btnClass : ( _buttons . length <= 2 && i == _buttons . length - 1 ) ? "primary" : "default" } ) ;
}
}
if ( _action ) {
options . onHidden = function ( input ) { _action ( input . button ? input . button . index : null ) ; } ;
}
return this . dialog ( options ) ;
} ;
var confirmdialog = function ( ) { return $ . fn . confirmdialog . apply ( $ ( '<div />' ) , arguments ) ; } ;
$ . fn . promptdialog = function ( _type , _title , _prompt , _buttons , _action ) {
var options = { show : true , title : _title , buttons : [ ] } ;
var uid = ++ ovms . uid ;
options . body = '<label for="prompt' + uid + '">' + _prompt + '</label><input id="prompt' + uid + '" type="' + _type + '" class="form-control">' ;
if ( _buttons ) {
for ( var i = 0 ; i < _buttons . length ; i ++ ) {
options . buttons . push ( { label : _buttons [ i ] ,
btnClass : ( _buttons . length <= 2 && i == _buttons . length - 1 ) ? "primary" : "default" } ) ;
}
}
options . onUpdate = function ( input ) {
var footer = $ ( this ) . find ( '.modal-footer' ) ;
$ ( this ) . find ( 'input' ) . val ( input . text ) . on ( 'keydown' , function ( ev ) {
if ( ev . which == 13 ) footer . find ( '.btn-primary' ) . trigger ( 'click' ) ;
} ) ;
} ;
options . onShown = function ( input ) {
$ ( this ) . find ( 'input' ) . first ( ) . focus ( ) ;
}
options . onHidden = function ( input ) {
if ( input . button ) input . text = $ ( this ) . find ( 'input' ) . val ( ) ;
if ( _action ) _action ( input . button ? input . button . index : null , input . text ) ;
} ;
return this . dialog ( options ) ;
} ;
var promptdialog = function ( ) { return $ . fn . promptdialog . apply ( $ ( '<div />' ) , arguments ) ; } ;
// FileBrowser:
ovms . FileBrowser = function ( el , options ) {
if ( el ) this . init ( el , options ) ;
}
$ . extend ( ovms . FileBrowser . prototype , ovms . Widget . prototype , {
cname : "filebrowser" ,
options : {
path : '' ,
quicknav : [ '/sd/' , '/store/' ] ,
filter : null ,
sortBy : null ,
sortDir : 1 ,
onUpdate : null ,
onPathChange : null ,
onAction : null ,
input : null ,
} ,
init : function ( el , options ) {
ovms . Widget . prototype . init . call ( this , el , options ) ;
this . input = options . input ? options . input : { } ;
$ . extend ( this . input , {
path : '' ,
dir : '' ,
file : '' ,
noload : false ,
} ) ;
this . data = {
lastpath : '' ,
lastdir : '' ,
listdir : '' ,
listsel : '' ,
} ;
this . xhr = null ;
if ( this . $el . children ( ) . length == 0 ) {
this . $el . html ( '<div class="fb-pathbox form-group"><label class="control-label" for="input-path-${uid}">Path (trailing slash = dir):</label><div class="input-group"><input type="text" class="form-control font-monospace" name="path" id="input-path-${uid}" value=""><div class="input-group-btn"><button type="button" class="btn btn-default fb-path-stop" disabled title="Stop">×</button><button type="button" class="btn btn-default fb-path-reload" title="Reload">⟲</button><button type="button" class="btn btn-default fb-path-up" title="Up">↰</button></div></div></div><div class="fb-quicknav form-group"/><div class="fb-files"><table class="table table-condensed table-hover table-scrollable font-monospace get-window-resize"><thead><tr><th class="col-xs-3 col-sm-2" data-key="size">Size</th><th class="hidden-xs col-sm-4" data-key="date">Date</th><th class="col-xs-9 col-sm-6" data-key="name">Name</th></tr></thead><tbody/></table></div>' . replace ( /\$\{uid\}/g , this . uid ) ) ;
}
this . $pathinput = this . $el . find ( "input[name=path]" ) ;
this . $pathinput . on ( 'change' , $ . proxy ( this . setPath , this ) ) . on ( 'keydown' , $ . proxy ( function ( ev ) {
if ( ev . which == 13 ) {
var lastdir = this . input . dir ;
this . setPath ( ) ;
if ( this . input . file || this . input . dir == lastdir )
this . onAction ( ) ;
ev . preventDefault ( ) ;
}
else if ( ev . which == 27 ) {
this . stopLoad ( ) ;
}
} , this ) ) ;
this . $el . find ( '.fb-path-up' ) . on ( 'click' , $ . proxy ( function ( ev ) {
var parent = this . input . dir . replace ( /\/[^/]+$/ , '' ) ;
if ( parent ) this . setPath ( parent + '/' ) ;
else this . setPath ( this . input . dir + '/' ) ;
} , this ) ) ;
this . $el . find ( '.fb-path-reload' ) . on ( 'click' , $ . proxy ( function ( ev ) {
this . setPath ( this . input . path , true ) ;
} , this ) ) ;
this . $btnstop = this . $el . find ( '.fb-path-stop' ) ;
this . $btnstop . on ( 'click' , $ . proxy ( function ( ev ) { this . stopLoad ( ) ; } , this ) ) ;
this . $quicknav = this . $el . find ( ".fb-quicknav" ) ;
this . $filetable = this . $el . find ( ".fb-files>table" ) ;
this . $filecols = this . $filetable . find ( "thead th" ) ;
this . $filecols . on ( 'click' , $ . proxy ( function ( ev ) {
var by = $ ( ev . currentTarget ) . data ( "key" ) ;
var dir = $ ( ev . currentTarget ) . hasClass ( 'sort-down' ) ? - 1 : 1 ;
if ( by == this . options . sortBy ) dir = - dir ;
this . sortList ( by , dir ) ;
if ( ! supportsTouch ) this . $pathinput . focus ( ) ;
} , this ) ) ;
this . $filebody = this . $filetable . find ( "tbody" ) ;
this . $filebody . on ( 'click' , 'tr' , $ . proxy ( function ( ev ) {
this . data . listsel = $ ( ev . currentTarget ) . data ( "name" ) ;
this . setPath ( this . data . listdir + "/" + this . data . listsel ) ;
ev . preventDefault ( ) ;
} , this ) ) . on ( 'dblclick' , 'tr' , $ . proxy ( this . onAction , this ) ) ;
this . update ( this . options ) ;
} ,
update : function ( options ) {
$ . extend ( this . options , options ) ;
this . $quicknav . empty ( ) ;
for ( var i = 0 ; i < this . options . quicknav . length ; i ++ ) {
this . $quicknav . append ( '<button type="button" class="btn btn-sm btn-default"><code>' + this . options . quicknav [ i ] + '</code></button>' ) ;
}
this . $quicknav . find ( "button" ) . on ( 'click' , $ . proxy ( function ( ev ) {
this . setPath ( $ ( ev . delegateTarget ) . text ( ) ) ;
} , this ) ) ;
if ( this . options . onUpdate )
this . options . onUpdate . call ( this . $el , this . input ) ;
this . sortList ( this . options . sortBy , this . options . sortDir ) ;
if ( options . path !== undefined ) {
this . setPath ( options . path ) ;
}
else if ( options . filter !== undefined ) {
this . loadDir ( ) ;
}
} ,
getInput : function ( ) {
return this . input ;
} ,
setPath : function ( newpath , reload ) {
if ( typeof newpath == "string" ) {
this . input . path = newpath ;
this . $pathinput . val ( newpath ) ;
} else {
this . input . path = this . $pathinput . val ( ) ;
}
this . input . dir = this . input . path . replace ( /\/[^/]*$/ , '' ) ;
this . input . file = this . input . path . substr ( this . input . dir . length + 1 ) ;
var fn = "" ;
if ( this . input . dir == this . data . listdir )
fn = this . input . path . substr ( this . data . listdir . length + 1 ) ;
this . $filebody . children ( ) . each ( function ( ) {
if ( $ ( this ) . data ( "name" ) === fn ) $ ( this ) . addClass ( "active" ) ;
else $ ( this ) . removeClass ( "active" ) ;
} ) ;
if ( reload )
this . data . lastdir = '' ;
if ( this . input . path != this . data . lastpath ) {
this . input . noload = false ;
if ( this . options . onPathChange )
this . options . onPathChange . call ( this . $el , this . input ) ;
this . data . lastpath = this . input . path ;
}
if ( this . input . dir != this . data . lastdir ) {
if ( this . input . noload ) {
this . stopLoad ( ) ;
this . data . lastdir = '' ;
this . $filebody . empty ( ) ;
this . data . listdir = '' ;
this . data . listsel = '' ;
} else {
this . loadDir ( ) ;
this . data . lastdir = this . input . dir ;
}
}
if ( ! supportsTouch ) this . $pathinput . focus ( ) ;
} ,
loadDir : function ( ) {
var self = this ;
var fbuf = "" ;
this . stopLoad ( ) ;
this . $filebody . empty ( ) ;
this . data . listdir = this . input . dir ;
this . data . listsel = '' ;
if ( ! this . data . listdir )
return ;
this . $btnstop . prop ( 'disabled' , false ) ;
this . xhr = loadcmd ( "vfs ls '" + this . input . dir + "'" , this . $filebody , function ( msg ) {
if ( msg . error ) {
if ( msg . error . startsWith ( "Request abort" ) )
return '<div class="bg-info">Stopped.</div>' ;
else
return '<div class="bg-danger">' + msg . error + '</div>' ;
}
fbuf += msg . text ;
var lines = fbuf . split ( "\n" ) ;
if ( ! lines || lines . length < 2 ) return "" ;
fbuf = lines [ lines . length - 1 ] ;
var res = '' , f = { } ;
for ( var i = 0 ; i < lines . length - 1 ; i ++ ) {
if ( ! lines [ i ] ) continue ;
if ( lines [ i ] . startsWith ( "Error" ) )
return '<div class="bg-danger">' + lines [ i ] + '</div>' ;
f . size = lines [ i ] . substring ( 0 , 10 ) . trim ( ) ;
f . date = lines [ i ] . substring ( 10 , 29 ) . trim ( ) ;
f . name = lines [ i ] . substring ( 29 ) . trim ( ) ;
if ( ! f . name ) {
console . log ( "FileBrowser.loadDir: can't parse line: '" + lines [ i ] + "'" ) ;
} else {
f . path = self . data . listdir + '/' + f . name ;
f . isdir = ( f . size == '[DIR]' ) ;
f . bytes = self . sizeToBytes ( f . size ) ;
f . isodate = self . dateToISO ( f . date ) ;
f . class = "" ;
if ( self . options . filter && (
( typeof self . options . filter == "string" && ! f . isdir && ! f . name . match ( self . options . filter ) ) ||
( typeof self . options . filter == "function" && ! self . options . filter ( f ) ) ) ) {
continue ;
}
if ( f . name == self . input . file ) {
f . class += " active" ;
self . data . listsel = f . name ;
} else {
f . class = "" ;
}
res += '<tr data-name="' + encode _html ( f . name ) + '" data-size="' + f . bytes +
'" data-date="' + f . isodate + '" class="' + f . class + '">' +
'<td class="col-xs-3 col-sm-2">' + f . size +
'</td><td class="hidden-xs col-sm-4">' + f . date +
'</td><td class="col-xs-9 col-sm-6">' + encode _html ( f . name ) + '</td></tr>' ;
}
}
if ( ! self . options . sortBy )
return res ;
if ( res ) {
var scrollpos = self . $filebody . get ( 0 ) . scrollTop ;
self . $filebody . detach ( ) . append ( res ) ;
self . sortList ( ) ;
self . $filebody . appendTo ( self . $filetable ) . scrollTop ( scrollpos ) ;
self . $filetable . trigger ( 'window-resize' ) ;
}
return null ;
} ) . always ( $ . proxy ( function ( ) {
this . $btnstop . prop ( 'disabled' , true ) ;
} , this ) ) ;
} ,
sizeToBytes : function ( size ) {
if ( size == "[DIR]" ) return - 1 ;
var unit = size [ size . length - 1 ] , val = parseFloat ( size ) ;
if ( unit == 'k' ) return val * 1024 ;
else if ( unit == 'M' ) return val * 1048576 ;
else if ( unit == 'G' ) return val * 1073741824 ;
else return val ;
} ,
dateToISO : function ( date ) {
var month = monthnames . indexOf ( date . substr ( 3 , 3 ) ) + 1 ;
return date . substr ( 7 , 4 ) + '-' + ( month < 10 ? '0' : '' ) + month + '-' + date . substr ( 0 , 2 ) + ' ' + date . substr ( 12 ) ;
} ,
stopLoad : function ( ) {
this . $btnstop . prop ( 'disabled' , true ) ;
if ( this . xhr )
this . xhr . abort ( ) ;
if ( ! supportsTouch ) this . $pathinput . focus ( ) ;
} ,
sortList : function ( by , dir ) {
if ( by == null ) {
by = this . options . sortBy ;
dir = this . options . sortDir ;
} else {
dir = dir || 1 ;
this . options . sortBy = by ;
this . options . sortDir = dir ;
this . $filecols . removeClass ( 'sort-up sort-down' )
. filter ( '[data-key="' + by + '"]' ) . addClass ( ( dir < 0 ) ? 'sort-down' : 'sort-up' ) ;
}
if ( ! by ) return ;
var rows = $ . makeArray ( this . $filebody . children ( ) ) ;
rows . sort ( function ( a , b ) {
if ( ! a . dataset [ by ] ) return 1 ; if ( ! b . dataset [ by ] ) return - 1 ;
var pri = a . dataset [ by ] . localeCompare ( b . dataset [ by ] ) ;
var sec = ( by != "name" ) ? a . dataset [ "name" ] . localeCompare ( b . dataset [ "name" ] ) : 0 ;
return dir * ( pri ? pri : sec ) ;
} ) ;
this . $filebody . html ( rows ) ;
} ,
newDir : function ( ) {
this . stopLoad ( ) ;
var path = this . input . dir + "/" ;
var self = this ;
promptdialog ( "text" , "Create new directory" , path + "… (empty = create this dir)" , [ "Cancel" , "Create" ] , function ( create , dirname ) {
if ( create ) {
path = ( path + dirname ) . replace ( /\/+$/ , "" ) ;
$ . post ( "/api/execute" , { "command" : "vfs mkdir '" + path + "'" } , function ( result ) {
if ( result . startsWith ( "Error" ) )
confirmdialog ( "Error" , result , [ "OK" ] ) ;
else
self . setPath ( path + "/" , path == self . input . dir ) ;
} ) . fail ( function ( request , textStatus , errorThrown ) {
confirmdialog ( "Error" , xhrErrorInfo ( request , textStatus , errorThrown ) , [ "OK" ] ) ;
} ) ;
}
} ) ;
} ,
onAction : function ( ) {
if ( this . options . onAction )
this . options . onAction . call ( this . $el , this . input ) ;
} ,
} ) ;
$ . pluginMaker ( ovms . FileBrowser ) ;
// FileDialog:
ovms . FileDialog = function ( el , options ) {
if ( el ) this . init ( el , options ) ;
}
$ . extend ( ovms . FileDialog . prototype , ovms . Widget . prototype , {
cname : "filedialog" ,
options : {
title : 'Select file' ,
submit : 'Select' ,
onSubmit : null ,
onCancel : null ,
path : '' ,
quicknav : [ '/sd/' , '/store/' ] ,
filter : null ,
sortBy : null ,
sortDir : 1 ,
select : 'f' ,
showNewDir : true ,
backdrop : true ,
keyboard : true ,
transition : 'fade' ,
size : 'lg' ,
onUpdate : null ,
show : false ,
} ,
init : function ( el , options ) {
ovms . Widget . prototype . init . call ( this , el , options ) ;
this . input = { } ;
this . $fb = null ;
this . $el . dialog ( {
input : this . input ,
body : '<div class="filebrowser"/>' ,
onHide : $ . proxy ( this . onHide , this ) ,
onHidden : $ . proxy ( this . onHidden , this ) ,
} ) ;
this . $fb = this . $el . find ( '.filebrowser' ) . filebrowser ( {
input : this . input ,
onPathChange : $ . proxy ( this . onPathChange , this ) ,
onAction : $ . proxy ( this . onAction , this ) ,
} ) ;
this . update ( this . options ) ;
} ,
update : function ( options ) {
$ . extend ( this . options , options ) ;
var newbtns = [ ] ;
if ( this . options . showNewDir ) newbtns . push (
{ label : "New dir" , btnClass : "default pull-left" , autoHide : false , action : $ . proxy ( this . newDir , this ) } ) ;
newbtns . push (
{ label : "Cancel" } ,
{ label : this . options . submit , btnClass : "primary" } ) ;
this . $el . dialog ( {
title : this . options . title ,
buttons : newbtns ,
backdrop : this . options . backdrop ,
keyboard : this . options . keyboard ,
transition : this . options . transition ,
size : this . options . size ,
} ) ;
this . $btnsubmit = this . $el . find ( ".modal-footer .btn-primary" ) ;
this . $fb . filebrowser ( {
path : ( options . path != null ) ? options . path : this . input . path ,
quicknav : this . options . quicknav ,
filter : this . options . filter ,
sortBy : this . options . sortBy ,
sortDir : this . options . sortDir ,
} ) ;
this . onPathChange ( ) ;
if ( this . options . onUpdate )
this . options . onUpdate . call ( this . $el , this . input ) ;
if ( options . show != null ) {
var showing = this . $el . dialog ( 'isShowing' ) ;
if ( options . show === true && ! showing )
this . $el . dialog ( 'show' ) ;
else if ( options . show === false && showing )
this . $el . dialog ( 'hide' ) ;
}
} ,
show : function ( options ) {
if ( options ) this . update ( options ) ;
this . $el . dialog ( 'show' ) ;
} ,
hide : function ( ) {
this . $el . dialog ( 'hide' ) ;
} ,
setPath : function ( newpath , reload ) {
this . $fb . filebrowser ( 'setPath' , newpath , reload ) ;
} ,
newDir : function ( ) {
this . $fb . filebrowser ( 'newDir' ) ;
} ,
onPathChange : function ( ) {
var ok = (
( this . options . select == 'f' && this . input . file ) ||
( this . options . select == 'd' && ! this . input . file ) ) ;
this . $btnsubmit . prop ( "disabled" , ! ok ) ;
} ,
onAction : function ( ) {
this . $el . dialog ( 'triggerButton' , this . options . submit ) ;
} ,
onHide : function ( ) {
this . $fb . filebrowser ( 'stopLoad' ) ;
} ,
onHidden : function ( input ) {
if ( input . button && input . button . label == this . options . submit ) {
if ( this . options . onSubmit )
this . options . onSubmit . call ( this . $el , this . input ) ;
}
else {
if ( this . options . onCancel )
this . options . onCancel . call ( this . $el , this . input ) ;
}
} ,
} ) ;
$ . pluginMaker ( ovms . FileDialog ) ;
// Template list editor:
$ . fn . listEditor = function ( op , data ) {
return this . each ( function ( ) {
if ( op ) {
$ ( this ) . trigger ( 'list:' + op , data ) ;
} else {
// init:
var $this = $ ( this ) ;
var el _itemid = $this . find ( '.list-item-id' ) ;
var el _body = $this . find ( '.list-items' ) ;
var el _template = $this . find ( 'template' ) . html ( ) ;
$this . on ( 'list:addItem' , function ( evt , data ) {
var id = Number ( el _itemid . val ( ) ) + 1 ;
el _itemid . val ( id ) ;
data = $ . extend ( { MODE : "upd" } , data ) ;
var txt = el _template . replace ( /ITEM_ID/g , id ) . replace ( /ITEM_MODE/g , data . MODE ) ;
for ( key in data )
txt = txt . replace ( new RegExp ( 'ITEM_' + key , 'g' ) , encode _html ( data [ key ] ) ) ;
txt = txt . replace ( /ITEM_\w+/g , '' ) ;
var $el = $ ( txt ) . appendTo ( el _body ) ;
$el . find ( "option[data-value]" ) . each ( function ( ) {
$ ( this ) . prop ( "selected" , $ ( this ) . val ( ) == $ ( this ) . data ( "value" ) ) ;
} ) ;
$el . find ( "input[data-value]" ) . each ( function ( ) {
var sel = $ ( this ) . val ( ) == $ ( this ) . data ( "value" ) ;
$ ( this ) . prop ( "checked" , sel ) ;
if ( sel ) $ ( this ) . parent ( ) . addClass ( "active" ) ;
else $ ( this ) . parent ( ) . removeClass ( "active" ) ;
} ) ;
var discl = ( data . MODE == "add" ) ? "add-disabled" : "list-disabled" ;
$el . find ( "select." + discl ) . each ( function ( ) {
$ ( this ) . prop ( "disabled" , true ) . append ( $ ( '<input type="hidden">' )
. attr ( "name" , $ ( this ) . attr ( "name" ) ) . attr ( "value" , $ ( this ) . val ( ) ) ) ;
} ) ;
$el . find ( "input." + discl ) . prop ( "readonly" , true ) ;
$el . find ( "button." + discl ) . prop ( "disabled" , true ) ;
$this . trigger ( 'list:validate' ) ;
} ) ;
$this . on ( 'change keyup' , 'input,select,textarea' , function ( ev ) {
$this . trigger ( 'list:validate' ) ;
} ) ;
$this . on ( 'click' , '.list-item-add' , function ( evt ) {
var data = $ . extend ( { MODE : "add" } , $ ( this ) . data ( 'preset' ) ) ;
$ ( this ) . trigger ( 'list:addItem' , data ) ;
} ) ;
$this . on ( 'click' , '.list-item-del' , function ( evt ) {
$ ( this ) . closest ( '.list-item' ) . remove ( ) ;
$this . trigger ( 'list:validate' ) ;
} ) ;
}
} ) ;
} ;
/ * *
* Slider widget plugin
* /
$ . fn . slider = function ( options ) {
return this . each ( function ( ) {
var $sld = $ ( this ) . closest ( '.slider' ) , data = $ . extend ( { checked : true } , $sld . data ( ) ) ;
var opts = ( typeof options == "object" ) ? options : data ;
// init?
if ( $sld . children ( ) . length == 0 ) {
var id = $sld . attr ( 'id' ) ;
$sld . html ( ' \
< div class = "slider-control form-inline" > \
< input class = "slider-enable" type = "checkbox" checked > \
< input class = "form-control slider-value" type = "number" id = "input-ID" name = "ID" > \
< span class = "slider-unit" > UNIT < / s p a n > \
< input class = "btn btn-default slider-down" type = "button" value = "➖ " > \
< input class = "btn btn-default slider-set" type = "button" value = "◈" > \
< input class = "btn btn-default slider-up" type = "button" value = "➕ " > \
< / d i v > \
< input class = "slider-input" type = "range" > '
. replace ( /ID/g , id ) . replace ( /UNIT/g , opts . unit || '' ) ) ;
}
// update:
var $inp = $sld . find ( '.slider-value, .slider-input' ) , $cb = $sld . find ( '.slider-enable' ) ,
$bt = $sld . find ( 'input[type=button]' ) , $sb = $bt . filter ( '.slider-set' ) ,
oldchk = data . checked , chk = ( opts . checked != null ) ? opts . checked : oldchk ,
dis = ( opts . disabled != null ) ? opts . disabled : ( $sld . prop ( 'disabled' ) == true ) ;
$ . extend ( data , opts ) ;
if ( opts . unit != null ) $sld . find ( '.slider-unit' ) . html ( opts . unit ) ;
if ( opts . min != null ) $inp . attr ( 'min' , opts . min ) ;
if ( opts . max != null ) $inp . attr ( 'max' , opts . max ) ;
if ( opts . step != null ) $inp . attr ( 'step' , opts . step ) ;
if ( opts . default != null ) {
if ( $sb . length == 1 )
$sb . data ( 'set' , opts . default ) ;
if ( ! chk )
$inp . val ( opts . default ) ;
data . default = Math . max ( data . min , Math . min ( data . max , 1 * opts . default ) ) ;
}
if ( opts . value !== undefined ) {
if ( opts . value === null )
data . value = data . uservalue = data . default ;
else
data . value = Math . max ( data . min , Math . min ( data . max , 1 * opts . value ) ) ;
if ( chk )
$inp . attr ( 'value' , data . value ) . val ( data . value ) ;
if ( chk || data . uservalue == null )
data . uservalue = data . value ;
}
if ( chk != oldchk ) {
$cb . prop ( 'checked' , chk ) ;
if ( chk ) {
if ( opts . reset || data . reset )
data . value = data . default ;
else
data . value = data . uservalue ;
} else {
data . uservalue = data . value ;
data . value = data . default ;
}
$inp . val ( data . value ) ;
}
$cb . prop ( 'disabled' , dis ) ;
$bt . prop ( 'disabled' , ! chk || dis ) ;
$inp . prop ( 'disabled' , ! chk || dis ) . prop ( 'checked' , chk ) ;
if ( dis )
$sld . addClass ( 'disabled' ) . prop ( 'disabled' , true ) . attr ( 'disabled' , true ) ;
else
$sld . removeClass ( 'disabled' ) . prop ( 'disabled' , false ) . attr ( 'disabled' , false ) ;
$sld . data ( data ) ;
} ) ;
} ;
/ * *
* Highcharts
* /
var highchartsLoader ;
$ . fn . chart = function ( options ) {
if ( this . length == 0 )
return this ;
var $this = this ;
if ( options === "destroy" ) {
// destroy:
var $cl = this . find ( ".has-chart" ) . add ( this . filter ( ".has-chart" ) ) ;
$cl . each ( function ( ) {
var chart = $ ( this ) . data ( "chart" ) ;
if ( typeof chart == "object" ) {
$ ( this ) . data ( "chart" , null ) ;
chart . destroy ( ) ;
}
} ) ;
} else {
// init:
function init _charts ( ) {
$this . each ( function ( ) {
var chart = Highcharts . chart ( this , options , function ( ) {
if ( this . userOptions && this . userOptions . onUpdate )
this . userOptions . onUpdate . call ( this , metrics ) ;
} ) ;
$ ( this ) . data ( "chart" , chart ) . addClass ( "has-chart get-window-resize" ) . on ( "window-resize" , function ( ) {
$ ( this ) . data ( "chart" ) . reflow ( ) ;
} ) ;
$ ( this ) . closest ( ".metric.chart" ) . data ( "chart" , chart ) ;
} ) ;
}
if ( window . Highcharts ) {
init _charts ( ) ;
} else if ( highchartsLoader ) {
highchartsLoader . then ( init _charts ) ;
} else {
highchartsLoader = $ . ajax ( {
url : ( window . assets && window . assets [ "charts_js" ] ) || "/assets/charts.js?v=6.0.7" ,
dataType : "script" ,
cache : true ,
success : function ( ) { init _charts ( ) ; }
} ) ;
}
}
return this ;
} ;
/ * *
* DataTables
* /
var datatablesLoader ;
$ . fn . table = function ( options ) {
if ( this . length == 0 )
return this ;
var $this = this ;
if ( options === "destroy" ) {
// destroy:
var $tl = this . find ( ".has-dataTable" ) . add ( this . filter ( ".has-dataTable" ) ) ;
$tl . each ( function ( ) {
var table = $ ( this ) . data ( "dataTable" ) ;
if ( typeof table == "object" ) {
$ ( this ) . data ( "dataTable" , null ) ;
table . destroy ( ) ;
}
} ) ;
} else {
// init:
function init _tables ( ) {
$this . each ( function ( ) {
$ ( this ) . one ( "init.dt" , function ( ev , settings ) {
if ( settings && settings . oInstance && settings . oInit && settings . oInit . onUpdate )
settings . oInit . onUpdate . call ( settings . oInstance . api ( ) , metrics ) ;
} ) ;
var table = $ ( this ) . DataTable ( options ) ;
$ ( this ) . data ( "dataTable" , table ) . addClass ( "has-dataTable" ) ;
$ ( this ) . closest ( ".metric.table" ) . data ( "dataTable" , table ) ;
} ) ;
}
if ( $ . fn . DataTables ) {
init _tables ( ) ;
} else if ( datatablesLoader ) {
datatablesLoader . then ( init _tables ) ;
} else {
datatablesLoader = $ . ajax ( {
url : ( window . assets && window . assets [ "tables_js" ] ) || "/assets/tables.js?v=1.10.18" ,
dataType : "script" ,
cache : true ,
success : function ( ) { init _tables ( ) ; }
} ) ;
}
}
return this ;
} ;
/ * *
* Framework Init
* /
$ ( function ( ) {
// Toggle night mode:
$ ( 'body' ) . on ( 'click' , '.toggle-night' , function ( event ) {
var nm = $ ( 'body' ) . toggleClass ( "night" ) . hasClass ( "night" ) ;
page . params [ "nm" ] = 0 + nm ;
updateLocation ( ) ;
event . stopPropagation ( ) ;
return false ;
} ) ;
// Toggle fullscreen mode:
document . fullScreenMode = document . fullScreen || document . mozFullScreen || document . webkitIsFullScreen ;
$ ( document ) . on ( 'mozfullscreenchange webkitfullscreenchange fullscreenchange' , function ( ) {
this . fullScreenMode = ! this . fullScreenMode ;
if ( this . fullScreenMode ) {
$ ( 'body' ) . addClass ( "fullscreened" ) ;
} else {
$ ( 'body' ) . removeClass ( "fullscreened" ) ;
}
$ ( window ) . trigger ( "resize" ) ;
} ) ;
$ ( 'body' ) . on ( 'click' , '.toggle-fullscreen' , function ( evt ) {
element = document . body ;
if ( element . requestFullscreen ) {
element . requestFullscreen ( ) ;
} else if ( element . mozRequestFullScreen ) {
element . mozRequestFullScreen ( ) ;
} else if ( element . webkitRequestFullscreen ) {
element . webkitRequestFullscreen ( Element . ALLOW _KEYBOARD _INPUT ) ;
} else if ( element . msRequestFullscreen ) {
element . msRequestFullscreen ( ) ;
}
return false ;
} ) ;
// AJAX page links, forms & buttons:
$ ( 'body' ) . on ( 'click' , 'a[target^="#"], form[target^="#"] .btn[type="submit"]' , function ( event ) {
var method = $ ( this ) . data ( "method" ) || "get" ;
var uri = $ ( this ) . attr ( "href" ) ;
var target = $ ( this ) . attr ( "target" ) ;
var data = { } ;
if ( method . toLowerCase ( ) == "post" ) {
var p = uri . split ( "?" ) ;
if ( p . length == 2 ) {
uri = p [ 0 ] ;
data = p [ 1 ] ;
}
if ( uri == "" || uri == "#" )
uri = $ ( "#main" ) . data ( "uri" ) ;
}
if ( ! uri ) {
var frm = $ ( this . form ) ;
method = frm . attr ( "method" ) || "get" ;
uri = frm . attr ( "action" ) ;
target = frm . attr ( "target" ) ;
data = frm . serialize ( ) ;
if ( this . name )
data += ( data ? "&" : "" ) + encodeURI ( this . name + "=" + ( this . value || "1" ) ) ;
}
if ( $ ( this ) . data ( "dismiss" ) == "modal" )
$ ( this ) . closest ( ".modal" ) . removeClass ( "fade" ) . modal ( "hide" ) ;
if ( ! loaduri ( target , method , uri , data ) )
return true ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
return false ;
} ) ;
$ ( 'body' ) . on ( 'submit' , 'form[target^="#"]' , function ( event ) {
var $frm = $ ( this ) ;
var method = $frm . attr ( "method" ) || "get" ;
var uri = $frm . attr ( "action" ) ;
var target = $frm . attr ( "target" ) ;
var data = $frm . serialize ( ) ;
var $btn = $frm . find ( 'input[type="submit"], button[type="submit"]' ) . first ( ) ;
if ( $btn . length && $btn . attr ( "name" ) )
data += ( data ? "&" : "" ) + encodeURI ( $btn . attr ( "name" ) + "=" + ( $btn . val ( ) || "1" ) ) ;
if ( $frm . data ( "dismiss" ) == "modal" || $btn . data ( "dismiss" ) == "modal" )
$frm . closest ( ".modal" ) . removeClass ( "fade" ) . modal ( "hide" ) ;
if ( ! loaduri ( target , method , uri , data ) )
return true ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
return false ;
} ) ;
// AJAX command links & buttons:
$ ( 'body' ) . on ( 'click' , '.btn[data-cmd]' , function ( event ) {
var btn = $ ( this ) ;
var cmd = btn . data ( "cmd" ) ;
var tgt = btn . data ( "target" ) ;
var updcnt = btn . data ( "watchcnt" ) || 0 ;
var updint = btn . data ( "watchint" ) || 2 ;
btn . prop ( "disabled" , true ) ;
$ ( tgt ) . data ( "updcnt" , 0 ) ;
loadcmd ( cmd , tgt ) . then ( function ( ) {
btn . prop ( "disabled" , false ) ;
$ ( tgt ) . data ( "updcnt" , updcnt ) ;
$ ( tgt ) . data ( "updint" , updint ) ;
$ ( tgt ) . data ( "updlast" , now ( ) ) ;
} , function ( ) {
btn . prop ( "disabled" , false ) ;
} ) ;
event . stopPropagation ( ) ;
return false ;
} ) ;
$ ( 'body' ) . on ( 'click' , '.btn[data-js]' , function ( event ) {
var btn = $ ( this ) ;
var js = btn . data ( "js" ) ;
var tgt = btn . data ( "target" ) ;
var updcnt = btn . data ( "watchcnt" ) || 0 ;
var updint = btn . data ( "watchint" ) || 2 ;
btn . prop ( "disabled" , true ) ;
$ ( tgt ) . data ( "updcnt" , 0 ) ;
loadjs ( js , tgt ) . then ( function ( ) {
btn . prop ( "disabled" , false ) ;
$ ( tgt ) . data ( "updcnt" , updcnt ) ;
$ ( tgt ) . data ( "updint" , updint ) ;
$ ( tgt ) . data ( "updlast" , now ( ) ) ;
} , function ( ) {
btn . prop ( "disabled" , false ) ;
} ) ;
event . stopPropagation ( ) ;
return false ;
} ) ;
// Long touch buttons:
var longtouchTimeout , $longtouchProgress ;
$ ( 'body' ) . on ( 'touchstart' , '.btn-longtouch .btn, .btn.btn-longtouch' , function ( ev ) {
var $this = $ ( this ) ;
if ( $this . prop ( 'disabled' ) ) return ;
var action = $this . attr ( "title" ) || $this . text ( ) ;
if ( navigator . vibrate ) navigator . vibrate ( [ 100 , 400 , 100 , 400 , 100 , 400 ] ) ;
$longtouchProgress = $ ( '<div class="hover-progress longtouch"><div class="hover-progress-body"><div class="info">Hold touch for/to</div><div class="action">' + encode _html ( action ) + '</div><div class="progress"><div class="progress-bar progress-bar-info" style="width:0%"></div></div></div></div>' ) . appendTo ( "body" ) . find ( ".progress-bar" ) ;
window . getComputedStyle ( $longtouchProgress . get ( 0 ) ) . width ;
$longtouchProgress . css ( "width" , "100%" ) ;
longtouchTimeout = window . setTimeout ( function ( ) {
if ( navigator . vibrate ) navigator . vibrate ( 1000 ) ;
if ( $longtouchProgress ) $longtouchProgress . closest ( ".hover-progress" ) . remove ( ) ;
longtouchTimeout = null ;
$longtouchProgress = null ;
$this . trigger ( 'click' ) ;
} , 1500 ) ;
ev . preventDefault ( ) ;
} ) . on ( 'touchcancel touchend' , '.btn-longtouch .btn, .btn.btn-longtouch' , function ( ev ) {
window . clearTimeout ( longtouchTimeout ) ;
if ( navigator . vibrate ) navigator . vibrate ( 0 ) ;
if ( $longtouchProgress ) $longtouchProgress . closest ( ".hover-progress" ) . remove ( ) ;
longtouchTimeout = null ;
$longtouchProgress = null ;
ev . preventDefault ( ) ;
} ) . on ( 'contextmenu' , function ( ev ) {
if ( $longtouchProgress ) {
ev . preventDefault ( ) ;
ev . stopPropagation ( ) ;
ev . stopImmediatePropagation ( ) ;
return false ;
}
} ) ;
// Slider widget event handling:
$ ( "body" ) . on ( "change" , ".slider-enable" , function ( evt ) {
var $this = $ ( this ) , $sld = $this . closest ( ".slider" ) , $inp = $sld . find ( ".slider-input, .slider-value" ) ;
$this . slider ( { checked : this . checked } ) ;
$inp . trigger ( "input" , true ) . trigger ( "change" , true ) ;
} ) ;
$ ( "body" ) . on ( "input change" , ".slider-value" , function ( evt , noprop ) {
var $this = $ ( this ) , $sld = $this . closest ( ".slider" ) , $inp = $sld . find ( ".slider-input" ) ;
if ( ! noprop ) {
$this . slider ( { value : this . value } ) ;
$inp . trigger ( evt . type , true ) ;
}
} ) ;
$ ( "body" ) . on ( "input change" , ".slider-input" , function ( evt , noprop ) {
var $this = $ ( this ) , $sld = $this . closest ( ".slider" ) , $inp = $sld . find ( ".slider-value" ) ;
if ( ! noprop ) {
$this . slider ( { value : this . value } ) ;
$inp . trigger ( evt . type , true ) ;
}
} ) ;
$ ( "body" ) . on ( "click" , ".slider-up" , function ( evt ) {
$ ( this ) . closest ( ".slider" ) . find ( ".slider-input" ) . val ( function ( ) {
return Math . min ( 1 * this . value + ( 1 * this . step || 1 ) , this . max ) ;
} ) . trigger ( "input" ) . trigger ( "change" ) ;
} ) ;
$ ( "body" ) . on ( "click" , ".slider-down" , function ( evt ) {
$ ( this ) . closest ( ".slider" ) . find ( ".slider-input" ) . val ( function ( ) {
return Math . max ( 1 * this . value - ( 1 * this . step || 1 ) , this . min ) ;
} ) . trigger ( "input" ) . trigger ( "change" ) ;
} ) ;
$ ( "body" ) . on ( "click" , ".slider-set" , function ( evt ) {
var $inp = $ ( this ) . closest ( ".slider" ) . find ( ".slider-input" ) ;
$inp . val ( $ ( this ) . data ( "set" ) ) . trigger ( "input" ) . trigger ( "change" ) ;
} ) ;
// data-toggle="filefialog":
$ ( "body" ) . on ( 'click' , '.btn[data-toggle="filedialog"]' , function ( evt ) {
var $this = $ ( this ) ;
var $tgt = $ ( $this . data ( "target" ) ) ;
var $inp = $ ( $this . data ( "input" ) ) ;
if ( $tgt . length && $inp . length ) {
var val = $inp . val ( ) ;
var opt = {
show : true ,
onSubmit : function ( input ) { $inp . val ( input . path ) ; }
} ;
if ( val ) opt . path = val ;
$tgt . filedialog ( opt ) ;
}
} ) ;
// Metrics displays:
$ ( "body" ) . on ( 'msg:metrics' , '.receiver' , function ( e , update ) {
$ ( this ) . find ( ".metric" ) . each ( function ( ) {
2023-01-24 20:50:05 +01:00
var $el = $ ( this ) , metric = $el . data ( "metric" ) , prec = $el . data ( "prec" ) , scale = $el . data ( "scale" ) , useUser = $el . data ( "user" ) ;
2022-04-06 00:04:46 +02:00
if ( ! metric ) return ;
// filter:
var keys = metric . split ( "," ) , val ;
2023-01-24 20:50:05 +01:00
var metricName = "" ;
2022-04-06 00:04:46 +02:00
for ( var i = 0 ; i < keys . length ; i ++ ) {
2023-01-24 20:50:05 +01:00
metricName = keys [ i ] ;
if ( ( val = update [ metricName ] ) != null ) {
break ;
}
2022-04-06 00:04:46 +02:00
}
if ( val == null ) return ;
2023-01-24 20:50:05 +01:00
2022-04-06 00:04:46 +02:00
// process:
if ( $el . hasClass ( "text" ) ) {
2023-01-24 20:50:05 +01:00
var elt = $el . children ( ".value" ) ;
if ( elt ) elt . text ( val ) ;
elt = $el . children ( ".unit" ) ;
if ( elt ) elt . text ( val ) ;
2022-04-06 00:04:46 +02:00
} else if ( $el . hasClass ( "number" ) ) {
var vf = val ;
2023-01-24 20:50:05 +01:00
if ( scale != null )
vf = Number ( vf ) * scale ;
else {
var mun = units . userUnitLabelFromMetric ( metricName ) ;
if ( mun != "" ) {
// If there's a .unit.. then convert it.
item = $el . children ( ".unit" ) ;
if ( item ) {
item . text ( mun ) ;
useUser = true ;
}
}
if ( useUser )
vf = units . convertMetricToUserUnits ( vf , metricName ) ;
}
2022-04-06 00:04:46 +02:00
if ( prec != null ) vf = Number ( vf ) . toFixed ( prec ) ;
$el . children ( ".value" ) . text ( vf ) ;
} else if ( $el . hasClass ( "progress" ) ) {
var vf = val ;
if ( scale != null ) vf = Number ( vf ) * scale ;
if ( prec != null ) vf = Number ( vf ) . toFixed ( prec ) ;
var $pb = $ ( this . firstElementChild ) , min = $pb . attr ( "aria-valuemin" ) , max = $pb . attr ( "aria-valuemax" ) ;
var vp = ( val - min ) / ( max - min ) * 100 ;
$pb . css ( "width" , vp + "%" ) . attr ( "aria-valuenow" , vp ) . find ( ".value" ) . text ( vf ) ;
var lw = 0 ; $pb . find ( "span" ) . each ( function ( ) { lw += $ ( this ) . width ( ) ; } ) ;
if ( ( $pb . parent ( ) . width ( ) * vp / 100 ) < lw ) $pb . addClass ( "value-low" ) ; else $pb . removeClass ( "value-low" ) ;
} else if ( $el . hasClass ( "chart" ) ) {
var ch = $ ( this ) . data ( "chart" ) ;
if ( ch && ch . userOptions && ch . userOptions . onUpdate )
ch . userOptions . onUpdate . call ( ch , update ) ;
} else if ( $el . hasClass ( "table" ) ) {
var dt = $ ( this ) . data ( "dataTable" ) ;
if ( dt && dt . settings ( ) && dt . settings ( ) [ 0 ] && dt . settings ( ) [ 0 ] . oInit && dt . settings ( ) [ 0 ] . oInit . onUpdate )
dt . settings ( ) [ 0 ] . oInit . onUpdate . call ( dt , update ) ;
} else {
$el . text ( val ) ;
}
} ) ;
} ) ;
// Modal autoclear:
$ ( "body" ) . on ( "hidden.bs.modal" , ".modal" , function ( evt ) {
$ ( this ) . find ( ".modal-autoclear" ) . empty ( ) ;
} ) ;
// Proxy window resize:
$ ( window ) . on ( "resize" , function ( event ) {
$ ( ".get-window-resize" ) . trigger ( "window-resize" ) ;
} ) ;
$ ( "body" ) . on ( "window-resize" , ".table-scrollable" , function ( evt ) {
var bw = $ ( this ) . find ( '>tbody' ) . get ( 0 ) . scrollWidth ;
if ( bw ) $ ( this ) . find ( '>thead' ) . css ( 'width' , bw ) ;
} ) ;
// Monitor timer:
if ( ! monitorTimer )
monitorTimer = window . setInterval ( monitorUpdate , 1000 ) ;
// AJAX page init:
window . onpopstate = loadPage ;
loadPage ( ) ;
} ) ;