From 0fb1bb046294b6d1a302e6f0ff1bad0ac9369767 Mon Sep 17 00:00:00 2001 From: Timon Ostertun Date: Wed, 30 Sep 2020 14:36:07 +0200 Subject: [PATCH 01/11] Added mobile console --- client/scripts/mobileconsole.js | 1484 +++++++++++++++++++++++++++++++ server/page/menus.php | 15 +- server/page/wrapper.php | 29 +- 3 files changed, 1509 insertions(+), 19 deletions(-) create mode 100644 client/scripts/mobileconsole.js diff --git a/client/scripts/mobileconsole.js b/client/scripts/mobileconsole.js new file mode 100644 index 0000000..6c463fb --- /dev/null +++ b/client/scripts/mobileconsole.js @@ -0,0 +1,1484 @@ +/*! + * hnl.mobileConsole - javascript mobile console - v1.3.6 - 7/1/2020 + * Adds html console to webpage. Especially useful for debugging JS on mobile devices. + * Supports 'log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'clear' + * Inspired by code by Jakub Fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d) + * Licensed under the MIT license + * + * Original author: @hnldesign + * Further changes, comments: @hnldesign + * Copyright (c) 2014-2019 HN Leussink + * Dual licensed under the MIT and GPL licenses. + * + * Info: http://www.hnldesign.nl/work/code/javascript-mobile-console/ + * Demo: http://code.hnldesign.nl/demo/hnl.MobileConsole.html + */ + +//Polyfills + +//Date.now polyfill +if (!Date.now) { + Date.now = function now() { + return new Date().getTime(); + }; +} +//Array.isArray polyfill +if (typeof Array.isArray === 'undefined') { + Array.isArray = function(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + }; +} +//Array.filter polyfill +if (!Array.prototype.filter) { + Array.prototype.filter = function(fun/*, thisArg*/) { + if (this === void 0 || this === null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== 'function') { + throw new TypeError(); + } + var res = []; + var thisArg = arguments.length >= 2 ? arguments[1] : void 0; + for (var i = 0; i < len; i++) { + if (i in t) { + var val = t[i]; + if (fun.call(thisArg, val, i, t)) { + res.push(val); + } + } + } + + return res; + }; +} +//Function.bind polyfill +if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + if (this.prototype) { + // Function.prototype doesn't have a prototype property + fNOP.prototype = this.prototype; + } + fBound.prototype = new fNOP(); + return fBound; + }; +} +//Array.prototype.indexOf polyfill +// Production steps of ECMA-262, Edition 5, 15.4.4.14 +// Referentie: http://es5.github.io/#x15.4.4.14 +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(searchElement, fromIndex) { + var k; + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + var o = Object(this); + var len = o.length >>> 0; + if (len === 0) { + return -1; + } + var n = +fromIndex || 0; + if (Math.abs(n) === Infinity) { + n = 0; + } + if (n >= len) { + return -1; + } + k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); + while (k < len) { + if (k in o && o[k] === searchElement) { + return k; + } + k++; + } + return -1; + }; +} +//String.prototype.trim polyfill +if (!String.prototype.trim) { + String.prototype.trim = function () { + return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + }; +} +//Array.prototype.map polyfill +// Production steps of ECMA-262, Edition 5, 15.4.4.19 +// Reference: http://es5.github.io/#x15.4.4.19 +if (!Array.prototype.map) { + Array.prototype.map = function(callback/*, thisArg*/) { + var T, A, k; + if (this == null) { + throw new TypeError('this is null or not defined'); + } + var O = Object(this); + var len = O.length >>> 0; + if (typeof callback !== 'function') { + throw new TypeError(callback + ' is not a function'); + } + if (arguments.length > 1) { + T = arguments[1]; + } + A = new Array(len); + k = 0; + while (k < len) { + var kValue, mappedValue; + if (k in O) { + kValue = O[k]; + mappedValue = callback.call(T, kValue, k, O); + A[k] = mappedValue; + } + k++; + } + return A; + }; +} + +// DocReady - Fires supplied function when document is ready +if (typeof 'docReady' !== 'function') { + (function (funcName, baseObj) { + // The public function name defaults to window.docReady + // but you can pass in your own object and own function name and those will be used + // if you want to put them in a different namespace + funcName = funcName || 'docReady'; + baseObj = baseObj || window; + var i, len, readyList = [], readyFired = false, readyEventHandlersInstalled = false; + + // call this when the document is ready + // this function protects itself against being called more than once + function ready() { + if (!readyFired) { + // this must be set to true before we start calling callbacks + readyFired = true; + for (i = 0, len = readyList.length; i < len; i = i + 1) { + // if a callback here happens to add new ready handlers, + // the docReady() function will see that it already fired + // and will schedule the callback to run right after + // this event loop finishes so all handlers will still execute + // in order and no new ones will be added to the readyList + // while we are processing the list + readyList[i].fn.call(window, readyList[i].ctx); + } + // allow any closures held by these functions to free + readyList = []; + } + } + + function readyStateChange() { + if (document.readyState === 'complete') { + ready(); + } + } + + // This is the one public interface + // docReady(fn, context); + // the context argument is optional - if present, it will be passed + // as an argument to the callback + baseObj[funcName] = function (callback, context) { + // if ready has already fired, then just schedule the callback + // to fire asynchronously, but right away + if (readyFired) { + setTimeout(function () {callback(context); }, 1); + return; + } + // add the function and context to the list + readyList.push({fn: callback, ctx: context}); + // if document already ready to go, schedule the ready function to run + if (document.readyState === 'complete') { + setTimeout(ready, 1); + } else if (!readyEventHandlersInstalled) { + // otherwise if we don't have event handlers installed, install them + if (document.addEventListener) { + // first choice is DOMContentLoaded event + document.addEventListener('DOMContentLoaded', ready, false); + // backup is window load event + window.addEventListener('load', ready, false); + } else { + // must be IE + document.attachEvent('onreadystatechange', readyStateChange); + window.attachEvent('onload', ready); + } + readyEventHandlersInstalled = true; + } + }; + }('docReady', window)); +} + +//define console variable +var console = window.console; + +var mobileConsole = (function () { + 'use strict'; + + //options and other variable containers + var options = { + overrideAutorun: true, //set this to true to skip mobile-detection and run the console no matter what. + startMinimized: false, + version: '1.3.5', + baseClass: 'mobileConsole_', + animParams: 'all 200ms ease', + browserinfo: { + isMobile: (function (a) { + return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))); + }(navigator.userAgent || navigator.vendor || window.opera)), + browserChrome: /chrome/.test(navigator.userAgent.toLowerCase()), + ffox: /firefox/.test(navigator.userAgent.toLowerCase()) && !/chrome/.test(navigator.userAgent.toLowerCase()), + safari: /safari/.test(navigator.userAgent.toLowerCase()) && !/chrome/.test(navigator.userAgent.toLowerCase()), + trident: /trident/.test(navigator.userAgent.toLowerCase()), + evtLstn: typeof window.addEventListener === 'function' + }, + methods: ['log', 'trace', 'info', 'warn', 'error', 'group', 'groupCollapsed', 'groupEnd', 'table', 'assert', 'time', 'timeEnd', 'clear'], + hideButtons: ['group', 'groupCollapsed', 'groupEnd', 'table', 'assert', 'time', 'timeEnd'], + ratio: 0.4, //screen/console-ratio that determines the height of the console (reevaluated on every minimize/maximize). + paddingLeft: 0, //used when grouping, no need to change as it will be reset. + groupDepth: 0, //used when grouping, no need to change as it will be reset. + truncate: 400 //hard limit for large strings. For speed/mem issues with consecutive logging of large strings + }, + messages = { + clear : 'Console was cleared', + empty: '(Empty string)' + }, + status = { + initialized: false, + acActive : false, + acHovered : false, + acInput : '', + timers : {} + }, + history = { + output : { + prevMsg : '', + prevMethod : '', + counter : 0 + }, + input : { + commands : window.sessionStorage ? (sessionStorage.getItem('mobileConsoleCommandHistory') ? JSON.parse(sessionStorage.getItem('mobileConsoleCommandHistory')) : []) : [], + commandIdx: window.sessionStorage ? (sessionStorage.getItem('mobileConsoleCommandHistory') ? JSON.parse(sessionStorage.getItem('mobileConsoleCommandHistory')).length : 0) : 0, + acIdx: 0, + acHovered: false + } + }, + //'backup' original console for reference & internal debugging + missingMethod = function() { return true; }, //method is not supported on this device's original console, return dummy + originalConsole = { + log: (console && typeof console.log === 'function') ? console.log.bind(console) : missingMethod, + info: (console && typeof console.info === 'function') ? console.info.bind(console) : missingMethod, + dir: (console && typeof console.dir === 'function') ? console.dir.bind(console) : missingMethod, + group: (console && typeof console.group === 'function') ? console.group.bind(console) : missingMethod, + groupEnd: (console && typeof console.groupEnd === 'function') ? console.groupEnd.bind(console) : missingMethod, + warn: (console && typeof console.warn === 'function') ? console.warn.bind(console) : missingMethod, + error: (console && typeof console.error === 'function') ? console.error.bind(console) : missingMethod, + trace: (console && typeof console.trace === 'function') ? console.trace.bind(console) : missingMethod, + clear: (console && typeof console.clear === 'function') ? console.clear.bind(console) : missingMethod + }, + // reference variables + mobileConsole, consoleElement, commandLine; + + //helpers for all sub functions + function setCSS(el, css) { + var i; + for (i in css) { + if (css.hasOwnProperty(i)) { + el.style[i] = css[i]; + } + } + return el; + } + function htmlToString(html) { + var string; + try { string = String(html); } catch(e) { string = JSON.stringify(html); } //this should be done differently, but works for now + return string.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/ /g, '\u00a0').replace(/(?:\r\n|\r|\n)/g, '
').trim(); + } + function createElem(type, className, css) { + if (!type) { return; } + var element = setCSS(document.createElement(type), css); + if (className) { element.className = options.baseClass + className; } + return setCSS(element, css); + } + function storeCommand(command) { + if (history) { + history.input.commands.push(encodeURI(command.trim())); + history.input.commandIdx = history.input.commands.length; + if (window.sessionStorage) { sessionStorage.setItem('mobileConsoleCommandHistory', JSON.stringify(history.input.commands)); } + } + } + function valBetween(val, min, max) { + return (Math.min(max, Math.max(min, val))); + } + function getMaxHeight() { + return valBetween(Math.floor((window.innerHeight || document.documentElement.clientHeight) * options.ratio), 55, 300); + } + function getClass(item) { + var returnVal = ''; + if (item && item.constructor) { + returnVal = item.constructor.name; + } else { + returnVal = Object.prototype.toString.call(item); + } + return String(returnVal); + } + + // elements + var elements = { + lines: [], + acItems: [], + base: createElem('div', 'base', { + boxSizing: 'border-box', + position: 'fixed', + resize: 'none', + fontSize: '12px', + lineHeight: '14px', + bottom: 0, + top: 'auto', + right: 0, + width: '100%', + zIndex: 10000, + padding: 0, + paddingBottom: options.browserinfo.isMobile ? '35px' : '25px', + margin: 0, + border: '0 none', + borderTop: '1px solid #808080', + backgroundColor: '#ffffff' + }), + topbar : createElem('div', 'topbar', { + boxSizing: 'border-box', + position: 'absolute', + height: '28px', + left: 0, + right: 0, + display: 'block', + padding: '0 2px', + overflow: 'hidden', + webkitOverflowScrolling: 'touch', + color: '#444444', + backgroundColor: '#f3f3f3', + border: '0 none', + borderTop: '1px solid #a3a3a3', + borderBottom: '1px solid #a3a3a3', + whiteSpace: 'nowrap', + overflowX: 'auto' + }), + scrollcontainer : createElem('div', 'scroller', { + boxSizing: 'border-box', + border: '0 none', + fontFamily: 'Consolas, monaco, monospace', + position: 'relative', + display: 'block', + height: getMaxHeight() + 'px', + overflow: 'auto', + webkitOverflowScrolling: 'touch', + '-webkit-transition': options.animParams, + '-moz-transition': options.animParams, + '-o-transition': options.animParams, + 'transition': options.animParams + }), + table : createElem('table', 'table', { + border: '0 none', + margin: 0, + position: 'relative', + tableLayout: 'auto', + width: '100%', + borderCollapse: 'collapse' + }), + stackTraceTable : createElem('table', 'stackTraceTable', { + border: '0 none', + margin: 0, + display: 'none', + marginLeft: '10px', + marginTop: options.browserinfo.isMobile ? '8px' : '4px', + tableLayout: 'auto', + maxWidth: '100%', + color: '#333333' + }), + tr : createElem('tr', 'table_row', { + verticalAlign: 'top' + }), + td : createElem('td', 'table_row', { + border: '0 none', + padding: '2px 4px', + verticalAlign: 'top' + }), + msgContainer : createElem('span', 'msgContainer', { + border: '0 none', + margin: 0, + display: 'inline', + overflow: 'hidden' + }), + tdLeft : createElem('td', 'table_row_data', { + border: '0 none', + textAlign: 'left', + padding: options.browserinfo.isMobile ? '8px 12px' : '4px 8px' + }), + tdRight : createElem('td', 'table_row_data', { + border: '0 none', + textAlign: 'left', + padding: options.browserinfo.isMobile ? '8px 12px' : '4px 8px', + whiteSpace: 'nowrap', + overflow: 'hidden' + }), + link : createElem('a', 'link', { + color: '#1155cc', + textDecoration: 'underline' + }), + dot : createElem('div', 'table_row_data_dot', { + display: 'inline', + borderRadius: '50%', + fontSize: '80%', + fontWeight: 'bold', + padding: '2px 5px', + textAlign: 'center', + marginRight: '5px', + backgroundColor: '#333333', + color: '#ffffff' + }), + button : createElem('button', 'button', { + display: 'inline-block', + fontFamily: '"Helvetica Neue",Helvetica,Arial,sans-serif', + fontWeight: 'normal', + textTransform: 'capitalize', + fontSize: '12px', + lineHeight: '26px', + height: '26px', + padding: '0 8px', + margin: 0, + textAlign: 'center', + marginRight: '5px', + border: '0 none', + backgroundColor: 'transparent', + color: 'inherit', + cursor: 'pointer' + }), + buttons : { + }, + input : createElem('div', 'input', { + boxSizing: 'border-box', + height: options.browserinfo.isMobile ? '35px' : '29px', + fontFamily: 'Consolas, monaco, monospace', + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + margin: 0, + border: '0 none', + borderTop: '1px solid #EEEEEE' + }), + gt : createElem('DIV', 'gt', { + position: 'absolute', + bottom: 0, + width: '25px', + lineHeight: options.browserinfo.isMobile ? '34px' : '28px', + height: options.browserinfo.isMobile ? '34px' : '28px', + textAlign: 'center', + fontSize: '16px', + fontFamily: 'Consolas, monaco, monospace', + fontWeight: 'bold', + color: '#3577B1', + zIndex: 2 + }), + consoleinput : createElem('input', 'consoleinput', { + boxSizing: 'border-box', + position: 'absolute', + bottom: 0, + width : '100%', + fontSize: options.browserinfo.isMobile ? '16px' : 'inherit', //prevents ios safari's zoom on focus + fontFamily: 'Consolas, monaco, monospace', + paddingLeft: '25px', + margin: 0, + height: options.browserinfo.isMobile ? '35px' : '25px', + border: '0 none', + outline: 'none', + outlineWidth: 0, + boxShadow: 'none', + '-moz-appearance': 'none', + '-webkit-appearance': 'none', + backgroundColor: 'transparent', + color: '#000000', + zIndex: 1 + }), + autocomplete : createElem('div', 'autocomplete', { + display: 'none', + position: 'absolute', + bottom: options.browserinfo.isMobile ? '35px' : '28px', + left: 0, + boxShadow: '1px 2px 5px rgba(0,0,0,0.1)', + color: '#000000', + backgroundColor: '#FFFFFF', + border: '1px solid #b5b5b5' + }), + autocompleteItem : createElem('a', 'autocompleteitem', { + display: 'block', + textDecoration: 'none', + fontSize: options.browserinfo.isMobile ? '16px' : 'inherit', + padding: '5px 8px', + wordWrap: 'break-word', + whiteSpace: 'nowrap' + }), + arrowUp: '', + arrowDown: '', + arrowRight: '' + }; + + //shared functions + + var setLineStyle = (function () { + var lineStyles = function (style) { + switch (style) { + case 'log': + return { + text : { + borderBottom: '1px solid #DDDDDD', + color: '#000000' + }, + dot : { + color: '#FFFFFF', + backgroundColor: '#8097bd' + } + }; + case 'info': + return { + text : { + borderBottom: '1px solid #DDDDDD', + color: '#1f3dc4' + }, + dot : { + color: '#FFFFFF', + backgroundColor: '#367AB4' + } + }; + case 'warn': + return { + text : { + borderBottom: '1px solid #DDDDDD', + color: '#CE8724', + backgroundColor : '#fff6e0' + }, + dot : { + color: '#FFFFFF', + backgroundColor: '#e8a400' + } + }; + case 'error': + case 'table': + return { + text : { + borderBottom: '1px solid #DDDDDD', + color: '#FF0000', + backgroundColor : '#ffe5e5' + }, + dot : { + color: '#FFFFFF', + backgroundColor: '#FF0000' + } + }; + case 'assert': + return { + text : { + borderBottom: '1px solid #DDDDDD', + color: '#FF0000', + backgroundColor : '#ffe5e5' + }, + dot : { + color: '#FFFFFF', + backgroundColor: '#FF0000' + } + }; + case 'trace': + return { + text : { + borderBottom: '1px solid #DDDDDD', + color: '#000000' + }, + dot : { + //will not happen + } + }; + case 'time': + case 'timeEnd': + return { + text : { + borderBottom: '1px solid #DDDDDD', + color: '#0000ff' + }, + dot : { + color: '#FFFFFF', + backgroundColor: '#0000ff' + } + }; + default: + return { + text : { + borderBottom: '1px solid #DDDDDD', + color: '#000000' + }, + dot : { + color: '#FFFFFF', + backgroundColor: '#8097bd' + } + }; + } + + }; + var color, dot; + + return function (element, type, msg) { + if (status.initialized) { + color = (typeof msg === 'undefined' || msg === htmlToString(messages.empty)) ? {color: '#808080'} : ((msg === htmlToString(messages.clear)) ? {color: '#808080', fontStyle: 'italic'} : (lineStyles(type) !== undefined ? lineStyles(type).text : lineStyles.log.text)); + dot = typeof lineStyles(type) !== 'undefined' ? lineStyles(type).dot : lineStyles.log.dot; + setCSS(element, color); + //has dot? + if (element.childNodes[0].childNodes[0].className.indexOf('dot') !== -1) { + setCSS(element.childNodes[0].childNodes[0], lineStyles(type).dot); + } + } + }; + }()), + getLink = function (href, textString) { + var HTMLurl = elements.link.cloneNode(false); + if (href) { + HTMLurl.setAttribute('href', href); + HTMLurl.setAttribute('target', '_blank'); + } + HTMLurl.innerHTML = textString || href.split('\\').pop().split('/').filter(Boolean).pop(); + return HTMLurl; + }, + toggleHeight = function () { + if (status.initialized) { + var existingPadding = parseInt(document.body.style.paddingBottom, 10) - Math.abs(elements.base.offsetHeight + elements.topbar.offsetHeight); + var newHeight = (elements.base.minimized) ? getMaxHeight() + 'px' : '0px'; + setCSS(elements.scrollcontainer, { + height: newHeight + }); + setCSS(document.body, { + paddingBottom: existingPadding + Math.abs(parseInt(newHeight, 10) + elements.topbar.offsetHeight) + 'px' + }); + elements.buttons.toggler.innerHTML = (elements.base.minimized) ? elements.arrowDown : elements.arrowUp; + elements.buttons.toggler.setAttribute('title', (elements.base.minimized) ? 'Minimize console' : 'Maximize console'); + elements.base.minimized = !elements.base.minimized; + return elements.base.minimized; + } + return 'Not built!'; + }, + about = (function () { + return function () { + console.info( + '--==## Mobile Console ' + (status.initialized ? 'active' : 'inactive') + ' ##==--' + '\n' + + '--===============================--' + '\n' + + 'MobileConsole v' + options.version + ', running on ' + navigator.userAgent.toLowerCase() + ); + }; + }()); + + // --==** sub functions start here **==-- + + function displayConsole() { + elements.base.style.display = "block"; + } + + //initializes the console HTML element + function initConsoleElement() { + //reference + var ref; + //core + function toggleScroll() { + elements.scrollcontainer.scrollTop = elements.scrollcontainer.scrollHeight; + elements.scrollcontainer.scrollLeft = 0; + } + function destroyConsole() { + //conan the destroyer. Very basic; just removes the console element. mobileConsole will still 'pipe' console logging + //don't see any reason for now to reverse that. + /*elements.base.parentNode.removeChild(elements.base); + status.initialized = false; + console.warn( + '--==## Mobile Console DESTROYED ##==--' + '\n' + + 'To enable again: reload the page. Tip: use the minimize button instead of closing.' + );*/ + elements.base.style.display = "none"; + } + function assemble() { + var i = options.methods.length, key; + + //add close button + elements.buttons.closer = elements.button.cloneNode(false); + elements.buttons.closer.innerHTML = '✕'; + elements.buttons.closer.setAttribute('title', 'Close (destroy) console'); + setCSS(elements.buttons.closer, { float: 'right', margin: '0'}); + //add buttons + while (i--) { + elements.buttons[options.methods[i]] = elements.button.cloneNode(false); + elements.buttons[options.methods[i]].innerHTML = options.methods[i].charAt(0).toUpperCase() + options.methods[i].slice(1); + elements.buttons[options.methods[i]].setAttribute('title', (options.methods[i] !== 'clear') ? 'Toggle the display of ' + options.methods[i] + ' messages' : 'Clear the console'); + } + //add min/maximize button + elements.buttons.toggler = elements.button.cloneNode(false); + elements.buttons.toggler.innerHTML = elements.arrowDown; + elements.buttons.toggler.setAttribute('title', 'Minimize console'); + + //assemble everything + for (key in elements.buttons) { + if (elements.buttons.hasOwnProperty(key)) { + elements.topbar.insertBefore(elements.buttons[key], elements.topbar.firstChild); + } + } + elements.scrollcontainer.appendChild(elements.table); + + elements.base.appendChild(elements.topbar); + elements.base.appendChild(elements.scrollcontainer); + + status.initialized = true; + return elements.base; + } + function attach(console) { + document.body.appendChild(console); + setCSS(elements.topbar, { + top: -Math.abs(elements.topbar.offsetHeight) + 'px' + }); + var existingPadding = isNaN(parseInt(document.body.style.paddingBottom, 10)) ? 0 : parseInt(document.body.style.paddingBottom, 10); + setCSS(document.body, { + paddingBottom: existingPadding + Math.abs(console.offsetHeight + elements.topbar.offsetHeight) + 'px' + }); + elements.scrollcontainer.scrollTop = elements.scrollcontainer.scrollHeight; + + return elements.base; + } + function toggleLogType(e) { + var button = e.currentTarget || e.srcElement; + var logType = button.innerHTML.toLowerCase(); + var elems = elements.lines[logType], i = elems.length; + button.toggled = (typeof button.toggled === 'undefined') ? true : !button.toggled; + setCSS(button, { opacity: (button.toggled) ? '0.5' : '' }); + while (i--) { + setCSS(elems[i], { display: (button.toggled) ? 'none' : '' }); + } + toggleScroll(); + button.blur(); + return button; + } + function setBinds() { + var methods = options.methods, i = methods.length; + while (i--) { + if (methods[i] !== 'clear') { + if (options.browserinfo.evtLstn) { + elements.buttons[methods[i]].addEventListener('click', toggleLogType, false); + } else { + elements.buttons[methods[i]].attachEvent('onclick', toggleLogType); + } + } + if (options.hideButtons.indexOf(methods[i]) !== -1) { //hide buttons that we don't want + setCSS(elements.buttons[methods[i]], { display: 'none' }); + } + } + if (options.browserinfo.evtLstn) { + elements.buttons.toggler.addEventListener('click', toggleHeight, false); + elements.buttons.closer.addEventListener('click', destroyConsole, false); + elements.buttons.clear.addEventListener('click', console.clear, false); + } else { + elements.buttons.toggler.attachEvent('onclick', toggleHeight); + elements.buttons.closer.attachEvent('onclick', destroyConsole); + elements.buttons.clear.attachEvent('onclick', console.clear); + } + } + //init + function init() { + var element = assemble(); + docReady(function () { + setBinds(); + attach(element); + if (options.startMinimized) { + toggleHeight(); + } + elements.base.style.display = "none"; + }); + //expose Public methods and variables + return { + toggleHeight : toggleHeight, + toggleScroll : toggleScroll, + destroy: destroyConsole + }; + } + if (!ref) { + ref = init(); + } + return ref; + } + + //initializes the new console logger + function initConsole() { + //reference + var ref; + //sub helpers + function isEmpty(obj) { + for(var prop in obj) { + if(obj.hasOwnProperty(prop)) + return false; + } + return JSON.stringify(obj) === JSON.stringify({}); + } + function isElement(o) { + return ( + typeof HTMLElement === 'object' ? o instanceof HTMLElement : //DOM2 + o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string' + ); + } + function objectToString(object) { + var prop, output = ''; + if (!isElement(object)) { + for (prop in object) { + if (!object.hasOwnProperty(prop)) { + continue; + } else if (typeof (object[prop]) === 'object') { + output += prop + ((Array.isArray(object[prop])) ? ': Array(' + object[prop].length + ')' : ': {…}'); + } else if (typeof (object[prop]) === 'function') { + output += 'func: f'; + } else { + output += prop + ': "' + object[prop] + '"'; + } + output += ', '; + } + return '{' + output.slice(0, -2) + '}'; // returns cleaned up JSON + } + return htmlToString(object.outerHTML); + } + function urlFromString(string) { + string = String(string); + //searches for url in string, returns url as string + var match, uriPattern = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig; + try { + match = string.match(uriPattern)[0]; + return match; + } catch (e) { + return ''; + } + } + function filterOut(array, match) { + return array.filter(function(item){ + return typeof item === 'string' && item.indexOf(match) === -1; + }); + } + function preFilterTrace(array) { + var newArray = array.split('\n').filter(Boolean), //filter cleans out empty values + isCommandLine = false, stealthThese, i; + if (newArray[0].indexOf('http') === -1) { newArray.shift(); } //remove first line if contains no 'http' (Chrome starts with 'Error', Firefox doesn't..) + if (newArray[0].indexOf('console.') !== -1 || newArray[0].indexOf('console[method]') !== -1) { newArray.shift(); } + if (newArray.length > 0) { + isCommandLine = newArray[newArray.length - 1].indexOf('keydown') !== -1; + newArray = newArray.filter(function(item){ return item !== ''; }); + + if (isCommandLine) { + stealthThese = ['submitCommand', 'eval', 'setBinds', 'interceptConsole', 'newConsole']; + newArray.pop(); //remove last index, as it is the keydown event. + i = stealthThese.length; + while(i--) { + newArray = filterOut(newArray, stealthThese[i]); + } + } + } + if (isCommandLine || newArray.length === 0) { + newArray.push('(anonymous function) console:1:1'); + } + return newArray; + } + //core + function formatStackTrace(trace, origtrace) { + var callStack = []; + //original stack is hidden inside trace object, if specified + var stackTraceOrig = (typeof trace !== 'undefined' && typeof trace[4] !== 'undefined') ? trace[4].stack : undefined; + //if the first line contains this, skip it. Meant for browsers that begin the stack with the error message itself (already captured before formatStackTrace) + var traceToProcess = (origtrace && origtrace !== '') ? origtrace : stackTraceOrig, + i, + lines, + url, + txt, + thisLine, + lineAndColumn, + caller, + separator = options.browserinfo.ffox ? '@' : '()'; + + //stop if no source trace can be determined + if (!traceToProcess) { return; } + + lines = preFilterTrace(traceToProcess); //pre filters all lines by filtering out all mobileConsole's own methods so mobileConsole runs Stealth and unobtrusive + i = lines.length; + while (i--) { + thisLine = lines[i].trim(); + lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/); + url = urlFromString(thisLine).replace(lineAndColumn[0], '').split('#')[0] || ''; + caller = htmlToString(thisLine.replace(urlFromString(thisLine), '').replace(separator, '').replace('at ', '').trim()); + if (caller === '' || caller === lineAndColumn[0]) { continue; } + if (url[url.length - 1] === '/') { + txt = '(index)'; + } else { + txt = url.split('\\').pop().split('/').filter(Boolean).pop() || caller; + } + callStack.push({ + caller: caller, + url: url ? url.split(':')[0] + ':' + url.split(':')[1] : caller, + linkText: txt + lineAndColumn[0], + line: lineAndColumn[1], + col: lineAndColumn[2], + originalLine: thisLine + }); + } + return callStack; + } + function traceToTable(table, trace) { + var i, tdLeft, tdRight, tr; + if (typeof trace === 'undefined') { + return; + } + trace.reverse(); //reverse order of trace, just like in a browser's console + i = trace.length; + while (i--) { + tdLeft = elements.td.cloneNode(false); + tdRight = elements.td.cloneNode(false); + tr = elements.tr.cloneNode(false); + tdLeft.innerHTML = trace[i].caller; + tdRight.innerHTML = ' @ '; + tdRight.appendChild(getLink((trace[i].url || ''), trace[i].linkText)); + tr.appendChild(tdLeft); + tr.appendChild(tdRight); + table.insertBefore(tr, table.firstChild); + } + return table; + } + function colorizeData(key, value) { + var valueColor = '#3c53da', keyColor = '#ae33b7', classname = getClass(value); + if (value && classname.indexOf('HTML') !== -1) { + value = htmlToString(value.outerHTML); + valueColor = '#ad8200'; + } else if (key === 'innerHTML' || key === 'outerHTML') { + value = htmlToString(value); + valueColor = '#ad8200'; + } + if (typeof value === 'string') { + valueColor = '#c54300'; + if (value.length > options.truncate) { + value = '"' + String(value).substring(0, options.truncate) + '" [...]
Note: string was truncated to ' + options.truncate + ' chars'; + } else { + value = '"' + value + '"'; + } + } else if (value === null) { + valueColor = '#808080'; + value = 'null'; + } else if (typeof value === 'undefined' || value === undefined) { + valueColor = '#808080'; + value = 'undefined'; + } else if (typeof value === 'object') { + if (isEmpty(value)) { + value = '{}'; + } else { + valueColor = ''; + //iterate over object to create another table inside + var tempTable = createElem('table', 'stackTraceSubTable', { + border: '0 none', + margin: 0, + display: 'none', + marginLeft: '10px', + marginTop: options.browserinfo.isMobile ? '8px' : '4px', + tableLayout: 'auto', + maxWidth: '100%', + color: '#333333' + }), + wrap = document.createElement('div'); + wrap.appendChild(objectToTable(tempTable, value).cloneNode(true)); + if (Array.isArray(value)) { + value = 'Array(' + value.length + ')' + wrap.innerHTML; + } else { + value = wrap.innerHTML; + } + } + } + + return '' + key + ': ' + value + ''; + } + function objectToTable(table, object) { + var i, tdLeft, tr; + if (isElement(object)){ + tdLeft = elements.td.cloneNode(false); tr = elements.tr.cloneNode(false); + tdLeft.innerHTML = htmlToString(object.outerHTML); + tr.appendChild(tdLeft); + table.appendChild(tr); + } else { + for (i in object) { + if (object.hasOwnProperty(i)) { + tdLeft = elements.td.cloneNode(false); tr = elements.tr.cloneNode(false); + tdLeft.innerHTML = colorizeData(i, object[i]); + tr.appendChild(tdLeft); + table.appendChild(tr); + } + } + } + return table; + } + function toggleDetails(e) { + var button = e.currentTarget || e.srcElement, i, hidden; + if (button.getAttribute('toggles') === 'table') { + var tables = button.parentElement.getElementsByTagName('table'); + i = tables.length; + while (i--) { + hidden = (tables[i].currentStyle ? tables[i].currentStyle.display : window.getComputedStyle(tables[i], null).display) === 'none'; + button.innerHTML = button.innerHTML.replace((hidden ? elements.arrowRight : elements.arrowDown), (hidden ? elements.arrowDown : elements.arrowRight)); + setCSS(tables[i], { display: hidden ? 'table' : 'none' }); + } + } + } + function isRepeat(message, method) { + return (history.output.prevMsg === message && history.output.prevMethod === method) && (typeof message !== 'object') && (method !== 'trace') && (method !== 'group') && (method !== 'groupCollapsed') && (method !== 'groupEnd'); + } + function newConsole() { + try { + //get arguments, set vars + var method = arguments[0], className, isHTMLElement, + message = (typeof arguments[1].newMessage !== 'undefined') ? arguments[1].newMessage : undefined, + stackTrace = (typeof arguments[1].newStackTrace !== 'undefined') ? arguments[1].newStackTrace : undefined; + + //if message emtpy or undefined, show empty message-message + if (message === '' || typeof message === 'undefined' || message === undefined) { message = messages.empty; } + + if (isRepeat(message, method) && method.indexOf('time') === -1) { + // up the counter and add the dot + history.output.counter = history.output.counter + 1; + elements.table.lastChild.countDot = elements.table.lastChild.countDot || elements.dot.cloneNode(false); + elements.table.lastChild.firstChild.insertBefore(elements.table.lastChild.countDot, elements.table.lastChild.firstChild.firstChild).innerHTML = history.output.counter; + setLineStyle(elements.table.lastChild, method, message); + } else { + history.output.prevMsg = message; + history.output.prevMethod = method; + history.output.counter = 1; + + //an object requires some more handling + if (typeof message === 'object' && method !== 'assert' && method !== 'timeEnd') { + message = isElement(message) ? + htmlToString(message.outerHTML.match(/<(.*?)>/g)[0] + '...' + message.outerHTML.match(/<(.*?)>/g).pop()) : //gets e.g.
...
+ objectToString(message); + } else if (method !== 'assert' && method.indexOf('time') === -1) { + message = htmlToString(message); + } + + var detailTable, + stackTable, + msgContainer = elements.msgContainer.cloneNode(false), + lineContainer = elements.tr.cloneNode(false), + leftContainer = elements.tdLeft.cloneNode(true), + rightContainer = elements.tdRight.cloneNode(false), + arrows = stackTrace ? elements.arrowRight + ' ' : ''; + + switch (method) { + case 'assert': + if (message[0] === false) { + msgContainer.innerHTML = arrows + 'Assertion failed: ' + message[1]; + } + stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace); + method = 'error'; //groups it under 'error' and is thus toggleable in view + break; + case 'log': + case 'debug': + case 'info': + case 'warn': + if (typeof arguments[1].newMessage === 'object') { + detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage); + msgContainer.innerHTML = elements.arrowRight + ' ' + message; + } else { + msgContainer.innerHTML = message; + } + break; + case 'error': + case 'trace': + case 'dir': + case 'table': + //left side + if (method === 'table' || typeof arguments[1].newMessage === 'object') { + detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage); + msgContainer.innerHTML = elements.arrowRight + ' ' + message; + } else if (method === 'trace') { + message = 'console.trace()'; + msgContainer.innerHTML = arrows + message; + } else { + msgContainer.innerHTML = arrows + message; + } + stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace); + break; + case 'group': + case 'groupCollapsed': + case 'groupEnd': + if (method !== 'groupEnd') { + options.groupDepth = options.groupDepth + 1; + msgContainer.innerHTML = '' + message + ''; + msgContainer.setAttribute('toggles', 'group_' + options.groupDepth); + } else { + options.groupDepth = valBetween(options.groupDepth - 1, 0, 99); + history.output.prevMsg = ''; + } + if (options.groupDepth > 0) { + options.paddingLeft = (options.groupDepth * 23) + 'px'; + } else { + options.paddingLeft = 0; + } + break; + case 'time': + case 'timeEnd': + var timerName = arguments[1].newMessage || 'default', now, passed; + if (method === 'time') { + status.timers[timerName] = Date.now(); + if (typeof arguments[1].original === 'function') { + arguments[1].original.apply(console, arguments[1].originalArguments); //make sure we still call the original console.time to start the browser's console timer + } + return; + } + now = Date.now(); + if (!status.timers[timerName]) { + console.warn('Timer "' + timerName + '" does not exist.'); + return; + } + passed = now - (status.timers[timerName] || 0); + message = timerName + ': ' + passed + 'ms'; + msgContainer.innerHTML = message; + delete status.timers[timerName]; + break; + default: + msgContainer.innerHTML = message; + } + + if (!msgContainer.innerHTML) { return; } + leftContainer.appendChild(msgContainer); + + if (detailTable || stackTable) { + setCSS(msgContainer, {cursor : 'pointer'}); + leftContainer.appendChild(detailTable || stackTable); + msgContainer.setAttribute('toggles', 'table'); + } + + //populate right side + if (stackTrace && typeof stackTrace[stackTrace.length - 1] !== 'undefined') { + rightContainer.appendChild(setCSS(getLink(stackTrace[0].url, stackTrace[0].linkText), {color: '#808080'})); + } + + //add to line + lineContainer.appendChild(leftContainer); + lineContainer.appendChild(rightContainer); + + //set colors + setCSS(lineContainer, { display: (elements.buttons[method].toggled ? 'none' : '') }); + setLineStyle(lineContainer, method, message); + + //set binds + if (options.browserinfo.evtLstn) { + msgContainer.addEventListener('click', toggleDetails, false); + } else { + msgContainer.attachEvent('onclick', toggleDetails); + } + + //store the lines in the object corresponding to the method used + elements.lines[method].push(lineContainer); + + //handle grouping (group and groupEnd + if (options.paddingLeft !== 0) { + setCSS(leftContainer, {paddingLeft: options.paddingLeft}); + setCSS(msgContainer, {borderLeft: '1px solid #808080', paddingLeft: '5px'}); + } + + //add the line to the table + elements.table.appendChild(lineContainer); + } + //scroll + consoleElement.toggleScroll(); + //========================================================== + //make sure we still call the original method, if applicable (not window.onerror) + if (typeof arguments[1].original === 'function') { + arguments[1].original.apply(console, arguments[1].originalArguments); + } + } catch (e) { + //not logging. why? throw error + if (options.browserinfo.isMobile) { alert(e); } + originalConsole.error('mobileConsole generated an error logging this event! (type: ' + typeof message + ')'); + originalConsole.error(arguments); + originalConsole.error(e); + //try to re-log it as an error + newConsole('error', e); + } + + } + function interceptConsole(method) { + var original = console ? console[method] : missingMethod(), i, stackTraceOrig; + if (!console) { console = {}; } //create empty console if we have no console (IE?) + console[method] = function () { + var args = Array.prototype.slice.call(arguments); + args.original = original; + args.originalArguments = arguments; + args.newMessage = (method === 'assert') ? [args[0], args[1]] : args[0]; + //create an Error and get its stack trace and format it + try { throw new Error(); } catch (e) { stackTraceOrig = e.stack; } + args.newStackTrace = formatStackTrace(args.newStackTrace, stackTraceOrig); + if (method === 'clear') { + try { + elements.table.innerHTML = ''; + } catch (e) { + console.log('This browser does not allow clearing tables, the console cannot be cleared.'); + return; + } + history.output.prevMethod = ''; + i = options.methods.length; + while (i--) { + elements.lines[options.methods[i]] = []; + } + options.groupDepth = 0; + options.paddingLeft = 0; + console.log(messages.clear); + originalConsole.clear(); + return; + } + //Handle the new console logging + newConsole(method, args); + }; + } + //init + function init() { + //Intercept all original console methods including trace. Register the event type as a line type. + var i = options.methods.length; + while (i--) { + elements.lines[options.methods[i]] = []; + interceptConsole(options.methods[i]); + } + //Bind to window.onerror + window.onerror = function() { + var args = Array.prototype.slice.call(arguments); + args.newMessage = args[0]; + args.newStackTrace = formatStackTrace(arguments); + newConsole('error', args); + }; + + return { + //nothing to expose + }; + } + //return + if (!ref) { + ref = init(); + } + return ref; + } + + //initialize the console commandline + function initCommandLine() { + //reference + var ref; + //sub helpers + function getFromArrayById(id) { + var pos = elements.acItems.map(function(x) {return x.id; }).indexOf(id); + return { + position: pos, + element: (pos !== -1) ? elements.acItems[pos] : undefined + }; + } + function findInArray(array, match) { + return array.filter(function(item, index, self){ + return (typeof item === 'string' && item.indexOf(match) > -1) && (index === self.indexOf(item)); + }); + } + //core + function assemble() { + elements.consoleinput.setAttribute('type', 'text'); + elements.consoleinput.setAttribute('autocapitalize', 'off'); + elements.consoleinput.setAttribute('autocorrect', 'off'); + elements.autocompleteItem.setAttribute('href', '#'); + elements.gt.innerHTML = '>'; + elements.input.appendChild(elements.gt); + elements.input.appendChild(elements.consoleinput); + elements.input.appendChild(elements.autocomplete); + elements.base.appendChild(elements.input); + + return elements.base; + } + function submitCommand(command) { + if (command !== '') { + storeCommand(command); + var result; + try { + result = eval.call(window, command.trim()); + console.log.call(window, result); + } catch(e) { + console.error(e.message); + } finally { + elements.consoleinput.value = ''; + } + } + } + function hoverAutoComplete(e) { + if (typeof e === 'undefined') { return; } + //unset any already hovered elements + var hovered = getFromArrayById('hover').element, target = e.target || e.srcElement, over; + if (typeof hovered !== 'undefined') { + setCSS(hovered, { + color: '', + backgroundColor: '' + }).id = ''; + } + if (e.type === 'mouseover') { + status.acHovered = true; + over = true; + } else { + over = false; + } + setCSS(target, { + color: over ? '#FFFFFF' : '', + backgroundColor: over ? 'rgb(66,139,202)' : '' + }).id = over ? 'hover' : ''; + } + function toggleAutoComplete(show) { + var hidden = (elements.autocomplete.currentStyle ? elements.autocomplete.currentStyle.display : window.getComputedStyle(elements.autocomplete, null).display) === 'none'; + show = (typeof show === 'undefined') ? hidden : show; + setCSS(elements.autocomplete, {display: (show) ? 'inherit' : 'none'}); + status.acActive = show; + if (!show) { status.acHovered = false; } + } + function clickAutoComplete(e) { + if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } + var tgt = e.target || e.srcElement; + elements.consoleinput.value = tgt.innerHTML; + elements.consoleinput.focus(); + toggleAutoComplete(); + } + function autoComplete(command) { + if (command.length < 1) { + toggleAutoComplete(false); + return; + } + var searchString = encodeURI(command), matches, match, row, i, maxAmount = options.browserinfo.isMobile ? 3 : 5; + elements.autocomplete.innerHTML = ''; + elements.acItems = []; + matches = findInArray(history.input.commands, searchString); + matches = matches.slice(Math.max(matches.length - maxAmount, 0)); + i = matches.length; + while (i--) { + match = decodeURI(matches[i]); + row = elements.autocompleteItem.cloneNode(false); + row.innerHTML = match; + row.onmouseover = hoverAutoComplete; + elements.autocomplete.insertBefore(row, elements.autocomplete.firstChild); + elements.acItems.unshift(row); + } + toggleAutoComplete(matches.length > 0); + } + function setBinds() { + if (options.browserinfo.evtLstn) { + elements.autocomplete.addEventListener('click', clickAutoComplete, false); + } else { + elements.autocomplete.attachEvent('onclick', clickAutoComplete); + } + document.onkeydown = function (e) { + e = e || window.event; + var tgt = e.target || e.srcElement; + if (tgt === elements.consoleinput) { + if ((e.key === 'Enter' || e.keyCode === 13)) { //enter + if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } + if(!status.acHovered) { + submitCommand(elements.consoleinput.value); + } else { + elements.consoleinput.value = getFromArrayById('hover').element.innerHTML; + elements.consoleinput.focus(); + } + toggleAutoComplete(false); + status.acInput = ''; + } else if ((e.keyCode === 38 || e.keyCode === 40)) { //up and down arrows for history browsing + if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } + var up = (e.keyCode === 40); + if(status.acActive) { + //autocomplete window is opened + //get id of currently hovered element + var hovered = getFromArrayById('hover').position; + var counter = (hovered === -1) ? elements.acItems.length : hovered; + //hover new (in- or decreased number) one + counter = valBetween((counter += (up) ? 1 : -1), 0, elements.acItems.length - 1); + hoverAutoComplete({target : elements.acItems[counter], type : 'mouseover'}); + } else { + //autocompete window not opened + var hist = history.input.commands; + history.input.commandIdx += (up) ? 1 : -1; + history.input.commandIdx = valBetween(history.input.commandIdx, 0, hist.length); + elements.consoleinput.value = typeof hist[history.input.commandIdx] === 'undefined' ? '' : decodeURI(hist[history.input.commandIdx]); + } + } + } + if (e.keyCode === 27 && status.acActive) { + toggleAutoComplete(false); + } + }; + document.onkeyup = function (e) { + e = e || window.event; + var tgt = e.target || e.srcElement; + if (tgt === elements.consoleinput && status.acInput !== elements.consoleinput.value && (e.keyCode !== 38 && e.keyCode !== 40 && e.keyCode !== 27 && e.key !== 'Enter' && e.keyCode !== 13)) { + status.acInput = elements.consoleinput.value.trim(); + autoComplete(elements.consoleinput.value); + } + }; + } + //init + function init() { + assemble(); + setBinds(); + return { + //nothing to expose + }; + } + //return + if (!ref) { + ref = init(); + } + return ref; + } + + function init() { + if (!status.initialized) { + if (consoleElement && mobileConsole) { + console.error( 'Mobile Console cannot be reconstructed! Reload the page to enable Mobile Console again.' + '\n' + + 'Tip: use the minimize button instead of closing.' ); + return; + } else { + status.initialized = true; + //populate references + if (!mobileConsole) { + //taps into native console and adds new functionality + mobileConsole = initConsole(); + } + if (!consoleElement && mobileConsole) { + //creates the new HTML console element and attaches it to document + consoleElement = initConsoleElement(); + } + if (!commandLine && consoleElement && mobileConsole) { + //creates an HTML commandline and attaches it to existing console element + commandLine = initCommandLine(); + } + } + } + //log a 'welcome' message + console.info( '--==## Mobile Console v' + options.version + ' ' + (status.initialized ? 'active' : 'inactive' ) + ' ##==--' ); + } + + //autorun if mobile + if (options.browserinfo.isMobile || options.overrideAutorun) { + init(); + } + + //expose the mobileConsole's methods + return { + init : init, + displayConsole: displayConsole, + about: about, + toggle : toggleHeight, + status : status, + options : options + }; + +}()); diff --git a/server/page/menus.php b/server/page/menus.php index f253dec..b3ce2ed 100644 --- a/server/page/menus.php +++ b/server/page/menus.php @@ -206,7 +206,7 @@ - @@ -258,7 +263,7 @@

Momentan kannst Du Dich leider nicht in der App registrieren.
Das ist aber kein Problem, registriere Dich einfach kostenlos auf unserer Website! -

+

Registrieren

Du kannst Dich danach in dieser App anmelden.

@@ -289,8 +294,8 @@

Update Verfügbar

Eine neue Version unserer App ist verfügbar. Keine Sorge, Du musst nichts machen. Wir aktuallisieren den Inhalt in wenigen Sekunden. -

+

Update

Die App wird neu laden und das Update ist abgeschlossen.

- \ No newline at end of file + diff --git a/server/page/wrapper.php b/server/page/wrapper.php index 7eca108..352537b 100644 --- a/server/page/wrapper.php +++ b/server/page/wrapper.php @@ -1,7 +1,8 @@ - + + @@ -16,36 +17,36 @@ - + - +
- +
- + - +
- + - + - + - +
- + - + @@ -55,5 +56,5 @@ - - \ No newline at end of file + + From 12c1a614bbb798730bd182fa5b20ea083819c0d5 Mon Sep 17 00:00:00 2001 From: Timon Ostertun Date: Wed, 30 Sep 2020 16:46:10 +0200 Subject: [PATCH 02/11] Fix #5 serviceWorker DB access serviceWorker openes DB only when needed and closes it directly --- service-worker.js.php | 138 ++++++++++++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 40 deletions(-) diff --git a/service-worker.js.php b/service-worker.js.php index 35b049b..0f52f7f 100644 --- a/service-worker.js.php +++ b/service-worker.js.php @@ -1,9 +1,9 @@ importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js'); @@ -41,7 +41,7 @@ workbox.precaching.precacheAndRoute([ return $hash; } $hash = md5(getDirHash(__DIR__)); - + $path = __DIR__ . '/server/content/'; $dir = opendir($path); while ($file = readdir($dir)) { @@ -50,7 +50,7 @@ workbox.precaching.precacheAndRoute([ echo "\t{url: '$file', revision: '$hash'},\n"; } closedir($dir); - + // ASSETS $filesToCache = [ '/manifest.json.php', @@ -58,7 +58,7 @@ workbox.precaching.precacheAndRoute([ $dirsToCache = [ '/client', ]; - + function addDir($path) { global $filesToCache; if ($dir = opendir(__DIR__ . $path)) { @@ -74,11 +74,11 @@ workbox.precaching.precacheAndRoute([ closedir($dir); } } - + foreach ($dirsToCache as $path) { addDir($path); } - + foreach ($filesToCache as $file) { $revision = md5_file(__DIR__ . $file); $file = SERVER_ADDR . $file; @@ -109,30 +109,41 @@ workbox.routing.registerRoute( // DB -var db = null; -if (indexedDB) { - var request = indexedDB.open('regatten_app_db_'); - request.onerror = function (e) { - console.log('[sW] Cannot open DB:', e.target.errorCode); - }; - request.onupgradeneeded = function (e) { - console.log('[sW] DB does not exist'); - e.target.transaction.abort(); - }; - request.onsuccess = function (e) { - console.log('[sW] DB loaded'); - db = e.target.result; - db.onerror = function (e) { - console.log('[sW] DB Error:', e) +function openDb() { + return new Promise(function(resolve) { + if (indexedDB) { + var request = indexedDB.open('regatten_app_db_'); + request.onerror = function (e) { + console.log('[sW] Cannot open DB:', e.targer.errorCode); + resolve(null); + }; + request.onupgradeneeded = function (e) { + console.log('[sW] DB does not exist'); + e.target.transaction.abort(); + resolve(null); + }; + request.onsuccess = function (e) { + console.log('[sW] DB loaded'); + var db = e.target.result; + db.onerror = function (e) { + console.log('[sW] DB Error:', e); + }; + resolve(db); + } + } else { + resolve(null); } - }; + }); } function dbSettingsGet(key) { - return new Promise(function(resolve) { - if (db != null) { + return new Promise(async function(resolve) { + var db = await openDb(); + if (db !== null) { var request = db.transaction('settings').objectStore('settings').get(key); request.onsuccess = function (event) { + db.close(); + console.log('[sW] DB closed'); resolve(typeof request.result != 'undefined' ? request.result.value : null); } } else { @@ -141,10 +152,20 @@ function dbSettingsGet(key) { }); } -function dbSettingsSet(key, value) { +async function dbSettingsSet(key, value) { + var db = await openDb(); if (db != null) { var os = db.transaction('settings', 'readwrite').objectStore('settings'); - os.put({ key: key, value: value}); + var request = os.put({ key: key, value: value}); + request.onerror = function (event) { + console.log('[sW] Error while saving data to DB:', e); + db.close(); + console.log('[sW] DB closed'); + } + request.onsuccess = function (event) { + db.close(); + console.log('[sW] DB closed'); + } } } @@ -167,7 +188,7 @@ function isMyRegatta(id) { self.addEventListener('push', async function(event) { console.log('[sW] Push received:', event.data.text()); - + var data; try { data = JSON.parse(event.data.text()); @@ -175,14 +196,14 @@ self.addEventListener('push', async function(event) { console.log(e); data = undefined; } - + if (typeof data.type !== "undefined") { switch (data.type) { case 'notification': if (typeof data.title === "undefined") break; if (typeof data.body === "undefined") break; if (typeof data.channel === "undefined") break; - + // check channel var okay = false; switch (data.channel) { @@ -214,7 +235,7 @@ self.addEventListener('push', async function(event) { console.log('Notification channel not subscribed'); return; } - + const options = { data: data, body: data.body, @@ -225,26 +246,63 @@ self.addEventListener('push', async function(event) { if ((image = getEntry(data, 'image', null)) !== null) { options.image = image; } - + // Force refresh on next app open - var os = db.transaction('update_times', 'readwrite').objectStore('update_times'); - os.put({ table: 'last_sync', time: 0 }); - + var db = await openDb(); + if (db != null) { + var os = db.transaction('update_times', 'readwrite').objectStore('update_times'); + var request = os.put({ table: 'last_sync', time: 0 }); + request.onerror = function (event) { + console.log('[sW] Error while saving data to DB:', e); + db.close(); + console.log('[sW] DB closed'); + } + request.onsuccess = function (event) { + db.close(); + console.log('[sW] DB closed'); + } + } + console.log('Showing notification'); self.registration.showNotification(data.title, options); break; + + case 'forcesync': + // Force refresh on next app open + var db = await openDb(); + if (db != null) { + var os = db.transaction('update_times', 'readwrite').objectStore('update_times'); + var request = os.put({ table: 'last_sync', time: 0 }); + request.onerror = function (event) { + console.log('[sW] Error while saving data to DB:', e); + db.close(); + console.log('[sW] DB closed'); + } + request.onsuccess = function (event) { + console.log('[sW] Data successfully saved'); + db.close(); + console.log('[sW] DB closed'); + } + } + break; + + default: + console.log('[sW] Push type unknown:', data.type); + break; } + } else { + console.log('[sW] No push type given!'); } }); self.addEventListener('notificationclick', function(event) { var data = event.notification.data; - + event.notification.close(); - + var url = '' + getEntry(data, 'url', ''); - + event.waitUntil( clients.openWindow(url) ); -}); \ No newline at end of file +}); From eb47cdf0161e76dda63fd2d676d0613ada6978c4 Mon Sep 17 00:00:00 2001 From: Timon Ostertun Date: Wed, 30 Sep 2020 22:25:59 +0200 Subject: [PATCH 03/11] RA-#7 Removed unnecessary files from cache --- service-worker.js.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/service-worker.js.php b/service-worker.js.php index 0f52f7f..e4c2322 100644 --- a/service-worker.js.php +++ b/service-worker.js.php @@ -56,15 +56,19 @@ workbox.precaching.precacheAndRoute([ '/manifest.json.php', ]; $dirsToCache = [ - '/client', + '/client/app', + '/client/fonts/css', + '/client/fonts/webfonts' + '/client/images' + '/client/scripts' + '/client/styles' ]; function addDir($path) { global $filesToCache; if ($dir = opendir(__DIR__ . $path)) { while (($file = readdir($dir)) !== false) { - if ($file == '.') continue; - if ($file == '..') continue; + if (substr($file, 0, 1) == '.') continue; if (is_dir(__DIR__ . $path . '/' . $file)) { addDir($path . '/' . $file); } else { From b739e6cc4e05e34ab6851369dd44834ada8b6296 Mon Sep 17 00:00:00 2001 From: Timon Ostertun Date: Wed, 30 Sep 2020 22:45:43 +0200 Subject: [PATCH 04/11] RA-#7 Fix serviceWorker --- service-worker.js.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/service-worker.js.php b/service-worker.js.php index e4c2322..15d28d5 100644 --- a/service-worker.js.php +++ b/service-worker.js.php @@ -58,10 +58,10 @@ workbox.precaching.precacheAndRoute([ $dirsToCache = [ '/client/app', '/client/fonts/css', - '/client/fonts/webfonts' - '/client/images' - '/client/scripts' - '/client/styles' + '/client/fonts/webfonts', + '/client/images', + '/client/scripts', + '/client/styles', ]; function addDir($path) { From a72c462483db74f37778af8fdfd2b8608bb63dc5 Mon Sep 17 00:00:00 2001 From: Timon Ostertun Date: Wed, 30 Sep 2020 22:46:20 +0200 Subject: [PATCH 05/11] RA-#4 Registration open icon is red when deadline expired --- server/scripts/index.js | 112 +++++++++++++++++++------------------ server/scripts/regattas.js | 70 ++++++++++++----------- 2 files changed, 94 insertions(+), 88 deletions(-) diff --git a/server/scripts/index.js b/server/scripts/index.js index 9f27bbe..89709fa 100644 --- a/server/scripts/index.js +++ b/server/scripts/index.js @@ -2,12 +2,12 @@ var today; var siteScript = async function() { today = getToday(); - + if (isLoggedIn()) { $('#card-notloggedin').hide(); - + var user = await dbGetData('users', localStorage.getItem('auth_user')); - + // Favorites var watched = []; for (var i = 1; i <= 5; i ++) { @@ -46,7 +46,7 @@ var siteScript = async function() { $('#p-favorites').show(); } $('#card-favorites').show(); - + // Your next var planningsDB = await dbGetDataIndex('plannings', 'user', user.id); var minDate = getToday(); @@ -75,35 +75,35 @@ var siteScript = async function() { for (i in plannings) { var planning = plannings[i]; var regatta = planning.regatta; - + if (regatta['length'] < 1) continue; - + var club = null; if (regatta['club'] != null) club = await dbGetData('clubs', regatta['club']); var dateFrom = regatta['dateFrom']; var dateTo = regatta['dateTo']; - + // output - + list += '
'; - + // ZEILE 1 // Name list += '
' + (regatta['canceled'] == 1 ? '' : '') + regatta['name'] + (regatta['canceled'] == 1 ? '' : '') + '
'; - + // ZEILE 2 list += '
'; - + // Number list += '
' + ((regatta['number'] != null) ? ('# ' + regatta['number']) : '') + '
'; - + // Club list += '
' + ((club != null) ? club['kurz'] : '') + '
'; - + // Special list += '
' + regatta['special'] + '
'; - + // Icons var icons = []; if (regatta['info'] != '') @@ -120,8 +120,10 @@ var siteScript = async function() { ms = parseDate(regatta['meldungSchluss']); } var diff = Math.round((ms - today) / 86400000); - if ((ms >= today) && (diff < 7)) { + if (ms < today) { color = ' color-red2-dark'; + } else if (diff < 7) { + color = ' color-yellow2-dark'; } } } @@ -134,18 +136,18 @@ var siteScript = async function() { icons.push(''); } list += '
' + icons.join(' ') + '
'; - + list += '
'; - + // ZEILE 3 list += '
'; - + // Date list += '
' + formatDate("d.m.Y", dateFrom) + ' - ' + formatDate("d.m.Y", dateTo) + '
'; - + // RLF list += '
' + parseFloat(regatta['rlf']).toFixed(2) + '
'; - + list += '
'; } $('#div-yournext').html(list); @@ -161,7 +163,7 @@ var siteScript = async function() { $('#card-yournext').hide(); $('#card-notloggedin').show(); } - + // Next var minDate = getToday(); minDate.setDate(minDate.getDate() - 1); @@ -172,35 +174,35 @@ var siteScript = async function() { list = ''; for (i in regattas) { var regatta = regattas[i]; - + if (regatta['length'] < 1) continue; - + var club = null; if (regatta['club'] != null) club = await dbGetData('clubs', regatta['club']); var plannings = await dbGetDataIndex('plannings', 'regatta', regatta['id']); var dateFrom = regatta['dateFrom']; var dateTo = regatta['dateTo']; - + // output list += '
'; - + // ZEILE 1 // Name list += '
' + (regatta['canceled'] == 1 ? '' : '') + regatta['name'] + (regatta['canceled'] == 1 ? '' : '') + '
'; - + // ZEILE 2 list += '
'; - + // Number list += '
' + ((regatta['number'] != null) ? ('# ' + regatta['number']) : '') + '
'; - + // Club list += '
' + ((club != null) ? club['kurz'] : '') + '
'; - + // Special list += '
' + regatta['special'] + '
'; - + // Icons var icons = []; if (regatta['info'] != '') @@ -227,8 +229,10 @@ var siteScript = async function() { ms = parseDate(regatta['meldungSchluss']); } var diff = Math.round((ms - today) / 86400000); - if ((ms >= today) && (diff < 7)) { + if (ms < today) { color = ' color-red2-dark'; + } else if (diff < 7) { + color = ' color-yellow2-dark'; } } } @@ -241,18 +245,18 @@ var siteScript = async function() { icons.push(''); } list += '
' + icons.join(' ') + '
'; - + list += '
'; - + // ZEILE 3 list += '
'; - + // Date list += '
' + formatDate("d.m.Y", dateFrom) + ' - ' + formatDate("d.m.Y", dateTo) + '
'; - + // RLF list += '
' + parseFloat(regatta['rlf']).toFixed(2) + '
'; - + list += '
'; } $('#div-next').html(list); @@ -262,7 +266,7 @@ var siteScript = async function() { $('#div-next').hide(); $('#p-next').show(); } - + // Last var minDate = getToday(); minDate.setDate(minDate.getDate() - 14); @@ -279,35 +283,35 @@ var siteScript = async function() { list = ''; for (i in regattas) { var regatta = regattas[i]; - + if (regatta['length'] < 1) continue; - + var club = null; if (regatta['club'] != null) club = await dbGetData('clubs', regatta['club']); var dateFrom = regatta['dateFrom']; var dateTo = regatta['dateTo']; - + // output - + list += '
'; - + // ZEILE 1 // Name list += '
' + (regatta['canceled'] == 1 ? '' : '') + regatta['name'] + (regatta['canceled'] == 1 ? '' : '') + '
'; - + // ZEILE 2 list += '
'; - + // Number list += '
' + ((regatta['number'] != null) ? ('# ' + regatta['number']) : '') + '
'; - + // Club list += '
' + ((club != null) ? club['kurz'] : '') + '
'; - + // Special list += '
' + regatta['special'] + '
'; - + // Icons var icons = []; if (regatta['info'] != '') @@ -320,18 +324,18 @@ var siteScript = async function() { icons.push(''); } list += '
' + icons.join(' ') + '
'; - + list += '
'; - + // ZEILE 3 list += '
'; - + // Date list += '
' + formatDate("d.m.Y", dateFrom) + ' - ' + formatDate("d.m.Y", dateTo) + '
'; - + // RLF list += '
' + parseFloat(regatta['rlf']).toFixed(2) + '
'; - + list += '
'; } $('#div-last').html(list); @@ -341,6 +345,6 @@ var siteScript = async function() { $('#div-last').hide(); $('#p-last').show(); } - + hideLoader(); -} \ No newline at end of file +} diff --git a/server/scripts/regattas.js b/server/scripts/regattas.js index 6b51e09..ffe2c6b 100644 --- a/server/scripts/regattas.js +++ b/server/scripts/regattas.js @@ -8,10 +8,10 @@ function selectChange(callSiteScript = true) { $('#input-from').parent().hide(); $('#input-to').parent().hide(); $('#button-show').hide(); - + $('#input-from').val(val + '-01-01'); $('#input-to').val(val + '-12-31'); - + if (callSiteScript && (typeof siteScript === 'function')) siteScript(); } @@ -20,10 +20,10 @@ function selectChange(callSiteScript = true) { function initYear() { var year = findGetParameter('year'); if (year === null) year = new Date().getFullYear(); - + $('#select-year').html(''); $('#select-year').val(year); - + selectChange(false); } @@ -53,9 +53,9 @@ var siteScript = async function() { $('#button-show').click(siteScript); $('#input-search').on('input', drawList); } - + today = getToday(); - + var minDate = parseDate($('#input-from').val()); var maxDate = parseDate($('#input-to').val()); var regattas = await dbGetRegattasRange(minDate, maxDate); @@ -65,9 +65,9 @@ var siteScript = async function() { var results = await dbGetDataIndex('results', 'regatta', entry['id']); regattaResults[entry['id']] = (results.length > 0); } - + var selectedYear = $('#select-year').val(); - + var years = await dbGetData('years'); years.sort(function (a, b) { if (a['year'] > b['year']) return -1; @@ -81,7 +81,7 @@ var siteScript = async function() { } $('#select-year').html(options); $('#select-year').val(selectedYear); - + var count = regattas.length; if (count > 0) { if (count == 1) { @@ -91,49 +91,49 @@ var siteScript = async function() { } $('#div-regattas').show(); $('#input-search').parent().show(); - + var heute = false; - + rows = []; - + for (id in regattas) { var entry = regattas[id]; var club = null; if (entry['club'] != null) club = await dbGetData('clubs', entry['club']); var plannings = await dbGetDataIndex('plannings', 'regatta', entry['id']); - + var dateFrom = entry['dateFrom']; var dateTo = entry['dateTo']; - + var row = { keywords: [], content: '' }; row.keywords.push(entry['name']); if (entry['number'] != null) row.keywords.push(entry['number']); if (club != null) row.keywords.push(club['kurz'], club['name']); - + if (!heute && (today <= dateFrom)) { rows.push(null); heute = true; } - + row.content += '
'; - + // ZEILE 1 // Name row.content += '
' + (entry['canceled'] == 1 ? '' : '') + entry['name'] + (entry['canceled'] == 1 ? '' : '') + '
'; - + // ZEILE 2 row.content += '
'; - + // Number row.content += '
' + ((entry['number'] != null) ? ('# ' + entry['number']) : '') + '
'; - + // Club row.content += '
' + ((club != null) ? club['kurz'] : '') + '
'; - + // Special row.content += '
' + entry['special'] + '
'; - + // Icons var icons = []; if (entry['info'] != '') @@ -160,8 +160,10 @@ var siteScript = async function() { ms = parseDate(entry['meldungSchluss']); } var diff = Math.round((ms - today) / 86400000); - if ((ms >= today) && (diff < 7)) { + if (ms < today) { color = ' color-red2-dark'; + } else if (diff < 7) { + color = ' color-yellow2-dark'; } } } @@ -178,12 +180,12 @@ var siteScript = async function() { icons.push(''); } row.content += '
' + icons.join(' ') + '
'; - + row.content += '
'; - + // ZEILE 3 row.content += '
'; - + // Date if (entry['length'] < 1) { if (formatDate('d.m', dateFrom) == '01.01') { @@ -194,26 +196,26 @@ var siteScript = async function() { } else { row.content += '
' + formatDate("d.m.Y", dateFrom) + ' - ' + formatDate("d.m.Y", dateTo) + '
'; } - + // RLF row.content += '
' + parseFloat(entry['rlf']).toFixed(2) + '
'; - + row.content += '
'; - + rows.push(row); } - + if (!heute) { rows.push(null); } - + drawList(); - + } else { $('#p-count').html('Keine Regatten gefunden!'); $('#div-regattas').hide(); $('#input-search').parent().hide(); } - + hideLoader(); -} \ No newline at end of file +} From 77ea57d64321dba79de14a25b7f73c3daeadc040 Mon Sep 17 00:00:00 2001 From: Timon Ostertun Date: Wed, 30 Sep 2020 23:24:24 +0200 Subject: [PATCH 06/11] RA-#1 Notifications: Badge Icon --- service-worker.js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-worker.js.php b/service-worker.js.php index 15d28d5..7eaae96 100644 --- a/service-worker.js.php +++ b/service-worker.js.php @@ -244,7 +244,7 @@ self.addEventListener('push', async function(event) { data: data, body: data.body, icon: getEntry(data, 'icon', '/client/app/icons/icon-512x512.png'), - badge: '/client/app/icons/icon-96x96.png', + badge: '/client/app/icons/badge-128x128.png', vibrate: [500,100,500] }; if ((image = getEntry(data, 'image', null)) !== null) { From 7afacc3fff0dfc42e6dc53c21f868dc7f7f7f942 Mon Sep 17 00:00:00 2001 From: Timon Ostertun <43936761+CaGrRj@users.noreply.github.com> Date: Wed, 30 Sep 2020 23:56:51 +0200 Subject: [PATCH 07/11] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From d1e5d753b4fc01adb475f14175c2b8eaacab0300 Mon Sep 17 00:00:00 2001 From: Timon Ostertun <43936761+ostertun@users.noreply.github.com> Date: Thu, 1 Oct 2020 01:03:41 +0200 Subject: [PATCH 08/11] Create README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..a936021 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# Regatten.net App + +Willkommen im GitHub-Repository unserer neuen Regatten.net App. + +Falls sich jemand dafür interessiert, ist hier der gesamte Quellcode unserer neuen App zu finden. +Bitte beachtet, dass diese App einschließlich dem gesamten Quellcode exklusivem Urheberrecht unterliegt. Sie darf also nicht kopiert, verteilt oder verändert werden. +Sollte Interesse an einer Anbindung an unser System bestehen, kontaktiere uns bitte einfach (https://regatten.net/contact) + +Der Hauptzweck dieses Repository besteht jedoch darin, Feedback von Euch zu unserer neuen App zu bekommen. +Selbstverständlich könnt Ihr uns auch weiterhin auf anderen Kanälen erreichen, doch für Problem-Meldungen und Verbesserungsvorschläge ist GitHub am besten geeignet. +Wie Du Teil unseres BETA-Programmes wirst, die App installierst und Feedback gibst, erfährst Du in unserem [Wiki](https://github.com/ostertun/RegattenApp/wiki). From 6ca6a79dc34b7a0d7fe1ed207ed93349a9b0f992 Mon Sep 17 00:00:00 2001 From: Timon Ostertun <43936761+ostertun@users.noreply.github.com> Date: Thu, 1 Oct 2020 02:59:12 +0200 Subject: [PATCH 09/11] Update issue templates --- .../ISSUE_TEMPLATE/-de--feature-anfrage.md | 20 ++++++++++ .github/ISSUE_TEMPLATE/-de--fehlerbericht.md | 38 +++++++++++++++++++ .github/ISSUE_TEMPLATE/-en--bug-report.md | 38 +++++++++++++++++++ .../ISSUE_TEMPLATE/-en--feature-request.md | 20 ++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 5 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/-de--feature-anfrage.md create mode 100644 .github/ISSUE_TEMPLATE/-de--fehlerbericht.md create mode 100644 .github/ISSUE_TEMPLATE/-en--bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/-en--feature-request.md diff --git a/.github/ISSUE_TEMPLATE/-de--feature-anfrage.md b/.github/ISSUE_TEMPLATE/-de--feature-anfrage.md new file mode 100644 index 0000000..bc7f499 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/-de--feature-anfrage.md @@ -0,0 +1,20 @@ +--- +name: "[DE] Feature-Anfrage" +about: Schlage eine Idee für dieses Projekt vor +title: '' +labels: enhancement +assignees: '' + +--- + +**Bezieht sich Deine Feature-Anfrage auf ein Problem? Bitte beschreiben.** +Eine klare und präzise Beschreibung des Problems. Z.B. Ich bin immer frustriert, wenn [...] + +**Beschreibe die gewünschte Lösung** +Eine klare und präzise Beschreibung dessen, was passieren soll. + +**Beschreibe Alternativen, die Du in Betracht gezogen hast** +Eine klare und präzise Beschreibung aller alternativen Lösungen oder Funktionen, die Du in Betracht gezogen hast. + +**Zusätzlicher Kontext** +Füge hier weitere Kontexte oder Screenshots zur Feature-Anfrage hinzu. diff --git a/.github/ISSUE_TEMPLATE/-de--fehlerbericht.md b/.github/ISSUE_TEMPLATE/-de--fehlerbericht.md new file mode 100644 index 0000000..cc0c162 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/-de--fehlerbericht.md @@ -0,0 +1,38 @@ +--- +name: "[DE] Fehlerbericht" +about: Erstelle einen Bericht, um uns zu helfen, die App zu verbessern +title: '' +labels: bug +assignees: '' + +--- + +**Beschreibe den Fehler** +Eine klare und präzise Beschreibung des Fehlers. + +**Reproduzierung** +Schritte zum Reproduzieren des Fehlers: +1. Gehe zu '...' +2. Klicke auf '....' +3. Scrolle nach unten zu '....' +4. Sieh den Fehler + +**Erwartetes Verhalten** +Eine klare und präzise Beschreibung dessen, was Du erwartet hattest. + +**Screenshots** +Füge gegebenenfalls Screenshots hinzu, um Dein Problem zu erklären. + +**Desktop (bitte verfolständige die folgenden Informationen):** + - Betriebssystem: [z.B. Windows 10] + - Browser [z.B. chrome, safari] + - Version [z.B. 2.14] + +**Smartphone (bitte verfolständige die folgenden Informationen):** + - Gerät: [z.B. iPhone 6] + - Betriebssystem: [z.B. iOS 8.1] + - Browser [z.B. stock browser, safari] + - Version [z.B. 2.14] + +**Zusätzlicher Kontext** +Füge hier einen anderen Kontext zum Problem hinzu. diff --git a/.github/ISSUE_TEMPLATE/-en--bug-report.md b/.github/ISSUE_TEMPLATE/-en--bug-report.md new file mode 100644 index 0000000..9f28316 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/-en--bug-report.md @@ -0,0 +1,38 @@ +--- +name: "[EN] Bug report" +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. Windows 10] + - Browser [e.g. chrome, safari] + - Version [e.g. 2.14] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone 6] + - OS: [e.g. iOS 8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 2.14] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/-en--feature-request.md b/.github/ISSUE_TEMPLATE/-en--feature-request.md new file mode 100644 index 0000000..1c578c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/-en--feature-request.md @@ -0,0 +1,20 @@ +--- +name: "[EN] Feature request" +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..11fc491 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: '' -labels: '' +labels: enhancement assignees: '' --- From b7dec825cad7a7ba771562d564b43a9a606fd4e2 Mon Sep 17 00:00:00 2001 From: Timon Ostertun <43936761+ostertun@users.noreply.github.com> Date: Thu, 1 Oct 2020 03:00:38 +0200 Subject: [PATCH 10/11] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ----------------------- .github/ISSUE_TEMPLATE/feature_request.md | 20 ------------ 2 files changed, 58 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index dd84ea7..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 11fc491..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. From 4317e4fe87baa12f7bc8dd5002788bf5d450abac Mon Sep 17 00:00:00 2001 From: ostertun Date: Thu, 1 Oct 2020 03:19:42 +0200 Subject: [PATCH 11/11] Release v_1.7 Prepare github repo small fixes --- server/version.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/version.php b/server/version.php index 878e216..722613f 100644 --- a/server/version.php +++ b/server/version.php @@ -1,5 +1,5 @@ \ No newline at end of file + + define('PWA_VERSION', '1.7'); + +?>