From 624862480dd95d5f414ce735f0999cb622ab97f3 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 00:14:46 +0100 Subject: [PATCH 01/23] Add huebee color-input --- templates/themes.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templates/themes.php b/templates/themes.php index 4040ee80..0e90a6d9 100755 --- a/templates/themes.php +++ b/templates/themes.php @@ -11,10 +11,14 @@

-
+
+
+ + +
From 665c40d0d45ef46d1584c7b51cc941e849a633d9 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 00:19:47 +0100 Subject: [PATCH 02/23] Update w/ huebee CDN --- index.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.php b/index.php index d6682b8e..7bdfe307 100755 --- a/index.php +++ b/index.php @@ -318,6 +318,10 @@ $bridgedEnabled = $arrHostapdConf['BridgedEnable']; + + + + From 480319a3f494d33b300d2918ad6b0043268b61be Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 00:20:12 +0100 Subject: [PATCH 03/23] Update gulp build --- gulpfile.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 49bbd482..5df9aaa2 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -77,7 +77,10 @@ function modules() { // SB Admin2 CSS var sbadmin2CSS = gulp.src('./node_modules/startbootstrap-sb-admin-2/css/*') .pipe(gulp.dest('./dist/sb-admin-2/css')); - return merge(bootstrapJS, bootstrapSCSS, chartJS, dataTables, fontAwesome, jquery, jqueryEasing, sbadmin2JS, sbadmin2CSS); + // Huebee + var huebee = gulp.src('./node_modules/huebee/dist/*') + .pipe(gulp.dest('./dist/huebee')); + return merge(bootstrapJS, bootstrapSCSS, chartJS, dataTables, fontAwesome, jquery, jqueryEasing, sbadmin2JS, sbadmin2CSS, huebee); } // CSS task From 4af08391fac4ff6ddbccbf31a52440aad77bd30a Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 00:27:54 +0100 Subject: [PATCH 04/23] Update files from gulp build --- dist/huebee/huebee.css | 83 +++ dist/huebee/huebee.min.css | 4 + dist/huebee/huebee.pkgd.js | 1125 ++++++++++++++++++++++++++++++++ dist/huebee/huebee.pkgd.min.js | 22 + 4 files changed, 1234 insertions(+) create mode 100644 dist/huebee/huebee.css create mode 100644 dist/huebee/huebee.min.css create mode 100644 dist/huebee/huebee.pkgd.js create mode 100644 dist/huebee/huebee.pkgd.min.js diff --git a/dist/huebee/huebee.css b/dist/huebee/huebee.css new file mode 100644 index 00000000..f6ef1357 --- /dev/null +++ b/dist/huebee/huebee.css @@ -0,0 +1,83 @@ +/*! Huebee v2.1.0 +https://huebee.buzz +---------------------------------------------- */ + +.huebee { + position: absolute; + z-index: 1; + transform: translateY(0px); + transition: opacity 0.15s, transform 0.15s; +} + +.huebee.is-hidden { + opacity: 0; + transform: translateY(10px); +} + +.huebee.is-static-open { + position: relative; + z-index: auto; +} + +.huebee__container { + position: absolute; + left: 0; + top: 5px; + padding: 10px; + background: #EEE; + border-radius: 5px; + box-shadow: 0 5px 10px hsla(0, 0%, 0%, 0.3); +} + +.huebee.is-static-open .huebee__container { + position: relative; + display: inline-block; + left: auto; + top: auto; + box-shadow: none; +} + +.huebee__canvas { + display: block; + cursor: pointer; +} + +.huebee__cursor { + width: 15px; + height: 15px; + position: absolute; + left: 0px; + top: 0px; + box-sizing: content-box; + border: 3px solid white; + border-radius: 5px; + pointer-events: none; +} + +.huebee__cursor.is-hidden { opacity: 0; } + +.huebee__close-button { + display: block; + position: absolute; + width: 24px; + height: 24px; + top: -9px; + right: -9px; + border-radius: 12px; + background: #222; +} + +.huebee__close-button__x { + stroke: white; + stroke-width: 3; + stroke-linecap: round; +} + +.huebee__close-button:hover { + background: white; + cursor: pointer; +} + +.huebee__close-button:hover .huebee__close-button__x { + stroke: #222; +} diff --git a/dist/huebee/huebee.min.css b/dist/huebee/huebee.min.css new file mode 100644 index 00000000..3f826deb --- /dev/null +++ b/dist/huebee/huebee.min.css @@ -0,0 +1,4 @@ +/*! Huebee v2.1.0 +https://huebee.buzz +---------------------------------------------- */ +.huebee{position:absolute;z-index:1;transform:translateY(0);transition:opacity .15s,transform .15s}.huebee.is-hidden{opacity:0;transform:translateY(10px)}.huebee.is-static-open{position:relative;z-index:auto}.huebee__container{position:absolute;left:0;top:5px;padding:10px;background:#eee;border-radius:5px;box-shadow:0 5px 10px hsla(0,0%,0%,.3)}.huebee.is-static-open .huebee__container{position:relative;display:inline-block;left:auto;top:auto;box-shadow:none}.huebee__canvas{display:block;cursor:pointer}.huebee__cursor{width:15px;height:15px;position:absolute;left:0;top:0;box-sizing:content-box;border:3px solid #fff;border-radius:5px;pointer-events:none}.huebee__cursor.is-hidden{opacity:0}.huebee__close-button{display:block;position:absolute;width:24px;height:24px;top:-9px;right:-9px;border-radius:12px;background:#222}.huebee__close-button__x{stroke:#fff;stroke-width:3;stroke-linecap:round}.huebee__close-button:hover{background:#fff;cursor:pointer}.huebee__close-button:hover .huebee__close-button__x{stroke:#222} \ No newline at end of file diff --git a/dist/huebee/huebee.pkgd.js b/dist/huebee/huebee.pkgd.js new file mode 100644 index 00000000..704bd961 --- /dev/null +++ b/dist/huebee/huebee.pkgd.js @@ -0,0 +1,1125 @@ +/*! + * Huebee PACKAGED v2.1.0 + * 1-click color picker + * MIT license + * https://huebee.buzz + * Copyright 2020 Metafizzy + */ + +/** + * EvEmitter v1.1.0 + * Lil' event emitter + * MIT License + */ + +/* jshint unused: true, undef: true, strict: true */ + +( function( global, factory ) { + // universal module definition + /* jshint strict: false */ /* globals define, module, window */ + if ( typeof define == 'function' && define.amd ) { + // AMD - RequireJS + define( 'ev-emitter/ev-emitter',factory ); + } else if ( typeof module == 'object' && module.exports ) { + // CommonJS - Browserify, Webpack + module.exports = factory(); + } else { + // Browser globals + global.EvEmitter = factory(); + } + +}( typeof window != 'undefined' ? window : this, function() { + +"use strict"; + +function EvEmitter() {} + +var proto = EvEmitter.prototype; + +proto.on = function( eventName, listener ) { + if ( !eventName || !listener ) { + return; + } + // set events hash + var events = this._events = this._events || {}; + // set listeners array + var listeners = events[ eventName ] = events[ eventName ] || []; + // only add once + if ( listeners.indexOf( listener ) == -1 ) { + listeners.push( listener ); + } + + return this; +}; + +proto.once = function( eventName, listener ) { + if ( !eventName || !listener ) { + return; + } + // add event + this.on( eventName, listener ); + // set once flag + // set onceEvents hash + var onceEvents = this._onceEvents = this._onceEvents || {}; + // set onceListeners object + var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; + // set flag + onceListeners[ listener ] = true; + + return this; +}; + +proto.off = function( eventName, listener ) { + var listeners = this._events && this._events[ eventName ]; + if ( !listeners || !listeners.length ) { + return; + } + var index = listeners.indexOf( listener ); + if ( index != -1 ) { + listeners.splice( index, 1 ); + } + + return this; +}; + +proto.emitEvent = function( eventName, args ) { + var listeners = this._events && this._events[ eventName ]; + if ( !listeners || !listeners.length ) { + return; + } + // copy over to avoid interference if .off() in listener + listeners = listeners.slice(0); + args = args || []; + // once stuff + var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; + + for ( var i=0; i < listeners.length; i++ ) { + var listener = listeners[i] + var isOnce = onceListeners && onceListeners[ listener ]; + if ( isOnce ) { + // remove listener + // remove before trigger to prevent recursion + this.off( eventName, listener ); + // unset once flag + delete onceListeners[ listener ]; + } + // trigger listener + listener.apply( this, args ); + } + + return this; +}; + +proto.allOff = function() { + delete this._events; + delete this._onceEvents; +}; + +return EvEmitter; + +})); +/*! + * Unipointer v2.3.0 + * base class for doing one thing with pointer event + * MIT license + */ + +/*jshint browser: true, undef: true, unused: true, strict: true */ + +( function( window, factory ) { + // universal module definition + /* jshint strict: false */ /*global define, module, require */ + if ( typeof define == 'function' && define.amd ) { + // AMD + define( 'unipointer/unipointer',[ + 'ev-emitter/ev-emitter' + ], function( EvEmitter ) { + return factory( window, EvEmitter ); + }); + } else if ( typeof module == 'object' && module.exports ) { + // CommonJS + module.exports = factory( + window, + require('ev-emitter') + ); + } else { + // browser global + window.Unipointer = factory( + window, + window.EvEmitter + ); + } + +}( window, function factory( window, EvEmitter ) { + +'use strict'; + +function noop() {} + +function Unipointer() {} + +// inherit EvEmitter +var proto = Unipointer.prototype = Object.create( EvEmitter.prototype ); + +proto.bindStartEvent = function( elem ) { + this._bindStartEvent( elem, true ); +}; + +proto.unbindStartEvent = function( elem ) { + this._bindStartEvent( elem, false ); +}; + +/** + * Add or remove start event + * @param {Boolean} isAdd - remove if falsey + */ +proto._bindStartEvent = function( elem, isAdd ) { + // munge isAdd, default to true + isAdd = isAdd === undefined ? true : isAdd; + var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener'; + + // default to mouse events + var startEvent = 'mousedown'; + if ( window.PointerEvent ) { + // Pointer Events + startEvent = 'pointerdown'; + } else if ( 'ontouchstart' in window ) { + // Touch Events. iOS Safari + startEvent = 'touchstart'; + } + elem[ bindMethod ]( startEvent, this ); +}; + +// trigger handler methods for events +proto.handleEvent = function( event ) { + var method = 'on' + event.type; + if ( this[ method ] ) { + this[ method ]( event ); + } +}; + +// returns the touch that we're keeping track of +proto.getTouch = function( touches ) { + for ( var i=0; i < touches.length; i++ ) { + var touch = touches[i]; + if ( touch.identifier == this.pointerIdentifier ) { + return touch; + } + } +}; + +// ----- start event ----- // + +proto.onmousedown = function( event ) { + // dismiss clicks from right or middle buttons + var button = event.button; + if ( button && ( button !== 0 && button !== 1 ) ) { + return; + } + this._pointerDown( event, event ); +}; + +proto.ontouchstart = function( event ) { + this._pointerDown( event, event.changedTouches[0] ); +}; + +proto.onpointerdown = function( event ) { + this._pointerDown( event, event ); +}; + +/** + * pointer start + * @param {Event} event + * @param {Event or Touch} pointer + */ +proto._pointerDown = function( event, pointer ) { + // dismiss right click and other pointers + // button = 0 is okay, 1-4 not + if ( event.button || this.isPointerDown ) { + return; + } + + this.isPointerDown = true; + // save pointer identifier to match up touch events + this.pointerIdentifier = pointer.pointerId !== undefined ? + // pointerId for pointer events, touch.indentifier for touch events + pointer.pointerId : pointer.identifier; + + this.pointerDown( event, pointer ); +}; + +proto.pointerDown = function( event, pointer ) { + this._bindPostStartEvents( event ); + this.emitEvent( 'pointerDown', [ event, pointer ] ); +}; + +// hash of events to be bound after start event +var postStartEvents = { + mousedown: [ 'mousemove', 'mouseup' ], + touchstart: [ 'touchmove', 'touchend', 'touchcancel' ], + pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ], +}; + +proto._bindPostStartEvents = function( event ) { + if ( !event ) { + return; + } + // get proper events to match start event + var events = postStartEvents[ event.type ]; + // bind events to node + events.forEach( function( eventName ) { + window.addEventListener( eventName, this ); + }, this ); + // save these arguments + this._boundPointerEvents = events; +}; + +proto._unbindPostStartEvents = function() { + // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug) + if ( !this._boundPointerEvents ) { + return; + } + this._boundPointerEvents.forEach( function( eventName ) { + window.removeEventListener( eventName, this ); + }, this ); + + delete this._boundPointerEvents; +}; + +// ----- move event ----- // + +proto.onmousemove = function( event ) { + this._pointerMove( event, event ); +}; + +proto.onpointermove = function( event ) { + if ( event.pointerId == this.pointerIdentifier ) { + this._pointerMove( event, event ); + } +}; + +proto.ontouchmove = function( event ) { + var touch = this.getTouch( event.changedTouches ); + if ( touch ) { + this._pointerMove( event, touch ); + } +}; + +/** + * pointer move + * @param {Event} event + * @param {Event or Touch} pointer + * @private + */ +proto._pointerMove = function( event, pointer ) { + this.pointerMove( event, pointer ); +}; + +// public +proto.pointerMove = function( event, pointer ) { + this.emitEvent( 'pointerMove', [ event, pointer ] ); +}; + +// ----- end event ----- // + + +proto.onmouseup = function( event ) { + this._pointerUp( event, event ); +}; + +proto.onpointerup = function( event ) { + if ( event.pointerId == this.pointerIdentifier ) { + this._pointerUp( event, event ); + } +}; + +proto.ontouchend = function( event ) { + var touch = this.getTouch( event.changedTouches ); + if ( touch ) { + this._pointerUp( event, touch ); + } +}; + +/** + * pointer up + * @param {Event} event + * @param {Event or Touch} pointer + * @private + */ +proto._pointerUp = function( event, pointer ) { + this._pointerDone(); + this.pointerUp( event, pointer ); +}; + +// public +proto.pointerUp = function( event, pointer ) { + this.emitEvent( 'pointerUp', [ event, pointer ] ); +}; + +// ----- pointer done ----- // + +// triggered on pointer up & pointer cancel +proto._pointerDone = function() { + this._pointerReset(); + this._unbindPostStartEvents(); + this.pointerDone(); +}; + +proto._pointerReset = function() { + // reset properties + this.isPointerDown = false; + delete this.pointerIdentifier; +}; + +proto.pointerDone = noop; + +// ----- pointer cancel ----- // + +proto.onpointercancel = function( event ) { + if ( event.pointerId == this.pointerIdentifier ) { + this._pointerCancel( event, event ); + } +}; + +proto.ontouchcancel = function( event ) { + var touch = this.getTouch( event.changedTouches ); + if ( touch ) { + this._pointerCancel( event, touch ); + } +}; + +/** + * pointer cancel + * @param {Event} event + * @param {Event or Touch} pointer + * @private + */ +proto._pointerCancel = function( event, pointer ) { + this._pointerDone(); + this.pointerCancel( event, pointer ); +}; + +// public +proto.pointerCancel = function( event, pointer ) { + this.emitEvent( 'pointerCancel', [ event, pointer ] ); +}; + +// ----- ----- // + +// utility function for getting x/y coords from event +Unipointer.getPointerPoint = function( pointer ) { + return { + x: pointer.pageX, + y: pointer.pageY + }; +}; + +// ----- ----- // + +return Unipointer; + +})); +/*! + * Huebee v2.1.0 + * 1-click color picker + * MIT license + * https://huebee.buzz + * Copyright 2020 Metafizzy + */ + +/* jshint browser: true, unused: true, undef: true */ + +( function( window, factory ) { + // universal module definition + if ( typeof define == 'function' && define.amd ) { + /* globals define */ // AMD + define( [ + 'ev-emitter/ev-emitter', + 'unipointer/unipointer', + ], function( EvEmitter, Unipointer ) { + return factory( window, EvEmitter, Unipointer ); + } ); + } else if ( typeof module == 'object' && module.exports ) { + // CommonJS + module.exports = factory( + window, + require('ev-emitter'), + require('unipointer') + ); + } else { + // browser global + window.Huebee = factory( + window, + window.EvEmitter, + window.Unipointer + ); + } + +}( window, function factory( window, EvEmitter, Unipointer ) { + +function Huebee( anchor, options ) { + // anchor + anchor = getQueryElement( anchor ); + if ( !anchor ) { + throw new Error( 'Bad element for Huebee: ' + anchor ); + } + this.anchor = anchor; + // options + this.options = {}; + this.option( Huebee.defaults ); + this.option( options ); + // kick things off + this.create(); +} + +Huebee.defaults = { + hues: 12, + hue0: 0, + shades: 5, + saturations: 3, + notation: 'shortHex', + setText: true, + setBGColor: true, +}; + +var proto = Huebee.prototype = Object.create( EvEmitter.prototype ); + +proto.option = function( options ) { + this.options = extend( this.options, options ); +}; + +// globally unique identifiers +var GUID = 0; +// internal store of all Colcade intances +var instances = {}; + +proto.create = function() { + // add guid for Colcade.data + var guid = this.guid = ++GUID; + this.anchor.huebeeGUID = guid; + instances[ guid ] = this; // associate via id + // properties + this.setBGElems = this.getSetElems( this.options.setBGColor ); + this.setTextElems = this.getSetElems( this.options.setText ); + // events + // HACK: this is getting ugly + this.outsideCloseIt = this.outsideClose.bind( this ); + this.onDocKeydown = this.docKeydown.bind( this ); + this.closeIt = this.close.bind( this ); + this.openIt = this.open.bind( this ); + this.onElemTransitionend = this.elemTransitionend.bind( this ); + // open events + this.isInputAnchor = this.anchor.nodeName == 'INPUT'; + if ( !this.options.staticOpen ) { + this.anchor.addEventListener( 'click', this.openIt ); + this.anchor.addEventListener( 'focus', this.openIt ); + } + // change event + if ( this.isInputAnchor ) { + this.anchor.addEventListener( 'input', this.inputInput.bind( this ) ); + } + // create element + var element = this.element = document.createElement('div'); + element.className = 'huebee '; + element.className += this.options.staticOpen ? 'is-static-open ' : + 'is-hidden '; + element.className += this.options.className || ''; + // create container + var container = this.container = document.createElement('div'); + container.className = 'huebee__container'; + // do not blur if padding clicked + function onContainerPointerStart( event ) { + if ( event.target == container ) { + event.preventDefault(); + } + } + container.addEventListener( 'mousedown', onContainerPointerStart ); + container.addEventListener( 'touchstart', onContainerPointerStart ); + // create canvas + this.createCanvas(); + // create cursor + this.cursor = document.createElement('div'); + this.cursor.className = 'huebee__cursor is-hidden'; + container.appendChild( this.cursor ); + // create close button + this.createCloseButton(); + + element.appendChild( container ); + // set relative position on parent + if ( !this.options.staticOpen ) { + var parentStyle = getComputedStyle( this.anchor.parentNode ); + if ( parentStyle.position != 'relative' && parentStyle.position != 'absolute' ) { + this.anchor.parentNode.style.position = 'relative'; + } + } + + // satY, y position where saturation grid starts + var customLength = this.getCustomLength(); + this.satY = customLength ? Math.ceil( customLength / this.options.hues ) + 1 : 0; + // colors + this.updateColors(); + this.setAnchorColor(); + if ( this.options.staticOpen ) { + this.open(); + } +}; + +proto.getSetElems = function( option ) { + if ( option === true ) { + return [ this.anchor ]; + } else if ( typeof option == 'string' ) { + return document.querySelectorAll( option ); + } +}; + +proto.getCustomLength = function() { + var customColors = this.options.customColors; + return customColors && customColors.length || 0; +}; + +proto.createCanvas = function() { + var canvas = this.canvas = document.createElement('canvas'); + canvas.className = 'huebee__canvas'; + this.ctx = canvas.getContext('2d'); + // canvas pointer events + var canvasPointer = this.canvasPointer = new Unipointer(); + canvasPointer._bindStartEvent( canvas ); + canvasPointer.on( 'pointerDown', this.canvasPointerDown.bind( this ) ); + canvasPointer.on( 'pointerMove', this.canvasPointerMove.bind( this ) ); + this.container.appendChild( canvas ); +}; + +var svgURI = 'http://www.w3.org/2000/svg'; + +proto.createCloseButton = function() { + if ( this.options.staticOpen ) { + return; + } + var svg = document.createElementNS( svgURI, 'svg' ); + svg.setAttribute( 'class', 'huebee__close-button' ); + svg.setAttribute( 'viewBox', '0 0 24 24' ); + svg.setAttribute( 'width', '24' ); + svg.setAttribute( 'height', '24' ); + var path = document.createElementNS( svgURI, 'path' ); + path.setAttribute( 'd', 'M 7,7 L 17,17 M 17,7 L 7,17' ); + path.setAttribute( 'class', 'huebee__close-button__x' ); + svg.appendChild( path ); + svg.addEventListener( 'click', this.closeIt ); + this.container.appendChild( svg ); +}; + +proto.updateColors = function() { + // hash of color, h, s, l according to x,y grid position + // [x,y] = { color, h, s, l } + this.swatches = {}; + // hash of gridX,gridY position according to color + // [#09F] = { x, y } + this.colorGrid = {}; + this.updateColorModer(); + + var shades = this.options.shades; + var sats = this.options.saturations; + var hues = this.options.hues; + + // render custom colors + if ( this.getCustomLength() ) { + var customI = 0; + this.options.customColors.forEach( function( color ) { + var x = customI % hues; + var y = Math.floor( customI/hues ); + var swatch = getSwatch( color ); + if ( swatch ) { + this.addSwatch( swatch, x, y ); + customI++; + } + }.bind( this ) ); + } + + // render saturation grids + var i; + for ( i = 0; i < sats; i++ ) { + var sat = 1 - i/sats; + var yOffset = shades * i + this.satY; + this.updateSaturationGrid( i, sat, yOffset ); + } + + // render grays + var grayCount = this.getGrayCount(); + for ( i = 0; i < grayCount; i++ ) { + var lum = 1 - i / ( shades + 1 ); + var color = this.colorModer( 0, 0, lum ); + var swatch = getSwatch( color ); + this.addSwatch( swatch, hues + 1, i ); + } +}; + +// get shades + black & white; else 0 +proto.getGrayCount = function() { + return this.options.shades ? this.options.shades + 2 : 0; +}; + +proto.updateSaturationGrid = function( i, sat, yOffset ) { + var shades = this.options.shades; + var hues = this.options.hues; + var hue0 = this.options.hue0; + for ( var row = 0; row < shades; row++ ) { + for ( var col = 0; col < hues; col++ ) { + var hue = Math.round( col * 360/hues + hue0 ) % 360; + var lum = 1 - ( row + 1 ) / ( shades + 1 ); + var color = this.colorModer( hue, sat, lum ); + var swatch = getSwatch( color ); + var gridY = row + yOffset; + this.addSwatch( swatch, col, gridY ); + } + } +}; + +proto.addSwatch = function( swatch, gridX, gridY ) { + // add swatch color to hash + this.swatches[ gridX + ',' + gridY ] = swatch; + // add color to colorGrid + this.colorGrid[ swatch.color.toUpperCase() ] = { + x: gridX, + y: gridY, + }; +}; + +var colorModers = { + hsl: function( h, s, l ) { + s = Math.round( s * 100 ); + l = Math.round( l * 100 ); + return 'hsl(' + h + ', ' + s + '%, ' + l + '%)'; + }, + hex: hsl2hex, + shortHex: function( h, s, l ) { + var hex = hsl2hex( h, s, l ); + return roundHex( hex ); + }, +}; + +proto.updateColorModer = function() { + this.colorModer = colorModers[ this.options.notation ] || colorModers.shortHex; +}; + +proto.renderColors = function() { + var gridSize = this.gridSize * 2; + for ( var position in this.swatches ) { + var swatch = this.swatches[ position ]; + var duple = position.split(','); + var gridX = duple[0]; + var gridY = duple[1]; + this.ctx.fillStyle = swatch.color; + this.ctx.fillRect( gridX * gridSize, gridY * gridSize, gridSize, gridSize ); + } +}; + +proto.setAnchorColor = function() { + if ( this.isInputAnchor ) { + this.setColor( this.anchor.value ); + } +}; + +// ----- events ----- // + +var docElem = document.documentElement; + +proto.open = function() { + /* jshint unused: false */ + if ( this.isOpen ) { + return; + } + var anchor = this.anchor; + var elem = this.element; + if ( !this.options.staticOpen ) { + elem.style.left = anchor.offsetLeft + 'px'; + elem.style.top = anchor.offsetTop + anchor.offsetHeight + 'px'; + } + this.bindOpenEvents( true ); + elem.removeEventListener( 'transitionend', this.onElemTransitionend ); + // add huebee to DOM + anchor.parentNode.insertBefore( elem, anchor.nextSibling ); + // measurements + var duration = getComputedStyle( elem ).transitionDuration; + this.hasTransition = duration && duration != 'none' && parseFloat( duration ); + + this.isOpen = true; + this.updateSizes(); + this.renderColors(); + this.setAnchorColor(); + + // trigger reflow for transition + /* eslint-disable-next-line no-unused-vars */ + var h = elem.offsetHeight; + elem.classList.remove('is-hidden'); +}; + +proto.bindOpenEvents = function( isAdd ) { + if ( this.options.staticOpen ) { + return; + } + var method = ( isAdd ? 'add' : 'remove' ) + 'EventListener'; + docElem[ method ]( 'mousedown', this.outsideCloseIt ); + docElem[ method ]( 'touchstart', this.outsideCloseIt ); + document[ method ]( 'focusin', this.outsideCloseIt ); + document[ method ]( 'keydown', this.onDocKeydown ); + this.anchor[ method ]( 'blur', this.closeIt ); +}; + +proto.updateSizes = function() { + var hues = this.options.hues; + var shades = this.options.shades; + var sats = this.options.saturations; + var grayCount = this.getGrayCount(); + var customLength = this.getCustomLength(); + + this.cursorBorder = parseInt( getComputedStyle( this.cursor ).borderTopWidth, 10 ); + this.gridSize = Math.round( this.cursor.offsetWidth - this.cursorBorder * 2 ); + this.canvasOffset = { + x: this.canvas.offsetLeft, + y: this.canvas.offsetTop, + }; + var cols, rows; + if ( customLength && !grayCount ) { + // custom colors only + cols = Math.min( customLength, hues ); + rows = Math.ceil( customLength/hues ); + } else { + cols = hues + 2; + rows = Math.max( shades * sats + this.satY, grayCount ); + } + var width = this.canvas.width = cols * this.gridSize * 2; + this.canvas.height = rows * this.gridSize * 2; + this.canvas.style.width = width/2 + 'px'; +}; + +// close if target is not anchor or element +proto.outsideClose = function( event ) { + var isAnchor = this.anchor.contains( event.target ); + var isElement = this.element.contains( event.target ); + if ( !isAnchor && !isElement ) { + this.close(); + } +}; + +var closeKeydowns = { + 13: true, // enter + 27: true, // esc +}; + +proto.docKeydown = function( event ) { + if ( closeKeydowns[ event.keyCode ] ) { + this.close(); + } +}; + +var supportsTransitions = typeof docElem.style.transform == 'string'; + +proto.close = function() { + if ( !this.isOpen ) { + return; + } + + if ( supportsTransitions && this.hasTransition ) { + this.element.addEventListener( 'transitionend', this.onElemTransitionend ); + } else { + this.remove(); + } + this.element.classList.add('is-hidden'); + + this.bindOpenEvents( false ); + this.isOpen = false; +}; + +proto.remove = function() { + var parent = this.element.parentNode; + if ( parent.contains( this.element ) ) { + parent.removeChild( this.element ); + } +}; + +proto.elemTransitionend = function( event ) { + if ( event.target != this.element ) { + return; + } + this.element.removeEventListener( 'transitionend', this.onElemTransitionend ); + this.remove(); +}; + +proto.inputInput = function() { + this.setColor( this.anchor.value ); +}; + +// ----- canvas pointer ----- // + +proto.canvasPointerDown = function( event, pointer ) { + event.preventDefault(); + this.updateOffset(); + this.canvasPointerChange( pointer ); +}; + +proto.updateOffset = function() { + var boundingRect = this.canvas.getBoundingClientRect(); + this.offset = { + x: boundingRect.left + window.pageXOffset, + y: boundingRect.top + window.pageYOffset, + }; +}; + +proto.canvasPointerMove = function( event, pointer ) { + this.canvasPointerChange( pointer ); +}; + +proto.canvasPointerChange = function( pointer ) { + var x = Math.round( pointer.pageX - this.offset.x ); + var y = Math.round( pointer.pageY - this.offset.y ); + var gridSize = this.gridSize; + var sx = Math.floor( x/gridSize ); + var sy = Math.floor( y/gridSize ); + + var swatch = this.swatches[ sx + ',' + sy ]; + this.setSwatch( swatch ); +}; + +// ----- select ----- // + +proto.setColor = function( color ) { + var swatch = getSwatch( color ); + this.setSwatch( swatch ); +}; + +proto.setSwatch = function( swatch ) { + var color = swatch && swatch.color; + if ( !swatch ) { + return; + } + var wasSameColor = color == this.color; + // color properties + this.color = color; + this.hue = swatch.hue; + this.sat = swatch.sat; + this.lum = swatch.lum; + // estimate if color can have dark or white text + var lightness = this.lum - Math.cos( ( this.hue + 70 )/180 * Math.PI ) * 0.15; + this.isLight = lightness > 0.5; + // cursor + var gridPosition = this.colorGrid[ color.toUpperCase() ]; + this.updateCursor( gridPosition ); + // set texts & backgrounds + this.setTexts(); + this.setBackgrounds(); + // event + if ( !wasSameColor ) { + this.emitEvent( 'change', [ color, swatch.hue, swatch.sat, swatch.lum ] ); + } +}; + +proto.setTexts = function() { + if ( !this.setTextElems ) { + return; + } + for ( var i = 0; i < this.setTextElems.length; i++ ) { + var elem = this.setTextElems[i]; + var property = elem.nodeName == 'INPUT' ? 'value' : 'textContent'; + elem[ property ] = this.color; + } +}; + +proto.setBackgrounds = function() { + if ( !this.setBGElems ) { + return; + } + var textColor = this.isLight ? '#222' : 'white'; + for ( var i = 0; i < this.setBGElems.length; i++ ) { + var elem = this.setBGElems[i]; + elem.style.backgroundColor = this.color; + elem.style.color = textColor; + } +}; + +proto.updateCursor = function( position ) { + if ( !this.isOpen ) { + return; + } + // show cursor if color is on the grid + var classMethod = position ? 'remove' : 'add'; + this.cursor.classList[ classMethod ]('is-hidden'); + + if ( !position ) { + return; + } + var gridSize = this.gridSize; + var offset = this.canvasOffset; + var border = this.cursorBorder; + this.cursor.style.left = position.x * gridSize + offset.x - border + 'px'; + this.cursor.style.top = position.y * gridSize + offset.y - border + 'px'; +}; + +// -------------------------- htmlInit -------------------------- // + +var console = window.console; + +function htmlInit() { + var elems = document.querySelectorAll('[data-huebee]'); + for ( var i = 0; i < elems.length; i++ ) { + var elem = elems[i]; + var attr = elem.getAttribute('data-huebee'); + var options; + try { + options = attr && JSON.parse( attr ); + } catch ( error ) { + // log error, do not initialize + if ( console ) { + console.error( 'Error parsing data-huebee on ' + elem.className + + ': ' + error ); + } + continue; + } + // initialize + new Huebee( elem, options ); + } +} + +var readyState = document.readyState; +if ( readyState == 'complete' || readyState == 'interactive' ) { + htmlInit(); +} else { + document.addEventListener( 'DOMContentLoaded', htmlInit ); +} + +// -------------------------- Huebee.data -------------------------- // + +Huebee.data = function( elem ) { + elem = getQueryElement( elem ); + var id = elem && elem.huebeeGUID; + return id && instances[ id ]; +}; + +// -------------------------- getSwatch -------------------------- // + +// proxy canvas used to check colors +var proxyCanvas = document.createElement('canvas'); +proxyCanvas.width = proxyCanvas.height = 1; +var proxyCtx = proxyCanvas.getContext('2d'); + +function getSwatch( color ) { + // check that color value is valid + proxyCtx.clearRect( 0, 0, 1, 1 ); + proxyCtx.fillStyle = '#010203'; // reset value + proxyCtx.fillStyle = color; + proxyCtx.fillRect( 0, 0, 1, 1 ); + var data = proxyCtx.getImageData( 0, 0, 1, 1 ).data; + // convert to array, imageData not array, #10 + data = [ data[0], data[1], data[2], data[3] ]; + if ( data.join(',') == '1,2,3,255' ) { + // invalid color + return; + } + // convert rgb to hsl + var hsl = rgb2hsl.apply( this, data ); + return { + color: color.trim(), + hue: hsl[0], + sat: hsl[1], + lum: hsl[2], + }; +} + +// -------------------------- utils -------------------------- // + +function extend( a, b ) { + for ( var prop in b ) { + a[ prop ] = b[ prop ]; + } + return a; +} + +function getQueryElement( elem ) { + if ( typeof elem == 'string' ) { + elem = document.querySelector( elem ); + } + return elem; +} + +function hsl2hex( h, s, l ) { + var rgb = hsl2rgb( h, s, l ); + return rgb2hex( rgb ); +} + +// thx jfsiii +// https://github.com/jfsiii/chromath/blob/master/src/static.js#L312 +/* eslint-disable max-statements-per-line */ +function hsl2rgb( h, s, l ) { + + var C = ( 1 - Math.abs( 2 * l - 1 ) ) * s; + var hp = h/60; + var X = C * ( 1 - Math.abs( hp % 2 - 1 ) ); + var rgb, m; + + switch ( Math.floor( hp ) ) { + case 0: rgb = [ C, X, 0 ]; break; + case 1: rgb = [ X, C, 0 ]; break; + case 2: rgb = [ 0, C, X ]; break; + case 3: rgb = [ 0, X, C ]; break; + case 4: rgb = [ X, 0, C ]; break; + case 5: rgb = [ C, 0, X ]; break; + default: rgb = [ 0, 0, 0 ]; + } + + m = l - ( C/2 ); + rgb = rgb.map( function( v ) { + return v + m; + } ); + + return rgb; +} + +function rgb2hsl( r, g, b ) { + r /= 255; g /= 255; b /= 255; + + var M = Math.max( r, g, b ); + var m = Math.min( r, g, b ); + var C = M - m; + var L = 0.5 * ( M + m ); + var S = C === 0 ? 0 : C / ( 1 - Math.abs( 2 * L - 1 ) ); + + var h; + if ( C === 0 ) { + h = 0; // spec'd as undefined, but usually set to 0 + } else if ( M === r ) { + h = ( ( g - b )/C ) % 6; + } else if ( M === g ) { + h = ( ( b - r )/C ) + 2; + } else if ( M === b ) { + h = ( ( r - g )/C ) + 4; + } + + var H = 60 * h; + + return [ H, parseFloat( S ), parseFloat( L ) ]; +} +/* eslint-enable max-statements-per-line */ + +function rgb2hex( rgb ) { + var hex = rgb.map( function( value ) { + value = Math.round( value * 255 ); + var hexNum = value.toString( 16 ).toUpperCase(); + // left pad 0 + hexNum = hexNum.length < 2 ? '0' + hexNum : hexNum; + return hexNum; + } ); + + return '#' + hex.join(''); +} + +// #123456 -> #135 +// grab first digit from hex +// not mathematically accurate, but makes for better palette +function roundHex( hex ) { + return '#' + hex[1] + hex[3] + hex[5]; +} + +// -------------------------- -------------------------- // + +return Huebee; + +} ) ); diff --git a/dist/huebee/huebee.pkgd.min.js b/dist/huebee/huebee.pkgd.min.js new file mode 100644 index 00000000..fdd8c3ac --- /dev/null +++ b/dist/huebee/huebee.pkgd.min.js @@ -0,0 +1,22 @@ +/*! + * Huebee PACKAGED v2.1.0 + * 1-click color picker + * MIT license + * https://huebee.buzz + * Copyright 2020 Metafizzy + */ +(function(t,e){if(typeof define=="function"&&define.amd){define("ev-emitter/ev-emitter",e)}else if(typeof module=="object"&&module.exports){module.exports=e()}else{t.EvEmitter=e()}})(typeof window!="undefined"?window:this,function(){"use strict";function t(){}var e=t.prototype;e.on=function(t,e){if(!t||!e){return}var i=this._events=this._events||{};var n=i[t]=i[t]||[];if(n.indexOf(e)==-1){n.push(e)}return this};e.once=function(t,e){if(!t||!e){return}this.on(t,e);var i=this._onceEvents=this._onceEvents||{};var n=i[t]=i[t]||{};n[e]=true;return this};e.off=function(t,e){var i=this._events&&this._events[t];if(!i||!i.length){return}var n=i.indexOf(e);if(n!=-1){i.splice(n,1)}return this};e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(!i||!i.length){return}i=i.slice(0);e=e||[];var n=this._onceEvents&&this._onceEvents[t];for(var o=0;o.5;var o=this.colorGrid[e.toUpperCase()];this.updateCursor(o);this.setTexts();this.setBackgrounds();if(!i){this.emitEvent("change",[e,t.hue,t.sat,t.lum])}};o.setTexts=function(){if(!this.setTextElems){return}for(var t=0;t Date: Tue, 30 Jun 2020 00:28:39 +0100 Subject: [PATCH 05/23] Replace CDN links w/ dist --- index.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/index.php b/index.php index 7bdfe307..4345c58b 100755 --- a/index.php +++ b/index.php @@ -82,6 +82,9 @@ $bridgedEnabled = $arrHostapdConf['BridgedEnable']; + + + @@ -318,9 +321,8 @@ $bridgedEnabled = $arrHostapdConf['BridgedEnable']; - - - + + From 8132296d4cdfd33a740da1c5af9e1b588f0bb339 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 00:29:27 +0100 Subject: [PATCH 06/23] Update dependencies --- package.json | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index a96a30c8..2d0f1236 100644 --- a/package.json +++ b/package.json @@ -30,20 +30,22 @@ "url": "https://github.com/billz/raspap-webgui.git" }, "dependencies": { - "startbootstrap-sb-admin-2": "4.0.7", - "jquery": "^3.5.0" - }, - "devDependencies": { "browser-sync": "^2.26.7", "del": "^5.1.0", "gulp": "^4.0.2", "gulp-autoprefixer": "^7.0.1", - "gulp-clean-css": "^4.2.0", + "gulp-clean-css": "^4.3.0", "gulp-header": "^2.0.9", "gulp-plumber": "^1.2.1", - "gulp-rename": "1.4.0", - "gulp-sass": "^4.0.2", + "gulp-rename": "^2.0.0", "gulp-uglify": "^3.0.2", - "merge-stream": "^2.0.0" + "huebee": "^2.1.0", + "jquery": "^3.5.0", + "merge-stream": "^2.0.0", + "node-gyp": "^7.0.0", + "startbootstrap-sb-admin-2": "4.0.7" + }, + "devDependencies": { + "gulp-sass": "^4.0.2" } } From 4eb3d6b24ce0903af9b48940c6a9d40e3bb26ac2 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 16:46:04 +0100 Subject: [PATCH 07/23] Convert to dynamic css --- app/css/custom.php | 258 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 app/css/custom.php diff --git a/app/css/custom.php b/app/css/custom.php new file mode 100644 index 00000000..6b50cc58 --- /dev/null +++ b/app/css/custom.php @@ -0,0 +1,258 @@ + + + +/* +Theme Name: RaspAP default +Author: @billz +Author URI: https://github.com/billz +Description: Default theme for RaspAP +License: GNU General Public License v3.0 +*/ + +body { + color: #212529; +} + +.page-header { + margin: 20px 0 20px; +} + +.navbar-logo { + margin-top: 0.5em; + margin-left: 0.5em; +} + +/* Small devices (portrait phones, up to 576px) */ +@media (max-width: 576px) { + .container-fluid, .card-body, .col-md-6 { padding-left: 0.5rem; padding-right: 0.5rem; } + .card .card-header { padding: .75rem .5rem; font-size: 1.0rem; } + .row { margin-left: 0rem; margin-right: 0rem; } + .col-lg-12 { padding-right: 0.25rem; padding-left: 0.25rem; } + .form-group.col-md-6 { margin-left: -0.5rem; } + .js-wifi-stations { margin-left: -0.5rem; margin-right: -0.5rem; } + h4.mt-3 { margin-left: 0.5rem; } +} + +.sidebar { + background-color: #f8f9fc; +} + +.sidebar-brand-text { + text-transform: none; + color: #212529; + font-size: 2.0rem; + font-weight: 500; + font-family: Helvetica, Arial, sans-serif; +} + +.sidebar .nav-item.active .nav-link { + font-weight: 500; +} + +.card .card-header { + border-color: ; + color: #fff; + background-color: ; +} + +.btn-primary { + color: ; + border-color: ; + background-color: #fff; +} + +.card-footer { + background-color: #f2f1f0; +} + +.nav-item { + font-size: 0.85rem; +} + +.nav-tabs .nav-link.active, +.nav-tabs .nav-link { + font-size: 1.0rem; +} + +.nav-tabs a.nav-link { + color: #6e707e; +} + +a.nav-link.active { + font-weight: bolder; +} + +.sidebar .nav-item .nav-link { + padding: 0.6rem 0.6rem 0.6rem 1.0rem; +} + +.alert-success { + background-color: #d4edda; +} + +.btn-primary { + background-color: #fff; +} + +.btn-warning { + color: #333; +} + +.btn-primary:hover { + background-color: ; + border-color: ; +} + +i.fa.fa-bars { + color: #d1d3e2; +} + +i.fa.fa-bars:hover{ + color: #6e707e; +} + +.info-item { + width: 10rem; + float: left; +} + +.info-item-xs { + font-size: 0.7rem; + margin-left: 0.3rem; +} + +.info-item-wifi { + width: 6rem; + float: left; +} + +.webconsole { + width:100%; + height:100%; + border:1px solid; +} + +#console { + height:500px; +} + +.systemtabcontent { + height:100%; + min-height:500px; +} + +.service-status { + border-width: 0; +} + +.service-status-up { + color: #a1ec38; +} + +.service-status-warn { + color: #f6f044; +} + +.service-status-down { + color: #f80107; + animation: flash 1s linear infinite; +} +@keyframes flash { + 50% { + opacity: 0; + } +} + +.logoutput { + width:100%; + height: 20rem; + border: 1px solid #d1d3e2; + border-radius: .35rem; +} + +pre.unstyled { + border-width: 0; + background-color: transparent; + padding: 0; +} + +.dhcp-static-leases { + margin-top: 1em; + margin-bottom: 1em; +} + +.dhcp-static-lease-row { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +.loading-spinner { + background: url("../../app/img/loading-spinner.gif") no-repeat scroll center center transparent; + min-height: 150px; + width: 100%; +} + +.js-reload-wifi-stations { + min-width: 10rem; +} + +.sidebar.toggled .nav-item .nav-link span { + display: none; +} .sidebar .nav-item .nav-link i, +.sidebar .nav-item .nav-link span { + font-size: 1.0rem; +} + +.btn-warning:hover { + color: #000; +} + +.toggle-off.btn { + padding-left: 1.2rem; + font-size: 0.9rem!important; +} + +.toggle-on.btn { + font-size: 0.9rem!important; +} + +canvas#divDBChartBandwidthhourly { + height: 350px!important; +} + +.chart-container { + height: 150px; + width: 200px; +} + +.table { + margin-bottom: 0rem; +} + +.check-hidden { + visibility: hidden; +} + +.check-updated { + opacity: 0; + color: #90ee90; +} + +.check-progress { + color: #999; +} + +.fa-check { + color: #90ee90; +} + +.fa-times { + color: #ff4500; +} + From 77d59c9788c518058a54518d977a62d9bc26d392 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 16:46:51 +0100 Subject: [PATCH 08/23] Initial commit --- app/js/huebee.js | 23 +++++++++++++++++++++++ app/lib/color.php | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 app/js/huebee.js create mode 100644 app/lib/color.php diff --git a/app/js/huebee.js b/app/js/huebee.js new file mode 100644 index 00000000..1aa5f278 --- /dev/null +++ b/app/js/huebee.js @@ -0,0 +1,23 @@ +// Initialize Huebee color picker +var elem = document.querySelector('.color-input'); +var hueb = new Huebee( elem, { + notation: 'hex', + saturations: 2, + customColors: [ '#d8224c', '#dd4814', '#ea0', '#19f', '#333' ], + className: 'light-picker', + hue0: 210 +}); + +// Set custom color if defined +var color = getCookie('color'); +if (color == null || color == '') { + color = '#d8224c'; +} +hueb.setColor(color); + +// Change event +hueb.on( 'change', function( color, hue, sat, lum ) { + setCookie('color',color,90); + console.log(color) +}) + diff --git a/app/lib/color.php b/app/lib/color.php new file mode 100644 index 00000000..3adb2978 --- /dev/null +++ b/app/lib/color.php @@ -0,0 +1,21 @@ + + + + +.card .card-header { + border-color: ; + color: #fff; + background-color: ; +} + +.btn-primary { + color: ; + border-color: ; + background-color: #fff; +} From 3603e099fedc640ff5a5d7b3367d009b9f1d6bfa Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 16:50:21 +0100 Subject: [PATCH 09/23] Update extraFooterScripts --- includes/dashboard.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/dashboard.php b/includes/dashboard.php index ab26c934..60c3aa83 100755 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -200,6 +200,7 @@ function DisplayDashboard(&$extraFooterScripts) ) ); $extraFooterScripts[] = array('src'=>'app/js/dashboardchart.js', 'defer'=>false); + $extraFooterScripts[] = array('src'=>'app/js/linkquality.js', 'defer'=>false); } From 2a1f531ffa32d265f9ee7788938ad086ccdce753 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 16:52:18 +0100 Subject: [PATCH 10/23] Cleanup --- index.php | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/index.php b/index.php index 4345c58b..45abcd92 100755 --- a/index.php +++ b/index.php @@ -45,12 +45,7 @@ require_once 'includes/torproxy.php'; $output = $return = 0; $page = $_GET['page']; -if (!isset($_COOKIE['theme'])) { - $theme = "custom.css"; -} else { - $theme = $_COOKIE['theme']; -} -$theme_url = 'app/css/'.htmlspecialchars($theme, ENT_QUOTES); +$theme_url = getThemeOpt(); if ($_COOKIE['sidebarToggled'] == 'true' ) { $toggleState = "toggled"; @@ -270,7 +265,7 @@ $bridgedEnabled = $arrHostapdConf['BridgedEnable']; SaveTORAndVPNConfig(); break; case "theme_conf": - DisplayThemeConfig(); + DisplayThemeConfig($extraFooterScripts); break; case "data_use": DisplayDataUsage($extraFooterScripts); @@ -321,17 +316,10 @@ $bridgedEnabled = $arrHostapdConf['BridgedEnable']; - - - - - - - Date: Tue, 30 Jun 2020 16:53:52 +0100 Subject: [PATCH 11/23] Create getThemeOpt --- includes/functions.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/includes/functions.php b/includes/functions.php index 55d1d1f7..a4945016 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -417,3 +417,14 @@ function formatDateAgo($datetime, $full = false) if (!$full) $string = array_slice($string, 0, 1); return $string ? implode(', ', $string) . ' ago' : 'just now'; } + +function getThemeOpt() +{ + if (!isset($_COOKIE['theme'])) { + $theme = "custom.php"; + } else { + $theme = $_COOKIE['theme']; + } + return 'app/css/'.htmlspecialchars($theme, ENT_QUOTES); +} + From 2916bd199827fa4aa19dc0ef865e19b75eced5aa Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 16:54:30 +0100 Subject: [PATCH 12/23] Dynamic theme color support --- app/css/custom.css | 245 ------------------------------------------- app/js/custom.js | 3 +- includes/themes.php | 7 +- templates/themes.php | 2 +- 4 files changed, 8 insertions(+), 249 deletions(-) delete mode 100644 app/css/custom.css diff --git a/app/css/custom.css b/app/css/custom.css deleted file mode 100644 index c8e6324d..00000000 --- a/app/css/custom.css +++ /dev/null @@ -1,245 +0,0 @@ -/* -Theme Name: RaspAP default -Author: @billz -Author URI: https://github.com/billz -Description: Default theme for RaspAP -License: GNU General Public License v3.0 -*/ - -body { - color: #212529; -} - -.page-header { - margin: 20px 0 20px; -} - -.navbar-logo { - margin-top: 0.5em; - margin-left: 0.5em; -} - -/* Small devices (portrait phones, up to 576px) */ -@media (max-width: 576px) { - .container-fluid, .card-body, .col-md-6 { padding-left: 0.5rem; padding-right: 0.5rem; } - .card .card-header { padding: .75rem .5rem; font-size: 1.0rem; } - .row { margin-left: 0rem; margin-right: 0rem; } - .col-lg-12 { padding-right: 0.25rem; padding-left: 0.25rem; } - .form-group.col-md-6 { margin-left: -0.5rem; } - .js-wifi-stations { margin-left: -0.5rem; margin-right: -0.5rem; } - h4.mt-3 { margin-left: 0.5rem; } -} - -.sidebar { - background-color: #f8f9fc; -} - -.sidebar-brand-text { - text-transform: none; - color: #212529; - font-size: 2.0rem; - font-weight: 500; - font-family: Helvetica, Arial, sans-serif; -} - -.sidebar .nav-item.active .nav-link { - font-weight: 500; -} - -.card .card-header { - border-color: #d8224c; - background-color: #d8224c; - color: #fff; -} - -.card-footer { - background-color: #f2f1f0; -} - -.nav-item { - font-size: 0.85rem; -} - -.nav-tabs .nav-link.active, -.nav-tabs .nav-link { - font-size: 1.0rem; -} - -.nav-tabs a.nav-link { - color: #6e707e; -} - -a.nav-link.active { - font-weight: bolder; -} - -.sidebar .nav-item .nav-link { - padding: 0.6rem 0.6rem 0.6rem 1.0rem; -} - -.alert-success { - background-color: #d4edda; -} - -.btn-primary { - color: #d8224c; - background-color: #fff; - border-color: #d8224c; -} - -.btn-warning { - color: #333; -} - -.btn-primary:hover { - background-color: #c61931; - border-color: #c61931; -} - -i.fa.fa-bars { - color: #d1d3e2; -} - -i.fa.fa-bars:hover{ - color: #6e707e; -} - -.info-item { - width: 10rem; - float: left; -} - -.info-item-xs { - font-size: 0.7rem; - margin-left: 0.3rem; -} - -.info-item-wifi { - width: 6rem; - float: left; -} - -.webconsole { - width:100%; - height:100%; - border:1px solid; -} - -#console { - height:500px; -} - -.systemtabcontent { - height:100%; - min-height:500px; -} - -.service-status { - border-width: 0; -} - -.service-status-up { - color: #a1ec38; -} - -.service-status-warn { - color: #f6f044; -} - -.service-status-down { - color: #f80107; - animation: flash 1s linear infinite; -} -@keyframes flash { - 50% { - opacity: 0; - } -} - -.logoutput { - width:100%; - height: 20rem; - border: 1px solid #d1d3e2; - border-radius: .35rem; -} - -pre.unstyled { - border-width: 0; - background-color: transparent; - padding: 0; -} - -.dhcp-static-leases { - margin-top: 1em; - margin-bottom: 1em; -} - -.dhcp-static-lease-row { - margin-top: 0.5em; - margin-bottom: 0.5em; -} - -.loading-spinner { - background: url("../../app/img/loading-spinner.gif") no-repeat scroll center center transparent; - min-height: 150px; - width: 100%; -} - -.js-reload-wifi-stations { - min-width: 10rem; -} - -.sidebar.toggled .nav-item .nav-link span { - display: none; -} .sidebar .nav-item .nav-link i, -.sidebar .nav-item .nav-link span { - font-size: 1.0rem; -} - -.btn-warning:hover { - color: #000; -} - -.toggle-off.btn { - padding-left: 1.2rem; - font-size: 0.9rem!important; -} - -.toggle-on.btn { - font-size: 0.9rem!important; -} - -canvas#divDBChartBandwidthhourly { - height: 350px!important; -} - -.chart-container { - height: 150px; - width: 200px; -} - -.table { - margin-bottom: 0rem; -} - -.check-hidden { - visibility: hidden; -} - -.check-updated { - opacity: 0; - color: #90ee90; -} - -.check-progress { - color: #999; -} - -.fa-check { - color: #90ee90; -} - -.fa-times { - color: #ff4500; -} - diff --git a/app/js/custom.js b/app/js/custom.js index 3fe9ef47..0a35d917 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -375,8 +375,9 @@ function getCookie(cname) { return (value != null) ? unescape(value[1]) : null; } +// Define themes var themes = { - "default": "custom.css", + "default": "custom.php", "hackernews" : "hackernews.css", "lightsout" : "lightsout.css", } diff --git a/includes/themes.php b/includes/themes.php index 7dde1824..58afe68a 100755 --- a/includes/themes.php +++ b/includes/themes.php @@ -3,7 +3,7 @@ * * */ -function DisplayThemeConfig() +function DisplayThemeConfig(&$extraFooterScripts) { $themes = [ "default" => "RaspAP (default)", @@ -11,11 +11,14 @@ function DisplayThemeConfig() "lightsout" => "Lights Out" ]; $themeFiles = [ - "default" => "custom.css", + "default" => "custom.php", "hackernews" => "hackernews.css", "lightsout" => "lightsout.css" ]; $selectedTheme = array_search($_COOKIE['theme'], $themeFiles); echo renderTemplate("themes", compact("themes", "selectedTheme")); + + $extraFooterScripts[] = array('src'=>'dist/huebee/huebee.pkgd.min.js', 'defer'=>false); + $extraFooterScripts[] = array('src'=>'app/js/huebee.js', 'defer'=>false); } diff --git a/templates/themes.php b/templates/themes.php index 0e90a6d9..d5629823 100755 --- a/templates/themes.php +++ b/templates/themes.php @@ -17,7 +17,7 @@
- +
From 29be22a8d3ab1c785b1384de1fc24b64691a362b Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 23:35:46 +0100 Subject: [PATCH 13/23] Create dashboard functions --- includes/functions.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/includes/functions.php b/includes/functions.php index a4945016..4d299314 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -428,3 +428,27 @@ function getThemeOpt() return 'app/css/'.htmlspecialchars($theme, ENT_QUOTES); } +function getColorOpt() +{ + if (!isset($_COOKIE['color'])) { + $color = "#d8224c"; + } else { + $color = $_COOKIE['color']; + } + return $color; +} +function getSidebarState() +{ + if ($_COOKIE['sidebarToggled'] == 'true' ) { + return"toggled"; + } +} + +// Returns bridged AP mode status +function getBridgedState() +{ + $arrHostapdConf = parse_ini_file(RASPI_CONFIG.'/hostapd.ini'); + // defaults to false + return $arrHostapdConf['BridgedEnable']; +} + From a77bb2720d354d847dc59c8f8e52039f84f8d5a8 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 23:37:08 +0100 Subject: [PATCH 14/23] Dynamic sidebar logo --- index.php | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/index.php b/index.php index 45abcd92..d7306a62 100755 --- a/index.php +++ b/index.php @@ -46,15 +46,8 @@ $output = $return = 0; $page = $_GET['page']; $theme_url = getThemeOpt(); - -if ($_COOKIE['sidebarToggled'] == 'true' ) { - $toggleState = "toggled"; -} - -// Get Bridged AP mode status -$arrHostapdConf = parse_ini_file('/etc/raspap/hostapd.ini'); -// defaults to false -$bridgedEnabled = $arrHostapdConf['BridgedEnable']; +$toggleState = getSidebarState(); +$bridgedEnabled = getBridgedState(); ?> @@ -116,7 +109,7 @@ $bridgedEnabled = $arrHostapdConf['BridgedEnable'];
Status
From 7c939fdeaf5dc027823f40cc362b7565b930db47 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 23:37:43 +0100 Subject: [PATCH 15/23] Update messages --- locale/en_US/LC_MESSAGES/messages.po | 3 +++ 1 file changed, 3 insertions(+) diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index 1ce53d99..cf6dc3c9 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -540,6 +540,9 @@ msgstr "Theme settings" msgid "Select a theme" msgstr "Select a theme" +msgid "Color" +msgstr "Color" + #: includes/data_usage.php msgid "Data usage" msgstr "Data usage" From 4b77a0b1442d499cd9cfc940fa2d7e65b647e32c Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 23:38:02 +0100 Subject: [PATCH 16/23] Renamed svg > php --- app/img/raspAP-logo.svg | 46 ----------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 app/img/raspAP-logo.svg diff --git a/app/img/raspAP-logo.svg b/app/img/raspAP-logo.svg deleted file mode 100644 index 294fd2cb..00000000 --- a/app/img/raspAP-logo.svg +++ /dev/null @@ -1,46 +0,0 @@ - -image/svg+xml \ No newline at end of file From 7b92f56cda28f9e95807a490df16bbdc0554edc2 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 30 Jun 2020 23:38:32 +0100 Subject: [PATCH 17/23] Dynamic svg logo --- app/img/raspAP-logo.php | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 app/img/raspAP-logo.php diff --git a/app/img/raspAP-logo.php b/app/img/raspAP-logo.php new file mode 100644 index 00000000..c23399ab --- /dev/null +++ b/app/img/raspAP-logo.php @@ -0,0 +1,51 @@ + + + +image/svg+xml From ed7ed044753683ebdc0ed782dc893a4d42041da8 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 1 Jul 2020 09:13:38 +0100 Subject: [PATCH 18/23] Create RASPI_BRAND_TEXT --- config/config.php | 1 + includes/defaults.php | 1 + index.php | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/config/config.php b/config/config.php index bfe464f6..86ad87bf 100755 --- a/config/config.php +++ b/config/config.php @@ -1,5 +1,6 @@ 'RaspAP', 'RASPI_VERSION' => '2.4.1', 'RASPI_CONFIG_NETWORKING' => RASPI_CONFIG.'/networking', 'RASPI_ADMIN_DETAILS' => RASPI_CONFIG.'/raspap.auth', diff --git a/index.php b/index.php index d7306a62..80106b2d 100755 --- a/index.php +++ b/index.php @@ -103,7 +103,7 @@ $bridgedEnabled = getBridgedState();