2
0
mirror of https://github.com/xcat2/confluent.git synced 2024-11-23 18:11:15 +00:00
confluent/confluent_web/js/consolewindow.js

743 lines
16 KiB
JavaScript

/**
* tty.js
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* Copyright 2014, IBM Corporation
* Copyright 2014, Lenovo
*/
;(function() {
'use strict';
/**
* Elements
*/
var document = this.document
, window = this
, root
, body
, h1
, open
, lights;
/**
* Helpers
*/
var EventEmitter = Terminal.EventEmitter
, inherits = Terminal.inherits
, on = Terminal.on
, off = Terminal.off
, cancel = Terminal.cancel;
function postRequest(url, data, success) {
var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/json');
request.setRequestHeader('Accept', 'application/json');
request.onload = function() {
if (this.status >= 200 && this.status < 400) {
success(JSON.parse(this.responseText));
}
};
if (data) {
request.send(JSON.stringify(data));
} else {
request.send("");
}
request = null;
}
/**
* Console
*/
function ConsoleWindow(consoleurl, nodename) {
var self = this;
if (!(this instanceof ConsoleWindow)) {
return new ConsoleWindow(consoleurl, nodename);
}
EventEmitter.call(this);
var el
, grip
, bar
, button
, title;
el = document.createElement('div');
el.className = 'window';
grip = document.createElement('div');
grip.className = 'grip';
bar = document.createElement('div');
bar.className = 'bar';
button = document.createElement('div');
button.innerHTML = 'x';
button.title = 'close';
button.className = 'tab';
title = document.createElement('div');
title.className = 'title';
title.innerHTML = nodename;
this.nodename = nodename;
this.element = el;
this.grip = grip;
this.bar = bar;
this.button = button;
this.title = title;
this.consoleurl = consoleurl;
this.tabs = [];
this.focused = null;
this.cols = 100; //Terminal.geometry[0];
this.rows = 30; //Terminal.geometry[1];
el.appendChild(grip);
el.appendChild(bar);
bar.appendChild(title);
bar.appendChild(button);
document.body.appendChild(el);
//tty.windows.push(this);
this.createTab();
this.focus();
this.bind();
this.tabs[0].once('open', function() {
//tty.emit('open window', self);
self.emit('open');
});
}
inherits(ConsoleWindow, EventEmitter);
ConsoleWindow.prototype.bind = function() {
var self = this
, el = this.element
, bar = this.bar
, grip = this.grip
, button = this.button
, last = 0;
on(button, 'click', function(ev) {
self.destroy();
return cancel(ev);
});
on(grip, 'mousedown', function(ev) {
self.focus();
self.resizing(ev);
return cancel(ev);
});
on(el, 'mousedown', function(ev) {
if (ev.target !== el && ev.target !== bar) return;
self.focus();
cancel(ev);
if (new Date - last < 600) {
return self.maximize();
}
last = new Date;
self.drag(ev);
return cancel(ev);
});
};
ConsoleWindow.prototype.focus = function() {
// Restack
var parent = this.element.parentNode;
if (parent) {
parent.removeChild(this.element);
parent.appendChild(this.element);
}
// Focus Foreground Tab
this.focused.focus();
//tty.emit('focus window', this);
this.emit('focus');
};
ConsoleWindow.prototype.destroy = function() {
if (this.destroyed) return;
this.destroyed = true;
if (this.minimize) this.minimize();
//splice(tty.windows, this);
//if (tty.windows.length) tty.windows[0].focus();
this.element.parentNode.removeChild(this.element);
this.each(function(term) {
term.destroy();
});
//tty.emit('close window', this);
this.emit('close');
};
ConsoleWindow.prototype.drag = function(ev) {
var self = this
, el = this.element;
if (this.minimize) return;
var drag = {
left: el.offsetLeft,
top: el.offsetTop,
pageX: ev.pageX,
pageY: ev.pageY
};
el.style.opacity = '0.60';
el.style.cursor = 'move';
document.documentElement.style.cursor = 'move';
function move(ev) {
el.style.left =
(drag.left + ev.pageX - drag.pageX) + 'px';
var tmptop = (drag.top + ev.pageY - drag.pageY);
if (tmptop < 0) {
tmptop = 0;
}
el.style.top = tmptop + 'px';
}
function up() {
el.style.opacity = '';
el.style.cursor = '';
document.documentElement.style.cursor = '';
off(document, 'mousemove', move);
off(document, 'mouseup', up);
var ev = {
left: el.style.left.replace(/\w+/g, ''),
top: el.style.top.replace(/\w+/g, '')
};
//tty.emit('drag window', self, ev);
self.emit('drag', ev);
}
on(document, 'mousemove', move);
on(document, 'mouseup', up);
};
ConsoleWindow.prototype.resizing = function(ev) {
var self = this
, el = this.element
, term = this.focused;
if (this.minimize) delete this.minimize;
var resize = {
w: el.clientWidth,
h: el.clientHeight
};
el.style.overflow = 'hidden';
el.style.opacity = '0.70';
el.style.cursor = 'se-resize';
document.documentElement.style.cursor = 'se-resize';
term.element.style.height = '100%';
function move(ev) {
var x, y;
y = el.offsetHeight - term.element.clientHeight;
x = ev.pageX - el.offsetLeft;
y = (ev.pageY - el.offsetTop) - y;
el.style.width = x + 'px';
el.style.height = y + 'px';
}
function up() {
var x, y;
x = el.clientWidth / resize.w;
y = el.clientHeight / resize.h;
x = (x * term.cols) | 0;
y = (y * term.rows) | 0;
self.resize(x, y);
el.style.width = '';
el.style.height = '';
el.style.overflow = '';
el.style.opacity = '';
el.style.cursor = '';
document.documentElement.style.cursor = '';
term.element.style.height = '';
off(document, 'mousemove', move);
off(document, 'mouseup', up);
}
on(document, 'mousemove', move);
on(document, 'mouseup', up);
};
ConsoleWindow.prototype.maximize = function() {
if (this.minimize) return this.minimize();
var self = this
, el = this.element
, term = this.focused
, x
, y;
var m = {
cols: term.cols,
rows: term.rows,
left: el.offsetLeft,
top: el.offsetTop,
root: root.className
};
this.minimize = function() {
delete this.minimize;
el.style.left = m.left + 'px';
el.style.top = m.top + 'px';
el.style.width = '';
el.style.height = '';
term.element.style.width = '';
term.element.style.height = '';
el.style.boxSizing = '';
self.grip.style.display = '';
root.className = m.root;
self.resize(m.cols, m.rows);
//tty.emit('minimize window', self);
self.emit('minimize');
};
window.scrollTo(0, 0);
x = root.clientWidth / term.element.offsetWidth;
y = root.clientHeight / term.element.offsetHeight;
x = (x * term.cols) | 0;
y = (y * term.rows) | 0;
el.style.left = '0px';
el.style.top = '0px';
el.style.width = '100%';
el.style.height = '100%';
term.element.style.width = '100%';
term.element.style.height = '100%';
el.style.boxSizing = 'border-box';
this.grip.style.display = 'none';
root.className = 'maximized';
this.resize(x, y);
//tty.emit('maximize window', this);
this.emit('maximize');
};
ConsoleWindow.prototype.resize = function(cols, rows) {
this.cols = cols;
this.rows = rows;
this.each(function(term) {
term.resize(cols, rows);
});
//tty.emit('resize window', this, cols, rows);
this.emit('resize', cols, rows);
};
ConsoleWindow.prototype.each = function(func) {
var i = this.tabs.length;
while (i--) {
func(this.tabs[i], i);
}
};
ConsoleWindow.prototype.createTab = function() {
return new Tab(this, this.consoleurl);
};
ConsoleWindow.prototype.highlight = function() {
var self = this;
this.element.style.borderColor = 'orange';
setTimeout(function() {
self.element.style.borderColor = '';
}, 200);
this.focus();
};
ConsoleWindow.prototype.focusTab = function(next) {
var tabs = this.tabs
, i = indexOf(tabs, this.focused)
, l = tabs.length;
if (!next) {
if (tabs[--i]) return tabs[i].focus();
if (tabs[--l]) return tabs[l].focus();
} else {
if (tabs[++i]) return tabs[i].focus();
if (tabs[0]) return tabs[0].focus();
}
return this.focused && this.focused.focus();
};
ConsoleWindow.prototype.nextTab = function() {
return this.focusTab(true);
};
ConsoleWindow.prototype.previousTab = function() {
return this.focusTab(false);
};
/**
* Tab
*/
function Tab(win, consoleurl) {
var self = this;
var cols = win.cols
, rows = win.rows;
Terminal.call(this, {
cols: cols,
rows: rows
});
var button = document.createElement('div');
button.className = 'tab';
button.innerHTML = '\u2022';
//win.bar.appendChild(button);
on(button, 'click', function(ev) {
if (ev.ctrlKey || ev.altKey || ev.metaKey || ev.shiftKey) {
self.destroy();
} else {
self.focus();
}
return cancel(ev);
});
this.id = '';
this.consoleurl = consoleurl;
this.clientcount = 0;
this.connectstate = 'unconnected';
this.lasterror = ''
this.window = win;
this.button = button;
this.element = null;
this.process = '';
this.open();
this.hookKeys();
// Now begins the code that will embarass me when I actually know my way
// around javascript -jbjohnso
this.sessid = '';
this.datapending = false;
this.waitingdata = false;
this.sentdata = function(data, textStatus, jqXHR) {
if (this.waitingdata) {
postRequest(consoleurl, { session: this.sessid, bytes: this.waitingdata }, this.sentdata);
this.waitingdata = false;
} else {
this.datapending = false;
}
}.bind(this);
this.on('data', function(data) {
// Send data to console from terminal
if (this.datapending) {
if (!this.waitingdata) {
this.waitingdata = data;
} else {
this.waitingdata = this.waitingdata + data;
}
return;
}
this.datapending = true;
postRequest(consoleurl, { session: this.sessid, bytes: data }, this.sentdata);
}.bind(this));
this.gotdata = function(data, textStatus, jqXHR) {
if ("data" in data) {
this.write(data.data);
}
var updatetitle = false;
var updateinfo = [];
if ("connectstate" in data) {
updatetitle = true;
this.connectstate = data.connectstate;
}
if (this.connectstate != "connected") {
updateinfo.push(this.connectstate);
} else {
self.lasterror = '';
}
if ("error" in data) {
updatetitle = true;
this.lasterror = data.error
}
if (this.lasterror != '') {
updateinfo.push(this.lasterror);
}
if ("clientcount" in data) {
updatetitle = true;
this.clientcount = data.clientcount;
}
if (this.clientcount > 1) {
updateinfo.push("clients: " + this.clientcount.toString());
}
if (updatetitle == true) {
if (updateinfo.length > 0) {
this.window.title.innerHTML = this.window.nodename + " [" + updateinfo.join() + "]";
} else {
this.window.title.innerHTML = this.window.nodename;
}
}
postRequest(consoleurl, { session: this.sessid }, this.gotdata);
}.bind(this);
this.gotsession = function(data, textStatus, jqXHR) {
this.sessid = data.session
postRequest(consoleurl, { session: this.sessid }, this.gotdata);
}.bind(this);
postRequest(consoleurl, false, this.gotsession);
win.tabs.push(this);
};
inherits(Tab, Terminal);
Tab.prototype._write = Tab.prototype.write;
Tab.prototype.write = function(data) {
if (this.window.focused !== this) this.button.style.color = 'red';
return this._write(data);
};
Tab.prototype._focus = Tab.prototype.focus;
Tab.prototype.focus = function() {
if (Terminal.focus === this) return;
var win = this.window;
// maybe move to Tab.prototype.switch
if (win.focused !== this) {
if (win.focused) {
if (win.focused.element.parentNode) {
win.focused.element.parentNode.removeChild(win.focused.element);
}
win.focused.button.style.fontWeight = '';
}
win.element.appendChild(this.element);
win.focused = this;
//win.title.innerHTML = this.process;
this.button.style.fontWeight = 'bold';
this.button.style.color = '';
}
this._focus();
win.focus();
//tty.emit('focus tab', this);
this.emit('focus');
};
Tab.prototype._resize = Tab.prototype.resize;
Tab.prototype.resize = function(cols, rows) {
//this.socket.emit('resize', this.id, cols, rows);
this._resize(cols, rows);
//tty.emit('resize tab', this, cols, rows);
this.emit('resize', cols, rows);
};
Tab.prototype.__destroy = Tab.prototype.destroy;
Tab.prototype._destroy = function() {
if (this.destroyed) return;
this.destroyed = true;
var win = this.window;
this.button.parentNode.removeChild(this.button);
if (this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
}
if (tty.terms[this.id]) delete tty.terms[this.id];
splice(win.tabs, this);
if (win.focused === this) {
win.previousTab();
}
if (!win.tabs.length) {
win.destroy();
}
this.__destroy();
};
Tab.prototype.destroy = function() {
if (this.destroyed) return;
//TODO: politely let server know of client closure
this._destroy();
//tty.emit('close tab', this);
this.emit('close');
};
Tab.prototype.hookKeys = function() {
var self = this;
// Alt-[jk] to quickly swap between windows.
this.on('key', function(key, ev) {
if (Terminal.focusKeys === false) {
return;
}
var offset
, i;
if (key === '\x1bj') {
offset = -1;
} else if (key === '\x1bk') {
offset = +1;
} else {
return;
}
i = indexOf(tty.windows, this.window) + offset;
this._ignoreNext();
if (tty.windows[i]) return tty.windows[i].highlight();
if (offset > 0) {
if (tty.windows[0]) return tty.windows[0].highlight();
} else {
i = tty.windows.length - 1;
if (tty.windows[i]) return tty.windows[i].highlight();
}
return this.window.highlight();
});
this.on('request paste', function(key) {
this.socket.emit('request paste', function(err, text) {
if (err) return;
self.send(text);
});
});
this.on('request create', function() {
this.window.createTab();
});
this.on('request term', function(key) {
if (this.window.tabs[key]) {
this.window.tabs[key].focus();
}
});
this.on('request term next', function(key) {
this.window.nextTab();
});
this.on('request term previous', function(key) {
this.window.previousTab();
});
};
Tab.prototype._ignoreNext = function() {
// Don't send the next key.
var handler = this.handler;
this.handler = function() {
this.handler = handler;
};
var showCursor = this.showCursor;
this.showCursor = function() {
this.showCursor = showCursor;
};
};
/**
* Program-specific Features
*/
Tab.prototype._bindMouse = Tab.prototype.bindMouse;
Tab.prototype.bindMouse = function() {
if (!Terminal.programFeatures) return this._bindMouse();
var self = this;
var wheelEvent = 'onmousewheel' in window
? 'mousewheel'
: 'DOMMouseScroll';
on(self.element, wheelEvent, function(ev) {
if (self.mouseEvents) return;
if ((ev.type === 'mousewheel' && ev.wheelDeltaY > 0)
|| (ev.type === 'DOMMouseScroll' && ev.detail < 0)) {
// page up
self.keyDown({keyCode: 33});
} else {
// page down
self.keyDown({keyCode: 34});
}
return cancel(ev);
});
return this._bindMouse();
};
/**
* Helpers
*/
function indexOf(obj, el) {
var i = obj.length;
while (i--) {
if (obj[i] === el) return i;
}
return -1;
}
function splice(obj, el) {
var i = indexOf(obj, el);
if (~i) obj.splice(i, 1);
}
function sanitize(text) {
if (!text) return '';
return (text + '').replace(/[&<>]/g, '')
}
this.ConsoleWindow = ConsoleWindow;
}).call(function() {
return this;
}());