/**@license *| __ _____ ________ __ *| / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / / *| __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / *| / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ *| \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ *| \/ /____/ version 0.4.22 * http://terminal.jcubic.pl * * Licensed under GNU LGPL Version 3 license * Copyright (c) 2011-2012 Jakub Jankiewicz * * Includes: * * 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 * * Date: Thu, 15 Nov 2012 07:12:21 +0000 */ /* TODO: add destroy method to terminal (cmd alrady have it) add support for - $(...).each(function() { ... }); $.fn.pluginname = function(options) { var settings = $.extend({}, $.fn.pluginname.defaultOptions, options); return this.each(function() { var $this = $(this); }); $.fn.pluginname.defaultOptions = { }; }; distinguish between paused and disabled paused should block keydown in terminal it should disable command line disable */ (function($, undefined) { "use strict"; // map object to object $.omap = function(o, fn) { var result = {}; $.each(o, function(k, v) { result[k] = fn.call(o, k, v); }); return result; }; // debug function function get_stack(caller) { "use strict"; if (caller) { return [caller.toString().match(/.*\n.*\n/)]. concat(get_stack(caller.caller)); } else { return []; } } // ---------------------------------------- // START Storage plugin // ---------------------------------------- // Private data var isLS = typeof window.localStorage !== 'undefined'; // Private functions function wls(n, v) { var c; if (typeof n === 'string' && typeof v === 'string') { localStorage[n] = v; return true; } else if (typeof n === 'object' && typeof v === 'undefined') { for (c in n) { if (n.hasOwnProperty(c)) { localStorage[c] = n[c]; } } return true; } return false; } function wc(n, v) { var dt, e, c; dt = new Date(); dt.setTime(dt.getTime() + 31536000000); e = '; expires=' + dt.toGMTString(); if (typeof n === 'string' && typeof v === 'string') { document.cookie = n + '=' + v + e + '; path=/'; return true; } else if (typeof n === 'object' && typeof v === 'undefined') { for (c in n) { if (n.hasOwnProperty(c)) { document.cookie = c + '=' + n[c] + e + '; path=/'; } } return true; } return false; } function rls(n) { return localStorage[n]; } function rc(n) { var nn, ca, i, c; nn = n + '='; ca = document.cookie.split(';'); for (i = 0; i < ca.length; i++) { c = ca[i]; while (c.charAt(0) === ' ') { c = c.substring(1, c.length); } if (c.indexOf(nn) === 0) { return c.substring(nn.length, c.length); } } return null; } function dls(n) { return delete localStorage[n]; } function dc(n) { return wc(n, '', -1); } /** * Public API * $.Storage.set("name", "value") * $.Storage.set({"name1":"value1", "name2":"value2", etc}) * $.Storage.get("name") * $.Storage.remove("name") */ $.extend({ Storage: { set: isLS ? wls : wc, get: isLS ? rls : rc, remove: isLS ? dls : dc } }); // ---------------------------------------- // END Storage plugin // ---------------------------------------- // START jQuery Timers // ---------------------------------------- jQuery.fn.extend({ everyTime: function(interval, label, fn, times, belay) { return this.each(function() { jQuery.timer.add(this, interval, label, fn, times, belay); }); }, oneTime: function(interval, label, fn) { return this.each(function() { jQuery.timer.add(this, interval, label, fn, 1); }); }, stopTime: function(label, fn) { return this.each(function() { jQuery.timer.remove(this, label, fn); }); } }); jQuery.extend({ timer: { guid: 1, global: {}, regex: /^([0-9]+)\s*(.*s)?$/, powers: { // Yeah this is major overkill... 'ms': 1, 'cs': 10, 'ds': 100, 's': 1000, 'das': 10000, 'hs': 100000, 'ks': 1000000 }, timeParse: function(value) { if (value === undefined || value === null) { return null; } var result = this.regex.exec(jQuery.trim(value.toString())); if (result[2]) { var num = parseInt(result[1], 10); var mult = this.powers[result[2]] || 1; return num * mult; } else { return value; } }, add: function(element, interval, label, fn, times, belay) { var counter = 0; if (jQuery.isFunction(label)) { if (!times) { times = fn; } fn = label; label = interval; } interval = jQuery.timer.timeParse(interval); if (typeof interval !== 'number' || isNaN(interval) || interval <= 0) { return; } if (times && times.constructor !== Number) { belay = !!times; times = 0; } times = times || 0; belay = belay || false; if (!element.$timers) { element.$timers = {}; } if (!element.$timers[label]) { element.$timers[label] = {}; } fn.$timerID = fn.$timerID || this.guid++; var handler = function() { if (belay && handler.inProgress) { return; } handler.inProgress = true; if ((++counter > times && times !== 0) || fn.call(element, counter) === false) { jQuery.timer.remove(element, label, fn); } handler.inProgress = false; }; handler.$timerID = fn.$timerID; if (!element.$timers[label][fn.$timerID]) { element.$timers[label][fn.$timerID] = window.setInterval(handler, interval); } if (!this.global[label]) { this.global[label] = []; } this.global[label].push(element); }, remove: function(element, label, fn) { var timers = element.$timers, ret; if (timers) { if (!label) { for (var lab in timers) { if (timers.hasOwnProperty(lab)) { this.remove(element, lab, fn); } } } else if (timers[label]) { if (fn) { if (fn.$timerID) { window.clearInterval(timers[label][fn.$timerID]); delete timers[label][fn.$timerID]; } } else { for (var _fn in timers[label]) { if (timers[label].hasOwnProperty(_fn)) { window.clearInterval(timers[label][_fn]); delete timers[label][_fn]; } } } for (ret in timers[label]) { if (timers[label].hasOwnProperty(ret)) { break; } } if (!ret) { ret = null; delete timers[label]; } } for (ret in timers) { if (timers.hasOwnProperty(ret)) { break; } } if (!ret) { element.$timers = null; } } } } }); if (jQuery.browser.msie) { jQuery(window).one('unload', function() { var global = jQuery.timer.global; for (var label in global) { if (global.hasOwnProperty(label)) { var els = global[label], i = els.length; while (--i) { jQuery.timer.remove(els[i], label); } } } }); } // ---------------------------------------- // START CROSS BROWSER SPLIT // ---------------------------------------- (function(undef) { // prevent double include if (!String.prototype.split.toString().match(/\[native/)) { return; } var nativeSplit = String.prototype.split, compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group self; self = function (str, separator, limit) { // If `separator` is not a regex, use `nativeSplit` if (Object.prototype.toString.call(separator) !== "[object RegExp]") { return nativeSplit.call(str, separator, limit); } var output = [], flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6 (separator.sticky ? "y" : ""), // Firefox 3+ lastLastIndex = 0, // Make `global` and avoid `lastIndex` issues by working with a copy separator2, match, lastIndex, lastLength; separator = new RegExp(separator.source, flags + "g"); str += ""; // Type-convert if (!compliantExecNpcg) { // Doesn't need flags gy, but they don't hurt separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags); } /* Values for `limit`, per the spec: * If undefined: 4294967295 // Math.pow(2, 32) - 1 * If 0, Infinity, or NaN: 0 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296; * If negative number: 4294967296 - Math.floor(Math.abs(limit)) * If other: Type-convert, then use the above rules */ // ? Math.pow(2, 32) - 1 : ToUint32(limit) limit = limit === undef ? -1 >>> 0 : limit >>> 0; while (match = separator.exec(str)) { // `separator.lastIndex` is not reliable cross-browser lastIndex = match.index + match[0].length; if (lastIndex > lastLastIndex) { output.push(str.slice(lastLastIndex, match.index)); // Fix browsers whose `exec` methods don't consistently return `undefined` for // nonparticipating capturing groups if (!compliantExecNpcg && match.length > 1) { match[0].replace(separator2, function () { for (var i = 1; i < arguments.length - 2; i++) { if (arguments[i] === undef) { match[i] = undef; } } }); } if (match.length > 1 && match.index < str.length) { Array.prototype.push.apply(output, match.slice(1)); } lastLength = match[0].length; lastLastIndex = lastIndex; if (output.length >= limit) { break; } } if (separator.lastIndex === match.index) { separator.lastIndex++; // Avoid an infinite loop } } if (lastLastIndex === str.length) { if (lastLength || !separator.test("")) { output.push(""); } } else { output.push(str.slice(lastLastIndex)); } return output.length > limit ? output.slice(0, limit) : output; }; // For convenience String.prototype.split = function (separator, limit) { return self(this, separator, limit); }; return self; })(); // ----------------------------------------------------------------------- /* function decodeHTML(str) { if (typeof str === 'string') { str = str.replace(/&/g, '&'); str = str.replace(/</g, '<').replace(/>/g, '>'); str = str.replace(/ /g, '\t'); str = str.replace(//g, '\n').replace(/ /g, ' '); return str; } else { return ''; } } */ //split string to array of strings with the same length function str_parts(str, length) { var result = []; var len = str.length; if (len < length) { return [str]; } for (var i = 0; i < len; i += length) { result.push(str.substring(i, i + length)); } return result; } // ----------------------------------------------------------------------- function skipFormattingCount(string) { return $('
' + $.terminal.strip(string) + '
').text().length; } // ----------------------------------------------------------------------- function formattingCount(string) { return string.length - skipFormattingCount(string); } // ----------------------------------------------------------------------- // CYCLE DATA STRUCTURE // ----------------------------------------------------------------------- function Cycle(init) { var data = init ? [init] : []; var pos = 0; $.extend(this, { rotate: function() { if (data.length === 1) { return data[0]; } else { if (pos === data.length - 1) { pos = 0; } else { ++pos; } return data[pos]; } }, length: function() { return data.length; }, set: function(item) { for (var i = data.length; i--;) { if (data[i] === item) { pos = i; return; } } this.append(item); }, front: function() { return data[pos]; }, append: function(item) { data.push(item); } }); } // ----------------------------------------------------------------------- // :: BCYCLE DATA STRUCTURE // Two way cycle // ----------------------------------------------------------------------- function BCycle(init) { var data = init instanceof Array ? init : init ? [init] : []; var pos = 0; $.extend(this, { left: function() { if (pos === 0) { pos = data.length - 1; } else { --pos; } return data[pos]; }, right: function() { if (pos === data.length - 1) { pos = 0; } else { ++pos; } return data[pos]; }, current: function() { return data[pos]; }, data: function() { return data; }, length: function() { return data.length; }, reset: function() { pos = 0; }, append: function(item) { data.push(item); this.reset(); } }); } // ----------------------------------------------------------------------- // :: STACK DATA STRUCTURE // ----------------------------------------------------------------------- function Stack(init) { var data = init ? [init] : []; $.extend(this, { size: function() { return data.length; }, pop: function() { if (data.length === 0) { return null; } else { var value = data[data.length - 1]; data = data.slice(0, data.length - 1); return value; } }, push: function(value) { data = data.concat([value]); return value; }, top: function() { return data.length > 0 ? data[data.length - 1] : null; } }); } // serialize object myself (biwascheme or prototype library do something // wiked with JSON serialization for Arrays) $.json_stringify = function(object, level) { var result = '', i; level = level === undefined ? 1 : level; var type = typeof object; switch (type) { case 'function': result += object; break; case 'boolean': result += object ? 'true' : 'false'; break; case 'object': if (object === null) { result += 'null'; } else if (object instanceof Array) { result += '['; var len = object.length; for (i = 0; i < len - 1; ++i) { result += $.json_stringify(object[i], level + 1); } result += $.json_stringify(object[len - 1], level + 1) + ']'; } else { result += '{'; for (var property in object) { if (object.hasOwnProperty(property)) { result += '"' + property + '":' + $.json_stringify(object[property], level + 1); } } result += '}'; } break; case 'string': var str = object; var repl = { '\\\\': '\\\\', '"': '\\"', '/': '\\/', '\\n': '\\n', '\\r': '\\r', '\\t': '\\t'}; for (i in repl) { if (repl.hasOwnProperty(i)) { str = str.replace(new RegExp(i, 'g'), repl[i]); } } result += '"' + str + '"'; break; case 'number': result += String(object); break; } result += (level > 1 ? ',' : ''); // quick hacks below if (level === 1) { // fix last comma result = result.replace(/,([\]}])/g, '$1'); } // fix comma before array or object return result.replace(/([\[{]),/g, '$1'); }; // ----------------------------------------------------------------------- // :: HISTORY CLASS // ----------------------------------------------------------------------- function History(name) { var enabled = true; if (typeof name === 'string' && name !== '') { name += '_'; } var data = $.Storage.get(name + 'commands'); var bc = new BCycle(data ? eval('(' + data + ')') : ['']); $.extend(this, { append: function(item) { if (enabled) { bc.append(item); $.Storage.set(name + 'commands', $.json_stringify(bc.data())); } }, data: function() { return bc.data(); }, next: function() { return bc.right(); }, last: function() { bc.reset(); }, previous: function() { return bc.left(); }, clear: function() { bc = new BCycle(); $.Storage.remove(name + 'commands'); }, enable: function() { enabled = true; }, disable: function() { enabled = false; } }); } // ----------------------------------------------------------------------- // :: COMMAND LINE PLUGIN // ----------------------------------------------------------------------- $.fn.cmd = function(options) { var self = this; self.addClass('cmd'); self.append('' + ' '); var clip = $('