diff --git a/CHANGELOG.md b/CHANGELOG.md index 35d6ff054..7a61c66ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. * `[pre.7]` — Fixes bug with injected JS that was causing `` to be ignored in templates. * `[pre.7]` — Fixes exception thrown when trying to delete a node due to a misnamed model. * `[pre.7]` — Fixes username vanishing on failed login attempts. +* `[pre.7]` — Terminal is now fixed to actually output all lines, rather than leaving one hanging in neverland until the browser is resized. ### Added * Login attempts and pasword reset requests are now protected by invisible ReCaptcha. This feature can be disabled with a `.env` variable. @@ -24,6 +25,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. * Routes are now handled in the `routes/` folder, and use a significantly cleaner syntax. Controller names and methods have been updated as well to be clearer as well as avoid conflicts with PHP reserved keywords. * API has been completely overhauled to use new permissions system. **Any old API keys will immediately become invalid and fail to operate properly anymore. You will need to generate new keys.** * Cleaned up dynamic database connection setting to use a single function call from the host model. +* `[pre.7]` — Corrected a config option for spigot servers to set a boolean value as boolean, and not as a string. ## v0.6.0-pre.7 (Courageous Carniadactylus) ### Fixed diff --git a/public/themes/pterodactyl/js/frontend/console.js b/public/themes/pterodactyl/js/frontend/console.js index 62d8c8119..2ec2c9b21 100644 --- a/public/themes/pterodactyl/js/frontend/console.js +++ b/public/themes/pterodactyl/js/frontend/console.js @@ -17,260 +17,236 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +var CONSOLE_PUSH_COUNT = Pterodactyl.config.console_count || 10; +var CONSOLE_PUSH_FREQ = Pterodactyl.config.console_freq || 200; +var CONSOLE_OUTPUT_LIMIT = Pterodactyl.config.console_limit || 2000; +var InitialLogSent = false; -var Console = (function () { - var CONSOLE_PUSH_COUNT = Pterodactyl.config.console_count; - var CONSOLE_PUSH_FREQ = Pterodactyl.config.console_freq; - - var terminalQueue; - var terminal; - var recievedInitialLog = false; - - var cpuChart; - var cpuData; - var memoryChart; - var memoryData; - var timeLabels; - - var $terminalNotify; - - function initConsole() { - terminalQueue = []; - terminal = $('#terminal').terminal(function (command, term) { - Socket.emit('send command', command); - }, { - greetings: '', - name: Pterodactyl.server.uuid, - height: 450, - exit: false, - prompt: Pterodactyl.server.username + ':~$ ', - scrollOnEcho: false, - scrollBottomOffset: 5, - onBlur: function (terminal) { - return false; - } - }); - - $terminalNotify = $('#terminalNotify'); - $terminalNotify.on('click', function () { - terminal.scroll_to_bottom(); - $terminalNotify.addClass('hidden'); - }) - - terminal.on('scroll', function () { - if (terminal.is_bottom()) { - $terminalNotify.addClass('hidden'); - } - }) - } - - function initGraphs() { - var ctc = $('#chart_cpu'); - timeLabels = []; - cpuData = []; - cpuChart = new Chart(ctc, { - type: 'line', - data: { - labels: timeLabels, - datasets: [ - { - label: "Percent Use", - fill: false, - lineTension: 0.03, - backgroundColor: "#3c8dbc", - borderColor: "#3c8dbc", - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0.0, - borderJoinStyle: 'miter', - pointBorderColor: "#3c8dbc", - pointBackgroundColor: "#fff", - pointBorderWidth: 1, - pointHoverRadius: 5, - pointHoverBackgroundColor: "#3c8dbc", - pointHoverBorderColor: "rgba(220,220,220,1)", - pointHoverBorderWidth: 2, - pointRadius: 1, - pointHitRadius: 10, - data: cpuData, - spanGaps: false, - } - ] - }, - options: { - title: { - display: true, - text: 'CPU Usage (as Percent Total)' - }, - legend: { - display: false, - }, - animation: { - duration: 1, - } - } - }); - - var ctm = $('#chart_memory'); - memoryData = []; - memoryChart = new Chart(ctm, { - type: 'line', - data: { - labels: timeLabels, - datasets: [ - { - label: "Memory Use", - fill: false, - lineTension: 0.03, - backgroundColor: "#3c8dbc", - borderColor: "#3c8dbc", - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0.0, - borderJoinStyle: 'miter', - pointBorderColor: "#3c8dbc", - pointBackgroundColor: "#fff", - pointBorderWidth: 1, - pointHoverRadius: 5, - pointHoverBackgroundColor: "#3c8dbc", - pointHoverBorderColor: "rgba(220,220,220,1)", - pointHoverBorderWidth: 2, - pointRadius: 1, - pointHitRadius: 10, - data: memoryData, - spanGaps: false, - } - ] - }, - options: { - title: { - display: true, - text: 'Memory Usage (in Megabytes)' - }, - legend: { - display: false, - }, - animation: { - duration: 1, - } - } - }); - } - - function addSocketListeners() { - // Update Listings on Initial Status - Socket.on('initial status', function (data) { - if (! recievedInitialLog) { - updateServerPowerControls(data.status); - - if (data.status === 1 || data.status === 2) { - Socket.emit('send server log'); - } - } - }); - - // Update Listings on Status - Socket.on('status', function (data) { - updateServerPowerControls(data.status); - }); - - Socket.on('server log', function (data) { - if (! recievedInitialLog) { - terminal.clear(); - terminalQueue.push(data); - recievedInitialLog = true; - } - }); - - Socket.on('console', function (data) { - terminalQueue.push(data.line); - }); - - Socket.on('proc', function (proc) { - if (cpuData.length > 10) { - cpuData.shift(); - memoryData.shift(); - timeLabels.shift(); - } - - var cpuUse = (Pterodactyl.server.cpu > 0) ? parseFloat(((proc.data.cpu.total / Pterodactyl.server.cpu) * 100).toFixed(3).toString()) : proc.data.cpu.total; - cpuData.push(cpuUse); - memoryData.push(parseInt(proc.data.memory.total / (1024 * 1024))); - - var m = new Date(); - timeLabels.push($.format.date(new Date(), 'HH:mm:ss')); - - cpuChart.update(); - memoryChart.update(); - }); - } - - function pushOutputQueue() { - if (terminalQueue.length > CONSOLE_PUSH_COUNT) { - // console throttled warning show +(function initConsole() { + window.TerminalQueue = []; + window.Terminal = $('#terminal').terminal(function (command, term) { + Socket.emit('send command', command); + }, { + greetings: '', + name: Pterodactyl.server.uuid, + height: 450, + exit: false, + echoCommand: false, + outputLimit: CONSOLE_OUTPUT_LIMIT, + prompt: Pterodactyl.server.username + ':~$ ', + scrollOnEcho: false, + scrollBottomOffset: 5, + onBlur: function (terminal) { + return false; } + }); - if (terminalQueue.length > 0) { - for (var i = 0; i < CONSOLE_PUSH_COUNT && terminalQueue.length > 0; i++) { - terminal.echo(terminalQueue[0], {flush: false}); - terminalQueue.shift(); - } - terminal.flush() + window.TerminalNotifyElement = $('#terminalNotify'); + TerminalNotifyElement.on('click', function () { + Terminal.scroll_to_bottom(); + TerminalNotifyElement.addClass('hidden'); + }) - // Show - if (!terminal.is_bottom()) { - $terminalNotify.removeClass('hidden'); - } + Terminal.on('scroll', function () { + if (Terminal.is_bottom()) { + TerminalNotifyElement.addClass('hidden'); } - - window.setTimeout(pushOutputQueue, CONSOLE_PUSH_FREQ); - } - - function updateServerPowerControls (data) { - // Server is On or Starting - if(data == 1 || data == 2) { - $('[data-attr="power"][data-action="start"]').addClass('disabled'); - $('[data-attr="power"][data-action="stop"], [data-attr="power"][data-action="restart"]').removeClass('disabled'); - } else { - if (data == 0) { - $('[data-attr="power"][data-action="start"]').removeClass('disabled'); - } - $('[data-attr="power"][data-action="stop"], [data-attr="power"][data-action="restart"]').addClass('disabled'); - } - - if(data !== 0) { - $('[data-attr="power"][data-action="kill"]').removeClass('disabled'); - } else { - $('[data-attr="power"][data-action="kill"]').addClass('disabled'); - } - } - - return { - init: function () { - - initConsole(); - pushOutputQueue(); - initGraphs(); - addSocketListeners(); - - $('[data-attr="power"]').click(function (event) { - if (! $(this).hasClass('disabled')) { - Socket.emit('set status', $(this).data('action')); - } - }); - }, - - getTerminal: function () { - return terminal - }, - - getTerminalQueue: function () { - return terminalQueue - }, - } - + }) + // Socket.on('initial status', function (data) { + // Terminal.clear(); + // if (data.status === 1 || data.status === 2) { + // Socket.emit('send server log'); + // } + // }); })(); +(function pushOutputQueue() { + if (TerminalQueue.length > CONSOLE_PUSH_COUNT) { + // console throttled warning show + } + + if (TerminalQueue.length > 0) { + for (var i = 0; i < CONSOLE_PUSH_COUNT && TerminalQueue.length > 0; i++) { + Terminal.echo(TerminalQueue[0], { flush: false }); + TerminalQueue.shift(); + } + + // Flush after looping through all. + Terminal.flush(); + + // Show Warning + if (! Terminal.is_bottom()) { + TerminalNotifyElement.removeClass('hidden'); + } + } + + window.setTimeout(pushOutputQueue, CONSOLE_PUSH_FREQ); +})(); + +(function setupSocketListeners() { + // Update Listings on Initial Status + Socket.on('initial status', function (data) { + console.log('initial status 2'); + if (! InitialLogSent) { + updateServerPowerControls(data.status); + + if (data.status === 1 || data.status === 2) { + Socket.emit('send server log'); + } + } + }); + + // Update Listings on Status + Socket.on('status', function (data) { + updateServerPowerControls(data.status); + }); + + Socket.on('server log', function (data) { + if (! InitialLogSent) { + Terminal.clear(); + TerminalQueue.push(data); + InitialLogSent = true; + } + }); + + Socket.on('console', function (data) { + TerminalQueue.push(data.line); + }); +})(); + + +function updateServerPowerControls (data) { + // Server is On or Starting + if(data == 1 || data == 2) { + $('[data-attr="power"][data-action="start"]').addClass('disabled'); + $('[data-attr="power"][data-action="stop"], [data-attr="power"][data-action="restart"]').removeClass('disabled'); + } else { + if (data == 0) { + $('[data-attr="power"][data-action="start"]').removeClass('disabled'); + } + $('[data-attr="power"][data-action="stop"], [data-attr="power"][data-action="restart"]').addClass('disabled'); + } + + if(data !== 0) { + $('[data-attr="power"][data-action="kill"]').removeClass('disabled'); + } else { + $('[data-attr="power"][data-action="kill"]').addClass('disabled'); + } +} + $(document).ready(function () { - Console.init(); + $('[data-attr="power"]').click(function (event) { + if (! $(this).hasClass('disabled')) { + Socket.emit('set status', $(this).data('action')); + } + }); + + Socket.on('proc', function (proc) { + if (CPUData.length > 10) { + CPUData.shift(); + MemoryData.shift(); + TimeLabels.shift(); + } + + var cpuUse = (Pterodactyl.server.cpu > 0) ? parseFloat(((proc.data.cpu.total / Pterodactyl.server.cpu) * 100).toFixed(3).toString()) : proc.data.cpu.total; + CPUData.push(cpuUse); + MemoryData.push(parseInt(proc.data.memory.total / (1024 * 1024))); + + TimeLabels.push($.format.date(new Date(), 'HH:mm:ss')); + + CPUChart.update(); + MemoryChart.update(); + }); + + + var ctc = $('#chart_cpu'); + var TimeLabels = []; + var CPUData = []; + var CPUChart = new Chart(ctc, { + type: 'line', + data: { + labels: TimeLabels, + datasets: [ + { + label: "Percent Use", + fill: false, + lineTension: 0.03, + backgroundColor: "#3c8dbc", + borderColor: "#3c8dbc", + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0.0, + borderJoinStyle: 'miter', + pointBorderColor: "#3c8dbc", + pointBackgroundColor: "#fff", + pointBorderWidth: 1, + pointHoverRadius: 5, + pointHoverBackgroundColor: "#3c8dbc", + pointHoverBorderColor: "rgba(220,220,220,1)", + pointHoverBorderWidth: 2, + pointRadius: 1, + pointHitRadius: 10, + data: CPUData, + spanGaps: false, + } + ] + }, + options: { + title: { + display: true, + text: 'CPU Usage (as Percent Total)' + }, + legend: { + display: false, + }, + animation: { + duration: 1, + } + } + }); + + var ctm = $('#chart_memory'); + MemoryData = []; + MemoryChart = new Chart(ctm, { + type: 'line', + data: { + labels: TimeLabels, + datasets: [ + { + label: "Memory Use", + fill: false, + lineTension: 0.03, + backgroundColor: "#3c8dbc", + borderColor: "#3c8dbc", + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0.0, + borderJoinStyle: 'miter', + pointBorderColor: "#3c8dbc", + pointBackgroundColor: "#fff", + pointBorderWidth: 1, + pointHoverRadius: 5, + pointHoverBackgroundColor: "#3c8dbc", + pointHoverBorderColor: "rgba(220,220,220,1)", + pointHoverBorderWidth: 2, + pointRadius: 1, + pointHitRadius: 10, + data: MemoryData, + spanGaps: false, + } + ] + }, + options: { + title: { + display: true, + text: 'Memory Usage (in Megabytes)' + }, + legend: { + display: false, + }, + animation: { + duration: 1, + } + } + }); }); diff --git a/public/themes/pterodactyl/js/frontend/server.socket.js b/public/themes/pterodactyl/js/frontend/server.socket.js index 4c0e77fa8..13ba4e23c 100644 --- a/public/themes/pterodactyl/js/frontend/server.socket.js +++ b/public/themes/pterodactyl/js/frontend/server.socket.js @@ -50,11 +50,13 @@ var Server = (function () { var notifySocketError = false; + console.log('Starting connection'); window.Socket = io(Pterodactyl.node.scheme + '://' + Pterodactyl.node.fqdn + ':' + Pterodactyl.node.daemonListen + '/ws/' + Pterodactyl.server.uuid, { 'query': 'token=' + Pterodactyl.server.daemonSecret, }); Socket.io.on('connect_error', function (err) { + console.log('connect_error'); if(typeof notifySocketError !== 'object') { notifySocketError = $.notify({ message: 'There was an error attempting to establish a WebSocket connection to the Daemon. This panel will not work as expected.

' + err, @@ -67,6 +69,7 @@ var Server = (function () { // Connected to Socket Successfully Socket.on('connect', function () { + console.log('connect'); if (notifySocketError !== false) { notifySocketError.close(); notifySocketError = false; @@ -74,6 +77,7 @@ var Server = (function () { }); Socket.on('initial status', function (data) { + console.log('initial status'); setStatusIcon(data.status); }); diff --git a/public/themes/pterodactyl/vendor/terminal/jquery.terminal-0.11.23.min.js b/public/themes/pterodactyl/vendor/terminal/jquery.terminal-0.11.23.min.js deleted file mode 100644 index 0fd1cd1fd..000000000 --- a/public/themes/pterodactyl/vendor/terminal/jquery.terminal-0.11.23.min.js +++ /dev/null @@ -1,38 +0,0 @@ -/**@license - * __ _____ ________ __ - * / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / / - * __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / - * / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ - * \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ - * \/ /____/ version 0.11.23 - * - * This file is part of jQuery Terminal. http://terminal.jcubic.pl - * - * Copyright (c) 2010-2016 Jakub Jankiewicz - * Released under the MIT license - * - * Contains: - * - * Storage plugin Distributed under the MIT License - * Copyright (c) 2010 Dave Schindler - * - * jQuery Timers licenced with the WTFPL - * - * - * Cross-Browser Split 1.1.1 - * Copyright 2007-2012 Steven Levithan - * Available under the MIT License - * - * jQuery Caret - * Copyright (c) 2009, Gideon Sireling - * 3 clause BSD License - * - * sprintf.js - * Copyright (c) 2007-2013 Alexandru Marasteanu - * licensed under 3 clause BSD license - * - * Date: Sat, 10 Dec 2016 10:56:53 +0000 - */ -(function(e){var n=function(){if(!n.cache.hasOwnProperty(arguments[0])){n.cache[arguments[0]]=n.parse(arguments[0])}return n.format.call(null,n.cache[arguments[0]],arguments)};n.format=function(e,t){var o=1,a=e.length,s="",l,f=[],c,u,h,p,m,g;for(c=0;c>>0;break;case"x":l=l.toString(16);break;case"X":l=l.toString(16).toUpperCase();break}l=/[def]/.test(h[8])&&h[3]&&l>=0?"+"+l:l;m=h[4]?h[4]=="0"?"0":h[4].charAt(1):" ";g=h[6]-String(l).length;p=h[6]?i(m,g):"";f.push(h[5]?l+p:p+l)}}return f.join("")};n.cache={};n.parse=function(e){var n=e,t=[],r=[],i=0;while(n){if((t=/^[^\x25]+/.exec(n))!==null){r.push(t[0])}else if((t=/^\x25{2}/.exec(n))!==null){r.push("%")}else if((t=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(n))!==null){if(t[2]){i|=1;var o=[],a=t[2],s=[];if((s=/^([a-z_][a-z_\d]*)/i.exec(a))!==null){o.push(s[1]);while((a=a.substring(s[0].length))!==""){if((s=/^\.([a-z_][a-z_\d]*)/i.exec(a))!==null){o.push(s[1])}else if((s=/^\[(\d+)\]/.exec(a))!==null){o.push(s[1])}else{throw"[sprintf] huh?"}}}else{throw"[sprintf] huh?"}t[2]=o}else{i|=2}if(i===3){throw"[sprintf] mixing positional and named placeholders is not (yet) supported"}r.push(t)}else{throw"[sprintf] huh?"}n=n.substring(t[0].length)}return r};var t=function(e,t,r){r=t.slice(0);r.splice(0,0,e);return n.apply(null,r)};function r(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}function i(e,n){for(var t=[];n>0;t[--n]=e){}return t.join("")}e.sprintf=n;e.vsprintf=t})(typeof global!="undefined"?global:window);(function(e,n){"use strict";e.omap=function(n,t){var r={};e.each(n,function(e,i){r[e]=t.call(n,e,i)});return r};var t={clone_object:function(n){var t={};if(typeof n=="object"){if(e.isArray(n)){return this.clone_array(n)}else if(n===null){return n}else{for(var r in n){if(e.isArray(n[r])){t[r]=this.clone_array(n[r])}else if(typeof n[r]=="object"){t[r]=this.clone_object(n[r])}else{t[r]=n[r]}}}}return t},clone_array:function(n){if(!e.isFunction(Array.prototype.map)){throw new Error("You'r browser don't support ES5 array map "+"use es5-shim")}return n.slice(0).map(function(e){if(typeof e=="object"){return this.clone_object(e)}else{return e}}.bind(this))}};var r=function(e){return t.clone_object(e)};var i=function(){var e="test",n=window.localStorage;try{n.setItem(e,"1");n.removeItem(e);return true}catch(t){return false}};var o=i();function a(e,n){var t;if(typeof e==="string"&&typeof n==="string"){localStorage[e]=n;return true}else if(typeof e==="object"&&typeof n==="undefined"){for(t in e){if(e.hasOwnProperty(t)){localStorage[t]=e[t]}}return true}return false}function s(e,n){var t,r,i;t=new Date;t.setTime(t.getTime()+31536e6);r="; expires="+t.toGMTString();if(typeof e==="string"&&typeof n==="string"){document.cookie=e+"="+n+r+"; path=/";return true}else if(typeof e==="object"&&typeof n==="undefined"){for(i in e){if(e.hasOwnProperty(i)){document.cookie=i+"="+e[i]+r+"; path=/"}}return true}return false}function l(e){return localStorage[e]}function f(e){var n,t,r,i;n=e+"=";t=document.cookie.split(";");for(r=0;ri&&i!==0||r.call(e,a)===false){h.timer.remove(e,t,r)}s.inProgress=false};s.$timerID=r.$timerID;if(!e.$timers[t][r.$timerID]){e.$timers[t][r.$timerID]=window.setInterval(s,n)}if(!this.global[t]){this.global[t]=[]}this.global[t].push(e)},remove:function(e,n,t){var r=e.$timers,i;if(r){if(!n){for(var o in r){if(r.hasOwnProperty(o)){this.remove(e,o,t)}}}else if(r[n]){if(t){if(t.$timerID){window.clearInterval(r[n][t.$timerID]);delete r[n][t.$timerID]}}else{for(var a in r[n]){if(r[n].hasOwnProperty(a)){window.clearInterval(r[n][a]);delete r[n][a]}}}for(i in r[n]){if(r[n].hasOwnProperty(i)){break}}if(!i){i=null;delete r[n]}}for(i in r){if(r.hasOwnProperty(i)){break}}if(!i){e.$timers=null}}}}});if(/(msie) ([\w.]+)/.exec(navigator.userAgent.toLowerCase())){h(window).one("unload",function(){var e=h.timer.global;for(var n in e){if(e.hasOwnProperty(n)){var t=e[n],r=t.length;while(--r){h.timer.remove(t[r],n)}}}})}(function(e){if(!String.prototype.split.toString().match(/\[native/)){return}var n=String.prototype.split,t=/()??/.exec("")[1]===e,r;r=function(r,i,o){if(Object.prototype.toString.call(i)!=="[object RegExp]"){return n.call(r,i,o)}var a=[],s=(i.ignoreCase?"i":"")+(i.multiline?"m":"")+(i.extended?"x":"")+(i.sticky?"y":""),l=0,f,c,u,h;i=new RegExp(i.source,s+"g");r+="";if(!t){f=new RegExp("^"+i.source+"$(?!\\s)",s)}o=o===e?-1>>>0:o>>>0;while(c=i.exec(r)){u=c.index+c[0].length;if(u>l){a.push(r.slice(l,c.index));if(!t&&c.length>1){c[0].replace(f,function(){for(var n=1;n1&&c.index=o){break}}if(i.lastIndex===c.index){i.lastIndex++}}if(l===r.length){if(h||!i.test("")){a.push("")}}else{a.push(r.slice(l))}return a.length>o?a.slice(0,o):a};String.prototype.split=function(e,n){return r(this,e,n)};return r})();e.fn.caret=function(e){var n=this[0];var t=n.contentEditable==="true";if(arguments.length==0){if(window.getSelection){if(t){n.focus();var r=window.getSelection().getRangeAt(0),i=r.cloneRange();i.selectNodeContents(n);i.setEnd(r.endContainer,r.endOffset);return i.toString().length}return n.selectionStart}if(document.selection){n.focus();if(t){var r=document.selection.createRange(),i=document.body.createTextRange();i.moveToElementText(n);i.setEndPoint("EndToEnd",r);return i.text.length}var e=0,o=n.createTextRange(),i=document.selection.createRange().duplicate(),a=i.getBookmark();o.moveToBookmark(a);while(o.moveStart("character",-1)!==0)e++;return e}return 0}if(e==-1)e=this[t?"text":"val"]().length;if(window.getSelection){if(t){n.focus();window.getSelection().collapse(n.firstChild,e)}else n.setSelectionRange(e,e)}else if(document.body.createTextRange){var o=document.body.createTextRange();o.moveToElementText(n);o.moveStart("character",e);o.collapse(true);o.select()}if(!t)n.focus();return e};function p(e,n){var t=[];var r=e.length;if(rt.length){if(n){break}e=0;n=true}}return t[e]}},append:function(e){t.push(e)}})}function g(n){var t=n instanceof Array?n:n?[n]:[];e.extend(this,{data:function(){return t},map:function(n){return e.map(t,n)},size:function(){return t.length},pop:function(){if(t.length===0){return null}else{var e=t[t.length-1];t=t.slice(0,t.length-1);return e}},push:function(e){t=t.concat([e]);return e},top:function(){return t.length>0?t[t.length-1]:null},clone:function(){return new g(t.slice(0))}})}function d(n,t,r){var i=true;var o="";if(typeof n==="string"&&n!==""){o=n+"_"}o+="commands";var a;if(r){a=[]}else{a=e.Storage.get(o);a=a?e.parseJSON(a):[]}var s=a.length-1;e.extend(this,{append:function(n){if(i){if(a[a.length-1]!==n){a.push(n);if(t&&a.length>t){a=a.slice(-t)}s=a.length-1;if(!r){e.Storage.set(o,JSON.stringify(a))}}}},data:function(){return a},reset:function(){s=a.length-1},last:function(){return a[a.length-1]},end:function(){return s===a.length-1},position:function(){return s},current:function(){return a[s]},next:function(){if(s0){--s}if(e!==-1){return a[s]}},clear:function(){a=[];this.purge()},enabled:function(){return i},enable:function(){i=true},purge:function(){if(!r){e.Storage.remove(o)}},disable:function(){i=false}})}var v=function(){var e=document.createElement("div");e.setAttribute("onpaste","return;");return typeof e.onpaste=="function"}();var y=true;e.fn.cmd=function(t){var r=this;var i=r.data("cmd");if(i){return i}r.addClass("cmd");r.append(''+' ');var o=e("