<link rel="stylesheet" href="/build/xterm.css" />
<link rel="stylesheet" href="/build/addons/fullscreen/fullscreen.css" />
<link rel="stylesheet" href="style.css" />
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.auto.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js"></script>
<script src="/build/xterm.js" ></script>
<script src="/build/addons/attach/attach.js" ></script>
<script src="/build/addons/fit/fit.js" ></script>
<script src="/build/addons/fullscreen/fullscreen.js" ></script>
+ <script src="/build/addons/search/search.js" ></script>
</head>
<body>
<h1>xterm.js: xterm, in the browser</h1>
let buildDir = process.env.BUILD_DIR || 'build';
let tsProject = ts.createProject('tsconfig.json');
+let tsProjectSearchAddon = ts.createProject('./src/addons/search/tsconfig.json');
let srcDir = tsProject.config.compilerOptions.rootDir;
let outDir = tsProject.config.compilerOptions.outDir;
let tsResult = tsProject.src().pipe(sourcemaps.init()).pipe(tsProject());
let tsc = tsResult.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest(outDir));
+ fs.emptyDirSync(`${outDir}/addons`);
+ fs.emptyDirSync(`${outDir}/addons/search`);
+ let tsResultSearchAddon = tsProjectSearchAddon.src().pipe(sourcemaps.init()).pipe(tsProjectSearchAddon());
+ let tscSearchAddon = tsResultSearchAddon.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest(`${outDir}/addons/search`));
+
// Copy all addons from ${srcDir}/ to ${outDir}/
- let copyAddons = gulp.src(`${srcDir}/addons/**/*`).pipe(gulp.dest(`${outDir}/addons`));
+ let copyAddons = gulp.src([`${srcDir}/addons/**/*`, `!${srcDir}/addons/search`, `!${srcDir}/addons/search/**`]).pipe(gulp.dest(`${outDir}/addons`));
// Copy stylesheets from ${srcDir}/ to ${outDir}/
let copyStylesheets = gulp.src(`${srcDir}/**/*.css`).pipe(gulp.dest(outDir));
- return merge(tsc, copyAddons, copyStylesheets);
+ return merge(tsc, tscSearchAddon, copyAddons, copyStylesheets);
});
/**
let browserifyOptions = {
basedir: buildDir,
debug: true,
- entries: [`../${outDir}/xterm.js`],
+ entries: [`${outDir}/xterm.js`],
standalone: 'Terminal',
cache: {},
packageCache: {}
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(buildDir));
+ let browserifyOptionsSearchAddon = {
+ basedir: buildDir,
+ debug: true,
+ entries: [`../${outDir}/addons/search/search.js`],
+ cache: {},
+ packageCache: {}
+ };
+ let bundleStreamSearchAddon = browserify(browserifyOptionsSearchAddon)
+ .bundle()
+ .pipe(source('./addons/search/search.js'))
+ .pipe(buffer())
+ .pipe(sourcemaps.init({loadMaps: true, sourceRoot: '..'}))
+ .pipe(sourcemaps.write('./'))
+ .pipe(gulp.dest(buildDir));
+
// Copy all add-ons from ${outDir}/ to buildDir
- let copyAddons = gulp.src(`${outDir}/addons/**/*`).pipe(gulp.dest(`${buildDir}/addons`));
+ let copyAddons = gulp.src([`${outDir}/addons/**/*`, `!${outDir}/addons/search`, `!${outDir}/addons/search/**`]).pipe(gulp.dest(`${buildDir}/addons`));
// Copy stylesheets from ${outDir}/ to ${buildDir}/
let copyStylesheets = gulp.src(`${outDir}/**/*.css`).pipe(gulp.dest(buildDir));
- return merge(bundleStream, copyAddons, copyStylesheets);
+ return merge(bundleStream, bundleStreamSearchAddon, copyAddons, copyStylesheets);
});
gulp.task('instrument-test', function () {
import { EventEmitter } from './EventEmitter';
import { ITerminal } from './Interfaces';
import { SelectionModel } from './SelectionModel';
+import { translateBufferLineToString } from './utils/BufferLine';
/**
* The number of pixels the mouse needs to be above or below the viewport in
* A string containing all characters that are considered word separated by the
* double click to select work logic.
*/
- const WORD_SEPARATORS = ' ()[]{}:\'"';
+ const WORD_SEPARATORS = ' ()[]{}\'"';
// TODO: Move these constants elsewhere, they belong in a buffer or buffer
// data/line class.
*/
public setBuffer(buffer: CircularList<any>): void {
this._buffer = buffer;
+ this.clearSelection();
}
+ public get selectionStart(): [number, number] { return this._model.finalSelectionStart; }
+ public get selectionEnd(): [number, number] { return this._model.finalSelectionEnd; }
+
/**
* Gets whether there is an active text selection.
*/
// Get first row
const startRowEndCol = start[1] === end[1] ? end[0] : null;
let result: string[] = [];
- result.push(this._translateBufferLineToString(this._buffer.get(start[1]), true, start[0], startRowEndCol));
+ result.push(translateBufferLineToString(this._buffer.get(start[1]), true, start[0], startRowEndCol));
// Get middle rows
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
const bufferLine = this._buffer.get(i);
- const lineText = this._translateBufferLineToString(bufferLine, true);
+ const lineText = translateBufferLineToString(bufferLine, true);
if (bufferLine.isWrapped) {
result[result.length - 1] += lineText;
} else {
// Get final row
if (start[1] !== end[1]) {
const bufferLine = this._buffer.get(end[1]);
- const lineText = this._translateBufferLineToString(bufferLine, true, 0, end[0]);
+ const lineText = translateBufferLineToString(bufferLine, true, 0, end[0]);
if (bufferLine.isWrapped) {
result[result.length - 1] += lineText;
} else {
// and joining the array into a multi-line string.
const formattedResult = result.map(line => {
return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' ');
- }).join('\n');
+ }).join(Browser.isMSWindows ? '\r\n' : '\n');
return formattedResult;
}
this.refresh();
}
- /**
- * Translates a buffer line to a string, with optional start and end columns.
- * Wide characters will count as two columns in the resulting string. This
- * function is useful for getting the actual text underneath the raw selection
- * position.
- * @param line The line being translated.
- * @param trimRight Whether to trim whitespace to the right.
- * @param startCol The column to start at.
- * @param endCol The column to end at.
- */
- private _translateBufferLineToString(line: any, trimRight: boolean, startCol: number = 0, endCol: number = null): string {
- // TODO: This function should live in a buffer or buffer line class
-
- // Get full line
- let lineString = '';
- let widthAdjustedStartCol = startCol;
- let widthAdjustedEndCol = endCol;
- for (let i = 0; i < line.length; i++) {
- const char = line[i];
- lineString += char[LINE_DATA_CHAR_INDEX];
- // Adjust start and end cols for wide characters if they affect their
- // column indexes
- if (char[LINE_DATA_WIDTH_INDEX] === 0) {
- if (startCol >= i) {
- widthAdjustedStartCol--;
- }
- if (endCol >= i) {
- widthAdjustedEndCol--;
- }
- }
- }
-
- // Calculate the final end col by trimming whitespace on the right of the
- // line if needed.
- let finalEndCol = widthAdjustedEndCol || line.length;
- if (trimRight) {
- const rightWhitespaceIndex = lineString.search(/\s+$/);
- if (rightWhitespaceIndex !== -1) {
- finalEndCol = Math.min(finalEndCol, rightWhitespaceIndex);
- }
- // Return the empty string if only trimmed whitespace is selected
- if (finalEndCol <= widthAdjustedStartCol) {
- return '';
- }
- }
-
- return lineString.substring(widthAdjustedStartCol, finalEndCol);
- }
-
/**
* Queues a refresh, redrawing the selection on the next opportunity.
* @param isNewSelection Whether the selection should be registered as a new
return charIndex;
}
+ public setSelection(col: number, row: number, length: number): void {
+ this._model.clearSelection();
+ this._removeMouseDownListeners();
+ this._model.selectionStart = [col, row];
+ this._model.selectionStartLength = length;
+ this.refresh();
+ }
+
/**
* Gets positional information for the word at the coordinated specified.
* @param coords The coordinates to get the word at.
*/
private _getWordAt(coords: [number, number]): IWordPosition {
const bufferLine = this._buffer.get(coords[1]);
- const line = this._translateBufferLineToString(bufferLine, false);
+ const line = translateBufferLineToString(bufferLine, false);
// Get actual index, taking into consideration wide characters
let endIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);
import * as Mouse from './utils/Mouse';
import { CHARSETS } from './Charsets';
import { getRawByteCoords } from './utils/Mouse';
+import { translateBufferLineToString } from './utils/BufferLine';
/**
* Terminal Emulation References:
}
switch (key) {
case 'scrollback':
+ if (value < this.rows) {
+ let msg = 'Setting the scrollback value less than the number of rows ';
+
+ msg += `(${this.rows}) is not allowed.`;
+
+ console.warn(msg);
+ return false;
+ }
+
if (this.options[key] !== value) {
if (this.lines.length > value) {
const amountToTrim = this.lines.length - value;
this.element.classList.add('xterm-theme-' + this.theme);
this.setCursorBlinking(this.options.cursorBlink);
- this.element.style.height;
this.element.setAttribute('tabindex', 0);
this.viewportElement = document.createElement('div');
self.viewport.onWheel(ev);
return self.cancel(ev);
});
+
+ on(el, 'touchstart', function(ev) {
+ if (self.mouseEvents) return;
+ self.viewport.onTouchStart(ev);
+ return self.cancel(ev);
+ });
+
+ on(el, 'touchmove', function(ev) {
+ if (self.mouseEvents) return;
+ self.viewport.onTouchMove(ev);
+ return self.cancel(ev);
+ });
};
/**
this.linkifier.linkifyRow(i);
}
}
- }
+ };
/**
* Display the cursor element
*/
Terminal.prototype.scrollPages = function(pageCount) {
this.scrollDisp(pageCount * (this.rows - 1));
- }
+ };
/**
* Scrolls the display of the terminal to the top.
*/
Terminal.prototype.scrollToTop = function() {
this.scrollDisp(-this.ydisp);
- }
+ };
/**
* Scrolls the display of the terminal to the bottom.
*/
Terminal.prototype.scrollToBottom = function() {
this.scrollDisp(this.ybase - this.ydisp);
- }
+ };
/**
* Writes text to the terminal.
- * @param {string} text The text to write to the terminal.
+ * @param {string} data The text to write to the terminal.
*/
Terminal.prototype.write = function(data) {
this.writeBuffer.push(data);
self.innerWrite();
});
}
- }
+ };
Terminal.prototype.innerWrite = function() {
var writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE);
/**
* Writes text to the terminal, followed by a break line character (\n).
- * @param {string} text The text to write to the terminal.
+ * @param {string} data The text to write to the terminal.
*/
Terminal.prototype.writeln = function(data) {
this.write(data + '\r\n');
let message = 'attachCustomKeydownHandler() is DEPRECATED and will be removed soon. Please use attachCustomKeyEventHandler() instead.';
console.warn(message);
this.attachCustomKeyEventHandler(customKeydownHandler);
- }
+ };
/**
* Attaches a custom key event handler which is run before keys are processed, giving consumers of
* xterm.js ultimate control as to what keys should be processed by the terminal and what keys
* should not.
- * @param {function} customKeypressHandler The custom KeyboardEvent handler to attach. This is a
+ * @param {function} customKeyEventHandler The custom KeyboardEvent handler to attach. This is a
* function that takes a KeyboardEvent, allowing consumers to stop propogation and/or prevent
* the default action. The function returns whether the event should be processed by xterm.js.
*/
Terminal.prototype.attachCustomKeyEventHandler = function(customKeyEventHandler) {
this.customKeyEventHandler = customKeyEventHandler;
- }
+ };
/**
* Attaches a http(s) link handler, forcing web links to behave differently to
* regular <a> tags. This will trigger a refresh as links potentially need to be
* reconstructed. Calling this with null will remove the handler.
- * @param {LinkHandler} handler The handler callback function.
+ * @param {LinkMatcherHandler} handler The handler callback function.
*/
Terminal.prototype.setHypertextLinkHandler = function(handler) {
if (!this.linkifier) {
this.linkifier.setHypertextLinkHandler(handler);
// Refresh to force links to refresh
this.refresh(0, this.rows - 1);
- }
+ };
/**
* Attaches a validation callback for hypertext links. This is useful to use
* @param {LinkMatcherValidationCallback} callback The callback to use, this can
* be cleared with null.
*/
- Terminal.prototype.setHypertextValidationCallback = function(handler) {
+ Terminal.prototype.setHypertextValidationCallback = function(callback) {
if (!this.linkifier) {
throw new Error('Cannot attach a hypertext validation callback before Terminal.open is called');
}
- this.linkifier.setHypertextValidationCallback(handler);
+ this.linkifier.setHypertextValidationCallback(callback);
// Refresh to force links to refresh
this.refresh(0, this.rows - 1);
- }
+ };
/**
* Registers a link matcher, allowing custom link patterns to be matched and
* @param {RegExp} regex The regular expression to search for, specifically
* this searches the textContent of the rows. You will want to use \s to match
* a space ' ' character for example.
- * @param {LinkHandler} handler The callback when the link is called.
+ * @param {LinkMatcherHandler} handler The callback when the link is called.
* @param {LinkMatcherOptions} [options] Options for the link matcher.
* @return {number} The ID of the new matcher, this can be used to deregister.
*/
this.refresh(0, this.rows - 1);
return matcherId;
}
- }
+ };
/**
* Deregisters a link matcher if it has been registered.
this.refresh(0, this.rows - 1);
}
}
- }
+ };
/**
* Gets whether the terminal has an active selection.
*/
Terminal.prototype.hasSelection = function() {
return this.selectionManager.hasSelection;
- }
+ };
/**
* Gets the terminal's current selection, this is useful for implementing copy
*/
Terminal.prototype.getSelection = function() {
return this.selectionManager.selectionText;
- }
+ };
/**
* Clears the current terminal selection.
*/
Terminal.prototype.clearSelection = function() {
this.selectionManager.clearSelection();
- }
+ };
/**
* Selects all text within the terminal.
*/
Terminal.prototype.selectAll = function() {
this.selectionManager.selectAll();
- }
+ };
/**
* Handle a keydown event
return;
}
+ if (y > this.getOption('scrollback')) {
+ this.setOption('scrollback', y)
+ }
+
var line
, el
, i
// There is room above the buffer and there are no empty elements below the line,
// scroll up
this.ybase--;
- addToY++
+ addToY++;
if (this.ydisp > 0) {
// Viewport is at the top of the buffer, must increase downwards
this.ydisp--;
* Expose
*/
+Terminal.translateBufferLineToString = translateBufferLineToString;
Terminal.EventEmitter = EventEmitter;
Terminal.inherits = inherits;