diff --git a/apps/Makefile b/apps/Makefile index af24906..0ee7a77 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -1,10 +1,23 @@ BUILDDIR = ../build/apps -copyfiles = ./* +copyfiles = router.lua controllers logs views models +coffees = assets/coffee/bootstrap.coffee \ + assets/coffee/BaseObject.coffee \ + assets/coffee/APIManager.coffee \ + assets/coffee/MarkOn.coffee \ -main: - - mkdir $(BUILDDIR) + +main: js + - mkdir -p $(BUILDDIR)/assets cp -rf $(copyfiles) $(BUILDDIR) + cp -r assets/css assets/scripts $(BUILDDIR)/assets - cd $(BUILDDIR) && ln -s ../grs ./rst + +js: + - rm assets/scripts/main.js + for f in $(coffees); do (cat "$${f}"; echo) >> assets/scripts/main.coffee; done + coffee --compile assets/scripts/main.coffee + - rm assets/scripts/main.coffee + clean: rm -rf $(BUILDDIR)/* \ No newline at end of file diff --git a/apps/assets/coffee/APIManager.coffee b/apps/assets/coffee/APIManager.coffee new file mode 100644 index 0000000..05bb5f3 --- /dev/null +++ b/apps/assets/coffee/APIManager.coffee @@ -0,0 +1,22 @@ +class APIManager extends window.classes.BaseObject + constructor: () -> + super "APIManager" + + init: (cname) -> + console.log(cname) + @ready() + .then () -> + if mobilecheck() + mobileConsole.init() + # load the class + return if not cname or cname is "" + return console.error("Cannot find class ", cname) unless window.classes[cname] + (new window.classes[cname]).init() + .catch ( m, s ) -> + console.error(m, s) + +APIManager.dependencies = [ + "/assets/scripts/mobile_console.js" +] + +makeclass "APIManager", APIManager \ No newline at end of file diff --git a/apps/assets/coffee/BaseObject.coffee b/apps/assets/coffee/BaseObject.coffee new file mode 100644 index 0000000..dadf740 --- /dev/null +++ b/apps/assets/coffee/BaseObject.coffee @@ -0,0 +1,25 @@ +class BaseObject + constructor: (@name) -> + + ready: () -> + me = @ + return new Promise (r, e) -> + me.resolveDep() + .then () -> r() + .catch (m, s) -> e(m, s) + + resolveDep: () -> + me = @ + return new Promise (r, e) -> + dep = window.classes[me.name].dependencies + r() unless dep + + fn = (l, i) -> + return r() if i >= dep.length + require(l[i]) + .then () -> fn(l, i + 1) + .catch (m, s) -> e(m, s) + fn dep, 0 + +makeclass "BaseObject", BaseObject + \ No newline at end of file diff --git a/apps/assets/coffee/MarkOn.coffee b/apps/assets/coffee/MarkOn.coffee new file mode 100644 index 0000000..57db38a --- /dev/null +++ b/apps/assets/coffee/MarkOn.coffee @@ -0,0 +1,17 @@ +class MarkOn extends window.classes.BaseObject + constructor: (@id) -> + super "MarkOn" + + init: () -> + me = @ + @ready() + .then () -> + me.editor = new SimpleMDE { element: $(me.id)[0] } + .catch (m, s) -> + console.error(m, s) + +MarkOn.dependencies = [ + "/rst/gscripts/mde/simplemde.min.js" +] + +makeclass "MarkOn", MarkOn \ No newline at end of file diff --git a/apps/assets/coffee/bootstrap.coffee b/apps/assets/coffee/bootstrap.coffee new file mode 100644 index 0000000..e8947f6 --- /dev/null +++ b/apps/assets/coffee/bootstrap.coffee @@ -0,0 +1,19 @@ +window.classes = {} +window.libraries = {} +window.myuri = "/" +window.mobilecheck = () -> + if navigator.userAgent.match(/Android/i) or navigator.userAgent.match(/webOS/i) or navigator.userAgent.match(/iPhone/i) or navigator.userAgent.match(/iPad/i) or navigator.userAgent.match(/iPod/i) or navigator.userAgent.match(/BlackBerry/i) or navigator.userAgent.match(/Windows Phone/i) + return true + return false + +window.makeclass = (n, o) -> window.classes[n] = o + +window.require = (lib) -> + return new Promise (r, e) -> + return r() if window.libraries[lib] + $.getScript window.myuri + lib + .done (d) -> + window.libraries[lib] = true + r() + .fail (m, s) -> + e(m, s) diff --git a/apps/assets/css/style.css b/apps/assets/css/style.css new file mode 100644 index 0000000..c81376a --- /dev/null +++ b/apps/assets/css/style.css @@ -0,0 +1,7 @@ +/** + * simplemde v1.11.2 + * Copyright Next Step Webs, Inc. + * @link https://github.com/NextStepWebs/simplemde-markdown-editor + * @license MIT + */ + .CodeMirror{color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important;-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:none;font-variant-ligatures:none}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected,.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.CodeMirror{height:auto;min-height:300px;border:1px solid #ddd;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px;font:inherit;z-index:1}.CodeMirror-scroll{min-height:300px}.CodeMirror-fullscreen{background:#fff;position:fixed!important;top:50px;left:0;right:0;bottom:0;height:auto;z-index:9}.CodeMirror-sided{width:50%!important}.editor-toolbar{position:relative;opacity:.6;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;padding:0 10px;border-top:1px solid #bbb;border-left:1px solid #bbb;border-right:1px solid #bbb;border-top-left-radius:4px;border-top-right-radius:4px}.editor-toolbar:after,.editor-toolbar:before{display:block;content:' ';height:1px}.editor-toolbar:before{margin-bottom:8px}.editor-toolbar:after{margin-top:8px}.editor-toolbar:hover,.editor-wrapper input.title:focus,.editor-wrapper input.title:hover{opacity:.8}.editor-toolbar.fullscreen{width:100%;height:50px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;padding-top:10px;padding-bottom:10px;box-sizing:border-box;background:#fff;border:0;position:fixed;top:0;left:0;opacity:1;z-index:9}.editor-toolbar.fullscreen::before{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,1)),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:linear-gradient(to right,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);position:fixed;top:0;left:0;margin:0;padding:0}.editor-toolbar.fullscreen::after{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(100%,rgba(255,255,255,1)));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);position:fixed;top:0;right:0;margin:0;padding:0}.editor-toolbar a{display:inline-block;text-align:center;text-decoration:none!important;color:#2c3e50!important;width:30px;height:30px;margin:0;border:1px solid transparent;border-radius:3px;cursor:pointer}.editor-toolbar a.active,.editor-toolbar a:hover{background:#fcfcfc;border-color:#95a5a6}.editor-toolbar a:before{line-height:30px}.editor-toolbar i.separator{display:inline-block;width:0;border-left:1px solid #d9d9d9;border-right:1px solid #fff;color:transparent;text-indent:-10px;margin:0 6px}.editor-toolbar a.fa-header-x:after{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;font-size:65%;vertical-align:text-bottom;position:relative;top:2px}.editor-toolbar a.fa-header-1:after{content:"1"}.editor-toolbar a.fa-header-2:after{content:"2"}.editor-toolbar a.fa-header-3:after{content:"3"}.editor-toolbar a.fa-header-bigger:after{content:"▲"}.editor-toolbar a.fa-header-smaller:after{content:"▼"}.editor-toolbar.disabled-for-preview a:not(.no-disable){pointer-events:none;background:#fff;border-color:transparent;text-shadow:inherit}@media only screen and (max-width:700px){.editor-toolbar a.no-mobile{display:none}}.editor-statusbar{padding:8px 10px;font-size:12px;color:#959694;text-align:right}.editor-statusbar span{display:inline-block;min-width:4em;margin-left:1em}.editor-preview,.editor-preview-side{padding:10px;background:#fafafa;overflow:auto;display:none;box-sizing:border-box}.editor-statusbar .lines:before{content:'lines: '}.editor-statusbar .words:before{content:'words: '}.editor-statusbar .characters:before{content:'characters: '}.editor-preview{position:absolute;width:100%;height:100%;top:0;left:0;z-index:7}.editor-preview-side{position:fixed;bottom:0;width:50%;top:50px;right:0;z-index:9;border:1px solid #ddd}.editor-preview-active,.editor-preview-active-side{display:block}.editor-preview-side>p,.editor-preview>p{margin-top:0}.editor-preview pre,.editor-preview-side pre{background:#eee;margin-bottom:10px}.editor-preview table td,.editor-preview table th,.editor-preview-side table td,.editor-preview-side table th{border:1px solid #ddd;padding:5px}.CodeMirror .CodeMirror-code .cm-tag{color:#63a35c}.CodeMirror .CodeMirror-code .cm-attribute{color:#795da3}.CodeMirror .CodeMirror-code .cm-string{color:#183691}.CodeMirror .CodeMirror-selected{background:#d9d9d9}.CodeMirror .CodeMirror-code .cm-header-1{font-size:200%;line-height:200%}.CodeMirror .CodeMirror-code .cm-header-2{font-size:160%;line-height:160%}.CodeMirror .CodeMirror-code .cm-header-3{font-size:125%;line-height:125%}.CodeMirror .CodeMirror-code .cm-header-4{font-size:110%;line-height:110%}.CodeMirror .CodeMirror-code .cm-comment{background:rgba(0,0,0,.05);border-radius:2px}.CodeMirror .CodeMirror-code .cm-link{color:#7f8c8d}.CodeMirror .CodeMirror-code .cm-url{color:#aab2b3}.CodeMirror .CodeMirror-code .cm-strikethrough{text-decoration:line-through}.CodeMirror .CodeMirror-placeholder{opacity:.5}.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){background:rgba(255,0,0,.15)} \ No newline at end of file diff --git a/apps/assets/scripts/main.js b/apps/assets/scripts/main.js new file mode 100644 index 0000000..bc47c88 --- /dev/null +++ b/apps/assets/scripts/main.js @@ -0,0 +1,145 @@ +// Generated by CoffeeScript 1.9.3 +(function() { + var APIManager, BaseObject, MarkOn, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + window.classes = {}; + + window.libraries = {}; + + window.myuri = "/"; + + window.mobilecheck = function() { + if (navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/webOS/i) || navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/BlackBerry/i) || navigator.userAgent.match(/Windows Phone/i)) { + return true; + } + return false; + }; + + window.makeclass = function(n, o) { + return window.classes[n] = o; + }; + + window.require = function(lib) { + return new Promise(function(r, e) { + if (window.libraries[lib]) { + return r(); + } + return $.getScript(window.myuri + lib).done(function(d) { + window.libraries[lib] = true; + return r(); + }).fail(function(m, s) { + return e(m, s); + }); + }); + }; + + BaseObject = (function() { + function BaseObject(name) { + this.name = name; + } + + BaseObject.prototype.ready = function() { + var me; + me = this; + return new Promise(function(r, e) { + return me.resolveDep().then(function() { + return r(); + })["catch"](function(m, s) { + return e(m, s); + }); + }); + }; + + BaseObject.prototype.resolveDep = function() { + var me; + me = this; + return new Promise(function(r, e) { + var dep, fn; + dep = window.classes[me.name].dependencies; + if (!dep) { + r(); + } + fn = function(l, i) { + if (i >= dep.length) { + return r(); + } + return require(l[i]).then(function() { + return fn(l, i + 1); + })["catch"](function(m, s) { + return e(m, s); + }); + }; + return fn(dep, 0); + }); + }; + + return BaseObject; + + })(); + + makeclass("BaseObject", BaseObject); + + APIManager = (function(superClass) { + extend(APIManager, superClass); + + function APIManager() { + APIManager.__super__.constructor.call(this, "APIManager"); + } + + APIManager.prototype.init = function(cname) { + console.log(cname); + return this.ready().then(function() { + if (mobilecheck()) { + mobileConsole.init(); + } + if (!cname || cname === "") { + return; + } + if (!window.classes[cname]) { + return console.error("Cannot find class ", cname); + } + return (new window.classes[cname]).init(); + })["catch"](function(m, s) { + return console.error(m, s); + }); + }; + + return APIManager; + + })(window.classes.BaseObject); + + APIManager.dependencies = ["/assets/scripts/mobile_console.js"]; + + makeclass("APIManager", APIManager); + + MarkOn = (function(superClass) { + extend(MarkOn, superClass); + + function MarkOn(id) { + this.id = id; + MarkOn.__super__.constructor.call(this, "MarkOn"); + } + + MarkOn.prototype.init = function() { + var me; + me = this; + return this.ready().then(function() { + return me.editor = new SimpleMDE({ + element: $(me.id)[0] + }); + })["catch"](function(m, s) { + return console.error(m, s); + }); + }; + + return MarkOn; + + })(window.classes.BaseObject); + + MarkOn.dependencies = ["/rst/gscripts/mde/simplemde.min.js"]; + + makeclass("MarkOn", MarkOn); + +}).call(this); diff --git a/apps/assets/scripts/mobile_console.js b/apps/assets/scripts/mobile_console.js new file mode 100644 index 0000000..66e7176 --- /dev/null +++ b/apps/assets/scripts/mobile_console.js @@ -0,0 +1,1325 @@ +/*! + * hnl.mobileConsole - javascript mobile console - v1.2.6 - 26/10/2016 + * 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-2016 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 + */ +var console = window.console; + +var mobileConsole = (function () { + 'use strict'; + + //stop if there is no console in this browser + if (!console) { + alert('mobileConsole not supported on this browser'); + return; + } + //polyfills + if (!Date.now) { + Date.now = function now() { + return new Date().getTime(); + }; + } + if (!Array.prototype.filter) { + Array.prototype.filter = function(fun/*, thisArg*/) { + 'use strict'; + + 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]; + + // NOTE: Technically this should Object.defineProperty at + // the next index, as push can be affected by + // properties on Object.prototype and Array.prototype. + // But that method's new, and collisions should be + // rare, so use the more-compatible alternative. + if (fun.call(thisArg, val, i, t)) { + res.push(val); + } + } + } + + return res; + }; + } + + //options and other variable containers + var options = { + overrideAutorun: false, + version : '1.2.6', + baseClass : 'mobileConsole_', + animParams: 'all 200ms ease', + browserinfo: { + 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', + isCrap: document.querySelectorAll === undefined + }, + 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, + paddingLeft: 0, + groupDepth: 0 + }, + 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 + originalConsole = { + log: (typeof console.log === 'function') ? console.log.bind(console) : null, + info: (typeof console.info === 'function') ? console.info.bind(console) : null, + dir: (typeof console.dir === 'function') ? console.dir.bind(console) : null, + group: (typeof console.group === 'function') ? console.group.bind(console) : null, + groupEnd: (typeof console.groupEnd === 'function') ? console.groupEnd.bind(console) : null, + warn: (typeof console.warn === 'function') ? console.warn.bind(console) : null, + error: (typeof console.error === 'function') ? console.error.bind(console) : null, + trace: (typeof console.trace === 'function') ? console.trace.bind(console) : null, + clear: (typeof console.clear === 'function') ? console.clear.bind(console) : null + }, + // reference variables + mobileConsole, consoleElement, commandLine; + + if(options.browserinfo.isCrap) { + console.error( + '--==## Error: Browser not supported by Mobile Console ##==--' + '\n' + + 'MobileConsole v' + options.version + ', running on ' + navigator.userAgent.toLowerCase() + ); + return false; + } + + //helpers for all sub functions + function isMobile() { + var check = false; + (function (a) { + if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|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))) { + check = true; + } + }(navigator.userAgent || navigator.vendor || window.opera)); + return check; + } + function setCSS(el, css) { + var i; + for (i in css) { + if (css.hasOwnProperty(i)) { + el.style[i] = css[i]; + } + } + return el; + } + function htmlToString(html) { + return String(html).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/ /g, '\u00a0').replace(/(?:\r\n|\r|\n)/g, '
').trim(); + } + function createElem(type, className, css) { + if (!type || typeof setCSS !== 'function') { 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); + } + + // 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)); + } + + // 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: 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: 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: isMobile() ? '8px 12px' : '4px 8px' + }), + tdRight : createElem('td', 'table_row_data', { + border: '0 none', + textAlign: 'left', + padding: 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: 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: isMobile() ? '34px' : '28px', + height: 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: isMobile() ? '16px' : 'inherit', //prevents ios safari's zoom on focus + fontFamily: 'Consolas, monaco, monospace', + paddingLeft: '25px', + margin: 0, + height: 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: 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: 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 = (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 = 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 **==-- + + //initializes the console HTML element + function initConsoleElement() { + //reference + var ref; + //core + function toggleScroll() { + elements.scrollcontainer.scrollTop = elements.scrollcontainer.scrollHeight; + elements.scrollcontainer.scrollLeft = 0; + } + function assemble() { + var i = options.methods.length, key; + + //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() { + //togglelogtype is a click handler; 'this' is the button that was clicked + var button = this; + var logType = button.innerHTML.toLowerCase(); + var elems = elements.lines[logType], i = elems.length; + button.toggled = (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) { + setCSS(elements.buttons[methods[i]], { display: 'none' }); + } + } + if (options.browserinfo.evtLstn) { + elements.buttons.toggler.addEventListener('click', toggleHeight, false); + elements.buttons.clear.addEventListener('click', console.clear, false); + } else { + elements.buttons.toggler.attachEvent('onclick', toggleHeight); + elements.buttons.clear.attachEvent('onclick', console.clear); + } + } + //init + function init() { + var element = assemble(); + docReady(function () { + setBinds(); + attach(element); + }); + //expose Public methods and variables + return { + toggleHeight : toggleHeight, + toggleScroll : toggleScroll + }; + } + if (!ref) { + ref = init(); + } + return ref; + } + + //initializes the new console logger + function initConsole() { + //reference + var ref; + //sub helpers + 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 simpleObject = {}, prop, classname = getClass(object); + if (!isElement(object)) { + for (prop in object) { + if (!object.hasOwnProperty(prop) || (typeof (object[prop]) === 'object') || (typeof (object[prop]) === 'function')) { + continue; + } + simpleObject[prop] = object[prop]; + } + return '' + classname + ' ' + JSON.stringify(simpleObject) + ''; // 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 = (trace !== undefined && 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 (trace === undefined) { + return; + } + trace.reverse(); //reverse order of trace, as it is 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 (value === null) { + valueColor = '#808080'; + } + if (typeof value === 'string') { + valueColor = '#c54300'; + //HARD limit, for speed/mem issues with consecutive logging of large strings + if (value.length > 400) { + value = '"' + String(value).substring(0, 400) + '" [...]
Note: string was truncated to 400 chars'; + } else { + value = '"' + value + '"'; + } + } + return '' + key + ': ' + value + ''; + } + function objectToTable(table, object) { + var i; + for (i in object) { + var 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() { + //toggleDetails is a click handler; 'this' is the button that was clicked + var button = this, 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, + message = (arguments[1].newMessage !== undefined) ? arguments[1].newMessage : undefined, + stackTrace = (arguments[1].newStackTrace !== undefined) ? arguments[1].newStackTrace : undefined; + + //if message emtpy, show empty message-message + if (message === '') { 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') { + className = getClass(message); + if (className.indexOf('HTML') !== -1 && className !== 'HTMLDocument') { + message = htmlToString(message.outerHTML.match(/<(.*?)>/g)[0] + '...' + message.outerHTML.match(/<(.*?)>/g).pop()); //gets first and last tag, adds '...' in middle. e.g.
...
+ } else { + message = 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 && 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 (isMobile()) { alert(e); } + originalConsole.error('mobileConsole generated an error logging this event!'); + originalConsole.error(arguments); + originalConsole.error(e); + //try to re-log it as an error + newConsole('error', e); + } + + + } + function interceptConsole(method) { + var original = console[method], i, stackTraceOrig; + 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') { + elements.table.innerHTML = ''; + 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); + }; + + //expose Public methods and variables + return { + //nothing yet 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 (e === undefined) { return; } + //unset any already hovered elements + var hovered = getFromArrayById('hover').element, target = e.target, over; + if (hovered !== undefined) { + setCSS(hovered, { + color: '', + backgroundColor: 'rgba(0, 0, 0, 0)' + }).id = ''; + } + if (e.type === 'mouseover') { + status.acHovered = true; + over = true; + } else { + over = false; + } + setCSS(target, { + color: over ? '#FFFFFF' : '', + backgroundColor: over ? 'rgba(66, 139, 202, 1)' : 'rgba(0, 0, 0, 0)' + }).id = over ? 'hover' : ''; + } + function toggleAutoComplete(show) { + var hidden = (elements.autocomplete.currentStyle ? elements.autocomplete.currentStyle.display : window.getComputedStyle(elements.autocomplete, null).display) === 'none'; + show = (show === undefined) ? hidden : show; + setCSS(elements.autocomplete, {display: (show) ? 'inherit' : 'none'}); + status.acActive = show; + if (!show) { status.acHovered = false; } + } + function clickAutoComplete(e) { + e.preventDefault(); + elements.consoleinput.value = e.target.innerHTML; + elements.consoleinput.focus(); + toggleAutoComplete(); + } + function autoComplete(command) { + if (command.length < 1) { + toggleAutoComplete(false); + return; + } + var searchString = encodeURI(command), matches, match, row, i, maxAmount = 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) { + if (e.target === elements.consoleinput) { + if ((e.key === 'Enter' || e.keyCode === 13)) { //enter + e.preventDefault(); + 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 + e.preventDefault(); + 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 = hist[history.input.commandIdx] === undefined ? '' : decodeURI(hist[history.input.commandIdx]); + } + } + } + if (e.keyCode === 27 && status.acActive) { + toggleAutoComplete(false); + } + }; + document.onkeyup = function (e) { + if (e.target === 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() { + var element = assemble(); + setBinds(); + //expose Public methods and variables + return { + //nothing yet to expose + }; + } + //return + if (!ref) { + ref = init(); + } + return ref; + } + + function init() { + if (!status.initialized) { + 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' ) + ' ##==--' ); + } else if (options.browserinfo.isCrap) { + console.error( + '--==## Error: Browser not supported by Mobile Console ##==--' + '\n' + + '--===============================--' + '\n' + + 'MobileConsole v' + options.version + ', running on ' + navigator.userAgent.toLowerCase() + ); + } + } + + //autorun if mobile + if (isMobile() || options.overrideAutorun) { + init(); + } + + //expose the mobileConsole + return { + init : init, + about: about, + toggle : toggleHeight, + status : status, + options : options + }; + +}()); \ No newline at end of file diff --git a/apps/controllers/IndexController.lua b/apps/controllers/IndexController.lua new file mode 100644 index 0000000..acd2d0e --- /dev/null +++ b/apps/controllers/IndexController.lua @@ -0,0 +1,12 @@ +BaseController:subclass("IndexController", { + registry = {} +}) + +function IndexController:index( ... ) + return true +end + +function IndexController:actionnotfound(...) + self.template:setView("index") + return self:index(table.unpack({...})) +end \ No newline at end of file diff --git a/apps/controllers/MarkOnController.lua b/apps/controllers/MarkOnController.lua new file mode 100644 index 0000000..e7b56b1 --- /dev/null +++ b/apps/controllers/MarkOnController.lua @@ -0,0 +1,13 @@ +BaseController:subclass("MarkOnController", { + registry = {} +}) + +function MarkOnController:index( ... ) + self.template:set("jsclass", "MarkOn") + return true +end + +function MarkOnController:actionnotfound(...) + self.template:setView("index") + return self:index(table.unpack({...})) +end \ No newline at end of file diff --git a/apps/controllers/PostController.lua b/apps/controllers/PostController.lua deleted file mode 100644 index d3d25d3..0000000 --- a/apps/controllers/PostController.lua +++ /dev/null @@ -1,35 +0,0 @@ -BaseController:subclass("PostController", { - registry = {}, - models = { "post" } -}) - -function PostController:index(...) - local args = {...} - self:setSession("postsession", "Huehuehue") - self.template:set("post", self.post:findAll()) - return true -end - -function PostController:edit(...) - if self:getSession("postsession") then - self.template:set("auth", true) - else - self.template:set("auth", false) - end - self:switchLayout("admin") - return true -end - -function PostController:add(...) - local args = {...} - local m = { - cid = tonumber(args[1]), - content = "This is the content for #cid="..args[1] - } - if(self.post:create(m)) then - self.template:set("status", "Post created") - else - self.template:set("status", "Cannot create post") - end - return true -end \ No newline at end of file diff --git a/apps/models/PostModel.lua b/apps/models/PostModel.lua deleted file mode 100644 index 1902edd..0000000 --- a/apps/models/PostModel.lua +++ /dev/null @@ -1,8 +0,0 @@ -BaseModel:subclass("PostModel", { - registry = {}, - name = "post", - fields = { - cid = "NUMERIC", - content = "TEXT" - } -}) \ No newline at end of file diff --git a/apps/router.lua b/apps/router.lua index 5eb7f2f..0cada1b 100644 --- a/apps/router.lua +++ b/apps/router.lua @@ -5,7 +5,7 @@ -- some global variables DIR_SEP = "/" WWW_ROOT = "/opt/www/htdocs/apps" -HTTP_ROOT = "https://apps.localhost:9195/" +HTTP_ROOT = "https://10.1.10.84:9195/apps" -- class path: path.to.class BASE_FRW = "" -- class path: path.to.class diff --git a/apps/style.css b/apps/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/apps/views/admin/layout.ls b/apps/views/admin/layout.ls deleted file mode 100644 index deaa5f6..0000000 --- a/apps/views/admin/layout.ls +++ /dev/null @@ -1,11 +0,0 @@ -") - local views = args[1] - for k, v in pairs(views) do - echo(k.." -> ") - v:render() - echo("
") - end - --views.__main__:render() -?> \ No newline at end of file diff --git a/apps/views/admin/post/edit.ls b/apps/views/admin/post/edit.ls deleted file mode 100644 index 6935591..0000000 --- a/apps/views/admin/post/edit.ls +++ /dev/null @@ -1,5 +0,0 @@ -") -echo(JSON.encode(args)) -?> \ No newline at end of file diff --git a/apps/views/default/MarkOn/index.ls b/apps/views/default/MarkOn/index.ls new file mode 100644 index 0000000..d1ae457 --- /dev/null +++ b/apps/views/default/MarkOn/index.ls @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/views/default/index/index.ls b/apps/views/default/index/index.ls new file mode 100644 index 0000000..6998991 --- /dev/null +++ b/apps/views/default/index/index.ls @@ -0,0 +1,3 @@ +

+ Site under construction +

\ No newline at end of file diff --git a/apps/views/default/layout.ls b/apps/views/default/layout.ls index ce19292..beca6a7 100644 --- a/apps/views/default/layout.ls +++ b/apps/views/default/layout.ls @@ -1,26 +1,34 @@ + Application pages - + + + + + + + +
") - v:render() - echo("
") - end - --views.__main__:render() + + main:render() ?>
diff --git a/apps/views/default/post/add.ls b/apps/views/default/post/add.ls deleted file mode 100644 index e184a2e..0000000 --- a/apps/views/default/post/add.ls +++ /dev/null @@ -1,5 +0,0 @@ -") -echo(JSON.encode(args)) -?> \ No newline at end of file diff --git a/apps/views/default/post/edit.ls b/apps/views/default/post/edit.ls deleted file mode 100644 index f0f0f46..0000000 --- a/apps/views/default/post/edit.ls +++ /dev/null @@ -1,3 +0,0 @@ -Public file of edit action
") -?> \ No newline at end of file diff --git a/apps/views/default/post/index.ls b/apps/views/default/post/index.ls deleted file mode 100644 index 11bf768..0000000 --- a/apps/views/default/post/index.ls +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/silk/Router.lua b/silk/Router.lua index 5b72985..1051a0c 100644 --- a/silk/Router.lua +++ b/silk/Router.lua @@ -21,7 +21,7 @@ function Router:infer(url) -- a is controller name -- b is action -- c,d,e is parameters - -- if user dont provice the url, try to infer it + -- if user dont provide the url, try to infer it -- from the REQUEST url = url or REQUEST.query.r url = std.trim(url, "/") @@ -84,6 +84,7 @@ function Router:delegate() data.controller.main = true views.__main__ = self:call(data) if not views.__main__ then + --self:error("No main template is set") return end -- get all visible routes