PageRenderTime 30ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/files/jquery.terminal/0.7.8/jquery.terminal-src.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 1207 lines | 1087 code | 27 blank | 93 comment | 95 complexity | fb5c95232952fce84aeeb2435220dd75 MD5 | raw file
  1. /**@license
  2. *| __ _____ ________ __
  3. *| / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / /
  4. *| __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ /
  5. *| / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__
  6. *| \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/
  7. *| \/ /____/ version {{VER}}
  8. * http://terminal.jcubic.pl
  9. *
  10. * Licensed under GNU LGPL Version 3 license
  11. * Copyright (c) 2011-2013 Jakub Jankiewicz <http://jcubic.pl>
  12. *
  13. * Includes:
  14. *
  15. * Storage plugin Distributed under the MIT License
  16. * Copyright (c) 2010 Dave Schindler
  17. *
  18. * jQuery Timers licenced with the WTFPL
  19. * <http://jquery.offput.ca/every/>
  20. *
  21. * Cross-Browser Split 1.1.1
  22. * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
  23. * Available under the MIT License
  24. *
  25. * Date: {{DATE}}
  26. */
  27. (function($, undefined) {
  28. //"use strict";
  29. // -----------------------------------------------------------------------
  30. // :: map object to object
  31. // -----------------------------------------------------------------------
  32. $.omap = function(o, fn) {
  33. var result = {};
  34. $.each(o, function(k, v) {
  35. result[k] = fn.call(o, k, v);
  36. });
  37. return result;
  38. };
  39. // -----------------------------------------------------------------------
  40. // :: Storage plugin
  41. // -----------------------------------------------------------------------
  42. // Private data
  43. var isLS = typeof window.localStorage !== 'undefined';
  44. // Private functions
  45. function wls(n, v) {
  46. var c;
  47. if (typeof n === 'string' && typeof v === 'string') {
  48. localStorage[n] = v;
  49. return true;
  50. } else if (typeof n === 'object' && typeof v === 'undefined') {
  51. for (c in n) {
  52. if (n.hasOwnProperty(c)) {
  53. localStorage[c] = n[c];
  54. }
  55. }
  56. return true;
  57. }
  58. return false;
  59. }
  60. function wc(n, v) {
  61. var dt, e, c;
  62. dt = new Date();
  63. dt.setTime(dt.getTime() + 31536000000);
  64. e = '; expires=' + dt.toGMTString();
  65. if (typeof n === 'string' && typeof v === 'string') {
  66. document.cookie = n + '=' + v + e + '; path=/';
  67. return true;
  68. } else if (typeof n === 'object' && typeof v === 'undefined') {
  69. for (c in n) {
  70. if (n.hasOwnProperty(c)) {
  71. document.cookie = c + '=' + n[c] + e + '; path=/';
  72. }
  73. }
  74. return true;
  75. }
  76. return false;
  77. }
  78. function rls(n) {
  79. return localStorage[n];
  80. }
  81. function rc(n) {
  82. var nn, ca, i, c;
  83. nn = n + '=';
  84. ca = document.cookie.split(';');
  85. for (i = 0; i < ca.length; i++) {
  86. c = ca[i];
  87. while (c.charAt(0) === ' ') {
  88. c = c.substring(1, c.length);
  89. }
  90. if (c.indexOf(nn) === 0) {
  91. return c.substring(nn.length, c.length);
  92. }
  93. }
  94. return null;
  95. }
  96. function dls(n) {
  97. return delete localStorage[n];
  98. }
  99. function dc(n) {
  100. return wc(n, '', -1);
  101. }
  102. /**
  103. * Public API
  104. * $.Storage.set("name", "value")
  105. * $.Storage.set({"name1":"value1", "name2":"value2", etc})
  106. * $.Storage.get("name")
  107. * $.Storage.remove("name")
  108. */
  109. $.extend({
  110. Storage: {
  111. set: isLS ? wls : wc,
  112. get: isLS ? rls : rc,
  113. remove: isLS ? dls : dc
  114. }
  115. });
  116. // -----------------------------------------------------------------------
  117. // :: jQuery Timers
  118. // -----------------------------------------------------------------------
  119. jQuery.fn.extend({
  120. everyTime: function(interval, label, fn, times, belay) {
  121. return this.each(function() {
  122. jQuery.timer.add(this, interval, label, fn, times, belay);
  123. });
  124. },
  125. oneTime: function(interval, label, fn) {
  126. return this.each(function() {
  127. jQuery.timer.add(this, interval, label, fn, 1);
  128. });
  129. },
  130. stopTime: function(label, fn) {
  131. return this.each(function() {
  132. jQuery.timer.remove(this, label, fn);
  133. });
  134. }
  135. });
  136. jQuery.extend({
  137. timer: {
  138. guid: 1,
  139. global: {},
  140. regex: /^([0-9]+)\s*(.*s)?$/,
  141. powers: {
  142. // Yeah this is major overkill...
  143. 'ms': 1,
  144. 'cs': 10,
  145. 'ds': 100,
  146. 's': 1000,
  147. 'das': 10000,
  148. 'hs': 100000,
  149. 'ks': 1000000
  150. },
  151. timeParse: function(value) {
  152. if (value === undefined || value === null) {
  153. return null;
  154. }
  155. var result = this.regex.exec(jQuery.trim(value.toString()));
  156. if (result[2]) {
  157. var num = parseInt(result[1], 10);
  158. var mult = this.powers[result[2]] || 1;
  159. return num * mult;
  160. } else {
  161. return value;
  162. }
  163. },
  164. add: function(element, interval, label, fn, times, belay) {
  165. var counter = 0;
  166. if (jQuery.isFunction(label)) {
  167. if (!times) {
  168. times = fn;
  169. }
  170. fn = label;
  171. label = interval;
  172. }
  173. interval = jQuery.timer.timeParse(interval);
  174. if (typeof interval !== 'number' ||
  175. isNaN(interval) ||
  176. interval <= 0) {
  177. return;
  178. }
  179. if (times && times.constructor !== Number) {
  180. belay = !!times;
  181. times = 0;
  182. }
  183. times = times || 0;
  184. belay = belay || false;
  185. if (!element.$timers) {
  186. element.$timers = {};
  187. }
  188. if (!element.$timers[label]) {
  189. element.$timers[label] = {};
  190. }
  191. fn.$timerID = fn.$timerID || this.guid++;
  192. var handler = function() {
  193. if (belay && handler.inProgress) {
  194. return;
  195. }
  196. handler.inProgress = true;
  197. if ((++counter > times && times !== 0) ||
  198. fn.call(element, counter) === false) {
  199. jQuery.timer.remove(element, label, fn);
  200. }
  201. handler.inProgress = false;
  202. };
  203. handler.$timerID = fn.$timerID;
  204. if (!element.$timers[label][fn.$timerID]) {
  205. element.$timers[label][fn.$timerID] = window.setInterval(handler, interval);
  206. }
  207. if (!this.global[label]) {
  208. this.global[label] = [];
  209. }
  210. this.global[label].push(element);
  211. },
  212. remove: function(element, label, fn) {
  213. var timers = element.$timers, ret;
  214. if (timers) {
  215. if (!label) {
  216. for (var lab in timers) {
  217. if (timers.hasOwnProperty(lab)) {
  218. this.remove(element, lab, fn);
  219. }
  220. }
  221. } else if (timers[label]) {
  222. if (fn) {
  223. if (fn.$timerID) {
  224. window.clearInterval(timers[label][fn.$timerID]);
  225. delete timers[label][fn.$timerID];
  226. }
  227. } else {
  228. for (var _fn in timers[label]) {
  229. if (timers[label].hasOwnProperty(_fn)) {
  230. window.clearInterval(timers[label][_fn]);
  231. delete timers[label][_fn];
  232. }
  233. }
  234. }
  235. for (ret in timers[label]) {
  236. if (timers[label].hasOwnProperty(ret)) {
  237. break;
  238. }
  239. }
  240. if (!ret) {
  241. ret = null;
  242. delete timers[label];
  243. }
  244. }
  245. for (ret in timers) {
  246. if (timers.hasOwnProperty(ret)) {
  247. break;
  248. }
  249. }
  250. if (!ret) {
  251. element.$timers = null;
  252. }
  253. }
  254. }
  255. }
  256. });
  257. if (jQuery.browser && jQuery.browser.msie ||
  258. /(msie) ([\w.]+)/.exec(navigator.userAgent.toLowerCase())) {
  259. jQuery(window).one('unload', function() {
  260. var global = jQuery.timer.global;
  261. for (var label in global) {
  262. if (global.hasOwnProperty(label)) {
  263. var els = global[label], i = els.length;
  264. while (--i) {
  265. jQuery.timer.remove(els[i], label);
  266. }
  267. }
  268. }
  269. });
  270. }
  271. // -----------------------------------------------------------------------
  272. // :: CROSS BROWSER SPLIT
  273. // -----------------------------------------------------------------------
  274. (function(undef) {
  275. // prevent double include
  276. if (!String.prototype.split.toString().match(/\[native/)) {
  277. return;
  278. }
  279. var nativeSplit = String.prototype.split,
  280. compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group
  281. self;
  282. self = function (str, separator, limit) {
  283. // If `separator` is not a regex, use `nativeSplit`
  284. if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
  285. return nativeSplit.call(str, separator, limit);
  286. }
  287. var output = [],
  288. flags = (separator.ignoreCase ? "i" : "") +
  289. (separator.multiline ? "m" : "") +
  290. (separator.extended ? "x" : "") + // Proposed for ES6
  291. (separator.sticky ? "y" : ""), // Firefox 3+
  292. lastLastIndex = 0,
  293. // Make `global` and avoid `lastIndex` issues by working with a copy
  294. separator2, match, lastIndex, lastLength;
  295. separator = new RegExp(separator.source, flags + "g");
  296. str += ""; // Type-convert
  297. if (!compliantExecNpcg) {
  298. // Doesn't need flags gy, but they don't hurt
  299. separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
  300. }
  301. /* Values for `limit`, per the spec:
  302. * If undefined: 4294967295 // Math.pow(2, 32) - 1
  303. * If 0, Infinity, or NaN: 0
  304. * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
  305. * If negative number: 4294967296 - Math.floor(Math.abs(limit))
  306. * If other: Type-convert, then use the above rules
  307. */
  308. // ? Math.pow(2, 32) - 1 : ToUint32(limit)
  309. limit = limit === undef ? -1 >>> 0 : limit >>> 0;
  310. while (match = separator.exec(str)) {
  311. // `separator.lastIndex` is not reliable cross-browser
  312. lastIndex = match.index + match[0].length;
  313. if (lastIndex > lastLastIndex) {
  314. output.push(str.slice(lastLastIndex, match.index));
  315. // Fix browsers whose `exec` methods don't consistently return `undefined` for
  316. // nonparticipating capturing groups
  317. if (!compliantExecNpcg && match.length > 1) {
  318. match[0].replace(separator2, function () {
  319. for (var i = 1; i < arguments.length - 2; i++) {
  320. if (arguments[i] === undef) {
  321. match[i] = undef;
  322. }
  323. }
  324. });
  325. }
  326. if (match.length > 1 && match.index < str.length) {
  327. Array.prototype.push.apply(output, match.slice(1));
  328. }
  329. lastLength = match[0].length;
  330. lastLastIndex = lastIndex;
  331. if (output.length >= limit) {
  332. break;
  333. }
  334. }
  335. if (separator.lastIndex === match.index) {
  336. separator.lastIndex++; // Avoid an infinite loop
  337. }
  338. }
  339. if (lastLastIndex === str.length) {
  340. if (lastLength || !separator.test("")) {
  341. output.push("");
  342. }
  343. } else {
  344. output.push(str.slice(lastLastIndex));
  345. }
  346. return output.length > limit ? output.slice(0, limit) : output;
  347. };
  348. // For convenience
  349. String.prototype.split = function (separator, limit) {
  350. return self(this, separator, limit);
  351. };
  352. return self;
  353. })();
  354. // -----------------------------------------------------------------------
  355. /*
  356. function decodeHTML(str) {
  357. if (typeof str === 'string') {
  358. str = str.replace(/&amp;/g, '&');
  359. str = str.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
  360. str = str.replace(/&#09;/g, '\t');
  361. str = str.replace(/<br\/?>/g, '\n').replace(/&nbsp;/g, ' ');
  362. return str;
  363. } else {
  364. return '';
  365. }
  366. }
  367. */
  368. //split string to array of strings with the same length
  369. // -----------------------------------------------------------------------
  370. // :: Split String into equal parts
  371. // -----------------------------------------------------------------------
  372. function str_parts(str, length) {
  373. var result = [];
  374. var len = str.length;
  375. if (len < length) {
  376. return [str];
  377. }
  378. for (var i = 0; i < len; i += length) {
  379. result.push(str.substring(i, i + length));
  380. }
  381. return result;
  382. }
  383. // -----------------------------------------------------------------------
  384. // :: CYCLE DATA STRUCTURE
  385. // -----------------------------------------------------------------------
  386. function Cycle(init) {
  387. var data = init ? [init] : [];
  388. var pos = 0;
  389. $.extend(this, {
  390. get: function() {
  391. return data;
  392. },
  393. rotate: function() {
  394. if (data.length === 1) {
  395. return data[0];
  396. } else {
  397. if (pos === data.length - 1) {
  398. pos = 0;
  399. } else {
  400. ++pos;
  401. }
  402. return data[pos];
  403. }
  404. },
  405. length: function() {
  406. return data.length;
  407. },
  408. set: function(item) {
  409. for (var i = data.length; i--;) {
  410. if (data[i] === item) {
  411. pos = i;
  412. return;
  413. }
  414. }
  415. this.append(item);
  416. },
  417. front: function() {
  418. return data[pos];
  419. },
  420. append: function(item) {
  421. data.push(item);
  422. }
  423. });
  424. }
  425. // -----------------------------------------------------------------------
  426. // :: STACK DATA STRUCTURE
  427. // -----------------------------------------------------------------------
  428. function Stack(init) {
  429. var data = init ? [init] : [];
  430. $.extend(this, {
  431. size: function() {
  432. return data.length;
  433. },
  434. pop: function() {
  435. if (data.length === 0) {
  436. return null;
  437. } else {
  438. var value = data[data.length - 1];
  439. data = data.slice(0, data.length - 1);
  440. return value;
  441. }
  442. },
  443. push: function(value) {
  444. data = data.concat([value]);
  445. return value;
  446. },
  447. top: function() {
  448. return data.length > 0 ? data[data.length - 1] : null;
  449. }
  450. });
  451. }
  452. // -----------------------------------------------------------------------
  453. // :: Serialize object myself (biwascheme or prototype library do something
  454. // :: wiked with JSON serialization for Arrays)
  455. // -----------------------------------------------------------------------
  456. $.json_stringify = function(object, level) {
  457. var result = '', i;
  458. level = level === undefined ? 1 : level;
  459. var type = typeof object;
  460. switch (type) {
  461. case 'function':
  462. result += object;
  463. break;
  464. case 'boolean':
  465. result += object ? 'true' : 'false';
  466. break;
  467. case 'object':
  468. if (object === null) {
  469. result += 'null';
  470. } else if (object instanceof Array) {
  471. result += '[';
  472. var len = object.length;
  473. for (i = 0; i < len - 1; ++i) {
  474. result += $.json_stringify(object[i], level + 1);
  475. }
  476. result += $.json_stringify(object[len - 1], level + 1) + ']';
  477. } else {
  478. result += '{';
  479. for (var property in object) {
  480. if (object.hasOwnProperty(property)) {
  481. result += '"' + property + '":' +
  482. $.json_stringify(object[property], level + 1);
  483. }
  484. }
  485. result += '}';
  486. }
  487. break;
  488. case 'string':
  489. var str = object;
  490. var repl = {
  491. '\\\\': '\\\\',
  492. '"': '\\"',
  493. '/': '\\/',
  494. '\\n': '\\n',
  495. '\\r': '\\r',
  496. '\\t': '\\t'};
  497. for (i in repl) {
  498. if (repl.hasOwnProperty(i)) {
  499. str = str.replace(new RegExp(i, 'g'), repl[i]);
  500. }
  501. }
  502. result += '"' + str + '"';
  503. break;
  504. case 'number':
  505. result += String(object);
  506. break;
  507. }
  508. result += (level > 1 ? ',' : '');
  509. // quick hacks below
  510. if (level === 1) {
  511. // fix last comma
  512. result = result.replace(/,([\]}])/g, '$1');
  513. }
  514. // fix comma before array or object
  515. return result.replace(/([\[{]),/g, '$1');
  516. };
  517. // -----------------------------------------------------------------------
  518. // :: HISTORY CLASS
  519. // -----------------------------------------------------------------------
  520. function History(name, size) {
  521. var enabled = true;
  522. if (typeof name === 'string' && name !== '') {
  523. name += '_';
  524. }
  525. var data = $.Storage.get(name + 'commands');
  526. data = data ? new Function('return ' + data + ';')() : [];
  527. var pos = data.length-1;
  528. $.extend(this, {
  529. append: function(item) {
  530. if (enabled) {
  531. if (data[data.length-1] !== item) {
  532. data.push(item);
  533. pos = data.length-1;
  534. if (size && data.length > size) {
  535. data = data.slice(-size);
  536. }
  537. $.Storage.set(name + 'commands', $.json_stringify(data));
  538. }
  539. }
  540. },
  541. data: function() {
  542. return data;
  543. },
  544. next: function() {
  545. if (pos < data.length-1) {
  546. ++pos;
  547. }
  548. if (pos !== -1) {
  549. return data[pos];
  550. }
  551. },
  552. reset: function() {
  553. pos = data.length-1;
  554. },
  555. last: function() {
  556. return data[length-1];
  557. },
  558. end: function() {
  559. return pos === data.length-1;
  560. },
  561. position: function() {
  562. return pos;
  563. },
  564. previous: function() {
  565. var old = pos;
  566. if (pos > 0) {
  567. --pos;
  568. }
  569. if (old !== -1) {
  570. return data[old];
  571. }
  572. },
  573. clear: function() {
  574. data = [];
  575. this.purge();
  576. },
  577. enabled: function() {
  578. return enabled;
  579. },
  580. enable: function() {
  581. enabled = true;
  582. },
  583. purge: function() {
  584. $.Storage.remove(name + 'commands');
  585. },
  586. disable: function() {
  587. enabled = false;
  588. }
  589. });
  590. }
  591. // -----------------------------------------------------------------------
  592. // :: COMMAND LINE PLUGIN
  593. // -----------------------------------------------------------------------
  594. $.fn.cmd = function(options) {
  595. var self = this;
  596. var maybe_data = self.data('cmd');
  597. if (maybe_data) {
  598. return maybe_data;
  599. }
  600. self.addClass('cmd');
  601. self.append('<span class="prompt"></span><span></span>' +
  602. '<span class="cursor">&nbsp;</span><span></span>');
  603. var clip = $('<textarea/>').addClass('clipboard').appendTo(self);
  604. if (options.width) {
  605. self.width(options.width);
  606. }
  607. var num_chars; // calculates by draw_prompt
  608. var prompt_len;
  609. var reverse_search = false;
  610. var reverse_search_string = '';
  611. var reverse_search_position = null;
  612. var backup_prompt;
  613. var mask = options.mask || false;
  614. var command = '';
  615. var position = 0;
  616. var prompt;
  617. var enabled = options.enabled;
  618. var historySize = options.historySize || 60;
  619. var name, history;
  620. var cursor = self.find('.cursor');
  621. // -----------------------------------------------------------------------
  622. // ::Blinking cursor function
  623. // -----------------------------------------------------------------------
  624. function blink(i) {
  625. cursor.toggleClass('inverted');
  626. }
  627. // -----------------------------------------------------------------------
  628. // :: Set prompt for reverse search
  629. // -----------------------------------------------------------------------
  630. function draw_reverse_prompt() {
  631. prompt = "(reverse-i-search)`" + reverse_search_string + "': ";
  632. draw_prompt();
  633. }
  634. // -----------------------------------------------------------------------
  635. // :: Disable reverse search
  636. // -----------------------------------------------------------------------
  637. function clear_reverse_state() {
  638. prompt = backup_prompt;
  639. reverse_search = false;
  640. reverse_search_position = null;
  641. reverse_search_string = '';
  642. }
  643. // -----------------------------------------------------------------------
  644. // :: Search through command line history. If next is not defined or false
  645. // :: it search for first item from the end. If true it search for next item
  646. // -----------------------------------------------------------------------
  647. function reverse_history_search(next) {
  648. var history_data = history.data();
  649. var regex;
  650. var len = history_data.length;
  651. if (next && reverse_search_position > 0) {
  652. len -= reverse_search_position;
  653. }
  654. if (reverse_search_string.length > 0) {
  655. for (var j=reverse_search_string.length; j>0; j--) {
  656. regex = new RegExp('^' + reverse_search_string.substring(0, j));
  657. for (var i=len; i--;) {
  658. if (regex.test(history_data[i])) {
  659. reverse_search_position = history_data.length - i;
  660. position = 0;
  661. self.set(history_data[i], true);
  662. redraw();
  663. if (reverse_search_string.length !== j) {
  664. reverse_search_string = reverse_search_string.substring(0, j);
  665. draw_reverse_prompt();
  666. }
  667. return;
  668. }
  669. }
  670. }
  671. }
  672. }
  673. // -----------------------------------------------------------------------
  674. // :: Recalculate number of characters in command line
  675. // -----------------------------------------------------------------------
  676. function change_num_chars() {
  677. var W = self.width();
  678. var w = cursor.innerWidth();
  679. num_chars = Math.floor(W / w);
  680. }
  681. // -----------------------------------------------------------------------
  682. // :: Return string repeated n times
  683. // -----------------------------------------------------------------------
  684. function str_repeat(str, n) {
  685. var result = '';
  686. for (var i = n; i--;) {
  687. result += str;
  688. }
  689. return result;
  690. }
  691. // -----------------------------------------------------------------------
  692. // :: Split String that fit into command line where first line need to
  693. // :: fit next to prompt (need to have less characters)
  694. // -----------------------------------------------------------------------
  695. function get_splited_command_line(string) {
  696. var first = string.substring(0, num_chars - prompt_len);
  697. var rest = string.substring(num_chars - prompt_len);
  698. return [first].concat(str_parts(rest, num_chars));
  699. }
  700. // -----------------------------------------------------------------------
  701. // :: Function that display command line. Split long line and place cursor
  702. // :: in right place
  703. // -----------------------------------------------------------------------
  704. var redraw = (function(self) {
  705. var before = cursor.prev();
  706. var after = cursor.next();
  707. // -----------------------------------------------------------------------
  708. // :: Draw line with the cursor
  709. // -----------------------------------------------------------------------
  710. function draw_cursor_line(string, position) {
  711. var len = string.length;
  712. if (position === len) {
  713. before.html($.terminal.encode(string, true));
  714. cursor.html('&nbsp;');
  715. after.html('');
  716. } else if (position === 0) {
  717. before.html('');
  718. //fix for tilda in IE
  719. cursor.html($.terminal.encode(string.slice(0, 1), true));
  720. //cursor.html($.terminal.encode(string[0]));
  721. after.html($.terminal.encode(string.slice(1), true));
  722. } else {
  723. var before_str = $.terminal.encode(string.slice(0, position), true);
  724. before.html(before_str);
  725. //fix for tilda in IE
  726. var c = string.slice(position, position + 1);
  727. //cursor.html(string[position]));
  728. cursor.html(c === ' ' ? '&nbsp;' : $.terminal.encode(c, true));
  729. if (position === string.length - 1) {
  730. after.html('');
  731. } else {
  732. after.html($.terminal.encode(string.slice(position + 1), true));
  733. }
  734. }
  735. }
  736. function div(string) {
  737. return '<div>' + $.terminal.encode(string, true) + '</div>';
  738. }
  739. // -----------------------------------------------------------------------
  740. // :: Display lines afer cursor
  741. // -----------------------------------------------------------------------
  742. function lines_after(lines) {
  743. var last_ins = after;
  744. $.each(lines, function(i, line) {
  745. last_ins = $(div(line)).insertAfter(last_ins).
  746. addClass('clear');
  747. });
  748. }
  749. // -----------------------------------------------------------------------
  750. // :: Display lines before the cursor
  751. // -----------------------------------------------------------------------
  752. function lines_before(lines) {
  753. $.each(lines, function(i, line) {
  754. before.before(div(line));
  755. });
  756. }
  757. var count = 0;
  758. // -----------------------------------------------------------------------
  759. // :: Redraw function
  760. // -----------------------------------------------------------------------
  761. return function() {
  762. var string = mask ? command.replace(/./g, '*') : command;
  763. var i, first_len;
  764. self.find('div').remove();
  765. before.html('');
  766. // long line
  767. if (string.length > num_chars - prompt_len - 1 ||
  768. string.match(/\n/)) {
  769. var array;
  770. var tabs = string.match(/\t/g);
  771. var tabs_rm = tabs ? tabs.length * 3 : 0;
  772. //quick tabulation hack
  773. if (tabs) {
  774. string = string.replace(/\t/g, '\x00\x00\x00\x00');
  775. }
  776. // command contain new line characters
  777. if (string.match(/\n/)) {
  778. var tmp = string.split("\n");
  779. first_len = num_chars - prompt_len - 1;
  780. // empty character after each line
  781. for (i=0; i<tmp.length-1; ++i) {
  782. tmp[i] += ' ';
  783. }
  784. // split first line
  785. if (tmp[0].length > first_len) {
  786. array = [tmp[0].substring(0, first_len)];
  787. array = array.concat(str_parts(tmp[0].substring(first_len), num_chars));
  788. } else {
  789. array = [tmp[0]];
  790. }
  791. // process rest of the lines
  792. for (i=1; i<tmp.length; ++i) {
  793. if (tmp[i].length > num_chars) {
  794. array = array.concat(str_parts(tmp[i], num_chars));
  795. } else {
  796. array.push(tmp[i]);
  797. }
  798. }
  799. } else {
  800. array = get_splited_command_line(string);
  801. }
  802. if (tabs) {
  803. array = $.map(array, function(line) {
  804. return line.replace(/\x00\x00\x00\x00/g, '\t');
  805. });
  806. }
  807. first_len = array[0].length;
  808. //cursor in first line
  809. if (first_len === 0 && array.length === 1) {
  810. // skip empty line
  811. } else if (position < first_len) {
  812. draw_cursor_line(array[0], position);
  813. lines_after(array.slice(1));
  814. } else if (position === first_len) {
  815. before.before(div(array[0]));
  816. draw_cursor_line(array[1], 0);
  817. lines_after(array.slice(2));
  818. } else {
  819. var num_lines = array.length;
  820. var offset = 0;
  821. if (position < first_len) {
  822. draw_cursor_line(array[0], position);
  823. lines_after(array.slice(1));
  824. } else if (position === first_len) {
  825. before.before(div(array[0]));
  826. draw_cursor_line(array[1], 0);
  827. lines_after(array.slice(2));
  828. } else {
  829. var last = array.slice(-1)[0];
  830. var from_last = string.length - position;
  831. var last_len = last.length;
  832. var pos = 0;
  833. if (from_last <= last_len) {
  834. lines_before(array.slice(0, -1));
  835. pos = last_len === from_last ? 0 : last_len-from_last;
  836. draw_cursor_line(last, pos+tabs_rm);
  837. } else {
  838. // in the middle
  839. if (num_lines === 3) {
  840. before.before('<div>' + $.terminal.encode(array[0], true) +
  841. '</div>');
  842. draw_cursor_line(array[1], position-first_len-1);
  843. after.after('<div class="clear">' +
  844. $.terminal.encode(array[2], true) +
  845. '</div>');
  846. } else {
  847. // more lines, cursor in the middle
  848. var line_index;
  849. var current;
  850. pos = position;
  851. for (i=0; i<array.length; ++i) {
  852. var current_len = array[i].length;
  853. if (pos > current_len) {
  854. pos -= current_len;
  855. } else {
  856. break;
  857. }
  858. }
  859. current = array[i];
  860. line_index = i;
  861. // cursor on first character in line
  862. if (pos === current.length) {
  863. pos = 0;
  864. current = array[++line_index];
  865. }
  866. draw_cursor_line(current, pos);
  867. lines_before(array.slice(0, line_index));
  868. lines_after(array.slice(line_index+1));
  869. }
  870. }
  871. }
  872. }
  873. } else {
  874. if (string === '') {
  875. before.html('');
  876. cursor.html('&nbsp;');
  877. after.html('');
  878. } else {
  879. draw_cursor_line(string, position);
  880. }
  881. }
  882. };
  883. })(self);
  884. var last_command;
  885. // -----------------------------------------------------------------------
  886. // :: Draw prompt that can be a function or a string
  887. // -----------------------------------------------------------------------
  888. var draw_prompt = (function() {
  889. var prompt_node = self.find('.prompt');
  890. function set(prompt) {
  891. prompt_len = skipFormattingCount(prompt);
  892. prompt_node.html($.terminal.format($.terminal.encode(prompt)));
  893. }
  894. return function() {
  895. switch (typeof prompt) {
  896. case 'string':
  897. set(prompt);
  898. break;
  899. case 'function':
  900. prompt(set);
  901. break;
  902. }
  903. };
  904. })();
  905. // -----------------------------------------------------------------------
  906. // :: Paste content to terminal using hidden textarea
  907. // -----------------------------------------------------------------------
  908. function paste() {
  909. clip.focus();
  910. //wait until Browser insert text to textarea
  911. self.oneTime(1, function() {
  912. self.insert(clip.val());
  913. clip.blur().val('');
  914. });
  915. }
  916. var first_up_history = true;
  917. //var prevent_keypress = false;
  918. // -----------------------------------------------------------------------
  919. // :: Keydown Event Handler
  920. // -----------------------------------------------------------------------
  921. function keydown_event(e) {
  922. var result, pos, len;
  923. if (typeof options.keydown == 'function') {
  924. result = options.keydown(e);
  925. if (result !== undefined) {
  926. //prevent_keypress = true;
  927. return result;
  928. }
  929. }
  930. if (enabled) {
  931. if (e.which !== 38 &&
  932. !(e.which === 80 && e.ctrlKey)) {
  933. first_up_history = true;
  934. }
  935. // arrows / Home / End / ENTER
  936. if (reverse_search && (e.which === 35 || e.which === 36 ||
  937. e.which === 37 || e.which === 38 ||
  938. e.which === 39 || e.which === 40 ||
  939. e.which === 13 || e.which === 27)) {
  940. clear_reverse_state();
  941. draw_prompt();
  942. if (e.which === 27) { // ESC
  943. command = '';
  944. }
  945. redraw();
  946. // finish reverse search and execute normal event handler
  947. keydown_event.call(this, e);
  948. } else if (e.altKey) {
  949. // Chrome on Windows set ctrlKey and altKey for alt
  950. // need to check for alt first
  951. //if (e.which === 18) { // press ALT
  952. if (e.which === 68) { //ALT+D
  953. self.set(command.slice(0, position) +
  954. command.slice(position).replace(/[^ ]+ |[^ ]+$/, ''),
  955. true);
  956. // chrome jump to address bar
  957. return false;
  958. }
  959. return true;
  960. } else if (e.keyCode === 13) { //enter
  961. if ((history && command) &&
  962. ((options.historyFilter &&
  963. options.historyFilter(command)) ||
  964. !options.historyFilter)) {
  965. history.append(command);
  966. }
  967. var tmp = command;
  968. history.reset();
  969. self.set('');
  970. if (options.commands) {
  971. options.commands(tmp);
  972. }
  973. if (typeof prompt === 'function') {
  974. draw_prompt();
  975. }
  976. } else if (e.which === 8) { //backspace
  977. if (reverse_search) {
  978. reverse_search_string = reverse_search_string.slice(0, -1);
  979. draw_reverse_prompt();
  980. } else {
  981. if (command !== '' && position > 0) {
  982. command = command.slice(0, position - 1) +
  983. command.slice(position, command.length);
  984. --position;
  985. redraw();
  986. }
  987. }
  988. } else if (e.which === 9 && !(e.ctrlKey || e.altKey)) { // TAB
  989. self.insert('\t');
  990. } else if (e.which === 46) {
  991. //DELETE
  992. if (command !== '' && position < command.length) {
  993. command = command.slice(0, position) +
  994. command.slice(position + 1, command.length);
  995. redraw();
  996. }
  997. return true;
  998. } else if (history && e.which === 38 ||
  999. (e.which === 80 && e.ctrlKey)) {
  1000. //UP ARROW or CTRL+P
  1001. if (first_up_history) {
  1002. last_command = command;
  1003. }
  1004. first_up_history = false;
  1005. self.set(history.previous());
  1006. } else if (history && e.which === 40 ||
  1007. (e.which === 78 && e.ctrlKey)) {
  1008. //DOWN ARROW or CTRL+N
  1009. self.set(history.end() ? last_command : history.next());
  1010. } else if (e.which === 37 ||
  1011. (e.which === 66 && e.ctrlKey)) {
  1012. //CTRL+LEFT ARROW or CTRL+B
  1013. if (e.ctrlKey && e.which !== 66) {
  1014. len = position - 1;
  1015. pos = 0;
  1016. if (command[len] === ' ') {
  1017. --len;
  1018. }
  1019. for (var i = len; i > 0; --i) {
  1020. if (command[i] === ' ' && command[i+1] !== ' ') {
  1021. pos = i + 1;
  1022. break;
  1023. } else if (command[i] === '\n' && command[i+1] !== '\n') {
  1024. pos = i;
  1025. break;
  1026. }
  1027. }
  1028. self.position(pos);
  1029. } else {
  1030. //LEFT ARROW or CTRL+B
  1031. if (position > 0) {
  1032. --position;
  1033. redraw();
  1034. }
  1035. }
  1036. } else if (e.which === 82 && e.ctrlKey) { // CTRL+R
  1037. if (reverse_search) {
  1038. reverse_history_search(true);
  1039. } else {
  1040. backup_prompt = prompt;
  1041. draw_reverse_prompt();
  1042. last_command = command;
  1043. command = '';
  1044. redraw();
  1045. reverse_search = true;
  1046. }
  1047. } else if (e.which == 71 && e.ctrlKey) { // CTRL+G
  1048. if (reverse_search) {
  1049. prompt = backup_prompt;
  1050. draw_prompt();
  1051. command = last_command;
  1052. redraw();
  1053. reverse_search = false;
  1054. }
  1055. } else if (e.which === 39 ||
  1056. (e.which === 70 && e.ctrlKey)) {
  1057. //RIGHT ARROW OR CTRL+F
  1058. if (e.ctrlKey && e.which !== 70) {
  1059. // jump to beginig or end of the word
  1060. if (command[position] === ' ') {
  1061. ++position;
  1062. }
  1063. var match = command.slice(position).match(/\S[\n\s]{2,}|[\n\s]+\S?/);
  1064. if (!match || match[0].match(/^\s+$/)) {
  1065. position = command.length;
  1066. } else {
  1067. if (match[0][0] !== ' ') {
  1068. position += match.index + 1;
  1069. } else {
  1070. position += match.index + match[0].length - 1;
  1071. if (match[0][match[0].length-1] !== ' ') {
  1072. --position;
  1073. }
  1074. }
  1075. }
  1076. redraw();
  1077. } else {
  1078. if (position < command.length) {
  1079. ++position;
  1080. redraw();
  1081. }
  1082. }
  1083. } else if (e.which === 123) { //F12 - Allow Firebug
  1084. return true;
  1085. } else if (e.which === 36) { //HOME
  1086. self.position(0);
  1087. } else if (e.which === 35) {
  1088. //END
  1089. self.position(command.length);
  1090. } else if (e.shiftKey && e.which == 45) { // Shift+Insert
  1091. paste();
  1092. return true;
  1093. } else if (e.ctrlKey || e.metaKey) {
  1094. if (e.which === 192) { // CMD+` switch browser window on Mac
  1095. return true;
  1096. }
  1097. if (e.metaKey) {
  1098. if(e.which === 82) { // CMD+r page reload in Chrome Mac
  1099. return true;
  1100. } else if(e.which === 76) {
  1101. return true; // CMD+l jump into Ominbox on Chrome Mac
  1102. }
  1103. }
  1104. if (e.shiftKey) { // CTRL+SHIFT+??
  1105. if (e.which === 84) {
  1106. //CTRL+SHIFT+T open closed tab
  1107. return true;
  1108. }
  1109. //} else if (e.altKey) { //ALT+CTRL+??
  1110. } else {
  1111. if (e.which === 87) { // CTRL+W
  1112. if (command !== '') {
  1113. var first = command.slice(0, position);
  1114. var last = command.slice(position+1);
  1115. var m = first.match(/([^ ]+ *$)/);
  1116. position = first.length-m[0].length;
  1117. command = first.slice(0, position) + last;
  1118. redraw();
  1119. }
  1120. return false;
  1121. } else if (e.which === 72) { // CTRL+H
  1122. if (command !== '' && position > 0) {
  1123. command = command.slice(0, --position);
  1124. if (position < command.length-1) {
  1125. command += command.slice(position);
  1126. }
  1127. redraw();
  1128. }
  1129. return false;
  1130. //NOTE: in opera charCode is undefined
  1131. } else if (e.which === 65) {
  1132. //CTRL+A
  1133. self.position(0);
  1134. } else if (e.which === 69) {
  1135. //CTRL+E
  1136. self.position(command.length);
  1137. } else if (e.which === 88 || e.which === 67 || e.which === 84) {
  1138. //CTRL+X CTRL+C CTRL+W CTRL+T
  1139. return true;
  1140. } else if (e.which === 86) {
  1141. //CTRL+V
  1142. paste();
  1143. return true;
  1144. } else if (e.which === 75) {
  1145. //CTRL+K
  1146. if (position === 0) {
  1147. self.set('');
  1148. } else if (position !== command.length) {
  1149. self.set(command.slice(0, position));
  1150. }
  1151. } else if (e.which === 85) { // CTRL+U
  1152. self.set(command.slice(position, command.length));
  1153. self.position(0);
  1154. } else if (e.which === 17) { //CTRL+TAB switch tab
  1155. return false;
  1156. }
  1157. }
  1158. } else {
  1159. return true;
  1160. }
  1161. return false;
  1162. } /*else {
  1163. if ((e.altKey && e.which === 68) ||
  1164. (e.ctrlKey &&
  1165. $.inArray(e.which, [65, 66, 68, 69, 80, 78, 70]) > -1) ||
  1166. // 68 === D
  1167. [35, 36, 37, 38, 39, 40].has(e.which)) {
  1168. return false;
  1169. }
  1170. } */
  1171. }
  1172. var history_list = [];
  1173. // -----------------------------------------------------------------------
  1174. // :: Command Line Methods
  1175. // -----------------------------------------------------------------------
  1176. $.extend(self, {
  1177. name: function(string) {
  1178. if (string !== undefined) {
  1179. name = string;
  1180. history = new History(str