Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

terminal.py: Major changes: Terminal() now supports turning on/off 'a…

…utowrap mode'. This means that lines that are longer than the width of the terminal will be wrapped by the browser instead of inside Terminal() *unless* autowrap mode is enabled. The biggest impact of this change will be if you copy a long line it will now show up as *one long line* instead of a bunch of lines (hard-wrapped based on the width of the terminal). Some intelligence was added to Terminal.carriage_return() to detect when the underlying program is expecting a hard-wrap even when autowrap is set to False. This should get the best of both worlds (as a default).

terminal.py:  Terminal.newline() now automatically sets the cursor back to the beginning of the line (CR).  No change in behavior here since Terminal.write() did this on its own previously.
terminal.py:  Renamed Terminal._carriage_return() to Terminal.carriage_return().  Not sure why I had an underscore there originally--there's nothing special or private about it.
gateone.js:  Changed the timeout for enabling scrollback back to 500.  3500ms is too long.
gateone.js:  Modified GateOne.Terminal.termUpdateFromWorker() to only call enableScrollback() if the incoming scrollback buffer is different from the existing scrollback buffer.  This should make terminals slightly more responsive as well.  The only downside to this change is this:  If (and only if) you have lines in the current screen that are longer than the screen's width it can result in the view being scrolled up slightly when resuming from an alternate screen buffer (e.g. you're done editing a file in vim).  This is a rare enough situation and is only mildly annoying so I think it will be OK until I figure out a workaround (low priority unless someone finds it to be a real problem).
gateone.js:  Fixed a bug where if you used middle click to paste highlighted text (note: not the same as native middle-click-to-paste on Unix) it would cause the next paste to also include the previously-pasted text (a double-paste).
gateone.js:  Tweaked the logic that hides the pastearea when the mouse is over it to act in a smoother and more natural fashion.
gateone.js:  Tweaked the logic that adjusts the size of terminals when the browser window is resized.  It will now adjust the size of *all* terminals; not just the one you're looking at.  Also, it will correctly deal with the situation of lines being added then later removed (multiple re-sizes could get a little wonky before this change).
gateone.js:  The terminal will no longer scroll to the bottom when pressing a modifier key (e.g. shift).
gateone.js:  GateOne.Visual.displayMessage() has been tweaked to include a close icon (X) that will make messages go away instantly when clicked.  This was necessary since there's always a possibility that a displayed message could be huge with a long timeout.  It would be really annoying to have to avoid moving your mouse while waiting for it to go away.
Text Colors:  Added some new syntax highlighting classes that could be useful for text transforms and tweaked things a bit all-around.
CSS Themes:  Added span.screen and span.scrollback to work in conjunction with the changes to gateone.js.
CSS Themes:  Added some extra classes for the new close X icon in #noticecontainer.
Logging Plugin:  In logging.js, added support for the additional console.log() options that exist in some browsers.  e.g. console.warn() and console.error().
Convenience Plugin:  Tweaked the IPv4 regex slightly so it won't match on things like 'SomePackage-3.4.5.10.rpm'.
Convenience Plugin:  Tweaked the syslog regex to make use of the multi-line flag ('m').  This should make it more reliable.
Convenience Plugin:  Tweaked the syslog replacement string to make lines clickable...  When you click on a line it will highlight it a bit with a background color.  It could be useful or it could be annoying.  Need actual feedback to make a determination either way.
  • Loading branch information...
commit 9ee072461d06a013cf1dc06e15873ab23c836332 1 parent 0127d7b
@liftoff authored
View
19 gateone/plugins/convenience/static/convenience.js
@@ -39,11 +39,11 @@ GateOne.Base.update(GateOne.Convenience, {
Registers a text transform that makes IPv4 addresses into spans that will execute `host <IP address>` when clicked.
*/
- var IPv4Pattern = /(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)(?!\.)/g,
+ var IPv4Pattern = /(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)(?![\.\w-_])/g,
IPv4ReplacementString = "<span class='clickable' onclick='GateOne.Convenience.IPInfo(this)'>$1</span>";
t.registerTextTransform("IPv4", IPv4Pattern, IPv4ReplacementString);
// Just a little regex to capture IPv6...
- var IPv6Pattern = /(\b((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\b)/g,
+ var IPv6Pattern = /(\b((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\b)(?![\:])/g,
IPv6ReplacementString = "<span class='clickable' onclick='GateOne.Convenience.IPInfo(this)'>$1</span>";
t.registerTextTransform("IPv6", IPv6Pattern, IPv6ReplacementString);
},
@@ -267,9 +267,20 @@ GateOne.Base.update(GateOne.Convenience, {
Registers a text transform that makes standard syslog output easier on the eyes.
*/
- var timeRegex = /(^|\n)((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+[0-9]+(st|th|nd)?)\s+([0-9][0-9]\:[0-9][0-9]\:[0-9][0-9])\s+(\w+)\s+(.+?\:)?/g,
- timeReplacementString = "$1<span class='date'>$2</span> <span class='time'>$5</span> <span class='hostname'>$6</span> <span class='service'>$7</span>";
+ var timeRegex = /^((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+[0-9]+(st|th|nd)?)\s+([0-9][0-9]\:[0-9][0-9]\:[0-9][0-9])\s+(\w+)\s+(.+?\:)?(.*?)$/mg,
+ timeReplacementString = "<span class='row' onclick='GateOne.Convenience.toggleBackground(this)'><span class='date' onclick='this.parentElement.onclick()'>$1</span> <span class='time' onclick='this.parentElement.onclick()'>$4</span> <span class='hostname' onclick='this.parentElement.onclick()'>$5</span> <span class='service' onclick='this.parentElement.onclick()'>$6</span><span class='message' onclick='this.parentElement.onclick()'>$7</span></span>";
t.registerTextTransform("syslogtime", timeRegex, timeReplacementString);
+ },
+ toggleBackground: function(elem) {
+ /**:GateOne.Convenience.groupInfoError(result)
+
+ Toggles a background color on and off for the given *elem* by adding or removing the 'selectedrow' class.
+ */
+ if (elem.className.indexOf('selectedrow') == -1) {
+ elem.className += ' selectedrow';
+ } else {
+ elem.className = elem.className.replace('selectedrow', '');
+ }
}
});
View
45 gateone/plugins/logging/static/logging.js
@@ -65,12 +65,43 @@ GateOne.Base.update(GateOne.Logging, {
l.level = level;
}
},
- /** @id MochiKit.Logging.Logger.prototype.logToConsole */
- logToConsole: function (msg) {
+ logToConsole: function (msg, /*opt*/level) {
+ /**:GateOne.Logging.logToConsole(msg, level)
+
+ Logs the given *msg* to the browser's JavaScript console. If *level* is provided it will attempt to use the appropriate console logger (e.g. console.warn()).
+
+ .. note:: Original version of this function is from: MochiKit.Logging.Logger.prototype.logToConsole.
+ */
if (typeof(window) != "undefined" && window.console && window.console.log) {
// Safari and FireBug 0.4
// Percent replacement is a workaround for cute Safari crashing bug
- window.console.log(msg.replace(/%/g, '\uFF05'));
+ msg = msg.replace(/%/g, '\uFF05');
+ if (!level) {
+ window.console.log(msg);
+ return;
+ } else if (level == 'ERROR' || level == 'FATAL') {
+ if (typeof(window.console.error) == "function") {
+ window.console.error(msg);
+ return;
+ }
+ } else if (level == 'WARN') {
+ if (typeof(window.console.warn) == "function") {
+ window.console.warn(msg);
+ return;
+ }
+ } else if (level == 'DEBUG') {
+ if (typeof(window.console.debug) == "function") {
+ window.console.debug(msg);
+ return;
+ }
+ } else if (level == 'INFO') {
+ if (typeof(window.console.info) == "function") {
+ window.console.info(msg);
+ return;
+ }
+ }
+ // Fallback to default
+ window.console.warn(msg);
} else if (typeof(opera) != "undefined" && opera.postError) {
// Opera
opera.postError(msg);
@@ -95,7 +126,7 @@ GateOne.Base.update(GateOne.Logging, {
If undefined, the level will be set to GateOne.Logging.level.
If null (as opposed to undefined), level info will not be included in the log message.
- If *destination* is given (must be a function) it will be used to log messages like so: destination(message). The usual conversion of *msg* to *message* will apply.
+ If *destination* is given (must be a function) it will be used to log messages like so: destination(message, levelStr). The usual conversion of *msg* to *message* will apply.
*/
var l = GateOne.Logging,
now = new Date(),
@@ -103,7 +134,7 @@ GateOne.Base.update(GateOne.Logging, {
if (typeof(level) == 'undefined') {
level = l.level;
}
- if (level === parseInt(level,10)) { // It's an integer
+ if (level === parseInt(level, 10)) { // It's an integer
if (l.levels[level]) {
levelStr = l.levels[level]; // Get string
} else {
@@ -125,10 +156,10 @@ GateOne.Base.update(GateOne.Logging, {
if (message) {
if (!destination) {
for (var dest in l.destinations) {
- l.destinations[dest](message);
+ l.destinations[dest](message, levelStr);
}
} else {
- destination(message);
+ destination(message, levelStr);
}
}
},
View
135 gateone/static/gateone.js
@@ -586,7 +586,9 @@ var go = GateOne.Base.update(GateOne, {
// In case the user changed the rows/cols or the font/size changed:
setTimeout(function() { // Wrapped in a timeout since it takes a moment for everything to change in the browser
go.Visual.updateDimensions();
- go.Net.sendDimensions();
+ for (term in GateOne.terminals) {
+ go.Net.sendDimensions(term);
+ };
}, 3000);
}
// Apply user-specified dimension styles and settings
@@ -694,7 +696,9 @@ var go = GateOne.Base.update(GateOne, {
emHeight = u.getEmDimensions(goDiv).h;
if (goDiv.style.display != "none") {
go.Visual.updateDimensions();
- go.Net.sendDimensions();
+ for (term in GateOne.terminals) {
+ go.Net.sendDimensions(term);
+ };
}
// Adjust the view so the scrollback buffer stays hidden unless the user scrolls
if (!go.prefs.embedded) {
@@ -703,12 +707,20 @@ var go = GateOne.Base.update(GateOne, {
go.resizeAdjustTimer = setTimeout(function() {
var distance = goDiv.clientHeight - screenNode.offsetHeight;
distance -= (emHeight * go.prefs.rowAdjust); // Have to adjust for the extra row we add for the playback controls
- if (GateOne.Utils.isVisible(termPre)) {
+ if (go.Utils.isVisible(termPre)) {
var transform = "translateY(-" + distance + "px)";
- GateOne.Visual.applyTransform(termPre, transform); // Move it to the top so the scrollback isn't visible unless you actually scroll
+ go.Visual.applyTransform(termPre, transform); // Move it to the top so the scrollback isn't visible unless you actually scroll
}
}, 500);
}
+ if (go.prefs.rows) { // If someone explicitly set rows/cols, scale the term to fit the screen
+ var nodeHeight = screenNode.getClientRects()[0].top;
+ if (nodeHeight < goDiv.clientHeight) { // Resize to fit
+ var scale = goDiv.clientHeight / (goDiv.clientHeight - nodeHeight),
+ transform = "scale(" + scale + ", " + scale + ")";
+ go.Visual.applyTransform(termPre, transform);
+ }
+ }
}, 750);
}
window.onresize = onResizeEvent;
@@ -1029,6 +1041,7 @@ GateOne.Base.update(GateOne.Utils, {
lineCounter = 0;
// We need two lines so we can factor in the line height and character spacing (if it has been messed with).
sizingDiv.className = "terminal";
+ sizingDiv.style.wordWrap = 'normal';
for (var i=0; i <= 63; i++) {
fillerX += "\u2588"; // Fill it with a single character (this is a unicode "full block": █). Using the \u syntax because minifiers don't seem to like unicode characters to be in the source as-is.
}
@@ -1926,6 +1939,7 @@ GateOne.Base.update(GateOne.Input, {
onPaste: function(e) {
var go = GateOne,
u = go.Utils,
+ prefix = go.prefs.prefix,
goDiv = u.getNode(go.prefs.goDiv);
logDebug("goDiv registered paste event.");
if (document.activeElement.tagName == "INPUT" || document.activeElement.tagName == "TEXTAREA" || document.activeElement.tagName == "SELECT" || document.activeElement.tagName == "BUTTON") {
@@ -1956,7 +1970,7 @@ GateOne.Base.update(GateOne.Input, {
// This is wrapped in a timeout so that the paste events that bubble up after the first get ignored
setTimeout(function() {
go.Input.handlingPaste = false;
- }, 250);
+ }, 100);
} else {
e.preventDefault(); // Prevent any funny business around queuing up pastes
}
@@ -2393,8 +2407,10 @@ GateOne.Base.update(GateOne.Input, {
if (key.string == "KEY_UNKNOWN") {
return; // Without this, unknown keys end up sending a null character which isn't a good idea =)
}
- // Scroll to bottom (seems like a normal convention for when a key is pressed in a terminal)
- u.scrollToBottom(go.terminals[term]['node']);
+ if (key.string != "KEY_SHIFT" && key.string != "KEY_CTRL" && key.string != "KEY_ALT" && key.string != "KEY_META") {
+ // Scroll to bottom (seems like a normal convention for when a key is pressed in a terminal)
+ u.scrollToBottom(go.terminals[term]['node']);
+ }
// Try using the keyTable first (so everything can be overridden)
if (key.string in goIn.keyTable) {
if (goIn.keyTable[key.string]) { // Not null
@@ -2902,8 +2918,10 @@ GateOne.Base.update(GateOne.Visual, {
timeDiff = now - go.Visual.sinceLastMessage,
noticeContainer = u.getNode('#'+prefix+'noticecontainer'),
notice = u.createElement('div', {'id': prefix+id}),
+ messageSpan = u.createElement('span'),
+ closeX = u.createElement('span', {'class': 'close_notice'}),
unique = u.randomPrime(),
- removeFunc = function() {
+ removeFunc = function(now) {
v.noticeTimers[unique] = setTimeout(function() {
go.Visual.applyStyle(notice, {'opacity': 0});
v.noticeTimers[unique] = setTimeout(function() {
@@ -2924,7 +2942,17 @@ GateOne.Base.update(GateOne.Visual, {
if (!removeTimeout) {
removeTimeout = 5000;
}
- notice.innerHTML = message;
+ messageSpan.innerHTML = message;
+ closeX.innerHTML = go.Icons['close'];
+ closeX.onclick = function(e) {
+ if (v.noticeTimers[unique]) {
+ clearTimeout(v.noticeTimers[unique]);
+ }
+ u.removeElement(notice);
+ go.Input.capture();
+ }
+ notice.appendChild(messageSpan);
+ notice.appendChild(closeX);
noticeContainer.appendChild(notice);
if (!v.noticeTimers) {
v.noticeTimers = {}
@@ -3010,7 +3038,7 @@ GateOne.Base.update(GateOne.Visual, {
clearTimeout(go.terminals[termNum]['scrollbackTimer']);
go.terminals[termNum]['scrollbackTimer'] = setTimeout(function() {
go.Visual.enableScrollback(termNum);
- }, 3500);
+ }, 500);
return;
}
// Only set the height of the terminal if we could measure it (depending on the CSS the parent element might have a height of 0)
@@ -3028,7 +3056,7 @@ GateOne.Base.update(GateOne.Visual, {
u.scrollToBottom(termPreNode);
} else {
// Create the span that holds the scrollback buffer
- termScrollback = u.createElement('span', {'id': 'term'+termNum+'scrollback'});
+ termScrollback = u.createElement('span', {'id': 'term'+termNum+'scrollback', 'class': 'scrollback'});
termScrollback.innerHTML = go.terminals[termNum]['scrollback'].join('\n') + '\n';
termPreNode.insertBefore(termScrollback, termScreen);
go.terminals[termNum]['scrollbackNode'] = termScrollback;
@@ -3193,6 +3221,7 @@ GateOne.Base.update(GateOne.Visual, {
} else {
v.applyTransform(termNode, 'translate(-' + wPX + 'px, -' + hPX + 'px) scale(0.5)');
}
+ u.scrollToBottom(termNode);
});
}, 1);
// Now hide everything but the terminal in the primary view
@@ -4292,7 +4321,8 @@ go.Base.update(GateOne.Terminal, {
if (existingLine) {
existingLine.innerHTML = screen[i] + '\n';
} else { // Size of the terminal increased
- var lineSpan = u.createElement('span', {'class': 'line_' + i});
+ var classes = 'termline ' + prefix + 'line_' + i,
+ lineSpan = u.createElement('span', {'class': classes});
lineSpan.innerHTML = screen[i] + '\n';
existingScreen.appendChild(lineSpan);
}
@@ -4301,7 +4331,6 @@ go.Base.update(GateOne.Terminal, {
}
}
}
-// u.scrollToBottom(existingPre);
},
termUpdateFromWorker: function(e) {
var u = GateOne.Utils,
@@ -4317,9 +4346,7 @@ go.Base.update(GateOne.Terminal, {
goDiv = u.getNode(GateOne.prefs.goDiv),
termContainer = u.getNode('#'+prefix+'term'+term),
existingPre = GateOne.terminals[term]['node'],
- existingScreen = GateOne.terminals[term]['screenNode'],
- reScrollback = u.partial(GateOne.Visual.enableScrollback, term),
- writeScrollback = u.partial(GateOne.Terminal.writeScrollback, term, scrollback);
+ existingScreen = GateOne.terminals[term]['screenNode'];
if (term && GateOne.terminals[term]) {
termTitle = go.terminals[term]['title'];
} else {
@@ -4330,24 +4357,42 @@ go.Base.update(GateOne.Terminal, {
try {
if (existingScreen && GateOne.terminals[term]['screen'].length != screen.length) {
// Resized
+ var prevLength = GateOne.terminals[term]['screen'].length;
GateOne.terminals[term]['screen'].length = screen.length; // Resize the array to match
- for (var i=0; i < screen.length; i++) {
- var existingLine = existingPre.querySelector(GateOne.prefs.goDiv + ' .' + prefix + 'line_' + i),
- lineSpan = u.createElement('span', {'class': prefix + 'line_' + i});
- if (!existingLine) {
- lineSpan.innerHTML = screen[i] + '\n';
- existingScreen.appendChild(lineSpan);
- // Update the existing screen array in-place to cut down on GC
- GateOne.terminals[term]['screen'][i] = screen[i];
+ if (prevLength < screen.length) {
+ // Grow to fit
+ for (var i=0; i < screen.length; i++) {
+ var classes = 'termline ' + prefix + 'line_' + i,
+ existingLine = existingPre.querySelector(GateOne.prefs.goDiv + ' .' + prefix + 'line_' + i),
+ lineSpan = u.createElement('span', {'class': classes});
+ if (!existingLine) {
+ lineSpan.innerHTML = screen[i] + '\n';
+ existingScreen.appendChild(lineSpan);
+ // Update the existing screen array in-place to cut down on GC
+ GateOne.terminals[term]['screen'][i] = screen[i];
+ }
+ }
+ } else {
+ // Shrink to fit
+ for (var i=0; i < prevLength; i++) {
+ var classes = 'termline ' + prefix + 'line_' + i,
+ existingLine = existingPre.querySelector(GateOne.prefs.goDiv + ' .' + prefix + 'line_' + i);
+ if (existingLine) {
+ if (i >= screen.length) {
+ u.removeElement(existingLine);
+ }
+ }
}
}
+ u.scrollToBottom(existingPre);
}
if (existingScreen) { // Update the terminal display
GateOne.Terminal.applyScreen(screen, term);
} else { // Create the elements necessary to display the screen
- var screenSpan = u.createElement('span', {'id': 'term'+term+'screen'});
+ var screenSpan = u.createElement('span', {'id': 'term'+term+'screen', 'class': 'screen'});
for (var i=0; i < screen.length; i++) {
- var lineSpan = u.createElement('span', {'class': prefix + 'line_' + i});
+ var classes = 'termline ' + prefix + 'line_' + i,
+ lineSpan = u.createElement('span', {'class': classes});
lineSpan.innerHTML = screen[i] + '\n';
screenSpan.appendChild(lineSpan);
// Update the existing screen array in-place to cut down on GC
@@ -4435,22 +4480,19 @@ go.Base.update(GateOne.Terminal, {
screen = null;
}
}
- if (scrollback.length) {
+ if (scrollback.length && GateOne.terminals[term]['scrollback'].toString() != scrollback.toString()) {
+ var reScrollback = u.partial(GateOne.Visual.enableScrollback, term),
+ writeScrollback = u.partial(GateOne.Terminal.writeScrollback, term, scrollback);
GateOne.terminals[term]['scrollback'] = scrollback;
// We wrap the logic that stores the scrollback buffer in a timer so we're not writing to localStorage (aka "to disk") every nth of a second for fast screen refreshes (e.g. fast typers). Writing to localStorage is a blocking operation so this could speed things up considerable for larger terminal sizes.
clearTimeout(GateOne.terminals[term]['scrollbackWriteTimer']);
GateOne.terminals[term]['scrollbackWriteTimer'] = null;
// This will save the scrollback buffer after 3.5 seconds of terminal inactivity (idle)
- try {
- GateOne.terminals[term]['scrollbackWriteTimer'] = setTimeout(writeScrollback, 3500);
- } finally {
- writeScrollback = null;
- scrollback = null;
- }
+ GateOne.terminals[term]['scrollbackWriteTimer'] = setTimeout(writeScrollback, 3500);
// This updates the scrollback buffer in the DOM
clearTimeout(GateOne.terminals[term]['scrollbackTimer']);
- // This timeout re-adds the scrollback buffer after .5 seconds. If we don't do this it can slow down the responsiveness quite a bit
- GateOne.terminals[term]['scrollbackTimer'] = setTimeout(reScrollback, 3500); // Just enough to de-bounce (to keep things smooth)
+ // This timeout re-adds the scrollback buffer after .75 seconds. If we don't do this it can slow down the responsiveness quite a bit
+ GateOne.terminals[term]['scrollbackTimer'] = setTimeout(reScrollback, 500); // Just enough to de-bounce (to keep things smooth)
}
if (consoleLog) {
// This is only used when debugging the Web Worker
@@ -4732,11 +4774,11 @@ go.Base.update(GateOne.Terminal, {
pastearea.onpaste = function(e) {
go.Input.onPaste(e);
// Start capturing input again
-// pastearea.value = '';
setTimeout(function() {
// For some reason when you paste the onmouseup event doesn't fire on goDiv; goFigure
go.Input.mouseDown = false;
GateOne.Input.capture();
+ pastearea.value = ''; // Empty it out to ensure there's no leftovers in subsequent pastes
}, 1);
}
pastearea.oncontextmenu = function(e) {
@@ -4750,25 +4792,14 @@ go.Base.update(GateOne.Terminal, {
Y = e.clientY;
u.hideElement(pastearea);
if (GateOne.Terminal.pasteAreaTimer) {
- clearTimeout(GateOne.Terminal.pasteAreaTimer);
- GateOne.Terminal.pasteAreaTimer = null;
+ return; // Let it return to visibility on its own
}
GateOne.Terminal.pasteAreaTimer = setTimeout(function() {
- u.showElement(pastearea);
- }, 250);
- var elementUnder = document.elementFromPoint(X, Y);
- if (elementUnder.tagName == "span") {
- // Fire the mouseover events as if there was no pastearea
- if (typeof(elementUnder.onclick) == "function") {
- pastearea.style.cursor = 'pointer';
- } else {
- pastearea.style.cursor = 'default';
+ if (!u.getSelText()) {
+ u.showElement(pastearea);
+ GateOne.Terminal.pasteAreaTimer = null;
}
- } else if (elementUnder.tagName == "A") {
- pastearea.style.cursor = 'pointer';
- } else {
- pastearea.style.cursor = 'default';
- }
+ }, 100);
}
pastearea.onmousedown = function(e) {
// When the user left-clicks assume they're trying to highlight text
View
1  gateone/static/go_process.js
@@ -84,7 +84,6 @@ var processScreen = function(scrollback, termUpdateObj, prefs, textTransforms) {
scrollback = [];
}
}
-
if (incoming_scrollback.length) {
// Process the scrollback buffer before we concatenate it
incoming_scrollback = processLines(incoming_scrollback, textTransforms);
View
14 gateone/templates/term_colors/default.css
@@ -261,8 +261,18 @@
cursor: pointer;
}
/* Syntax highlighting classes below */
+#{{container}} span.colheader { font-weight: bold; }
+/* Rows alternate their background color; I love CSS3! */
+#{{container}} span.row { display: inline-block; width: 100%; }
+#{{container}} span.row:hover { background-color: rgba(9, 38, 173, 0.2); }
+#{{container}} span.selectedrow { background-color: rgba(64, 166, 166, 0.2); }
#{{container}} span.date { font-weight: bold; }
-#{{container}} span.time { color: #C14855; }
-#{{container}} span.hostname { color: #4199C1; }
+#{{container}} span.time { color: #169416; font-weight: bold; }
+#{{container}} span.hostname { color: #008DCE; font-weight: bold; }
#{{container}} span.service { color: #C56300; font-weight: bold; }
#{{container}} span.string { color: #0AAC0A; }
+#{{container}} span.crit { font-weight: bold; color: #D50000; }
+#{{container}} span.error { color: #D50000; }
+#{{container}} span.warn { color: #B97800; }
+#{{container}} span.info { /* Defaults are probably fine for info */ }
+#{{container}} span.debug { color: #919191; }
View
12 gateone/templates/term_colors/gnome-terminal.css
@@ -261,3 +261,15 @@
#{{container}} span.clickable:hover {
cursor: pointer;
}
+/* Syntax highlighting classes below */
+#{{container}} span.colheader { font-weight: bold; }
+#{{container}} span.date { font-weight: bold; }
+#{{container}} span.time { color: #C14855; }
+#{{container}} span.hostname { color: #4199C1; }
+#{{container}} span.service { color: #C56300; font-weight: bold; }
+#{{container}} span.string { color: #0AAC0A; }
+#{{container}} span.crit { font-weight: bold; color: #D50000; }
+#{{container}} span.error { color: #D50000; }
+#{{container}} span.warn { color: #B97800; }
+#{{container}} span.info { /* Defaults are probably fine for info */ }
+#{{container}} span.debug { color: #919191; }
View
12 gateone/templates/term_colors/solarized.css
@@ -248,3 +248,15 @@
#{{container}} span.clickable:hover {
cursor: pointer;
}
+/* Syntax highlighting classes below */
+#{{container}} span.colheader { font-weight: bold; }
+#{{container}} span.date { font-weight: bold; }
+#{{container}} span.time { color: #C14855; }
+#{{container}} span.hostname { color: #4199C1; }
+#{{container}} span.service { color: #C56300; font-weight: bold; }
+#{{container}} span.string { color: #0AAC0A; }
+#{{container}} span.crit { font-weight: bold; color: #D50000; }
+#{{container}} span.error { color: #D50000; }
+#{{container}} span.warn { color: #B97800; }
+#{{container}} span.info { /* Defaults are probably fine for info */ }
+#{{container}} span.debug { color: #919191; }
View
30 gateone/templates/themes/black.css
@@ -229,6 +229,18 @@ hr {
-o-transform-origin: left bottom;
transform-origin: left bottom;
}
+#{{container}} .terminal span.screen {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ margin-right: 1em;
+ display: block;
+}
+#{{container}} .terminal span.scrollback {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ margin-right: 1em;
+ display: block;
+}
/* 256-color support is generated by gateone.py and shouldn't change (because they're very specific--unlike generic colors like "Yellow") */
{{colors_256}}
/* The "reverse" default needs to be set in the theme since the background will vary */
@@ -286,6 +298,24 @@ hr {
right: 3em;
z-index: 9999;
}
+#{{prefix}}noticecontainer .close_notice {
+ float: right;
+ opacity: .5;
+ -webkit-transform: scale(.6);
+ -webkit-transform-origin: right top;
+ -moz-transform: scale(.5);
+ -moz-transform-origin: right top;
+ -ms-transform: scale(.5);
+ -ms-transform-origin: right top;
+ -o-transform: scale(.5);
+ -o-transform-origin: right top;
+ transform: scale(.5);
+ transform-origin: right top;
+}
+#{{prefix}}noticecontainer .close_notice:hover {
+ cursor: pointer;
+ opacity: 1;
+}
#{{prefix}}terminfo {
color: #fff;
font-weight: bold;
View
30 gateone/templates/themes/dark-black.css
@@ -230,6 +230,18 @@ hr {
-o-transform-origin: left bottom;
transform-origin: left bottom;
}
+#{{container}} .terminal span.screen {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ margin-right: 1em;
+ display: block;
+}
+#{{container}} .terminal span.scrollback {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ margin-right: 1em;
+ display: block;
+}
/* 256-color support is generated by gateone.py and shouldn't change (because they're very specific--unlike generic colors like "Yellow") */
{{colors_256}}
/* The "reverse" default needs to be set in the theme since the background will vary */
@@ -287,6 +299,24 @@ hr {
right: 3em;
z-index: 9999;
}
+#{{prefix}}noticecontainer .close_notice {
+ float: right;
+ opacity: .5;
+ -webkit-transform: scale(.6);
+ -webkit-transform-origin: right top;
+ -moz-transform: scale(.5);
+ -moz-transform-origin: right top;
+ -ms-transform: scale(.5);
+ -ms-transform-origin: right top;
+ -o-transform: scale(.5);
+ -o-transform-origin: right top;
+ transform: scale(.5);
+ transform-origin: right top;
+}
+#{{prefix}}noticecontainer .close_notice:hover {
+ cursor: pointer;
+ opacity: 1;
+}
#{{prefix}}terminfo {
color: #fff;
font-weight: bold;
View
30 gateone/templates/themes/solarized.css
@@ -231,6 +231,18 @@ hr {
-o-transform-origin: left bottom;
transform-origin: left bottom;
}
+#{{container}} .terminal span.screen {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ margin-right: 1em;
+ display: block;
+}
+#{{container}} .terminal span.scrollback {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ margin-right: 1em;
+ display: block;
+}
/* 256-color support is generated by gateone.py and shouldn't change (because they're very specific--unlike generic colors like "Yellow") */
{{colors_256}}
/* The "reverse" default needs to be set in the theme since the background will vary */
@@ -288,6 +300,24 @@ hr {
right: 3em;
z-index: 9999;
}
+#{{prefix}}noticecontainer .close_notice {
+ float: right;
+ opacity: .5;
+ -webkit-transform: scale(.6);
+ -webkit-transform-origin: right top;
+ -moz-transform: scale(.5);
+ -moz-transform-origin: right top;
+ -ms-transform: scale(.5);
+ -ms-transform-origin: right top;
+ -o-transform: scale(.5);
+ -o-transform-origin: right top;
+ transform: scale(.5);
+ transform-origin: right top;
+}
+#{{prefix}}noticecontainer .close_notice:hover {
+ cursor: pointer;
+ opacity: 1;
+}
#{{prefix}}terminfo {
color: #fff;
font-weight: bold;
View
30 gateone/templates/themes/white.css
@@ -229,6 +229,18 @@ hr {
-o-transform-origin: left bottom;
transform-origin: left bottom;
}
+#{{container}} .terminal span.screen {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ margin-right: 1em;
+ display: block;
+}
+#{{container}} .terminal span.scrollback {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ margin-right: 1em;
+ display: block;
+}
/* 256-color support is generated by gateone.py and shouldn't change (because they're very specific--unlike generic colors like "Yellow") */
{{colors_256}}
/* The "reverse" default needs to be set in the theme since the background will vary */
@@ -288,6 +300,24 @@ hr {
right: 3em;
z-index: 9999;
}
+#{{prefix}}noticecontainer .close_notice {
+ float: right;
+ opacity: .5;
+ -webkit-transform: scale(.6);
+ -webkit-transform-origin: right top;
+ -moz-transform: scale(.5);
+ -moz-transform-origin: right top;
+ -ms-transform: scale(.5);
+ -ms-transform-origin: right top;
+ -o-transform: scale(.5);
+ -o-transform-origin: right top;
+ transform: scale(.5);
+ transform-origin: right top;
+}
+#{{prefix}}noticecontainer .close_notice:hover {
+ cursor: pointer;
+ opacity: 1;
+}
#{{prefix}}terminfo {
color: #000;
font-weight: bold;
View
67 gateone/terminal.py
@@ -717,7 +717,7 @@ def initialize(self, rows=24, cols=80, em_dimensions=None):
self.ASCII_LF: self.linefeed,
self.ASCII_VT: self.linefeed,
self.ASCII_FF: self.linefeed,
- self.ASCII_CR: self._carriage_return,
+ self.ASCII_CR: self.carriage_return,
self.ASCII_SO: self.use_g1_charset,
self.ASCII_SI: self.use_g0_charset,
self.ASCII_XON: self._xon,
@@ -804,7 +804,7 @@ def initialize(self, rows=24, cols=80, em_dimensions=None):
'4': self.__ignore, # Smooth (Slow) Scroll (DECSCLM)
'5': self.__ignore, # Reverse video (might support in future)
'6': self.__ignore, # Origin Mode (DECOM)
- '7': self.__ignore, # Wraparound Mode (DECAWM)
+ '7': self.toggle_autowrap, # Wraparound Mode (DECAWM)
'8': self.__ignore, # Auto-repeat Keys (DECARM)
'9': self.__ignore, # Send Mouse X & Y on button press (maybe)
'12': self.send_receive_mode, # SRM
@@ -875,6 +875,7 @@ def initialize(self, rows=24, cols=80, em_dimensions=None):
# an "alternate buffer"
self.alt_screen = None
self.alt_renditions = None
+ self.autowrap = False
self.alt_cursorX = 0
self.alt_cursorY = 0
self.saved_cursorX = 0
@@ -1009,6 +1010,7 @@ def reset(self, *args, **kwargs):
self.bottom_margin = self.rows - 1
self.alt_screen = None
self.alt_renditions = None
+ self.autowrap = False
self.alt_cursorX = 0
self.alt_cursorY = 0
self.saved_cursorX = 0
@@ -1227,7 +1229,7 @@ def set_G0_charset(self, char):
7 Swedish
= Swiss
"""
- logging.debug("Setting G0 charset to %s" % repr(char))
+ #logging.debug("Setting G0 charset to %s" % repr(char))
try:
self.G0_charset = self.charsets[char]
except KeyError:
@@ -1258,7 +1260,7 @@ def set_G1_charset(self, char):
7 Swedish
= Swiss
"""
- logging.debug("Setting G1 charset to %s" % repr(char))
+ #logging.debug("Setting G1 charset to %s" % repr(char))
try:
self.G1_charset = self.charsets[char]
except KeyError:
@@ -1271,8 +1273,8 @@ def use_g0_charset(self):
Sets the current charset to G0. This should get called when ASCII_SO
is encountered.
"""
- logging.debug(
- "Switching to G0 charset (which is %s)" % repr(self.G0_charset))
+ #logging.debug(
+ #"Switching to G0 charset (which is %s)" % repr(self.G0_charset))
self.current_charset = 0
self.charset = self.G0_charset
@@ -1281,8 +1283,8 @@ def use_g1_charset(self):
Sets the current charset to G1. This should get called when ASCII_SI
is encountered.
"""
- logging.debug(
- "Switching to G1 charset (which is %s)" % repr(self.G1_charset))
+ #logging.debug(
+ #"Switching to G1 charset (which is %s)" % repr(self.G1_charset))
self.current_charset = 1
self.charset = self.G1_charset
@@ -1307,7 +1309,7 @@ def write(self, chars, special_checks=True):
cursor_right = self.cursor_right
magic = self.magic
changed = False
- #logging.debug('handling chars: %s' % repr(chars))
+ logging.debug('handling chars: %s' % repr(chars))
if special_checks:
# NOTE: Special checks are limited to PNGs and JPEGs right now
before_chars = ""
@@ -1413,23 +1415,14 @@ def write(self, chars, special_checks=True):
))
self.esc_buffer = ''
continue # We're done here
-# TODO: Figure out a way to write characters past the edge of the screen so that users can copy & paste without having newlines in the middle of everything.
changed = True
if self.cursorX >= self.cols:
- # Start a newline but NOTE: Not really the best way to
- # handle this because it means copying and pasting lines
- # will end up broken into pieces of size=self.cols
- self.newline()
- self.cursorX = 0
- # This actually works but until I figure out a way to
- # get the browser to properly wrap the line without
- # freaking out whenever someone clicks on the page it
- # will have to stay commented. NOTE: This might be a
- # browser bug.
- #self.screen[self.cursorY].append(unicode(char))
- #self.renditions[self.cursorY].append([])
- # To try it just uncomment the above two lines and
- # comment out the self.newline() and self.cusorX lines
+ if self.autowrap:
+ self.cursorX = 0
+ self.newline()
+ else:
+ self.screen[self.cursorY].append(u' ') # Make room
+ self.renditions[self.cursorY].append(u' ')
try:
self.renditions[self.cursorY][
self.cursorX] = self.cur_rendition
@@ -1462,7 +1455,6 @@ def write(self, chars, special_checks=True):
# This can happen when escape sequences go haywire
logging.error(_(
"IndexError in write() (rate limiter?): %s" % e))
- pass
cursor_right()
if changed:
self.modified = True
@@ -1657,17 +1649,26 @@ def newline(self):
(usually the bottom of the screen).
"""
self.cursorY += 1
+ # Do CR with every NL because that's how every other terminal emulator
+ # seems to do it
+ self.cursorX = 0
if self.cursorY > self.bottom_margin:
self.scroll_up()
self.cursorY = self.bottom_margin
self.clear_line()
- def _carriage_return(self):
+ def carriage_return(self):
"""
Executes a carriage return (sets :attr:`self.cursorX` to 0). In other
words it moves the cursor back to position 0 on the line.
"""
- self.cursorX = 0
+ if self.cursorX >= self.cols:
+ if self.screen[self.cursorY][self.cursorX-1] == u' ':
+ # This is just the underlying program telling us to wrap
+ # Let the browser do it for us.
+ # NOTE: I know this assumes HTML output. Deal with it :P
+ return
+ self.cursorX = 0 # Yeah this is redundant if newline() is called.
def _xon(self):
"""
@@ -1999,6 +2000,7 @@ def set_expanded_mode(self, setting):
'?1h' - Application Cursor Keys
'?5h' - DECSCNM (default off): Set reverse-video mode.
+ '?7h' - DECAWM: Autowrap mode
'?12h' - Local echo (SRM or Send Receive Mode)
'?25h' - Hide cursor
'?1049h' - Save cursor and screen
@@ -2096,6 +2098,17 @@ def toggle_alternate_screen_buffer_cursor(self, alt):
self.cursorY = self.alt_cursorY
self.toggle_alternate_screen_buffer(alt)
+ def toggle_autowrap(self, boolean):
+ """
+ Sets :attr:`self.autowrap equal to *boolean*. Literally:
+
+ .. code-block:: python
+
+ self.autowrap = boolean
+ """
+ logging.debug('setting autowrap: %s' % boolean)
+ self.autowrap = boolean
+
def show_hide_cursor(self, boolean):
"""
Literally:
Please sign in to comment.
Something went wrong with that request. Please try again.