diff --git a/WiFi_profile/assets/fonts/Inconsolata.woff2 b/WiFi_profile/assets/fonts/Inconsolata.woff2 new file mode 100644 index 00000000..e2514070 Binary files /dev/null and b/WiFi_profile/assets/fonts/Inconsolata.woff2 differ diff --git a/WiFi_profile/assets/js/common.js b/WiFi_profile/assets/js/common.js index d134fa88..ad971013 100644 --- a/WiFi_profile/assets/js/common.js +++ b/WiFi_profile/assets/js/common.js @@ -1,277 +1,16 @@ -/* -bash() -$.fn.press -banner() copy errorDisplay() -info() infoPower() infoPowerCommand() infoWarning() -loader() local() selectSet() -*/ - -var page = location.search.replace( '?p=', '' ); -var dirbash = '/srv/http/bash/'; -var dirsettings = '/srv/http/bash/settings/'; -var iconwarning = ico( 'warning i-22 yl' ) +' '; -var localhost = [ 'localhost', '127.0.0.1' ].includes( location.hostname ); -var orange = '#de810e'; -var red = '#bb2828'; -var blinkdot = '···'; - -// ---------------------------------------------------------------------- -/* -$( ELEMENT ).press( DELEGATE, function( e ) { - - this not applicable - - cannot be attached with .on - - DELEGATE : optional -} ); -events: - - move : mouseenter > mousemove > mouseleave > mouseout - - click : mousedown > mouseup > click - - touch : touchstart > touchmove > touchend -*/ -$.fn.press = function( arg1, arg2 ) { - var callback, delegate, timeout; - if ( arg2 ) { - delegate = arg1; - callback = arg2; - } else { - delegate = ''; - callback = arg1; - } - this.on( 'touchstart mousedown', delegate, function( e ) { - timeout = setTimeout( () => { - V.press = true; - callback( e ); // e.currentTarget = ELEMENT - }, 1000 ); - } ).on( 'touchend mouseup mouseleave', delegate, function() { - clearTimeout( timeout ); - setTimeout( () => V.press = false, 300 ); // needed for mouse events - } ); - return this // allow chain -} - -// ---------------------------------------------------------------------- -/* -Simple spaced arguments - - [ 'CMD.sh', v1, v2, ... ] - CMD.sh $1 $2 ... -Multiline arguments - no escape \" \` in js values > escape in php instead - - [ CMD, v1, v2, ... ] - script.sh $CMD ON=1 "${args[1]}" "${args[2]}" ... - - [ CMD, 'OFF' ] - script.sh $CMD ON= (disable CMD) - - [ CMD, v1, v2, ..., 'CMD K1 K2 ...' ] - script.sh $CMD ON=1 "$K1" "$K2" ... - - [ CMD, v1, v2, ..., 'CFG K1 K2 ...' ] - ^^^ and save K1=v1; K2=v2; ... to $dirsystem/$CMD.conf - - { cmd: [ CMD, ... ], json: JSON } - ^^^ and save {"K1":"v1", ... } to $dirsystem/$CMD.json - -- js > php >> common.js - bash() - - string : - - array of lines : [ 'CMD' v1, v2, ..., 'CMD K1 K2 ...' ] - - multiline : 'l1\\nl2\\nl3...' - - json : json.sringify( JSON ) -- php > bash >> cmd.php - $_POST[ 'cmd' ] === 'bash' - - array : covert to multiline with " ` escaped > CMD "...\"...\n...\`..." - - json : decode > reencode > save to $dirsystem/$CMD.json ($_POST[ 'json' ]) - - js cannot escape " as \\" double backslash which disappeared in bash -- bash >> common.sh - args2var - - convert to array > assign values - - No 'CMD' : ${args[1]} == v1; ${args[2]} == v2; ... - - With 'CMD' : $K1 == v1; $K2 == v2; ... ($VAR in capital) - - With 'CFG' : - - the same as 'CMD' - - save to $dirsystem/$CMD.conf with " ` escaped and quote > K1="... ...\"...\n...\`..." -*/ -function bash( args, callback, json ) { - var data = { cmd: 'bash' } - if ( 'json' in args ) { - data.json = JSON.stringify( args.json ); - args = args.cmd; - } - var args0 = args[ 0 ]; - if ( [ '.sh', '.py' ].includes( args0.slice( -3 ) ) ) { // CMD.sh / CMD.py - data.filesh = args.join( ' ' ); - args = false; - } else if ( page ) { // CMD - settings - data.filesh = 'settings/'+ page +'.sh'; // default - if ( args0 === 'mount' ) data.filesh = 'settings/system-mount.sh'; // not default - } else { // CMD - playback - data.filesh = 'cmd.sh'; - if ( [ 'scrobble', 'tageditor' ].includes( args0 ) ) data.filesh = args0 +'.sh'; - } - if ( args ) data.args = args; -/* -V.debug - press: $( '#debug' ) - - all - - console.log commands - - active push status (no pageInactive) -V.consolelog - press: $( '#infoOk' ) / $( '.switch' ) - - each - - console.log commands only (NOT run) -*/ - if ( V.debug || V.consoleonly ) { - var bashcmd = data.filesh.replace( 'settings/', '' ); - if ( data.args ) bashcmd += ' "\\\n'+ data.args.join( '\n' ).replace( /"/g, '\\"' ) +'"'; - console.log( data ); - console.log( bashcmd ); - if ( V.consoleonly ) { - V.consoleonly = false; - setTimeout( () => page ? switchCancel() : bannerHide(), 5000 ); - return - } - } - - $.post( - 'cmd.php' - , data - , callback || null - , json || null - ); -} -// debug -$( '.page-icon' ).press( () => location.reload() ); -$( '#debug' ).press( function() { - V.debug = true; - banner( 'gear', 'Debug', 'Console.log + Push status', 5000 ); - bash( [ 'cmd.sh', 'cachebust' ] ); -} ); -$( '#infoOverlay' ).press( '#infoOk', function() { - V.consoleonly = true; - I.ok(); -} ); -$( '.col-r .switch' ).press( function( e ) { - if ( $( '#setting-'+ e.target.id ).length && ! S[ e.target.id ] ) { - $( '#setting-'+ e.target.id ).trigger( 'click' ); - return - } - - V.consoleonly = true; - switchIdIconTitle( e.target.id ); - notifyCommon( S[ SW.id ] ? 'Disable ...' : 'Enable ...' ); - bash( S[ SW.id ] ? [ SW.id, 'OFF' ] : [ SW.id ] ); -} ); - -// ---------------------------------------------------------------------- -function banner( icon, title, message, delay ) { - clearTimeout( I.timeoutbanner ); - var bottom = $( '#bar-bottom' ).is( '.transparent, :hidden' ) || ! $( '#loader' ).hasClass( 'hide' ) ? '10px' : ''; - $( '#banner' ) - .html( '
'+ ico( icon ) +'
'+ title +'
' - +'
'+ message +'
' ) - .css( 'bottom', bottom ) - .removeClass( 'hide' ); - if ( delay !== -1 ) I.timeoutbanner = setTimeout( bannerHide, delay || 3000 ); -} -function bannerHide() { - if ( $( '#banner' ).hasClass( 'hide' ) ) return - - $( '#banner' ) - .addClass( 'hide' ) - .empty(); -} -$( '#banner' ).on( 'click', bannerHide ); - -// ---------------------------------------------------------------------- -$( '#data' ).on( 'click', '.copy', function() { - banner( 'copy', 'Error Data', 'Errors copied to clipboard.' ); - // copy2clipboard - for non https which cannot use clipboard API - $( 'body' ).prepend( '' ); - $( '#error' ).focus().select(); - document.execCommand( 'copy' ); - $( '#error' ).remove(); -} ); - -// ---------------------------------------------------------------------- -function errorDisplay( msg, list ) { - var pos = msg.replace( /.* position /, '' ); - if ( msg.includes( 'position' ) ) { - pos = msg.replace( /.*position /, '' ).replace( / .line.*/, '' ); - } else if ( msg.includes( 'column' ) ) { - pos = msgx.replace( /.* column /, '' ).replace( ')', '' ); - } - if ( pos ) msg = msg.replace( pos, ''+ pos +'' ); - var error = '
Errors: '+ msg - +' '+ ico( 'copy' ) +'Copy' - +'
' - +'
' - + list.slice( 0, pos ).replace( /<'+ list.slice( pos ).replace( /' - $( '#data' ) - .html( error ) - .removeClass( 'hide' ); - $( '#button-data' ).addClass( 'hide' ); - loaderHide(); -} - -// ---------------------------------------------------------------------- -function highlightJSON( json ) { - var json = Object.keys( json ) - .sort() - .reduce( ( r, k ) => ( r[ k ] = json[ k ], r ), {} ); // https://stackoverflow.com/a/29622653 - json = JSON.stringify( json, null, '\t' ) - .replace( /'+ match +'' - } - } else if ( /true/.test( match ) ) { // true - return ''+ match +'' - } else if ( /false/.test( match ) ) { // false - return ''+ match +'' - } else if ( /[0-9]/.test( match ) ) { // number - return ''+ match +'' - } else if ( /[{}\[\]]/.test( match ) ) { // braces - return ''+ match +'' - } - } ); - return '\n\n'+ json.replace( /: null,/g, ': null,' ); -} +// info ---------------------------------------------------------------------- function ico( cls, id ) { return '' } +function local( delay ) { + V.local = true; + setTimeout( () => V.local = false, delay || 300 ); +} -// info ---------------------------------------------------------------------- -$( '#infoOverlay' ).press( '#infoIcon', function() { // usage - window.open( 'https://github.com/rern/js/blob/master/info/README.md#infojs', '_blank' ); -} ); -$( '#infoOverlay' ).on( 'click', '#infoList', function() { - $( '.infobtn, .filebtn' ).removeClass( 'active' ); -} ); -$( '#infoOverlay' ).on( 'keydown', function( e ) { -/* -all: [Tab] - focus / next input - [Shift+Tab] - previous input -radio: [L] [R] - check -checkbox: [space] - check -select: [U] [D] - check -*/ - if ( ! I.active ) return - - e.stopPropagation(); // suppress others - var key = e.key; - switch ( key ) { - case 'Enter': - if ( ! $( '#infoOk' ).hasClass( 'disabled' ) && ! $( 'textarea' ).is( ':focus' ) ) $( '#infoOk' ).trigger( 'click' ); - break; - case 'Escape': - $( '#infoX' ).trigger( 'click' ); - break; - case 'ArrowLeft': - case 'ArrowRight': - var activeinput = $( document.activeElement ).attr( 'type' ); - if ( [ 'text', 'number', 'password', 'range', 'textarea' ].includes( activeinput ) ) return - - var $tabactive = $( '#infoTab a.active' ); - if ( key === 'ArrowLeft' ) { - $tabactive.is(':first-child') ? $( '#infoTab a:last-child' ).trigger( 'click' ) : $tabactive.prev().trigger( 'click' ); - } else { - $tabactive.is(':last-child') ? $( '#infoTab a:first-child' ).trigger( 'click' ) : $tabactive.next().trigger( 'click' ); - } - break; - } -} ); - I = { active: false } function info( json ) { - local(); // flag for consecutive info + local(); I = json; if ( 'keyvalue' in I ) $.each( I.keyvalue, ( k, v ) => I[ k ] = v ); if ( 'values' in I ) { @@ -575,10 +314,6 @@ function info( json ) { I.active = true; 'focus' in I ? $inputbox.eq( I.focus ).focus() : $( '#infoOverlay' ).focus(); if ( $( '#infoBox' ).height() > window.innerHeight - 10 ) $( '#infoBox' ).css( { top: '5px', transform: 'translateY( 0 )' } ); - infoButtonWidth(); - // set width: text / password / textarea - infoWidth(); - if ( [ 'localhost', '127.0.0.1' ].includes( location.hostname ) ) $( '#infoList a' ).removeAttr( 'href' ); // check inputs: blank / length / change if ( I.checkblank ) { if ( I.checkblank === true ) I.checkblank = [ ...Array( $inputbox.length ).keys() ]; @@ -597,73 +332,6 @@ function info( json ) { I.nochange = I.values && I.checkchanged ? true : false; $( '#infoOk' ).toggleClass( 'disabled', I.blank || I.notip || I.short || I.nochange ); // initial check infoCheckSet(); - if ( I.range ) { - var timeout, val; - $( '.inforange input' ).on( 'input', function() { - var $this = $( this ); - $this.siblings( '.value' ).text( +$this.val() ); - } ); - var rangeset = ( $range, up ) => { - val = +$range.val(); - up ? val++ : val--; - $range - .val( val ) - .siblings( '.value' ).text( val ); - } - $( '.inforange i' ).on( 'touchend mouseup keyup', function() { // increment up/dn - clearTimeout( timeout ); - var $this = $( this ); - if ( ! V.press ) rangeset( $this.siblings( 'input' ), $this.hasClass( 'up' ) ); - } ).press( function( e ) { - var $this = $( e.target ); - var $range = $this.siblings( 'input' ) - var up = $this.hasClass( 'up' ); - timeout = setInterval( () => rangeset( $range, up ), 100 ); - } ); - } - if ( I.updn.length ) { - I.updn.forEach( ( el, i ) => { - var $td = $( '#infoList .updn' ).parent().eq( i ); - var $updn = $td.find( '.updn' ); - var $num = $td.prev().find( 'input' ); - var step = el.step; - var v = 0; - var interval, timeout; - function numberset( up ) { - v = +$num.val(); - v = up ? v + step : v - step; - if ( v === el.min || v === el.max ) { - clearInterval( interval ); - clearTimeout( timeout ); - } - $num.val( v ); - if ( I.checkchanged ) $num.trigger( 'input' ); - updnToggle( v ); - } - function updnToggle( v ) { - $updn.eq( 0 ).toggleClass( 'disabled', v === el.min ); - $updn.eq( 1 ).toggleClass( 'disabled', v === el.max ); - } - updnToggle( +$num.val() ); - $updn.on( 'click', function() { - if ( ! V.press ) numberset( $( this ).hasClass( 'up' ) ); - } ).press( function( e ) { - var up = $( e.target ).hasClass( 'up' ); - interval = setInterval( () => numberset( up ), 100 ); - timeout = setTimeout( () => { // @5 after 3s - clearInterval( interval ); - step *= 5; - v = v > 0 ? v + ( step - v % step ) : v - ( step + v % step ); - $num.val( v ); - interval = setInterval( () => numberset( up ), 100 ); - }, 3000 ); - } ).on( 'touchend mouseup keyup', function() { - clearInterval( interval ); - clearTimeout( timeout ); - step = el.step; - } ); - } ); - } // custom function before show if ( I.beforeshow ) I.beforeshow(); } ); @@ -954,179 +622,3 @@ function infoVal( array ) { I.keys.forEach( ( k, i ) => v[ k ] = values[ i ] ); return v // json } -function infoWarning( icon, title, message ) { - info( { - icon : icon - , title : title - , message : iconwarning + message - } ); -} -function infoWidth() { - if ( I.boxwidth ) { - var widthmax = I.boxwidth === 'max'; - if ( widthmax ) { - if ( I.width ) { - var maxW = I.width < V.wW ? I.width : V.wW; - } else { - var maxW = V.wW > 600 ? 600 : V.wW; - } - $( '#infoBox' ).css( 'width', maxW +'px' ); - } - var allW = $( '#infoList' ).width(); - var labelW = Math.round( $( '#infoList td:first-child' ).width() ) || 0; - var boxW = ( widthmax ? allW - labelW - 20 : I.boxwidth ); - } else { - var boxW = 230; - } - $( '#infoList table' ).find( 'input:text, input[type=number], input:password, textarea' ).parent().css( 'width', boxW ); - if ( $( '#infoList select' ).length ) { - selectSet(); // render select to set width - $( '#infoList .select2-container' ).attr( 'style', 'width: '+ boxW +'px !important' ); - } - if ( I.headeralign || I.messagealign || I.footeralign ) { - $( '#infoList' ).find( '.infoheader, .infomessage, .infofooter' ).css( 'width', $( '#infoList table' ).width() ); - } - if ( I.checkboxonly ) $( '#infoList td' ).css( 'text-align', 'left' ); -} - -// common info functions -------------------------------------------------- -function infoPower() { - info( { - icon : 'power' - , title : 'Power' - , message : ico( 'raudio i-30 gr' ) +'  r A u d i o' - , buttonlabel : ico( 'reboot' ) +'Reboot' - , buttoncolor : orange - , button : () => infoPowerCommand( 'reboot' ) - , oklabel : ico( 'power' ) +'Off' - , okcolor : red - , ok : () => infoPowerCommand( 'off' ) - } ); -} -function infoPowerCommand( action ) { - loader(); - bash( [ 'power.sh', action ], nfs => { - if ( nfs != -1 ) return - - loaderHide(); - var off = action === 'off'; - info( { - icon : 'power' - , title : 'Power' - , message : 'This Server rAudio '+ ico( 'rserver' ) +' is currently active.' - +'
Shared Data on clients will stop.' - +'
(Resume when server online again)' - +'

Continue?' - , oklabel : off ? ico( 'power' ) +'Off' : ico( 'reboot' ) +'Reboot' - , okcolor : off ? red : orange - , ok : () => { - bash( [ 'power.sh', action, 'confirm' ] ); - banner( 'rserver', 'Server rAudio', 'Notify clients ...', -1 ); - } - } ); - } ); -} - -function capitalize( str ) { - return str.replace( /\b\w/g, l => l.toUpperCase() ); -} -function htmlOption( el ) { - if ( typeof el === 'number' ) el = [ ...Array( el ).keys() ]; - var options = ''; - if ( Array.isArray( el ) ) { // name = value - el.sort( ( a, b ) => a.toString().localeCompare( b.toString(), 'en', { numeric: true } ) ); - el.forEach( v => options += '' ); - } else { // json - el = jsonSort( el ); - $.each( el, ( k, v ) => options += '' ); - } - return options -} -function jsonChanged( a, b ) { - if ( ! a || ! b || ! Object.keys( a ).length || ! Object.keys( b ).length ) return true - - var changed = false; - $.each( a, ( k, v ) => { - if ( typeof v === 'object' ) { - if ( jsonChanged( v, b[ k ] ) ) { - changed = true; - return false - } - } else { - if ( v !== b[ k ] ) { - changed = true; - return false - } - } - } ); - return changed -} -function jsonClone( json ) { - return JSON.parse( JSON.stringify( json ) ) -} -function jsonSort( json ) { - return Object.keys( json ).sort().reduce( function ( result, key ) { - result[ key ] = json[ key ]; - return result; - }, {} ); -} -// ---------------------------------------------------------------------- -function loader() { - $( '#loader' ).removeClass( 'hide' ); -} -function loaderHide() { - $( '#loader' ).addClass( 'hide' ); -} - -// ---------------------------------------------------------------------- -function local( delay ) { - V.local = true; - setTimeout( () => V.local = false, delay || 300 ); -} -function qrCode( msg ) { - var qr = QRCode( { - msg : msg - , dim : 115 - , pad : 0 - , pal : [ '#969a9c' ] - } ); - return qr.outerHTML -} - -// select2 -------------------------------------------------------------------- -function selectSet( $select ) { - var options = { minimumResultsForSearch: 10 } - if ( ! $select ) { - $select = $( '#infoList select' ); - if ( $( '#eq' ).length ) options.dropdownParent = $( '#eq' ); - } - $select - .select2( options ) - .on( 'select2:open', () => { // fix: scroll on info - set current value 3rd from top - local(); // fix: onblur / onpagehide - setTimeout( () => { - var scroll = $( '.select2-results__option--selected' ).index() * 36 - 72; - if ( ! navigator.maxTouchPoints ) scroll -= 12; - $( '.select2-results ul' ).scrollTop( scroll ); - }, 0 ); - } ) - .on( 'select2:closing', local ) // fix: onblur / onpagehide - .each( ( i, el ) => { - var $this = $( el ); - $this.prop( 'disabled', $this.find( 'option' ).length === 1 ); - } ); -} -function selectText2Html( pattern ) { - function htmlSet( $el ) { - $.each( pattern, ( k, v ) => { - if ( $el.text() === k ) $el.html( v ); - } ); - } - var $rendered = $( '.select2-selection__rendered' ).eq( 0 ); - htmlSet( $rendered ); - $( '#infoList select' ).on( 'select2:open', () => { - setTimeout( () => $( '.select2-results__options li' ).each( ( i, el ) => htmlSet( $( el ) ) ), 0 ); - } ).on( 'select2:select', function() { - htmlSet( $rendered ); - } ); -} diff --git a/WiFi_profile/index.html b/WiFi_profile/index.html index b284dc87..950325ba 100644 --- a/WiFi_profile/index.html +++ b/WiFi_profile/index.html @@ -18,6 +18,10 @@ @@ -29,7 +33,7 @@
- +