| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891 |  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;    };  }    if (!String.prototype.startsWith) {    String.prototype.startsWith = function(searchString, position) {      position = position || 0;      return this.indexOf(searchString, position) === position;    };  }      /**   * Stack for holding previous commands for retrieval with the up arrow.    * Stores data in localStorage. Won't push consecutive duplicates.   *   * @author   Jake Gully, chimpytk@gmail.com   * @license  MIT License   */    /**   * Constructor   * @param {string}  id       Unique id for this stack   * @param {integer} max_size Number of commands to store   */  function CmdStack(id, max_size) {    "use strict";      var instance_id = id,        cur = 0,        arr = []; // This is a fairly meaningless name but                  // makes it sound like this function was                  // written by a pirate.  I'm keeping it.      if (typeof id !== 'string') {      throw 'Stack error: id should be a string.';    }      if (typeof max_size !== 'number') {      throw 'Stack error: max_size should be a number.';    }        /**     * Store the array in localstorage     */    function setArray(arr) {      localStorage['cmd_stack_' + instance_id] = JSON.stringify(arr);    }      /**     * Load array from localstorage     */    function getArray() {      if (!localStorage['cmd_stack_' + instance_id]) {        arr = [];        setArray(arr);      }        try {        arr = JSON.parse(localStorage['cmd_stack_' + instance_id]);      } catch (err) {        return [];      }      return arr;    }      /**     * Push a command to the array     * @param  {string} cmd Command to append to stack     */    function push(cmd) {      arr = getArray();            // don't push if same as last command      if (cmd === arr[arr.length - 1]) {        return false;      }        arr.push(cmd);        // crop off excess      while (arr.length > max_size) {        arr.shift();      }        cur = arr.length;      setArray(arr);    }      /**     * Get previous command from stack (up key)     * @return {string} Retrieved command string     */    function prev() {      cur -= 1;        if (cur < 0) {        cur = 0;      }        return arr[cur];    }      /**     * Get next command from stack (down key)     * @return {string} Retrieved command string     */    function next() {      cur = cur + 1;        // Return a blank string as last item      if (cur === arr.length) {        return "";      }        // Limit      if (cur > (arr.length - 1)) {        cur = (arr.length - 1);      }        return arr[cur];    }      /**     * Move cursor to last element     */    function reset() {      arr = getArray();      cur = arr.length;    }      /**     * Is stack empty     * @return {Boolean} True if stack is empty     */    function isEmpty() {      arr = getArray();      return (arr.length === 0);    }        /**     * Empty array and remove from localstorage     */    function empty() {      arr = undefined;      localStorage.clear();      reset();    }      /**     * Get current cursor location     * @return {integer} Current cursor index     */    function getCur() {      return cur;    }      /**     * Get entire stack array     * @return {array} The stack array     */    function getArr() {      return arr;    }      /**     * Get size of the stack     * @return {Integer} Size of stack     */    function getSize(){      return arr.size;    }      return {      push: push,      prev: prev,      next: next,      reset: reset,      isEmpty: isEmpty,      empty: empty,      getCur: getCur,      getArr: getArr,      getSize: getSize    };  }      /**   * HTML5 Command Line Terminal   *   * @author   Jake Gully (chimpytk@gmail.com)   * @license  MIT License   */    (function (root, factory) {    if ( typeof define === 'function' && define.amd ) {      define(factory);    } else if ( typeof exports === 'object' ) {      module.exports = factory();    } else {      root.Cmd = factory();    }  }(this, function () {    "use strict";      var Cmd = function (user_config) {      this.keys_array    = [9, 13, 38, 40, 27],      this.style         = 'dark',      this.popup         = false,      this.prompt_str    = '$ ',      this.speech_synth_support = ('speechSynthesis' in window && typeof SpeechSynthesisUtterance !== 'undefined'),      this.options       = {        busy_text:           'Communicating...',        external_processor:  function() {},        filedrop_enabled:    false,        file_upload_url:     'ajax/uploadfile.php',        history_id:          'cmd_history',        remote_cmd_list_url: '',        selector:            '#cmd',        tabcomplete_url:     '',        talk:                false,        unknown_cmd:         'Unrecognised command',        typewriter_time:     32      },      this.voices = false;      this.remote_commands = [];      this.all_commands = [];      this.local_commands = [        'clear',        'clr',        'cls',        'clearhistory',        'invert',        'shh',        'talk'      ];      this.autocompletion_attempted = false;        $.extend(this.options, user_config);        if (this.options.remote_cmd_list_url) {        $.ajax({          url: this.options.remote_cmd_list_url,          context: this,          dataType: 'json',          method: 'GET',          success: function (data) {            this.remote_commands = data;            this.all_commands = $.merge(this.remote_commands, this.local_commands)          }        });      } else {        this.all_commands = this.local_commands;      }        if (!$(this.options.selector).length) {        throw 'Cmd err: Invalid selector.';      }        this.cmd_stack = new CmdStack(this.options.history_id, 30);        if (this.cmd_stack.isEmpty()) {        this.cmd_stack.push('secretmagicword!');      }        this.cmd_stack.reset();      this.setupDOM();      this.input.focus();    }      // ====== Layout / IO / Alter Interface =========      /**     * Create DOM elements, add click & key handlers     */    Cmd.prototype.setupDOM = function() {      this.wrapper = $(this.options.selector).addClass('cmd-interface');        this.container = $('<div/>')      .addClass('cmd-container')      .appendTo(this.wrapper);        if (this.options.filedrop_enabled) {        setupFiledrop(); // adds dropzone div      }        this.clearScreen(); // adds output, input and prompt        $(this.options.selector).on('click', $.proxy(this.focusOnInput, this));      $(window).resize($.proxy(this.resizeInput, this));        this.wrapper.keydown($.proxy(this.handleKeyDown, this));      this.wrapper.keyup($.proxy(this.handleKeyUp, this));      this.wrapper.keydown($.proxy(this.handleKeyPress, this));    }      /**     * Changes the input type     */    Cmd.prototype.showInputType = function(input_type) {      switch (input_type) {        case 'password':          this.input = $('<input/>')            .attr('type', 'password')            .attr('maxlength', 512)            .addClass('cmd-in');          break;        case 'textarea':          this.input = $('<textarea/>')            .addClass('cmd-in')          break;        default:          this.input = $('<input/>')            .attr('type', 'text')            .attr('maxlength', 512)            .addClass('cmd-in');      }        this.container.children('.cmd-in').remove();        this.input.appendTo(this.container)        .attr('title', 'Chimpcom input');        this.focusOnInput();    }      /**     * Takes the client's input and the server's output     * and displays them appropriately.     *     * @param   string  cmd_in      The command as entered by the user     * @param   string  cmd_out     The server output to write to screen     */    Cmd.prototype.displayOutput = function(cmd_out) {      if (typeof cmd_out !== 'string') {        cmd_out = 'Error: invalid cmd_out returned.';      }        if (this.output.html())  {        this.output.append('<br>');      }        this.output.append(cmd_out + '<br>');        if (this.options.talk) {        this.speakOutput(cmd_out);      }        this.cmd_stack.reset();        this.input.val('').removeAttr('disabled');        this.enableInput();      this.focusOnInput();      this.activateAutofills();    }      /**     * Take an input string and output it to the screen     */    Cmd.prototype.displayInput = function(cmd_in) {      cmd_in = cmd_in.replace(/&/g, "&")        .replace(/</g, "<")        .replace(/>/g, ">")        .replace(/"/g, """)        .replace(/'/g, "'");        this.output.append('<span class="prompt">' + this.prompt_str + '</span> ' +        '<span class="grey_text">' + cmd_in + '</span>');    }      /**     * Set the prompt string     * @param {string} new_prompt The new prompt string     */    Cmd.prototype.setPrompt = function(new_prompt) {      if (typeof new_prompt !== 'string') {        throw 'Cmd error: invalid prompt string.';      }        this.prompt_str = new_prompt;      this.prompt_elem.html(this.prompt_str);    }      /**     * Post-file-drop dropzone reset     */    Cmd.prototype.resetDropzone = function() {      dropzone.css('display', 'none');    }      /**     * Add file drop handlers     */    Cmd.prototype.setupFiledrop = function() {      this.dropzone = $('<div/>')      .addClass('dropzone')      .appendTo(wrapper)      .filedrop({        url: this.options.file_upload_url,        paramname: 'dropfile', // POST parameter name used on serverside to reference file        maxfiles: 10,        maxfilesize: 2, // MBs        error: function (err, file) {          switch (err) {          case 'BrowserNotSupported':            alert('Your browser does not support html5 drag and drop.');            break;          case 'TooManyFiles':            this.displayInput('[File Upload]');            this.displayOutput('Too many files!');            this.resetDropzone();            break;          case 'FileTooLarge':            // FileTooLarge also has access to the file which was too large            // use file.name to reference the filename of the culprit file            this.displayInput('[File Upload]');            this.displayOutput('File too big!');            this.resetDropzone();            break;          default:            this.displayInput('[File Upload]');            this.displayOutput('Fail D:');            this.resetDropzone();            break;          }        },        dragOver: function () { // user dragging files over #dropzone          this.dropzone.css('display', 'block');        },        dragLeave: function () { // user dragging files out of #dropzone          this.resetDropzone();        },        docOver: function () { // user dragging files anywhere inside the browser document window          this.dropzone.css('display', 'block');        },        docLeave: function () { // user dragging files out of the browser document window          this.resetDropzone();        },        drop: function () { // user drops file          this.dropzone.append('<br>File dropped.');        },        uploadStarted: function (i, file, len) {          this.dropzone.append('<br>Upload started...');          // a file began uploading          // i = index => 0, 1, 2, 3, 4 etc          // file is the actual file of the index          // len = total files user dropped        },        uploadFinished: function (i, file, response, time) {          // response is the data you got back from server in JSON format.          if (response.error !== '') {            upload_error = response.error;          }          this.dropzone.append('<br>Upload finished! ' + response.result);        },        progressUpdated: function (i, file, progress) {          // this function is used for large files and updates intermittently          // progress is the integer value of file being uploaded percentage to completion          this.dropzone.append('<br>File uploading...');        },        speedUpdated: function (i, file, speed) { // speed in kb/s          this.dropzone.append('<br>Upload speed: ' + speed);        },        afterAll: function () {          // runs after all files have been uploaded or otherwise dealt with          if (upload_error !== '') {            this.displayInput('[File Upload]');            this.displayOutput('Error: ' + upload_error);          } else {            this.displayInput('[File Upload]');            this.displayOutput('[File Upload]', 'Success!');          }            upload_error = '';            this.dropzone.css('display', 'none');          this.resetDropzone();        }      });    }      /**     * [invert description]     * @return {[type]} [description]     */    Cmd.prototype.invert = function() {      this.wrapper.toggleClass('inverted');    }          // ====== Handlers ==============================      /**     * Do something     */    Cmd.prototype.handleInput = function(input_str) {      var cmd_array = input_str.split(' ');      var shown_input = input_str;        if (this.input.attr('type') === 'password') {        shown_input = new Array(shown_input.length + 1).join("•");      }        this.displayInput(shown_input);        switch (cmd_array[0]) {        case '':          this.displayOutput('');          break;        case 'clear':        case 'cls':        case 'clr':          this.clearScreen();          break;        case 'clearhistory':          this.cmd_stack.empty();          this.cmd_stack.reset();          this.displayOutput('Command history cleared. ');          break;        case 'invert':          this.invert();          this.displayOutput('Shazam.');          break;        case 'shh':          if (this.options.talk) {            window.speechSynthesis.cancel();            this.options.talk = false;            this.displayOutput('Speech stopped. Talk mode is still enabled. Type TALK to disable talk mode.');            this.options.talk = true;          } else {            this.displayOutput('Ok.');          }          break;        case 'talk':          if (!this.speech_synth_support) {            this.displayOutput('You browser doesn\'t support speech synthesis.');            return false;          }            this.options.talk = !this.options.talk;          this.displayOutput((this.options.talk ? 'Talk mode enabled.' : 'Talk mode disabled.'));          break;        default:          if (typeof this.options.external_processor !== 'function') {            this.displayOutput(this.options.unknown_cmd);            return false;          }            var result = this.options.external_processor(input_str, this);            switch (typeof result) {            // If undefined, external handler should            // call handleResponse when done            case 'boolean':              if (!result) {                this.displayOutput(this.options.unknown_cmd);              }              break;            // If we get a response object, deal with it directly            case 'object':              this.handleResponse(result);              break;            // If we have a string, output it. This shouldn't            // really happen but it might be useful            case 'string':              this.displayOutput(result);              break;            default:              this.displayOutput(this.options.unknown_cmd);          }      }    }      /**     * Handle JSON responses. Used as callback by external command handler     * @param  {object} res Chimpcom command object     */    Cmd.prototype.handleResponse = function(res) {      if (res.redirect !== undefined) {        document.location.href = res.redirect;      }        if (res.openWindow !== undefined) {        window.open(res.openWindow, '_blank', res.openWindowSpecs);      }        if (res.log !== undefined && res.log !== '') {        console.log(res.log);      }        if (res.show_pass === true) {        this.showInputType('password');      } else {        this.showInputType();      }        this.displayOutput(res.cmd_out);        if (res.cmd_fill !== '') {        this.wrapper.children('.cmd-container').children('.cmd-in').first().val(res.cmd_fill);      }    }      /**     * Handle keypresses     */    Cmd.prototype.handleKeyPress = function(e) {      var keyCode = e.keyCode || e.which,        input_str = this.input.val(),        autocompletions;        if (keyCode === 9) { //tab        this.tabComplete(input_str);      } else {        this.autocompletion_attempted = false;        if (this.autocomplete_ajax) {          this.autocomplete_ajax.abort();        }          if (keyCode === 13) { // enter          if (this.input.attr('disabled')) {            return false;          }            if (e.ctrlKey) {            this.cmd_stack.push(input_str);            this.goToURL(input_str);          } else {            this.disableInput();              // push command to stack if using text input, i.e. no passwords            if (this.input.get(0).type === 'text') {              this.cmd_stack.push(input_str);            }              this.handleInput(input_str);          }        } else if (keyCode === 38) { // up arrow          if (input_str !== "" && this.cmd_stack.cur === (this.cmd_stack.getSize() - 1)) {            this.cmd_stack.push(input_str);          }            this.input.val(this.cmd_stack.prev());        } else if (keyCode === 40) { // down arrow          this.input.val(this.cmd_stack.next());        } else if (keyCode === 27) { // esc          if (this.container.css('opacity') > 0.5) {            this.container.animate({'opacity': 0}, 300);          } else {            this.container.animate({'opacity': 1}, 300);          }        }      }    }      /**     * Prevent default action of special keys     */    Cmd.prototype.handleKeyUp = function(e) {      var key = e.which;        if ($.inArray(key, this.keys_array) > -1) {        e.preventDefault();        return false;      }        return true;    }      /**     * Prevent default action of special keys     */    Cmd.prototype.handleKeyDown = function(e) {      var key = e.which;        if ($.inArray(key, this.keys_array) > -1) {        e.preventDefault();          return false;      }      return true;    }      /**     * Complete command names when tab is pressed     */    Cmd.prototype.tabComplete = function(str) {      // If we have a space then offload to external processor      if (str.indexOf(' ') !== -1) {        if (this.options.tabcomplete_url) {          if (this.autocomplete_ajax) {            this.autocomplete_ajax.abort();          }            this.autocomplete_ajax = $.ajax({            url: this.options.tabcomplete_url,            context: this,            dataType: 'json',            data: {              cmd_in: str            },            success: function (data) {              if (data) {                this.input.val(data);              }            }          });        }        this.autocompletion_attempted = false;        return;      }        var autocompletions = this.all_commands.filter(function (value) {        return value.startsWith(str);      });          if (autocompletions.length === 0) {        return false;      } else if (autocompletions.length === 1) {        this.input.val(autocompletions[0]);      } else {        if (this.autocompletion_attempted) {          this.displayOutput(autocompletions.join(', '));          this.autocompletion_attempted = false;          this.input.val(str);          return;        } else {          this.autocompletion_attempted = true;        }      }    }        // ====== Helpers ===============================      /**     * Takes a user to a given url. Adds "http://" if necessary.     */    Cmd.prototype.goToURL = function(url) {      if (url.substr(0, 4) !== 'http' && url.substr(0, 2) !== '//') {        url = 'http://' + url;      }        if (popup) {        window.open(url, '_blank');        window.focus();      } else {        // break out of iframe - used by chrome plugin        if (top.location !== location) {          top.location.href = document.location.href;        }          location.href = url;      }    }      /**     * Give focus to the command input and     * scroll to the bottom of the page     */    Cmd.prototype.focusOnInput = function() {      var cmd_width;        $(this.options.selector).scrollTop($(this.options.selector)[0].scrollHeight);        this.input.focus();    }      /**     * Make prompt and input fit on one line     */    Cmd.prototype.resizeInput = function() {      var cmd_width = this.wrapper.width() - this.wrapper.find('.main-prompt').first().width() - 45;        this.input.focus().css('width', cmd_width);    }      /**     * Clear the screen     */    Cmd.prototype.clearScreen = function() {      this.container.empty();        this.output = $('<div/>')        .addClass('cmd-output')        .appendTo(this.container);        this.prompt_elem = $('<span/>')        .addClass('main-prompt')        .addClass('prompt')        .html(this.prompt_str)        .appendTo(this.container);        this.input = $('<input/>')        .addClass('cmd-in')        .attr('type', 'text')        .attr('maxlength', 512)        .appendTo(this.container);        this.showInputType();        this.input.val('');    }      /**     * Attach click handlers to 'autofills' - divs which, when clicked,     * will insert text into the input     */    Cmd.prototype.activateAutofills = function() {      var input = this.input;        this.wrapper        .find('[data-type=autofill]')        .on('click', function() {          input.val($(this).data('autofill'));        });    }      /**     * Temporarily disable input while runnign commands     */    Cmd.prototype.disableInput = function() {      this.input        .attr('disabled', true)        .val(this.options.busy_text);    }      /**     * Reenable input after running disableInput()     */    Cmd.prototype.enableInput = function() {      this.input        .removeAttr('disabled')        .val('');    }      /**     * Speak output aloud using speech synthesis API     *     * @param {String} output Text to read     */    Cmd.prototype.speakOutput = function(output) {      var msg = new SpeechSynthesisUtterance();        msg.volume = 1; // 0 - 1      msg.rate   = 1; // 0.1 - 10      msg.pitch  = 2; // 0 - 2      msg.lang   = 'fr-FR';      msg.text   = output;        window.speechSynthesis.speak(msg);    }      return Cmd;  }));
 |