domConsole.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import { LocalizedStrings } from "./../services/localizedStringsService";
  2. export class DOMConsole {
  3. static get USER () {
  4. return 0;
  5. }
  6. static get INFO () {
  7. return 1;
  8. }
  9. static get ERR () {
  10. return 2;
  11. }
  12. static get INPUT () {
  13. return 3;
  14. }
  15. constructor (elementID) {
  16. this.input = null;
  17. this.cursorInterval = null;
  18. this.inputDiv = null;
  19. this.inputCMD = null;
  20. this.inputSpan = null;
  21. this.cursorRef = null;
  22. this.needInput = false;
  23. this.clearBtn = null;
  24. this.hideBtn = null;
  25. this.showBtn = null;
  26. this.termDiv = null;
  27. this.anyKey = false;
  28. let actualID = elementID
  29. if (elementID[0] === '#') {
  30. actualID = elementID.substring(1);
  31. }
  32. this.parent = document.getElementById(actualID)
  33. this.setup();
  34. this.inputListeners = [];
  35. this.hideInput();
  36. }
  37. setup () {
  38. this._setupDom();
  39. this._setupEvents();
  40. }
  41. _setupEvents () {
  42. this.input.addEventListener('keydown', this.registerInput.bind(this));
  43. this.clearBtn.addEventListener('click', this.clearBtnClick.bind(this));
  44. this.hideBtn.addEventListener('click', this.hideBtnClick.bind(this));
  45. this.showBtn.addEventListener('click', this.showBtnClick.bind(this));
  46. }
  47. registerInput (event) {
  48. if (!this.needInput) {
  49. return;
  50. }
  51. const keyCode = event.which;
  52. if (keyCode === 13 || this.anyKey) {
  53. let text = this.input.value;
  54. text = text.replace('[\n\r]+', '');
  55. this.notifyListeners(text);
  56. this._appendUserInput(text);
  57. this.input.value = '';
  58. this.inputSpan.innerHTML = '';
  59. }
  60. }
  61. _setupDom () {
  62. const bashNode = document.createElement('div');
  63. bashNode.classList.add('bash');
  64. bashNode.innerHTML = `
  65. <div class="bash-title">
  66. <i id="ivprog-console-clearbtn" class="icon eraser" style="float:left;padding-left: 5px"></i>
  67. <span>Terminal</span>
  68. <i id="ivprog-console-showbtn" class="icon window maximize outline" style="float:right"></i>
  69. <i id="ivprog-console-hidebtn" class="icon window minimize outline" style="float:right"></i>
  70. </div>
  71. <div id='ivprog-term' class="bash-body"></div>`;
  72. this.termDiv = bashNode.querySelector("#ivprog-term");
  73. this.termDiv.classList.add("ivprog-term-div");
  74. this.inputDiv = document.createElement("div");
  75. this.inputDiv.id = "ivprog-terminal-inputdiv";
  76. this.inputDiv.innerHTML = `
  77. <div id="cmd">
  78. <span></span>
  79. <div id="cursor"></div>
  80. </div>`;
  81. this.input = document.createElement("input");
  82. this.input.setAttribute("name", "command");
  83. this.input.setAttribute("value", "");
  84. this.input.setAttribute("type", "text");
  85. this.inputDiv.append(this.input);
  86. this.termDiv.append(this.inputDiv);
  87. bashNode.append(this.termDiv);
  88. this.parent.append(bashNode);
  89. this.inputCMD = this.inputDiv.querySelector("#cmd");
  90. this.cursorRef = this.inputCMD.querySelector("#cursor");
  91. this.inputSpan = this.inputCMD.querySelector('span');
  92. this.clearBtn = bashNode.querySelector('#ivprog-console-clearbtn');
  93. this.hideBtn = bashNode.querySelector('#ivprog-console-hidebtn');
  94. this.showBtn = bashNode.querySelector('#ivprog-console-showbtn');
  95. this._setupCursor();
  96. //Jquery tooltips....
  97. $(this.clearBtn).popup({content:LocalizedStrings.getUI("terminal_clear")});
  98. $(this.showBtn).popup({content:LocalizedStrings.getUI("terminal_show")});
  99. $(this.hideBtn).popup({content:LocalizedStrings.getUI("terminal_hide")});
  100. }
  101. _setupCursor () {
  102. this.inputCMD.addEventListener('click', this.blinkCaretAndFocus.bind(this));
  103. this.inputCMD.click();
  104. this.input.addEventListener('keyup', this.updateSpanText.bind(this));
  105. this.input.addEventListener('blur', this.stopBlinkCaret.bind(this));
  106. }
  107. blinkCaretAndFocus () {
  108. if(this.cursorInterval != null) {
  109. return;
  110. }
  111. this.input.focus();
  112. const outerRef = this;
  113. this.cursorInterval = window.setInterval(function() {
  114. if (outerRef.cursorRef.style.visibility === 'visible') {
  115. outerRef.cursorRef.style.visibility = 'hidden';
  116. } else {
  117. outerRef.cursorRef.style.visibility = 'visible';
  118. }
  119. }, 500);
  120. }
  121. updateSpanText () {
  122. this.inputSpan.innerHTML = this.input.value;
  123. }
  124. stopBlinkCaret () {
  125. clearInterval(this.cursorInterval);
  126. this.cursorInterval = null;
  127. this.cursorRef.style.visibility = 'visible';
  128. }
  129. notifyListeners (text) {
  130. this.inputListeners.forEach(resolve => resolve(text));
  131. this.inputListeners.splice(0, this.inputListeners.length);
  132. this.hideInput();
  133. this.anyKey = false;
  134. }
  135. write (text) {
  136. this._appendText(text, DOMConsole.USER);
  137. }
  138. info (text) {
  139. this._appendText(text, DOMConsole.INFO);
  140. }
  141. err (text) {
  142. this._appendText(text, DOMConsole.ERR);
  143. }
  144. _appendText (text, type) {
  145. const divClass = this.getClassForType(type);
  146. const textDiv = document.createElement('div');
  147. textDiv.classList.add(divClass);
  148. textDiv.innerHTML = `<span>${text}</span>`;
  149. this.termDiv.insertBefore(textDiv, this.inputDiv);
  150. this.scrollTerm();
  151. }
  152. _appendUserInput (text) {
  153. const divClass = this.getClassForType(DOMConsole.INPUT);
  154. const textDiv = document.createElement('div');
  155. textDiv.innerHTML = `
  156. <i class="icon keyboard outline" style="float:left"></i>
  157. <span>${text}</span>`;
  158. textDiv.classList.add(divClass);
  159. this.termDiv.insertBefore(textDiv, this.inputDiv);
  160. this.scrollTerm();
  161. }
  162. scrollTerm () {
  163. //scrollIt(this.inputDiv.previousSibling,200);
  164. this.inputDiv.previousSibling.scrollIntoView();
  165. }
  166. focus () {
  167. this.termDiv.style.display = 'block';
  168. const prev = this.inputDiv.closest('div');
  169. if(prev != null)
  170. prev.scrollIntoView();
  171. }
  172. hide () {
  173. this.termDiv.style.display = 'none';
  174. }
  175. getClassForType (type) {
  176. switch (type) {
  177. case DOMConsole.INPUT:
  178. return "ivprog-term-userInput";
  179. case DOMConsole.USER:
  180. return "ivprog-term-userText";
  181. case DOMConsole.INFO:
  182. return "ivprog-term-info";
  183. case DOMConsole.ERR:
  184. return "ivprog-term-error";
  185. }
  186. }
  187. dispose () {
  188. this.input.removeEventListener('keyup', this.updateSpanText.bind(this));
  189. this.input.removeEventListener('blur', this.stopBlinkCaret.bind(this));
  190. this.input.removeEventListener('keydown', this.registerInput.bind(this));
  191. this.inputCMD.removeEventListener('click', this.blinkCaretAndFocus.bind(this));
  192. this.clearBtn.removeEventListener('click', this.clearBtnClick.bind(this));
  193. this.hideBtn.removeEventListener('click', this.hideBtnClick.bind(this));
  194. this.showBtn.removeEventListener('click', this.showBtnClick.bind(this));
  195. this.input = null;
  196. this.inputCMD = null;
  197. this.inputDiv = null;
  198. this.termDiv = null;
  199. this.inputSpan = null;
  200. this.cursorRef = null;
  201. this.clearBtn = null;
  202. this.hideBtn = null;
  203. this.showBtn = null;
  204. const cNode = this.parent.cloneNode(false);
  205. this.parent.parentNode.replaceChild(cNode, this.parent);
  206. if(this.cursorInterval != null) {
  207. clearInterval(this.cursorInterval);
  208. }
  209. }
  210. showInput () {
  211. this.needInput = true;
  212. this.inputDiv.style.display = 'block';
  213. this.inputCMD.click();
  214. this.inputCMD.scrollIntoView();
  215. }
  216. hideInput () {
  217. this.needInput = false;
  218. this.inputDiv.style.display = ' none';
  219. clearInterval(this.cursorInterval);
  220. this.cursorInterval = null;
  221. }
  222. requestInput (callback, anyKey = false) {
  223. this.inputListeners.push(callback);
  224. this.anyKey = anyKey;
  225. this.showInput();
  226. }
  227. sendOutput (text) {
  228. const output = ""+text;
  229. output.split("\n").forEach(t => {
  230. t = t.replace(/\t/g,'&#9;');
  231. this.write(t)
  232. });
  233. }
  234. clear () {
  235. while(this.inputDiv.parentElement.childNodes.length > 1) {
  236. this.inputDiv.parentElement.removeChild(this.inputDiv.parentElement.firstChild);
  237. }
  238. this.input.value = "";
  239. this.inputSpan.innerHTML = '';
  240. }
  241. clearBtnClick () {
  242. this.clear();
  243. }
  244. showBtnClick () {
  245. this.focus();
  246. }
  247. hideBtnClick () {
  248. this.hide();
  249. }
  250. }