From 42b7bea83904acb3830dd6aefafecd50ee5fb7ac Mon Sep 17 00:00:00 2001 From: ha7ilm Date: Wed, 3 May 2017 16:32:47 +0200 Subject: [PATCH] Added nanoscroller, retabbed index.wrx and did some work on the digidemod UI --- .gitignore | 1 + htdocs/index.wrx | 290 +++++----- htdocs/jquery.nanoscroller.js | 1000 +++++++++++++++++++++++++++++++++ htdocs/nanoscroller.css | 55 ++ htdocs/openwebrx.css | 20 +- htdocs/openwebrx.js | 107 +++- 6 files changed, 1303 insertions(+), 170 deletions(-) create mode 100644 htdocs/jquery.nanoscroller.js create mode 100644 htdocs/nanoscroller.css diff --git a/.gitignore b/.gitignore index c9b568f..6a211b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pyc *.swp +tags diff --git a/htdocs/index.wrx b/htdocs/index.wrx index 31f9c60..12b1314 100644 --- a/htdocs/index.wrx +++ b/htdocs/index.wrx @@ -1,9 +1,9 @@ - - OpenWebRX | Open Source SDR Web App for Everyone! - - - - - - - - + + OpenWebRX | Open Source SDR Web App for Everyone! + + + + + + + + + +
-
-
- -
%[RX_PHOTO_TITLE]
-
%[RX_PHOTO_DESC]
-
-
-
- - - - -
%[RX_TITLE]
-
%[RX_LOC] | Loc: %[RX_QRA], ASL: %[RX_ASL] m, [maps]
-
- - -
-
-
    -

  • Status
  • -

  • Log
  • -

  • Receiver
  • -
-
-
-
-
-
- -
-
+
+
+ +
%[RX_PHOTO_TITLE]
+
%[RX_PHOTO_DESC]
+
+
+
+ + + + +
%[RX_TITLE]
+
%[RX_LOC] | Loc: %[RX_QRA], ASL: %[RX_ASL] m, [maps]
+
+ + +
+
+
    +

  • Status
  • +

  • Log
  • +

  • Receiver
  • +
+
+
+
+
+
+ +
+
-
- -
-
-
-
---.--- MHz
-
---.--- MHz
-
-
FM
-
AM
-
LSB
-
USB
-
CW
-
-
-
DIG
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
-
-
0 dB
-
-
-
-
-
-
-
-
-
-
OpenWebRX client log
- Author: András Retzler, HA7ILM
You can support OpenWebRX development via PayPal!
-
-
-
-
-
Audio buffer [0 ms]
-
Audio output [0 sps]
-
Audio stream [0 kbps]
-
Network usage [0 kbps]
-
Server CPU [0%]
-
Clients [1]
-
-
- Under construction -
We're working on the code right now, so the application might fail. -
-
-
-
+
+ +
+
+
+
---.--- MHz
+
---.--- MHz
+
+
FM
+
AM
+
LSB
+
USB
+
CW
+
+
+
DIG
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+
0 dB
+
+
+
+
+
+
+
+
+
+
+
OpenWebRX client log
+ Author: András Retzler, HA7ILM
You can support OpenWebRX development via PayPal!
+
+
+
+
+
+
Audio buffer [0 ms]
+
Audio output [0 sps]
+
Audio stream [0 kbps]
+
Network usage [0 kbps]
+
Server CPU [0%]
+
Clients [1]
+
+
+ Under construction +
We're working on the code right now, so the application might fail. +
+
+
+
- macska +
-
-
-
-
+
+
+
+
-
-
- -

Start OpenWebRX -
-
- +
+
+ +

Start OpenWebRX +
+
+ diff --git a/htdocs/jquery.nanoscroller.js b/htdocs/jquery.nanoscroller.js new file mode 100644 index 0000000..edcbfaa --- /dev/null +++ b/htdocs/jquery.nanoscroller.js @@ -0,0 +1,1000 @@ +/*! nanoScrollerJS - v0.8.7 - 2015 +* http://jamesflorentino.github.com/nanoScrollerJS/ +* Copyright (c) 2015 James Florentino; Licensed MIT */ +(function(factory) { + if (typeof define === 'function' && define.amd) { + return define(['jquery'], function($) { + return factory($, window, document); + }); + } else if (typeof exports === 'object') { + return module.exports = factory(require('jquery'), window, document); + } else { + return factory(jQuery, window, document); + } +})(function($, window, document) { + "use strict"; + var BROWSER_IS_IE7, BROWSER_SCROLLBAR_WIDTH, DOMSCROLL, DOWN, DRAG, ENTER, KEYDOWN, KEYUP, MOUSEDOWN, MOUSEENTER, MOUSEMOVE, MOUSEUP, MOUSEWHEEL, NanoScroll, PANEDOWN, RESIZE, SCROLL, SCROLLBAR, TOUCHMOVE, UP, WHEEL, cAF, defaults, getBrowserScrollbarWidth, hasTransform, isFFWithBuggyScrollbar, rAF, transform, _elementStyle, _prefixStyle, _vendor; + defaults = { + + /** + a classname for the pane element. + @property paneClass + @type String + @default 'nano-pane' + */ + paneClass: 'nano-pane', + + /** + a classname for the slider element. + @property sliderClass + @type String + @default 'nano-slider' + */ + sliderClass: 'nano-slider', + + /** + a classname for the content element. + @property contentClass + @type String + @default 'nano-content' + */ + contentClass: 'nano-content', + + /** + a classname for enabled mode + @property enabledClass + @type String + @default 'has-scrollbar' + */ + enabledClass: 'has-scrollbar', + + /** + a classname for flashed mode + @property flashedClass + @type String + @default 'flashed' + */ + flashedClass: 'flashed', + + /** + a classname for active mode + @property activeClass + @type String + @default 'active' + */ + activeClass: 'active', + + /** + a setting to enable native scrolling in iOS devices. + @property iOSNativeScrolling + @type Boolean + @default false + */ + iOSNativeScrolling: false, + + /** + a setting to prevent the rest of the page being + scrolled when user scrolls the `.content` element. + @property preventPageScrolling + @type Boolean + @default false + */ + preventPageScrolling: false, + + /** + a setting to disable binding to the resize event. + @property disableResize + @type Boolean + @default false + */ + disableResize: false, + + /** + a setting to make the scrollbar always visible. + @property alwaysVisible + @type Boolean + @default false + */ + alwaysVisible: false, + + /** + a default timeout for the `flash()` method. + @property flashDelay + @type Number + @default 1500 + */ + flashDelay: 1500, + + /** + a minimum height for the `.slider` element. + @property sliderMinHeight + @type Number + @default 20 + */ + sliderMinHeight: 20, + + /** + a maximum height for the `.slider` element. + @property sliderMaxHeight + @type Number + @default null + */ + sliderMaxHeight: null, + + /** + an alternate document context. + @property documentContext + @type Document + @default null + */ + documentContext: null, + + /** + an alternate window context. + @property windowContext + @type Window + @default null + */ + windowContext: null + }; + + /** + @property SCROLLBAR + @type String + @static + @final + @private + */ + SCROLLBAR = 'scrollbar'; + + /** + @property SCROLL + @type String + @static + @final + @private + */ + SCROLL = 'scroll'; + + /** + @property MOUSEDOWN + @type String + @final + @private + */ + MOUSEDOWN = 'mousedown'; + + /** + @property MOUSEENTER + @type String + @final + @private + */ + MOUSEENTER = 'mouseenter'; + + /** + @property MOUSEMOVE + @type String + @static + @final + @private + */ + MOUSEMOVE = 'mousemove'; + + /** + @property MOUSEWHEEL + @type String + @final + @private + */ + MOUSEWHEEL = 'mousewheel'; + + /** + @property MOUSEUP + @type String + @static + @final + @private + */ + MOUSEUP = 'mouseup'; + + /** + @property RESIZE + @type String + @final + @private + */ + RESIZE = 'resize'; + + /** + @property DRAG + @type String + @static + @final + @private + */ + DRAG = 'drag'; + + /** + @property ENTER + @type String + @static + @final + @private + */ + ENTER = 'enter'; + + /** + @property UP + @type String + @static + @final + @private + */ + UP = 'up'; + + /** + @property PANEDOWN + @type String + @static + @final + @private + */ + PANEDOWN = 'panedown'; + + /** + @property DOMSCROLL + @type String + @static + @final + @private + */ + DOMSCROLL = 'DOMMouseScroll'; + + /** + @property DOWN + @type String + @static + @final + @private + */ + DOWN = 'down'; + + /** + @property WHEEL + @type String + @static + @final + @private + */ + WHEEL = 'wheel'; + + /** + @property KEYDOWN + @type String + @static + @final + @private + */ + KEYDOWN = 'keydown'; + + /** + @property KEYUP + @type String + @static + @final + @private + */ + KEYUP = 'keyup'; + + /** + @property TOUCHMOVE + @type String + @static + @final + @private + */ + TOUCHMOVE = 'touchmove'; + + /** + @property BROWSER_IS_IE7 + @type Boolean + @static + @final + @private + */ + BROWSER_IS_IE7 = window.navigator.appName === 'Microsoft Internet Explorer' && /msie 7./i.test(window.navigator.appVersion) && window.ActiveXObject; + + /** + @property BROWSER_SCROLLBAR_WIDTH + @type Number + @static + @default null + @private + */ + BROWSER_SCROLLBAR_WIDTH = null; + rAF = window.requestAnimationFrame; + cAF = window.cancelAnimationFrame; + _elementStyle = document.createElement('div').style; + _vendor = (function() { + var i, transform, vendor, vendors, _i, _len; + vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT']; + for (i = _i = 0, _len = vendors.length; _i < _len; i = ++_i) { + vendor = vendors[i]; + transform = vendors[i] + 'ransform'; + if (transform in _elementStyle) { + return vendors[i].substr(0, vendors[i].length - 1); + } + } + return false; + })(); + _prefixStyle = function(style) { + if (_vendor === false) { + return false; + } + if (_vendor === '') { + return style; + } + return _vendor + style.charAt(0).toUpperCase() + style.substr(1); + }; + transform = _prefixStyle('transform'); + hasTransform = transform !== false; + + /** + Returns browser's native scrollbar width + @method getBrowserScrollbarWidth + @return {Number} the scrollbar width in pixels + @static + @private + */ + getBrowserScrollbarWidth = function() { + var outer, outerStyle, scrollbarWidth; + outer = document.createElement('div'); + outerStyle = outer.style; + outerStyle.position = 'absolute'; + outerStyle.width = '100px'; + outerStyle.height = '100px'; + outerStyle.overflow = SCROLL; + outerStyle.top = '-9999px'; + document.body.appendChild(outer); + scrollbarWidth = outer.offsetWidth - outer.clientWidth; + document.body.removeChild(outer); + return scrollbarWidth; + }; + isFFWithBuggyScrollbar = function() { + var isOSXFF, ua, version; + ua = window.navigator.userAgent; + isOSXFF = /(?=.+Mac OS X)(?=.+Firefox)/.test(ua); + if (!isOSXFF) { + return false; + } + version = /Firefox\/\d{2}\./.exec(ua); + if (version) { + version = version[0].replace(/\D+/g, ''); + } + return isOSXFF && +version > 23; + }; + + /** + @class NanoScroll + @param element {HTMLElement|Node} the main element + @param options {Object} nanoScroller's options + @constructor + */ + NanoScroll = (function() { + function NanoScroll(el, options) { + this.el = el; + this.options = options; + BROWSER_SCROLLBAR_WIDTH || (BROWSER_SCROLLBAR_WIDTH = getBrowserScrollbarWidth()); + this.$el = $(this.el); + this.doc = $(this.options.documentContext || document); + this.win = $(this.options.windowContext || window); + this.body = this.doc.find('body'); + this.$content = this.$el.children("." + this.options.contentClass); + this.$content.attr('tabindex', this.options.tabIndex || 0); + this.content = this.$content[0]; + this.previousPosition = 0; + if (this.options.iOSNativeScrolling && (this.el.style.WebkitOverflowScrolling != null)) { + this.nativeScrolling(); + } else { + this.generate(); + } + this.createEvents(); + this.addEvents(); + this.reset(); + } + + + /** + Prevents the rest of the page being scrolled + when user scrolls the `.nano-content` element. + @method preventScrolling + @param event {Event} + @param direction {String} Scroll direction (up or down) + @private + */ + + NanoScroll.prototype.preventScrolling = function(e, direction) { + if (!this.isActive) { + return; + } + if (e.type === DOMSCROLL) { + if (direction === DOWN && e.originalEvent.detail > 0 || direction === UP && e.originalEvent.detail < 0) { + e.preventDefault(); + } + } else if (e.type === MOUSEWHEEL) { + if (!e.originalEvent || !e.originalEvent.wheelDelta) { + return; + } + if (direction === DOWN && e.originalEvent.wheelDelta < 0 || direction === UP && e.originalEvent.wheelDelta > 0) { + e.preventDefault(); + } + } + }; + + + /** + Enable iOS native scrolling + @method nativeScrolling + @private + */ + + NanoScroll.prototype.nativeScrolling = function() { + this.$content.css({ + WebkitOverflowScrolling: 'touch' + }); + this.iOSNativeScrolling = true; + this.isActive = true; + }; + + + /** + Updates those nanoScroller properties that + are related to current scrollbar position. + @method updateScrollValues + @private + */ + + NanoScroll.prototype.updateScrollValues = function() { + var content, direction; + content = this.content; + this.maxScrollTop = content.scrollHeight - content.clientHeight; + this.prevScrollTop = this.contentScrollTop || 0; + this.contentScrollTop = content.scrollTop; + direction = this.contentScrollTop > this.previousPosition ? "down" : this.contentScrollTop < this.previousPosition ? "up" : "same"; + this.previousPosition = this.contentScrollTop; + if (direction !== "same") { + this.$el.trigger('update', { + position: this.contentScrollTop, + maximum: this.maxScrollTop, + direction: direction + }); + } + if (!this.iOSNativeScrolling) { + this.maxSliderTop = this.paneHeight - this.sliderHeight; + this.sliderTop = this.maxScrollTop === 0 ? 0 : this.contentScrollTop * this.maxSliderTop / this.maxScrollTop; + } + }; + + + /** + Updates CSS styles for current scroll position. + Uses CSS 2d transfroms and `window.requestAnimationFrame` if available. + @method setOnScrollStyles + @private + */ + + NanoScroll.prototype.setOnScrollStyles = function() { + var cssValue; + if (hasTransform) { + cssValue = {}; + cssValue[transform] = "translate(0, " + this.sliderTop + "px)"; + } else { + cssValue = { + top: this.sliderTop + }; + } + if (rAF) { + if (cAF && this.scrollRAF) { + cAF(this.scrollRAF); + } + this.scrollRAF = rAF((function(_this) { + return function() { + _this.scrollRAF = null; + return _this.slider.css(cssValue); + }; + })(this)); + } else { + this.slider.css(cssValue); + } + }; + + + /** + Creates event related methods + @method createEvents + @private + */ + + NanoScroll.prototype.createEvents = function() { + this.events = { + down: (function(_this) { + return function(e) { + _this.isBeingDragged = true; + _this.offsetY = e.pageY - _this.slider.offset().top; + if (!_this.slider.is(e.target)) { + _this.offsetY = 0; + } + _this.pane.addClass(_this.options.activeClass); + _this.doc.bind(MOUSEMOVE, _this.events[DRAG]).bind(MOUSEUP, _this.events[UP]); + _this.body.bind(MOUSEENTER, _this.events[ENTER]); + return false; + }; + })(this), + drag: (function(_this) { + return function(e) { + _this.sliderY = e.pageY - _this.$el.offset().top - _this.paneTop - (_this.offsetY || _this.sliderHeight * 0.5); + _this.scroll(); + if (_this.contentScrollTop >= _this.maxScrollTop && _this.prevScrollTop !== _this.maxScrollTop) { + _this.$el.trigger('scrollend'); + } else if (_this.contentScrollTop === 0 && _this.prevScrollTop !== 0) { + _this.$el.trigger('scrolltop'); + } + return false; + }; + })(this), + up: (function(_this) { + return function(e) { + _this.isBeingDragged = false; + _this.pane.removeClass(_this.options.activeClass); + _this.doc.unbind(MOUSEMOVE, _this.events[DRAG]).unbind(MOUSEUP, _this.events[UP]); + _this.body.unbind(MOUSEENTER, _this.events[ENTER]); + return false; + }; + })(this), + resize: (function(_this) { + return function(e) { + _this.reset(); + }; + })(this), + panedown: (function(_this) { + return function(e) { + _this.sliderY = (e.offsetY || e.originalEvent.layerY) - (_this.sliderHeight * 0.5); + _this.scroll(); + _this.events.down(e); + return false; + }; + })(this), + scroll: (function(_this) { + return function(e) { + _this.updateScrollValues(); + if (_this.isBeingDragged) { + return; + } + if (!_this.iOSNativeScrolling) { + _this.sliderY = _this.sliderTop; + _this.setOnScrollStyles(); + } + if (e == null) { + return; + } + if (_this.contentScrollTop >= _this.maxScrollTop) { + if (_this.options.preventPageScrolling) { + _this.preventScrolling(e, DOWN); + } + if (_this.prevScrollTop !== _this.maxScrollTop) { + _this.$el.trigger('scrollend'); + } + } else if (_this.contentScrollTop === 0) { + if (_this.options.preventPageScrolling) { + _this.preventScrolling(e, UP); + } + if (_this.prevScrollTop !== 0) { + _this.$el.trigger('scrolltop'); + } + } + }; + })(this), + wheel: (function(_this) { + return function(e) { + var delta; + if (e == null) { + return; + } + delta = e.delta || e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.detail || (e.originalEvent && -e.originalEvent.detail); + if (delta) { + _this.sliderY += -delta / 3; + } + _this.scroll(); + return false; + }; + })(this), + enter: (function(_this) { + return function(e) { + var _ref; + if (!_this.isBeingDragged) { + return; + } + if ((e.buttons || e.which) !== 1) { + return (_ref = _this.events)[UP].apply(_ref, arguments); + } + }; + })(this) + }; + }; + + + /** + Adds event listeners with jQuery. + @method addEvents + @private + */ + + NanoScroll.prototype.addEvents = function() { + var events; + this.removeEvents(); + events = this.events; + if (!this.options.disableResize) { + this.win.bind(RESIZE, events[RESIZE]); + } + if (!this.iOSNativeScrolling) { + this.slider.bind(MOUSEDOWN, events[DOWN]); + this.pane.bind(MOUSEDOWN, events[PANEDOWN]).bind("" + MOUSEWHEEL + " " + DOMSCROLL, events[WHEEL]); + } + this.$content.bind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]); + }; + + + /** + Removes event listeners with jQuery. + @method removeEvents + @private + */ + + NanoScroll.prototype.removeEvents = function() { + var events; + events = this.events; + this.win.unbind(RESIZE, events[RESIZE]); + if (!this.iOSNativeScrolling) { + this.slider.unbind(); + this.pane.unbind(); + } + this.$content.unbind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]); + }; + + + /** + Generates nanoScroller's scrollbar and elements for it. + @method generate + @chainable + @private + */ + + NanoScroll.prototype.generate = function() { + var contentClass, cssRule, currentPadding, options, pane, paneClass, sliderClass; + options = this.options; + paneClass = options.paneClass, sliderClass = options.sliderClass, contentClass = options.contentClass; + if (!(pane = this.$el.children("." + paneClass)).length && !pane.children("." + sliderClass).length) { + this.$el.append("
"); + } + this.pane = this.$el.children("." + paneClass); + this.slider = this.pane.find("." + sliderClass); + if (BROWSER_SCROLLBAR_WIDTH === 0 && isFFWithBuggyScrollbar()) { + currentPadding = window.getComputedStyle(this.content, null).getPropertyValue('padding-right').replace(/[^0-9.]+/g, ''); + cssRule = { + right: -14, + paddingRight: +currentPadding + 14 + }; + } else if (BROWSER_SCROLLBAR_WIDTH) { + cssRule = { + right: -BROWSER_SCROLLBAR_WIDTH + }; + this.$el.addClass(options.enabledClass); + } + if (cssRule != null) { + this.$content.css(cssRule); + } + return this; + }; + + + /** + @method restore + @private + */ + + NanoScroll.prototype.restore = function() { + this.stopped = false; + if (!this.iOSNativeScrolling) { + this.pane.show(); + } + this.addEvents(); + }; + + + /** + Resets nanoScroller's scrollbar. + @method reset + @chainable + @example + $(".nano").nanoScroller(); + */ + + NanoScroll.prototype.reset = function() { + var content, contentHeight, contentPosition, contentStyle, contentStyleOverflowY, paneBottom, paneHeight, paneOuterHeight, paneTop, parentMaxHeight, right, sliderHeight; + if (this.iOSNativeScrolling) { + this.contentHeight = this.content.scrollHeight; + return; + } + if (!this.$el.find("." + this.options.paneClass).length) { + this.generate().stop(); + } + if (this.stopped) { + this.restore(); + } + content = this.content; + contentStyle = content.style; + contentStyleOverflowY = contentStyle.overflowY; + if (BROWSER_IS_IE7) { + this.$content.css({ + height: this.$content.height() + }); + } + contentHeight = content.scrollHeight + BROWSER_SCROLLBAR_WIDTH; + parentMaxHeight = parseInt(this.$el.css("max-height"), 10); + if (parentMaxHeight > 0) { + this.$el.height(""); + this.$el.height(content.scrollHeight > parentMaxHeight ? parentMaxHeight : content.scrollHeight); + } + paneHeight = this.pane.outerHeight(false); + paneTop = parseInt(this.pane.css('top'), 10); + paneBottom = parseInt(this.pane.css('bottom'), 10); + paneOuterHeight = paneHeight + paneTop + paneBottom; + sliderHeight = Math.round(paneOuterHeight / contentHeight * paneHeight); + if (sliderHeight < this.options.sliderMinHeight) { + sliderHeight = this.options.sliderMinHeight; + } else if ((this.options.sliderMaxHeight != null) && sliderHeight > this.options.sliderMaxHeight) { + sliderHeight = this.options.sliderMaxHeight; + } + if (contentStyleOverflowY === SCROLL && contentStyle.overflowX !== SCROLL) { + sliderHeight += BROWSER_SCROLLBAR_WIDTH; + } + this.maxSliderTop = paneOuterHeight - sliderHeight; + this.contentHeight = contentHeight; + this.paneHeight = paneHeight; + this.paneOuterHeight = paneOuterHeight; + this.sliderHeight = sliderHeight; + this.paneTop = paneTop; + this.slider.height(sliderHeight); + this.events.scroll(); + this.pane.show(); + this.isActive = true; + if ((content.scrollHeight === content.clientHeight) || (this.pane.outerHeight(true) >= content.scrollHeight && contentStyleOverflowY !== SCROLL)) { + this.pane.hide(); + this.isActive = false; + } else if (this.el.clientHeight === content.scrollHeight && contentStyleOverflowY === SCROLL) { + this.slider.hide(); + } else { + this.slider.show(); + } + this.pane.css({ + opacity: (this.options.alwaysVisible ? 1 : ''), + visibility: (this.options.alwaysVisible ? 'visible' : '') + }); + contentPosition = this.$content.css('position'); + if (contentPosition === 'static' || contentPosition === 'relative') { + right = parseInt(this.$content.css('right'), 10); + if (right) { + this.$content.css({ + right: '', + marginRight: right + }); + } + } + return this; + }; + + + /** + @method scroll + @private + @example + $(".nano").nanoScroller({ scroll: 'top' }); + */ + + NanoScroll.prototype.scroll = function() { + if (!this.isActive) { + return; + } + this.sliderY = Math.max(0, this.sliderY); + this.sliderY = Math.min(this.maxSliderTop, this.sliderY); + this.$content.scrollTop(this.maxScrollTop * this.sliderY / this.maxSliderTop); + if (!this.iOSNativeScrolling) { + this.updateScrollValues(); + this.setOnScrollStyles(); + } + return this; + }; + + + /** + Scroll at the bottom with an offset value + @method scrollBottom + @param offsetY {Number} + @chainable + @example + $(".nano").nanoScroller({ scrollBottom: value }); + */ + + NanoScroll.prototype.scrollBottom = function(offsetY) { + if (!this.isActive) { + return; + } + this.$content.scrollTop(this.contentHeight - this.$content.height() - offsetY).trigger(MOUSEWHEEL); + this.stop().restore(); + return this; + }; + + + /** + Scroll at the top with an offset value + @method scrollTop + @param offsetY {Number} + @chainable + @example + $(".nano").nanoScroller({ scrollTop: value }); + */ + + NanoScroll.prototype.scrollTop = function(offsetY) { + if (!this.isActive) { + return; + } + this.$content.scrollTop(+offsetY).trigger(MOUSEWHEEL); + this.stop().restore(); + return this; + }; + + + /** + Scroll to an element + @method scrollTo + @param node {Node} A node to scroll to. + @chainable + @example + $(".nano").nanoScroller({ scrollTo: $('#a_node') }); + */ + + NanoScroll.prototype.scrollTo = function(node) { + if (!this.isActive) { + return; + } + this.scrollTop(this.$el.find(node).get(0).offsetTop); + return this; + }; + + + /** + To stop the operation. + This option will tell the plugin to disable all event bindings and hide the gadget scrollbar from the UI. + @method stop + @chainable + @example + $(".nano").nanoScroller({ stop: true }); + */ + + NanoScroll.prototype.stop = function() { + if (cAF && this.scrollRAF) { + cAF(this.scrollRAF); + this.scrollRAF = null; + } + this.stopped = true; + this.removeEvents(); + if (!this.iOSNativeScrolling) { + this.pane.hide(); + } + return this; + }; + + + /** + Destroys nanoScroller and restores browser's native scrollbar. + @method destroy + @chainable + @example + $(".nano").nanoScroller({ destroy: true }); + */ + + NanoScroll.prototype.destroy = function() { + if (!this.stopped) { + this.stop(); + } + if (!this.iOSNativeScrolling && this.pane.length) { + this.pane.remove(); + } + if (BROWSER_IS_IE7) { + this.$content.height(''); + } + this.$content.removeAttr('tabindex'); + if (this.$el.hasClass(this.options.enabledClass)) { + this.$el.removeClass(this.options.enabledClass); + this.$content.css({ + right: '' + }); + } + return this; + }; + + + /** + To flash the scrollbar gadget for an amount of time defined in plugin settings (defaults to 1,5s). + Useful if you want to show the user (e.g. on pageload) that there is more content waiting for him. + @method flash + @chainable + @example + $(".nano").nanoScroller({ flash: true }); + */ + + NanoScroll.prototype.flash = function() { + if (this.iOSNativeScrolling) { + return; + } + if (!this.isActive) { + return; + } + this.reset(); + this.pane.addClass(this.options.flashedClass); + setTimeout((function(_this) { + return function() { + _this.pane.removeClass(_this.options.flashedClass); + }; + })(this), this.options.flashDelay); + return this; + }; + + return NanoScroll; + + })(); + $.fn.nanoScroller = function(settings) { + return this.each(function() { + var options, scrollbar; + if (!(scrollbar = this.nanoscroller)) { + options = $.extend({}, defaults, settings); + this.nanoscroller = scrollbar = new NanoScroll(this, options); + } + if (settings && typeof settings === "object") { + $.extend(scrollbar.options, settings); + if (settings.scrollBottom != null) { + return scrollbar.scrollBottom(settings.scrollBottom); + } + if (settings.scrollTop != null) { + return scrollbar.scrollTop(settings.scrollTop); + } + if (settings.scrollTo) { + return scrollbar.scrollTo(settings.scrollTo); + } + if (settings.scroll === 'bottom') { + return scrollbar.scrollBottom(0); + } + if (settings.scroll === 'top') { + return scrollbar.scrollTop(0); + } + if (settings.scroll && settings.scroll instanceof $) { + return scrollbar.scrollTo(settings.scroll); + } + if (settings.stop) { + return scrollbar.stop(); + } + if (settings.destroy) { + return scrollbar.destroy(); + } + if (settings.flash) { + return scrollbar.flash(); + } + } + return scrollbar.reset(); + }); + }; + $.fn.nanoScroller.Constructor = NanoScroll; +}); + +//# sourceMappingURL=jquery.nanoscroller.js.map diff --git a/htdocs/nanoscroller.css b/htdocs/nanoscroller.css new file mode 100644 index 0000000..4267d96 --- /dev/null +++ b/htdocs/nanoscroller.css @@ -0,0 +1,55 @@ +/** initial setup **/ +.nano { + position : relative; + width : 100%; + height : 100%; + overflow : hidden; +} +.nano > .nano-content { + position : absolute; + overflow : scroll; + overflow-x : hidden; + top : 0; + right : 0; + bottom : 0; + left : 0; +} +.nano > .nano-content:focus { + outline: thin dotted; +} +.nano > .nano-content::-webkit-scrollbar { + display: none; +} +.has-scrollbar > .nano-content::-webkit-scrollbar { + display: block; +} +.nano > .nano-pane { + background : rgba(0,0,0,.25); + position : absolute; + width : 8px; + right : 0; + top : 0; + bottom : 0; + visibility : hidden\9; /* Target only IE7 and IE8 with this hack */ + opacity : .01; + -webkit-transition : .2s; + -moz-transition : .2s; + -o-transition : .2s; + transition : .2s; + -moz-border-radius : 3px; + -webkit-border-radius : 3px; + border-radius : 3px; +} +.nano > .nano-pane > .nano-slider { + background: #444; + background: rgba(0,0,0,.5); + position : relative; + margin : 0 0px; + -moz-border-radius : 4px; + -webkit-border-radius : 4px; + border-radius : 4px; +} +.nano:hover > .nano-pane, .nano-pane.active, .nano-pane.flashed { + visibility : visible\9; /* Target only IE7 and IE8 with this hack */ + opacity : 0.99; +} diff --git a/htdocs/openwebrx.css b/htdocs/openwebrx.css index 126c823..17d1a35 100644 --- a/htdocs/openwebrx.css +++ b/htdocs/openwebrx.css @@ -415,11 +415,15 @@ input[type=range]:focus::-ms-fill-upper height: 396px; }*/ -/*#webrx-debugdiv +#openwebrx-log-scroll { - font-size: 10pt; - /*overflow-y:scroll;*/ -/*}*/ + /*overflow-y:auto;*/ + height: 125px; + width: 619px +} + +.nano .nano-pane { background: #444; } +.nano .nano-slider { background: #eee !important; } #webrx-main-container { @@ -504,7 +508,7 @@ input[type=range]:focus::-ms-fill-upper .openwebrx-panel { - transform: perspective( 600px ); + transform: perspective( 600px ) rotateX( 90deg ); visibility: hidden; background-color: #575757; padding: 10px; @@ -821,7 +825,7 @@ img.openwebrx-mirror-img background-color: #333; } -#openwebrx-select-digimod +#openwebrx-secondary-demod-listbox { width: 195px; height: 27px; @@ -839,7 +843,7 @@ img.openwebrx-mirror-img padding-left:3px; } -#openwebrx-select-digimod option +#openwebrx-secondary-demod-listbox option { border-width: 0px; background-color: #373737; @@ -856,6 +860,8 @@ img.openwebrx-mirror-img width: 8px; background-color: White; display: inline-block; + position: relative; + top: 1px; /*perspective: 60px;*/ } diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index d5194f6..bd88055 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -585,7 +585,11 @@ last_digital_demodulator_subtype = 'bpsk31'; function demodulator_analog_replace(subtype, for_digital) { //this function should only exist until the multi-demodulator capability is added - if(typeof for_digital !== "undefined" && for_digital && secondary_demod) secondary_demod_close_window(); + if(!(typeof for_digital !== "undefined" && for_digital && secondary_demod)) + { + secondary_demod_close_window(); + secondary_demod_listbox_update(); + } last_analog_demodulator_subtype = subtype; var temp_offset=0; if(demodulators.length) @@ -1062,7 +1066,7 @@ function zoom_step(out, where, onscreen) zoom_center_rel=canvas_get_freq_offset(where); //console.log("zoom_step || zlevel: "+zoom_level.toString()+" zlevel_val: "+zoom_levels[zoom_level].toString()+" zoom_center_rel: "+zoom_center_rel.toString()); zoom_center_where=onscreen; - console.log(zoom_center_where, zoom_center_rel, where); + //console.log(zoom_center_where, zoom_center_rel, where); resize_canvases(true); mkscale(); } @@ -1137,7 +1141,7 @@ function on_ws_recv(evt) if(firstChars=="CLI") { var stringData=arrayBufferToString(evt.data); - if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Acknowledged WebSocket connection: "+stringData); + if(stringData.substring(0,16)=="CLIENT DE SERVER") divlog("Server acknowledged WebSocket connection."); } if(firstChars=="AUD") @@ -1161,7 +1165,8 @@ function on_ws_recv(evt) var waterfall_i16=fft_codec.decode(new Uint8Array(evt.data,4)); var waterfall_f32=new Float32Array(waterfall_i16.length-COMPRESS_FFT_PAD_N); for(var i=0;i"; - var wls=e("openwebrx-log-scroll"); - wls.scrollTop=wls.scrollHeight; //scroll to bottom + //var wls=e("openwebrx-log-scroll"); + //wls.scrollTop=wls.scrollHeight; //scroll to bottom + $(".nano").nanoScroller(); + $(".nano").nanoScroller({ scroll: 'bottom' }); } var audio_context; @@ -1778,7 +1785,7 @@ function resize_canvases(zoom) function waterfall_init() { init_canvas_container(); - waterfall_timer = window.setInterval(waterfall_dequeue,900/fft_fps); + waterfall_timer = window.setInterval(()=>{waterfall_dequeue(); secondary_demod_waterfall_dequeue();},900/fft_fps); resize_waterfall_container(false); /* then */ resize_canvases(); scale_setup(); mkzoomlevels(); @@ -1922,6 +1929,7 @@ function openwebrx_init() (opb=e("openwebrx-play-button-text")).style.marginTop=(window.innerHeight/2-opb.clientHeight/2).toString()+"px"; init_rx_photo(); open_websocket(); + secondary_demod_init(); place_panels(first_show_panel); window.setTimeout(function(){window.setInterval(debug_audio,1000);},1000); window.addEventListener("resize",openwebrx_resize); @@ -2015,9 +2023,14 @@ function pop_bottommost_panel(from) return to_return; } -function toggle_panel(what) +function toggle_panel(what, on) { - var item=e(what); + var item=e(what); + if(typeof on !== "undefined") + { + if(item.openwebrxHidden && !on) return; + if(!item.openwebrxHidden && on) return; + } if(item.openwebrxDisableClick) return; item.style.transitionDuration="599ms"; item.style.transitionDelay="0ms"; @@ -2106,6 +2119,7 @@ function place_panels(function_apply) p.style.visibility="visible"; y+=p.openwebrxPanelHeight+((p.openwebrxPanelTransparent)?0:3)*panel_margin; if(function_apply) function_apply(p); + //console.log(p.id, y, p.openwebrxPanelTransparent); } y=hoffset; while(right_col.length>0) @@ -2114,7 +2128,7 @@ function place_panels(function_apply) p.style.right=(e("webrx-canvas-container").offsetWidth-e("webrx-canvas-container").clientWidth).toString()+"px"; //get scrollbar width p.style.bottom=y.toString()+"px"; p.style.visibility="visible"; - y+=p.openwebrxPanelHeight+((p.openwebrxPanelTransparent)?0:3)*panel_margin; + y+=p.openwebrxPanelHeight+((p.openwebrxPanelTransparent)?0:3)*panel_margin; if(function_apply) function_apply(p); } } @@ -2142,7 +2156,8 @@ function progressbar_set(obj,val,text,over) function demodulator_buttons_update() { $(".openwebrx-demodulator-button").removeClass("highlighted"); - switch(demodulators[0].subtype) + if(secondary_demod) $("#openwebrx-button-dig").addClass("highlighted"); + else switch(demodulators[0].subtype) { case "nfm": $("#openwebrx-button-nfm").addClass("highlighted"); @@ -2181,25 +2196,36 @@ function demodulator_analog_replace_last() { demodulator_analog_replace(last_ana secondary_demod = false; secondary_demod_offset_freq = 0; -secondary_demod_ffts = []; +secondary_demod_waterfall_queue = []; -function demodulator_digital_replace_last() { demodulator_digital_replace(last_digital_demodulator_subtype); } +function demodulator_digital_replace_last() +{ + demodulator_digital_replace(last_digital_demodulator_subtype); + secondary_demod_listbox_update(); +} function demodulator_digital_replace(subtype) { switch(subtype) { case "bpsk31": case "rtty": - demodulator_analog_replace('usb', true); secondary_demod_start(subtype); + demodulator_analog_replace('usb', true); + demodulator_buttons_update(); break; } + toggle_panel("openwebrx-panel-digimodes", true); +} + +function secondary_demod_init() +{ + $("#openwebrx-panel-digimodes")[0].openwebrxHidden = true; } function secondary_demod_start(subtype) { ws.send("SET secondary_mod="+subtype); - secondary_demod = true; + secondary_demod = subtype; } function secondary_demod_set() @@ -2211,21 +2237,62 @@ function secondary_demod_stop() { ws.send("SET secondary_mod=off"); secondary_demod = false; + secondary_demod_waterfall_queue = []; } -function secondary_demod_push_fft(x) +function secondary_demod_waterfall_add_queue(x) { - + secondary_demod_waterfall_queue.push(what); } function secondary_demod_push_data(x) { - //$("#openwebrx-digimode-content").append(""+x+""); $("#openwebrx-cursor-blink").before(""+x+""); } - - function secondary_demod_close_window() { + secondary_demod_stop(); + toggle_panel("openwebrx-panel-digimodes", false); +} + +function secondary_demod_waterfall_add(x) +{ +} + +function secondary_demod_waterfall_dequeue() +{ + if(!secondary_demod) return; + if(secondary_demod_waterfall_queue.length) secondary_demod_waterfall_add(waterfall_queue.shift()); + if(secondary_demod_waterfall_queue.length>Math.max(fft_fps/2,20)) //in case of fft overflow + { + console.log("secondary waterfall overflow, queue length:", secondary_demod_waterfall_queue.length); + while(secondary_demod_waterfall_queue.length) secondary_demod_waterfall_add(secondary_demod_waterfall_queue.shift()); + } +} + +secondary_demod_listbox_updating = false; +function secondary_demod_listbox_changed() +{ + if(secondary_demod_listbox_updating) return; + switch ($("#openwebrx-secondary-demod-listbox")[0].value) + { + case "none": + demodulator_analog_replace_last(); + break; + case "bpsk31": + demodulator_digital_replace('bpsk31'); + break; + case "rtty": + demodulator_digital_replace('rtty'); + break; + } +} + +function secondary_demod_listbox_update() +{ + secondary_demod_listbox_updating = true; + $("#openwebrx-secondary-demod-listbox").val((secondary_demod)?secondary_demod:"none"); + console.log("update"); + secondary_demod_listbox_updating = false; }