Advertisement
johnmahugu

wsh.js

Mar 3rd, 2016
182
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (function(exports) {
  2.  
  3. var win = window,
  4.     doc = document,
  5.     body = doc.body
  6. ;
  7.  
  8. function gEl(id) {
  9.   return doc.getElementById(id);
  10. }
  11.  
  12. var Wsh;
  13. (Wsh = function(conf) {
  14.   this.lineId = 0;
  15.   this.init(conf || {});
  16. }).prototype = {
  17.   init: function(conf) {
  18.     this.conf = conf || {};
  19.     var prop, defaultConfig = Wsh.defaultConfig;
  20.     for (prop in defaultConfig) {
  21.       if (typeof(this.conf[prop]) === "undefined") {
  22.         this.conf[prop] = defaultConfig[prop];
  23.       }
  24.     }
  25.     this.id = this.conf.id || (Wsh.prefix + (++Wsh.id));
  26.     this.listeners = {};
  27.     if (this.conf.listeners) this.addListener(this.conf.listeners);
  28.   },
  29.   addListener: function(event, handler, scope) {
  30.     if (arguments.length === 1) {
  31.       if (event.constructor === Array) {
  32.         var i, n = event.length;
  33.         for (i = 0; i < n; i++) this.addListener(event[i]);
  34.         return;
  35.       }
  36.       else
  37.       if (typeof(event) === "object") {
  38.         if (typeof(event.event) === "undefined") {
  39.           var name, listener;
  40.           for (name in event) {
  41.             listener = event[name];
  42.             switch (typeof(listener)) {
  43.               case "function":
  44.                 this.addListener(name, listener);
  45.                 break;
  46.               case "object":
  47.                 this.addListener(name, listener.handler, listener.scope);
  48.                 break;
  49.             }
  50.           }
  51.           return;
  52.         }
  53.         else {
  54.           handler = event.handler;
  55.           scope = event.scope;
  56.           event = event.event;
  57.         }
  58.       }
  59.     }
  60.     var handlers = this.listeners[event];
  61.     if (typeof(handlers) === "undefined") {
  62.       handlers = this.listeners[event] = [];
  63.     }
  64.     handlers.push({
  65.       handler: handler,
  66.       scope: scope || this
  67.     });
  68.   },
  69.   fireEvent: function(event, data) {
  70.     var listeners = this.listeners[event];
  71.     if (typeof(listeners) === "undefined") return;
  72.     var n = listeners.length, listener;
  73.     for (i = 0; i < n; i++) {
  74.       listener = listeners[i];
  75.       if (listener.handler.call(
  76.         listener.scope,
  77.         this, event, data
  78.       ) === false) {
  79.         return false;
  80.       }
  81.     }
  82.     return true;
  83.   },
  84.   getId: function() {
  85.     return this.id;
  86.   },
  87.   getTextAreaId: function() {
  88.     return this.getId() + "-textarea";
  89.   },
  90.   getCaretId: function() {
  91.     return this.getId() + "-caret";
  92.   },
  93.   getContainer: function() {
  94.     return this.conf.container || body;
  95.   },
  96.   getCaret: function() {
  97.     return gEl(this.getCaretId());
  98.   },
  99.   initCaretInterval: function(interval) {
  100.     var me = this;
  101.     if (!interval) interval = this.getCaretInterval();
  102.     if (me.caretIntervalId) win.clearInterval(me.caretIntervalId);
  103.     this.caretIntervalId = win.setInterval(function(){
  104.       var caret = me.getCaret();
  105.       caret.style.visibility = caret.style.visibility==="hidden" ? "" : "hidden";
  106.     }, interval);
  107.   },
  108.   setCaretInterval: function(interval) {
  109.     this.initCaretInterval(interval);
  110.     this.caretInterval = interval;
  111.   },
  112.   getCaretInterval: function() {
  113.     return (this.caretInterval || this.conf.caretInterval || Wsh.defaultConfig.caretInterval);
  114.   },
  115.   render: function() {
  116.     var me = this;
  117.     var id = me.getId();
  118.     var dom = doc.createElement("DIV");
  119.     dom.className = Wsh.prefix;
  120.     dom.id = id;
  121.     dom.onclick = function() {
  122.       me.focus();
  123.     }
  124.  
  125.     var textarea = doc.createElement("TEXTAREA");
  126.     textarea.className = Wsh.prefix + "-textarea";
  127.     textarea.id = me.getTextAreaId();
  128.     textarea.onkeydown = function(e) {
  129.       var textarea = me.getTextArea();
  130.       if (!e) {
  131.         e = win.event;
  132.       }
  133.       win.setTimeout(function(){
  134.         me.keyDownHandler(e);
  135.       }, 0);
  136.     }
  137.     dom.appendChild(textarea);
  138.  
  139.     var caret = doc.createElement("SPAN");
  140.     caret.className = Wsh.prefix + "-caret";
  141.     caret.id = me.getCaretId();
  142.     dom.appendChild(caret);
  143.     this.initCaretInterval();
  144.  
  145.     var container = me.getContainer();
  146.     container.appendChild(dom);
  147.     if (me.lines){
  148.       for (var i = 0; i < me.lines.length; i++) {
  149.         me.createLine(me.lines[i], "", "init");
  150.       }
  151.     }
  152.     this.fireEvent("rendered", this);
  153.     me.createLine();
  154.     me.updateCaretPosition();
  155.     me.focus();
  156.   },
  157.   getLineBreak: function(interval) {
  158.     return this.conf.lineBreak || Wsh.defaultConfig.lineBreak;
  159.   },
  160.   replaceNbsp: function(text) {
  161.     return text.replace(/\xA0/g, " ");
  162.   },
  163.   updateText: function(){
  164.     var textarea = this.getTextArea();
  165.     var text = textarea.value;
  166.     text = this.replaceNbsp(text);
  167.     var lineBreak = this.conf.lineBreak;
  168.     var line;
  169.     if (lineBreak.test(text)) {
  170.       var lines = text.split(lineBreak);
  171.       var i, n = lines.length;
  172.       for (i = 0; i < n; i++){
  173.         line = lines[i];
  174.         this.setLineText(line);
  175.         if (i < n-1) {
  176.           this.fireEvent("leaveLine", {
  177.             dom: this.getCurrentLine(),
  178.             string: line
  179.           });
  180.           this.createLine();
  181.         }
  182.       }
  183.       this.setTextAreaText(line);
  184.       this.updateCaretPosition();
  185.     }
  186.     else {
  187.       line = this.getCurrentLine();
  188.       this.setLineText(textarea.value, line);
  189.     }
  190.     this.updateCaretPosition();
  191.   },
  192.   blockInput: function(blocked){
  193.     var textarea = this.getTextArea();
  194.     if (blocked) {
  195.       var text = textarea.value;
  196.       textarea.blur();
  197.       textarea.disabled = true;
  198.       this.oldValue = text;
  199.       this.inputBlocked = true;
  200.     }
  201.     else {
  202.       if (typeof(this.oldValue) === "string") {
  203.         this.setTextAreaText(this.oldValue);
  204.       }
  205.       textarea.disabled = false;
  206.       textarea.focus();
  207.       this.inputBlocked = false;
  208.     }
  209.   },
  210.   insertAtCaretPosition: function(insertText){
  211.     var textarea = this.getTextArea();
  212.     var text = textarea.value;
  213.     var caretPosition = this.getCaretPosition();
  214.     var newText = text.substr(0, caretPosition) + insertText + text.substr(caretPosition);
  215.     textarea.value = newText;
  216.     this.setCaretPosition(caretPosition + insertText.length);
  217.   },
  218.   keyDownHandler: function(e) {
  219.     var keyCode = e.keyCode;
  220.     switch (keyCode) {
  221.       case 8: //backspace key
  222.         if (e.stopPropagation) {
  223.           e.stopPropagation();
  224.         }
  225.         break;
  226.       case 9: //tab key
  227.         this.focus();
  228.         break;
  229.     }
  230.  
  231.     if (this.inputBlocked) {
  232.       return;
  233.     }
  234.  
  235.     var textarea = this.getTextArea();
  236.     var text = textarea.value;
  237.     if (keyCode === 9) {
  238.       this.insertAtCaretPosition(String.fromCharCode(9));
  239.       this.focus();
  240.     }
  241.  
  242.     if (this.fireEvent("keydown", e) === false) {
  243.       if (e.preventDefault) {
  244.         e.preventDefault();
  245.       }
  246.       else {
  247.         e.returnValue = false;
  248.       }
  249.       if (e.stopPropagation) {
  250.         e.stopPropagation();
  251.       }
  252.       return;
  253.     }
  254.     var lineBreak = this.conf.lineBreak;
  255.     var line = this.getCurrentLine();
  256.     switch (keyCode) {
  257.       case 16:  //shift
  258.       case 33:  //page up
  259.       case 34:  //page down
  260.       case 38:  //arrow up
  261.       case 40:  //arrow down
  262.         this.restoreCaretPosition();
  263.         return;
  264.       case 13:
  265.         var string = text.replace(lineBreak, "");
  266.         this.setLineText(string, line);
  267.         this.fireEvent("leaveLine", {
  268.           dom: line,
  269.           string: string
  270.         });
  271.         this.createLine();
  272.         break;
  273.       default:
  274.         this.updateText();
  275.         break;
  276.     }
  277.     //console.log("textArea: \"" + textarea.value + "\"");
  278.     this.fireEvent("textChanged", {
  279.       text: textarea.value,
  280.       position: this.getCaretPosition(),
  281.       keyCode: keyCode
  282.     });
  283.   },
  284.   getCaretPosition: function () {
  285.     var el = this.getTextArea();
  286.     if (el.selectionStart) {
  287.       return el.selectionStart;
  288.     }
  289.     else
  290.     if (doc.selection) {
  291.       var r = doc.selection.createRange();
  292.       if (r == null) {
  293.         return 0;
  294.       }
  295.       var re = el.createTextRange(), rc = re.duplicate();
  296.       re.moveToBookmark(r.getBookmark());
  297.       rc.setEndPoint('EndToStart', re);
  298.       return rc.text.length;
  299.     }
  300.     return null;
  301.   },
  302.   restoreCaretPosition: function(){
  303.     this.setCaretPosition(this.prevCaretPosition);
  304.   },
  305.   setCaretPosition: function(position){
  306.     var el = this.getTextArea();
  307.     if (el.setSelectionRange) {
  308.       el.setSelectionRange(position, position);
  309.     }
  310.     else
  311.     if (el.createTextRange) {
  312.       var range = el.createTextRange();
  313.       range.collapse(true);
  314.       range.moveStart("character", position);
  315.       range.moveEnd("character", position);
  316.       range.select();
  317.     }
  318.     this.updateCaretPosition();
  319.   },
  320.   getDom: function() {
  321.     return gEl(this.getId());
  322.   },
  323.   getTextArea: function() {
  324.     return gEl(this.getTextAreaId());
  325.   },
  326.   setTextAreaText: function(text){
  327.     this.getTextArea().value = text;
  328.   },
  329.   getTextAreaText: function() {
  330.     return this.getTextArea().value;
  331.   },
  332.   focus: function() {
  333.     if (this.fireEvent("beforeFocus", {}) === false) {
  334.       return;
  335.     }
  336.     this.getTextArea().focus();
  337.     this.fireEvent("afterFocus", {});
  338.   },
  339.   escapeHTML: function(string){
  340.     return string.replace(/\&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;");
  341.   },
  342.   createLine: function(textString, promptString, className) {
  343.     var cls = Wsh.prefix + "-line";
  344.     var id = cls + (++this.lineId);
  345.     if (this.fireEvent("beforeCreateLine", {
  346.       id: id
  347.     }) === false) return;
  348.     var line = doc.createElement("DIV");
  349.     line.id = id;
  350.     if (className){
  351.       cls += " " + className;
  352.     }
  353.     line.className = cls;
  354.  
  355.     var prompt = doc.createElement("SPAN");
  356.     prompt.className = Wsh.prefix + "-prompt";
  357.     if (typeof(promptString) === "undefined") {
  358.       promptString =  typeof(this.prompt)==="string" ?  this.prompt : (typeof(this.conf.prompt) === "string" ? this.conf.prompt : Wsh.defaultConfig.prompt);
  359.     }
  360.     prompt.innerHTML = promptString;
  361.     line.appendChild(prompt);
  362.  
  363.     var text = doc.createElement("SPAN");
  364.     if (typeof(textString) !== "undefined") {
  365.       text.innerHTML = this.escapeHTML(textString);
  366.     }
  367.     text.className = Wsh.prefix + "-text";
  368.     line.appendChild(text);
  369.  
  370.     var caret = this.getCaret();
  371.     line.appendChild(caret);
  372.  
  373.     var dom = this.getDom();
  374.     dom.appendChild(line);
  375.  
  376.     this.setTextAreaText("");
  377.     this.fireEvent("afterCreateLine", {
  378.       id: id,
  379.       dom: line
  380.     });
  381.     this.alignDom();
  382.     this.updateCaretPosition();
  383.     this.currentLine = line;
  384.     return line;
  385.   },
  386.   alignDom: function(){
  387.     var dom = this.getDom();
  388.     dom.scrollLeft = 0;
  389.     if (dom.scrollHeight > dom.clientHeight) {
  390.       dom.scrollTop = (dom.scrollHeight - dom.clientHeight);
  391.     }
  392.     this.updateCaretPosition();
  393.   },
  394.   getLines: function() {
  395.     return this.getDom().getElementsByTagName("DIV");
  396.   },
  397.   getLine: function(index) {
  398.     var lines = this.getLines();
  399.     return lines[index];
  400.   },
  401.   getCurrentLine: function() {
  402.     if (this.currentLine) {
  403.       return this.currentLine;
  404.     }
  405.     var lines = this.getLines();
  406.     return lines[lines.length - 1];
  407.   },
  408.   getLinePrompt: function(line) {
  409.     if (!line) line = this.getCurrentLine();
  410.     var spans = line.getElementsByTagName("SPAN");
  411.     return spans[0];
  412.   },
  413.   getLinePromptString: function(line) {
  414.     var prompt = this.getLinePrompt(line);
  415.     return prompt.textContent || prompt.innerText;
  416.   },
  417.   setLinePrompt: function(string, line) {
  418.     var prompt = this.getLinePrompt(line);
  419.     prompt.innerHTML = string;
  420.   },
  421.   getLineText: function(line) {
  422.     if (!line) {
  423.       line = this.getCurrentLine();
  424.     }
  425.     var spans = line.getElementsByTagName("SPAN");
  426.     return spans[1];
  427.   },
  428.   getLineTextString: function(line) {
  429.     var text = this.getLineText(line);
  430.     return text.textContent || text.innerText;
  431.   },
  432.   setLineText: function(string, line) {
  433.     if (!line) {
  434.       line = this.getCurrentLine();
  435.     }
  436.     this.lineContent = this.escapeHTML(string);
  437.     this.fireEvent("beforeSetLineText", {
  438.       dom: line,
  439.       string: string
  440.     });
  441.     var text = this.getLineText(line);
  442.     text.innerHTML = this.lineContent;
  443.     this.updateCaretPosition();
  444.     this.fireEvent("afterSetLineText", {
  445.       dom: line,
  446.       string: string
  447.     });
  448.   },
  449.   updateCaretPosition: function() {
  450.     var caretPosition = this.getCaretPosition();
  451.     this.prevCaretPosition = caretPosition;
  452.     var prompt = this.getLinePrompt();
  453.     var caret = this.getCaret();
  454.     var line = this.getCurrentLine();
  455.     var text = line.getElementsByTagName("SPAN")[1];
  456.     var str = text.textContent || text.innerText || "";
  457.     var head = str.substr(0, caretPosition);
  458.     var tail = str.substr(caretPosition);
  459.     text.innerHTML = this.escapeHTML(head);
  460.     caret.style.left = text.offsetLeft + text.offsetWidth + "px";
  461.     caret.style.top = "0px";
  462.     text.innerHTML = this.escapeHTML(str);
  463.  
  464.     var textArea = this.getTextArea();
  465.     var style = textArea.style;
  466.     style.top = (caret.parentNode.offsetTop + caret.parentNode.clientHeight) + "px";
  467.     //style.top = dom.scrollHeight + "px";
  468.     var dom = this.getDom();
  469.     //style.top = (dom.clientHeight < dom.scrollHeight ? dom.clientHeight : caret.parentNode.offsetTop) + "px";
  470.     //style.top = (dom.scrollHeight + dom.clientHeight) + "px";
  471.     style.left = (caret.offsetLeft + caret.clientWidth) + "px";
  472.     //textArea.scrollIntoView(true);
  473.     textArea.focus();
  474.     this.fireEvent("caretPositionChanged", caret);
  475.     //console.log("Update CaretPosition: " + this.prevCaretPosition + ", " + this.getCaretPosition());
  476.   }
  477. };
  478. Wsh.id = 0;
  479. Wsh.prefix = "wsh";
  480. Wsh.defaultConfig = {
  481.   lineBreak: /\r?\n/,
  482.   prompt: "wsh&gt; ",
  483.   caretInterval: 200
  484. };
  485.  
  486. var WshHistory;
  487. WshHistory = function(wsh){
  488.   wsh.addListener("keydown", this.keyDown, this);
  489.   this.index = 0;
  490. };
  491.  
  492. WshHistory.prototype = {
  493.   keyDown: function(wsh, name, event){
  494.     var keyCode = event.keyCode;
  495.     var i = 0, l = 0, idx, oldIndex = this.index;
  496.     switch (keyCode) {
  497.       case 38:  //up arrow
  498.         this.index++;
  499.         break;
  500.       case 40:  //down arrow
  501.         if (this.index) {
  502.           this.index--;
  503.           l = 1;
  504.         }
  505.         break;
  506.       case 13:
  507.         this.index = 0;
  508.       default:
  509.         return;
  510.     }
  511.     var lines = wsh.getLines(), line, text, texts = {};
  512.     while (this.index > i) {
  513.       while (true) {
  514.         ++l;
  515.         idx = lines.length - l;
  516.         if (idx < 0) {
  517.           this.index = oldIndex;
  518.           return;
  519.         };
  520.         line = lines[idx];
  521.         if (line.className !== "wsh-line") {
  522.           continue;
  523.         }
  524.         text = wsh.getLineTextString(line);
  525.         if (text === "") {
  526.           continue;
  527.         }
  528.         if (texts[text]) {
  529.           continue;
  530.         }
  531.         texts[text] = true;
  532.         break;
  533.       };
  534.       i++;
  535.     }
  536.     if (!line) {
  537.       this.index = oldIndex;
  538.     }
  539.     text = wsh.getLineTextString(line);
  540.     wsh.setTextAreaText(text);
  541.     wsh.updateText();
  542.   }
  543. };
  544.  
  545. exports.WshHistory = WshHistory;
  546.  
  547. return exports.Wsh = Wsh;
  548. })(typeof(exports) === "object" ? exports : window);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement