blob: aa6f7d32a1c9043b62fc5fb031d0e814a761d20a [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
lib.rtdep('lib.Storage');
/**
* @fileoverview Declares the hterm.* namespace and some basic shared utilities
* that are too small to deserve dedicated files.
*/
var hterm = {};
/**
* The type of window hosting hterm.
*
* This is set as part of hterm.init(). The value is invalid until
* initialization completes.
*/
hterm.windowType = null;
/**
* Warning message to display in the terminal when browser zoom is enabled.
*
* You can replace it with your own localized message.
*/
hterm.zoomWarningMessage = 'ZOOM != 100%';
/**
* Brief overlay message displayed when text is copied to the clipboard.
*
* By default it is the unicode BLACK SCISSORS character, but you can
* replace it with your own localized message.
*
* This is only displayed when the 'enable-clipboard-notice' preference
* is enabled.
*/
hterm.notifyCopyMessage = '\u2702';
/**
* Text shown in a desktop notification for the terminal
* bell. \u226a is a unicode EIGHTH NOTE, %(title) will
* be replaced by the terminal title.
*/
hterm.desktopNotificationTitle = '\u266A %(title) \u266A';
/**
* List of known hterm test suites.
*
* A test harness should ensure that they all exist before running.
*/
hterm.testDeps = ['hterm.ScrollPort.Tests', 'hterm.Screen.Tests',
'hterm.Terminal.Tests', 'hterm.VT.Tests',
'hterm.VT.CannedTests'];
/**
* The hterm init function, registered with lib.registerInit().
*
* This is called during lib.init().
*
* @param {function} onInit The function lib.init() wants us to invoke when
* initialization is complete.
*/
lib.registerInit('hterm', function(onInit) {
function onWindow(window) {
hterm.windowType = window.type;
setTimeout(onInit, 0);
}
function onTab(tab) {
if (tab && window.chrome) {
chrome.windows.get(tab.windowId, null, onWindow);
} else {
// TODO(rginda): This is where we end up for a v1 app's background page.
// Maybe windowType = 'none' would be more appropriate, or something.
hterm.windowType = 'normal';
setTimeout(onInit, 0);
}
}
if (!hterm.defaultStorage) {
if (window.chrome && chrome.storage && chrome.storage.sync) {
hterm.defaultStorage = new lib.Storage.Chrome(chrome.storage.sync);
} else {
hterm.defaultStorage = new lib.Storage.Local();
}
}
// The chrome.tabs API is not supported in packaged apps, and detecting if
// you're a packaged app is a little awkward.
var isPackagedApp = false;
if (window.chrome && chrome.runtime && chrome.runtime.getManifest) {
var manifest = chrome.runtime.getManifest();
isPackagedApp = manifest.app && manifest.app.background;
}
if (isPackagedApp) {
// Packaged apps are never displayed in browser tabs.
setTimeout(onWindow.bind(null, {type: 'popup'}), 0);
} else {
if (window.chrome && chrome.tabs) {
// The getCurrent method gets the tab that is "currently running", not the
// topmost or focused tab.
chrome.tabs.getCurrent(onTab);
} else {
setTimeout(onWindow.bind(null, {type: 'normal'}), 0);
}
}
});
/**
* Return decimal { width, height } for a given dom node.
*/
hterm.getClientSize = function(dom) {
return dom.getBoundingClientRect();
};
/**
* Return decimal width for a given dom node.
*/
hterm.getClientWidth = function(dom) {
return dom.getBoundingClientRect().width;
};
/**
* Return decimal height for a given dom node.
*/
hterm.getClientHeight = function(dom) {
return dom.getBoundingClientRect().height;
};
/**
* Copy the current selection to the system clipboard.
*
* @param {HTMLDocument} The document with the selection to copy.
*/
hterm.copySelectionToClipboard = function(document) {
try {
document.execCommand('copy');
} catch (firefoxException) {
// Ignore this. FF throws an exception if there was an error, even though
// the spec says just return false.
}
};
/**
* Paste the system clipboard into the element with focus.
*
* Note: In Chrome/Firefox app/extension environments, you'll need the
* "clipboardRead" permission. In other environments, this might always
* fail as the browser frequently blocks access for security reasons.
*
* @param {HTMLDocument} The document to paste into.
* @return {boolean} True if the paste succeeded.
*/
hterm.pasteFromClipboard = function(document) {
try {
return document.execCommand('paste');
} catch (firefoxException) {
// Ignore this. FF 40 and older would incorrectly throw an exception if
// there was an error instead of returning false.
return false;
}
};
/**
* Create a new notification.
*
* @param {Object} params Various parameters for the notification.
* @param {string} params.title The title (defaults to the window's title).
* @param {string} params.body The message body (main text).
*/
hterm.notify = function(params) {
var def = (curr, fallback) => curr !== undefined ? curr : fallback;
if (params === undefined || params === null)
params = {};
// Merge the user's choices with the default settings. We don't take it
// directly in case it was stuffed with excess junk.
var options = {
'body': params.body,
'icon': def(params.icon, lib.resource.getDataUrl('hterm/images/icon-96')),
}
var title = def(params.title, window.document.title);
if (!title)
title = 'hterm';
title = lib.f.replaceVars(hterm.desktopNotificationTitle, {'title': title});
var n = new Notification(title, options);
n.onclick = function() {
window.focus();
this.close();
};
return n;
};
/**
* Launches url in a new tab.
*
* @param {string} url URL to launch in a new tab.
*/
hterm.openUrl = function(url) {
if (window.chrome && chrome.browser && chrome.browser.openTab) {
// For Chrome v2 apps, we need to use this API to properly open windows.
chrome.browser.openTab({'url': url});
} else {
const win = window.open(url, '_blank');
win.focus();
}
}
/**
* Constructor for a hterm.Size record.
*
* Instances of this class have public read/write members for width and height.
*
* @param {integer} width The width of this record.
* @param {integer} height The height of this record.
*/
hterm.Size = function(width, height) {
this.width = width;
this.height = height;
};
/**
* Adjust the width and height of this record.
*
* @param {integer} width The new width of this record.
* @param {integer} height The new height of this record.
*/
hterm.Size.prototype.resize = function(width, height) {
this.width = width;
this.height = height;
};
/**
* Return a copy of this record.
*
* @return {hterm.Size} A new hterm.Size instance with the same width and
* height.
*/
hterm.Size.prototype.clone = function() {
return new hterm.Size(this.width, this.height);
};
/**
* Set the height and width of this instance based on another hterm.Size.
*
* @param {hterm.Size} that The object to copy from.
*/
hterm.Size.prototype.setTo = function(that) {
this.width = that.width;
this.height = that.height;
};
/**
* Test if another hterm.Size instance is equal to this one.
*
* @param {hterm.Size} that The other hterm.Size instance.
* @return {boolean} True if both instances have the same width/height, false
* otherwise.
*/
hterm.Size.prototype.equals = function(that) {
return this.width == that.width && this.height == that.height;
};
/**
* Return a string representation of this instance.
*
* @return {string} A string that identifies the width and height of this
* instance.
*/
hterm.Size.prototype.toString = function() {
return '[hterm.Size: ' + this.width + ', ' + this.height + ']';
};
/**
* Constructor for a hterm.RowCol record.
*
* Instances of this class have public read/write members for row and column.
*
* This class includes an 'overflow' bit which is use to indicate that an
* attempt has been made to move the cursor column passed the end of the
* screen. When this happens we leave the cursor column set to the last column
* of the screen but set the overflow bit. In this state cursor movement
* happens normally, but any attempt to print new characters causes a cr/lf
* first.
*
* @param {integer} row The row of this record.
* @param {integer} column The column of this record.
* @param {boolean} opt_overflow Optional boolean indicating that the RowCol
* has overflowed.
*/
hterm.RowCol = function(row, column, opt_overflow) {
this.row = row;
this.column = column;
this.overflow = !!opt_overflow;
};
/**
* Adjust the row and column of this record.
*
* @param {integer} row The new row of this record.
* @param {integer} column The new column of this record.
* @param {boolean} opt_overflow Optional boolean indicating that the RowCol
* has overflowed.
*/
hterm.RowCol.prototype.move = function(row, column, opt_overflow) {
this.row = row;
this.column = column;
this.overflow = !!opt_overflow;
};
/**
* Return a copy of this record.
*
* @return {hterm.RowCol} A new hterm.RowCol instance with the same row and
* column.
*/
hterm.RowCol.prototype.clone = function() {
return new hterm.RowCol(this.row, this.column, this.overflow);
};
/**
* Set the row and column of this instance based on another hterm.RowCol.
*
* @param {hterm.RowCol} that The object to copy from.
*/
hterm.RowCol.prototype.setTo = function(that) {
this.row = that.row;
this.column = that.column;
this.overflow = that.overflow;
};
/**
* Test if another hterm.RowCol instance is equal to this one.
*
* @param {hterm.RowCol} that The other hterm.RowCol instance.
* @return {boolean} True if both instances have the same row/column, false
* otherwise.
*/
hterm.RowCol.prototype.equals = function(that) {
return (this.row == that.row && this.column == that.column &&
this.overflow == that.overflow);
};
/**
* Return a string representation of this instance.
*
* @return {string} A string that identifies the row and column of this
* instance.
*/
hterm.RowCol.prototype.toString = function() {
return ('[hterm.RowCol: ' + this.row + ', ' + this.column + ', ' +
this.overflow + ']');
};