mirror of
https://github.com/paradoxxxzero/butterfly.git
synced 2026-05-26 07:08:08 +00:00
- Add alarm mode [alt+A] for now
- isolate ext modules - remove now useless scss - update node deps
This commit is contained in:
@@ -12,11 +12,15 @@ module.exports = (grunt) ->
|
||||
butterfly:
|
||||
files:
|
||||
'butterfly/static/main.min.js': 'butterfly/static/main.js'
|
||||
'butterfly/static/ext.min.js': 'butterfly/static/ext.js'
|
||||
|
||||
sass:
|
||||
options:
|
||||
includePaths: ['butterfly/sass/']
|
||||
|
||||
butterfly:
|
||||
expand: true
|
||||
cwd: 'butterfly/sass'
|
||||
cwd: 'butterfly/sass/'
|
||||
src: '*.sass'
|
||||
dest: 'butterfly/static/'
|
||||
ext: '.css'
|
||||
@@ -27,12 +31,8 @@ module.exports = (grunt) ->
|
||||
|
||||
butterfly:
|
||||
files:
|
||||
'butterfly/static/main.js': [
|
||||
'coffees/term.coffee'
|
||||
'coffees/selection.coffee'
|
||||
'coffees/virtual_input.coffee'
|
||||
'coffees/main.coffee'
|
||||
]
|
||||
'butterfly/static/main.js': 'coffees/*.coffee'
|
||||
'butterfly/static/ext.js': 'coffees/ext/*.coffee'
|
||||
|
||||
coffeelint:
|
||||
butterfly:
|
||||
@@ -43,6 +43,7 @@ module.exports = (grunt) ->
|
||||
livereload: true
|
||||
coffee:
|
||||
files: [
|
||||
'coffees/ext/*.coffee'
|
||||
'coffees/*.coffee'
|
||||
'Gruntfile.coffee'
|
||||
]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
__version__ = '1.5.4'
|
||||
__version__ = '1.5.5'
|
||||
|
||||
|
||||
import os
|
||||
|
||||
@@ -25,15 +25,23 @@ $shadow-alpha: .5 !default
|
||||
|
||||
&.bell
|
||||
-webkit-filter: blur(2px)
|
||||
filter: blur(2px)
|
||||
|
||||
&.skip
|
||||
-webkit-filter: sepia(1)
|
||||
filter: sepia(1)
|
||||
|
||||
&.selection
|
||||
-webkit-filter: unquote("saturate(2)")
|
||||
filter: unquote("saturate(2)")
|
||||
|
||||
&.alarm
|
||||
-webkit-filter: hue-rotate(150deg)
|
||||
filter: hue-rotate(150deg)
|
||||
|
||||
&.dead
|
||||
-webkit-filter: unquote("grayscale(1)")
|
||||
filter: unquote("grayscale(1)")
|
||||
|
||||
&:after
|
||||
content: "CLOSED"
|
||||
@@ -15,11 +15,11 @@
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
@import 'font'
|
||||
@import 'layout'
|
||||
@import 'fx'
|
||||
@import 'colors'
|
||||
@import '16_colors'
|
||||
@import '256_colors'
|
||||
@import 'cursor'
|
||||
@import 'term_styles'
|
||||
@import font
|
||||
@import layout
|
||||
@import fx
|
||||
@import colors
|
||||
@import 16_colors
|
||||
@import 256_colors
|
||||
@import cursor
|
||||
@import term_styles
|
||||
445
butterfly/static/ext.js
Normal file
445
butterfly/static/ext.js
Normal file
@@ -0,0 +1,445 @@
|
||||
(function() {
|
||||
var Selection, alt, cancel, ctrl, first, next_leaf, previous_leaf, selection, set_alarm, virtual_input,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
set_alarm = function(notification) {
|
||||
var alarm;
|
||||
alarm = function(data) {
|
||||
var note;
|
||||
butterfly.element.classList.remove('alarm');
|
||||
note = "New activity on butterfly terminal [" + butterfly.title + "]";
|
||||
if (notification) {
|
||||
new Notification(note, {
|
||||
body: data.data,
|
||||
icon: '/static/images/favicon.png'
|
||||
});
|
||||
} else {
|
||||
alert(note + '\n' + data.data);
|
||||
}
|
||||
return butterfly.ws.removeEventListener('message', alarm);
|
||||
};
|
||||
butterfly.ws.addEventListener('message', alarm);
|
||||
return butterfly.element.classList.add('alarm');
|
||||
};
|
||||
|
||||
cancel = function(ev) {
|
||||
if (ev.preventDefault) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.stopPropagation) {
|
||||
ev.stopPropagation();
|
||||
}
|
||||
ev.cancelBubble = true;
|
||||
return false;
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (!(e.altKey && e.keyCode === 65)) {
|
||||
return true;
|
||||
}
|
||||
if (Notification && Notification.permission === 'default') {
|
||||
Notification.requestPermission(function() {
|
||||
return set_alarm(Notification.permission === 'granted');
|
||||
});
|
||||
} else {
|
||||
set_alarm(Notification.permission === 'granted');
|
||||
}
|
||||
return cancel(e);
|
||||
});
|
||||
|
||||
selection = null;
|
||||
|
||||
cancel = function(ev) {
|
||||
if (ev.preventDefault) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.stopPropagation) {
|
||||
ev.stopPropagation();
|
||||
}
|
||||
ev.cancelBubble = true;
|
||||
return false;
|
||||
};
|
||||
|
||||
previous_leaf = function(node) {
|
||||
var previous;
|
||||
previous = node.previousSibling;
|
||||
if (!previous) {
|
||||
previous = node.parentNode.previousSibling;
|
||||
}
|
||||
if (!previous) {
|
||||
previous = node.parentNode.parentNode.previousSibling;
|
||||
}
|
||||
while (previous.lastChild) {
|
||||
previous = previous.lastChild;
|
||||
}
|
||||
return previous;
|
||||
};
|
||||
|
||||
next_leaf = function(node) {
|
||||
var next;
|
||||
next = node.nextSibling;
|
||||
if (!next) {
|
||||
next = node.parentNode.nextSibling;
|
||||
}
|
||||
if (!next) {
|
||||
next = node.parentNode.parentNode.nextSibling;
|
||||
}
|
||||
while (next.firstChild) {
|
||||
next = next.firstChild;
|
||||
}
|
||||
return next;
|
||||
};
|
||||
|
||||
Selection = (function() {
|
||||
function Selection() {
|
||||
butterfly.element.classList.add('selection');
|
||||
this.selection = getSelection();
|
||||
}
|
||||
|
||||
Selection.prototype.reset = function() {
|
||||
var fake_range, _ref, _results;
|
||||
this.selection = getSelection();
|
||||
fake_range = document.createRange();
|
||||
fake_range.setStart(this.selection.anchorNode, this.selection.anchorOffset);
|
||||
fake_range.setEnd(this.selection.focusNode, this.selection.focusOffset);
|
||||
this.start = {
|
||||
node: this.selection.anchorNode,
|
||||
offset: this.selection.anchorOffset
|
||||
};
|
||||
this.end = {
|
||||
node: this.selection.focusNode,
|
||||
offset: this.selection.focusOffset
|
||||
};
|
||||
if (fake_range.collapsed) {
|
||||
_ref = [this.end, this.start], this.start = _ref[0], this.end = _ref[1];
|
||||
}
|
||||
this.start_line = this.start.node;
|
||||
while (!this.start_line.classList || __indexOf.call(this.start_line.classList, 'line') < 0) {
|
||||
this.start_line = this.start_line.parentNode;
|
||||
}
|
||||
this.end_line = this.end.node;
|
||||
_results = [];
|
||||
while (!this.end_line.classList || __indexOf.call(this.end_line.classList, 'line') < 0) {
|
||||
_results.push(this.end_line = this.end_line.parentNode);
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Selection.prototype.clear = function() {
|
||||
return this.selection.removeAllRanges();
|
||||
};
|
||||
|
||||
Selection.prototype.destroy = function() {
|
||||
butterfly.element.classList.remove('selection');
|
||||
return this.clear();
|
||||
};
|
||||
|
||||
Selection.prototype.text = function() {
|
||||
return this.selection.toString();
|
||||
};
|
||||
|
||||
Selection.prototype.up = function() {
|
||||
return this.go(-1);
|
||||
};
|
||||
|
||||
Selection.prototype.down = function() {
|
||||
return this.go(+1);
|
||||
};
|
||||
|
||||
Selection.prototype.go = function(n) {
|
||||
var index;
|
||||
index = butterfly.children.indexOf(this.start_line) + n;
|
||||
if (!((0 <= index && index < butterfly.children.length))) {
|
||||
return;
|
||||
}
|
||||
while (!butterfly.children[index].textContent.match(/\S/)) {
|
||||
index += n;
|
||||
if (!((0 <= index && index < butterfly.children.length))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return this.select_line(index);
|
||||
};
|
||||
|
||||
Selection.prototype.apply = function() {
|
||||
var range;
|
||||
this.clear();
|
||||
range = document.createRange();
|
||||
range.setStart(this.start.node, this.start.offset);
|
||||
range.setEnd(this.end.node, this.end.offset);
|
||||
return this.selection.addRange(range);
|
||||
};
|
||||
|
||||
Selection.prototype.select_line = function(index) {
|
||||
var line, line_end, line_start;
|
||||
line = butterfly.children[index];
|
||||
line_start = {
|
||||
node: line.firstChild,
|
||||
offset: 0
|
||||
};
|
||||
line_end = {
|
||||
node: line.lastChild,
|
||||
offset: line.lastChild.textContent.length
|
||||
};
|
||||
this.start = this.walk(line_start, /\S/);
|
||||
return this.end = this.walk(line_end, /\S/, true);
|
||||
};
|
||||
|
||||
Selection.prototype.collapsed = function(start, end) {
|
||||
var fake_range;
|
||||
fake_range = document.createRange();
|
||||
fake_range.setStart(start.node, start.offset);
|
||||
fake_range.setEnd(end.node, end.offset);
|
||||
return fake_range.collapsed;
|
||||
};
|
||||
|
||||
Selection.prototype.shrink_right = function() {
|
||||
var end, node;
|
||||
node = this.walk(this.end, /\s/, true);
|
||||
end = this.walk(node, /\S/, true);
|
||||
if (!this.collapsed(this.start, end)) {
|
||||
return this.end = end;
|
||||
}
|
||||
};
|
||||
|
||||
Selection.prototype.shrink_left = function() {
|
||||
var node, start;
|
||||
node = this.walk(this.start, /\s/);
|
||||
start = this.walk(node, /\S/);
|
||||
if (!this.collapsed(start, this.end)) {
|
||||
return this.start = start;
|
||||
}
|
||||
};
|
||||
|
||||
Selection.prototype.expand_right = function() {
|
||||
var node;
|
||||
node = this.walk(this.end, /\S/);
|
||||
return this.end = this.walk(node, /\s/);
|
||||
};
|
||||
|
||||
Selection.prototype.expand_left = function() {
|
||||
var node;
|
||||
node = this.walk(this.start, /\S/, true);
|
||||
return this.start = this.walk(node, /\s/, true);
|
||||
};
|
||||
|
||||
Selection.prototype.walk = function(needle, til, backward) {
|
||||
var i, node, text;
|
||||
if (backward == null) {
|
||||
backward = false;
|
||||
}
|
||||
if (needle.node.firstChild) {
|
||||
node = needle.node.firstChild;
|
||||
} else {
|
||||
node = needle.node;
|
||||
}
|
||||
text = node.textContent;
|
||||
i = needle.offset;
|
||||
if (backward) {
|
||||
while (node) {
|
||||
while (i > 0) {
|
||||
if (text[--i].match(til)) {
|
||||
return {
|
||||
node: node,
|
||||
offset: i + 1
|
||||
};
|
||||
}
|
||||
}
|
||||
node = previous_leaf(node);
|
||||
text = node.textContent;
|
||||
i = text.length;
|
||||
}
|
||||
} else {
|
||||
while (node) {
|
||||
while (i < text.length) {
|
||||
if (text[i++].match(til)) {
|
||||
return {
|
||||
node: node,
|
||||
offset: i - 1
|
||||
};
|
||||
}
|
||||
}
|
||||
node = next_leaf(node);
|
||||
text = node.textContent;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
return needle;
|
||||
};
|
||||
|
||||
return Selection;
|
||||
|
||||
})();
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
var _ref, _ref1;
|
||||
if (_ref = e.keyCode, __indexOf.call([16, 17, 18, 19], _ref) >= 0) {
|
||||
return true;
|
||||
}
|
||||
if (e.shiftKey && e.keyCode === 13 && !selection && !getSelection().isCollapsed) {
|
||||
butterfly.handler(getSelection().toString());
|
||||
getSelection().removeAllRanges();
|
||||
return cancel(e);
|
||||
}
|
||||
if (selection) {
|
||||
selection.reset();
|
||||
if (!e.ctrlKey && e.shiftKey && (37 <= (_ref1 = e.keyCode) && _ref1 <= 40)) {
|
||||
return true;
|
||||
}
|
||||
if (e.shiftKey && e.ctrlKey) {
|
||||
if (e.keyCode === 38) {
|
||||
selection.up();
|
||||
} else if (e.keyCode === 40) {
|
||||
selection.down();
|
||||
}
|
||||
} else if (e.keyCode === 39) {
|
||||
selection.shrink_left();
|
||||
} else if (e.keyCode === 38) {
|
||||
selection.expand_left();
|
||||
} else if (e.keyCode === 37) {
|
||||
selection.shrink_right();
|
||||
} else if (e.keyCode === 40) {
|
||||
selection.expand_right();
|
||||
} else {
|
||||
return cancel(e);
|
||||
}
|
||||
if (selection != null) {
|
||||
selection.apply();
|
||||
}
|
||||
return cancel(e);
|
||||
}
|
||||
if (!selection && e.ctrlKey && e.shiftKey && e.keyCode === 38) {
|
||||
selection = new Selection();
|
||||
selection.select_line(butterfly.y - 1);
|
||||
selection.apply();
|
||||
return cancel(e);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', function(e) {
|
||||
var _ref, _ref1;
|
||||
if (_ref = e.keyCode, __indexOf.call([16, 17, 18, 19], _ref) >= 0) {
|
||||
return true;
|
||||
}
|
||||
if (selection) {
|
||||
if (e.keyCode === 13) {
|
||||
butterfly.handler(selection.text());
|
||||
selection.destroy();
|
||||
selection = null;
|
||||
return cancel(e);
|
||||
}
|
||||
if (_ref1 = e.keyCode, __indexOf.call([37, 38, 39, 40], _ref1) < 0) {
|
||||
selection.destroy();
|
||||
selection = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
document.addEventListener('dblclick', function(e) {
|
||||
var anchorNode, anchorOffset, new_range, range, sel;
|
||||
if (e.ctrlKey || e.altkey) {
|
||||
return;
|
||||
}
|
||||
sel = getSelection();
|
||||
if (sel.isCollapsed || sel.toString().match(/\s/)) {
|
||||
return;
|
||||
}
|
||||
range = document.createRange();
|
||||
range.setStart(sel.anchorNode, sel.anchorOffset);
|
||||
range.setEnd(sel.focusNode, sel.focusOffset);
|
||||
if (range.collapsed) {
|
||||
sel.removeAllRanges();
|
||||
new_range = document.createRange();
|
||||
new_range.setStart(sel.focusNode, sel.focusOffset);
|
||||
new_range.setEnd(sel.anchorNode, sel.anchorOffset);
|
||||
sel.addRange(new_range);
|
||||
}
|
||||
range.detach();
|
||||
while (!(sel.toString().match(/\s/) || !sel.toString())) {
|
||||
sel.modify('extend', 'forward', 'character');
|
||||
}
|
||||
sel.modify('extend', 'backward', 'character');
|
||||
anchorNode = sel.anchorNode;
|
||||
anchorOffset = sel.anchorOffset;
|
||||
sel.collapseToEnd();
|
||||
sel.extend(anchorNode, anchorOffset);
|
||||
while (!(sel.toString().match(/\s/) || !sel.toString())) {
|
||||
sel.modify('extend', 'backward', 'character');
|
||||
}
|
||||
return sel.modify('extend', 'forward', 'character');
|
||||
});
|
||||
|
||||
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
||||
ctrl = false;
|
||||
alt = false;
|
||||
first = true;
|
||||
virtual_input = document.createElement('input');
|
||||
virtual_input.type = 'password';
|
||||
virtual_input.style.position = 'fixed';
|
||||
virtual_input.style.top = 0;
|
||||
virtual_input.style.left = 0;
|
||||
virtual_input.style.border = 'none';
|
||||
virtual_input.style.outline = 'none';
|
||||
virtual_input.style.opacity = 0;
|
||||
virtual_input.value = '0';
|
||||
document.body.appendChild(virtual_input);
|
||||
virtual_input.addEventListener('blur', function() {
|
||||
return setTimeout(((function(_this) {
|
||||
return function() {
|
||||
return _this.focus();
|
||||
};
|
||||
})(this)), 10);
|
||||
});
|
||||
addEventListener('click', function() {
|
||||
return virtual_input.focus();
|
||||
});
|
||||
addEventListener('touchstart', function(e) {
|
||||
if (e.touches.length === 2) {
|
||||
return ctrl = true;
|
||||
} else if (e.touches.length === 3) {
|
||||
ctrl = false;
|
||||
return alt = true;
|
||||
} else if (e.touches.length === 4) {
|
||||
ctrl = true;
|
||||
return alt = true;
|
||||
}
|
||||
});
|
||||
virtual_input.addEventListener('keydown', function(e) {
|
||||
butterfly.keyDown(e);
|
||||
return true;
|
||||
});
|
||||
virtual_input.addEventListener('input', function(e) {
|
||||
var len;
|
||||
len = this.value.length;
|
||||
if (len === 0) {
|
||||
e.keyCode = 8;
|
||||
butterfly.keyDown(e);
|
||||
this.value = '0';
|
||||
return true;
|
||||
}
|
||||
e.keyCode = this.value.charAt(1).charCodeAt(0);
|
||||
if ((ctrl || alt) && !first) {
|
||||
e.keyCode = this.value.charAt(1).charCodeAt(0);
|
||||
e.ctrlKey = ctrl;
|
||||
e.altKey = alt;
|
||||
if (e.keyCode >= 97 && e.keyCode <= 122) {
|
||||
e.keyCode -= 32;
|
||||
}
|
||||
butterfly.keyDown(e);
|
||||
this.value = '0';
|
||||
ctrl = alt = false;
|
||||
return true;
|
||||
}
|
||||
butterfly.keyPress(e);
|
||||
first = false;
|
||||
this.value = '0';
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
|
||||
//# sourceMappingURL=ext.js.map
|
||||
4
butterfly/static/ext.min.js
vendored
Normal file
4
butterfly/static/ext.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,3 +1,29 @@
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
@font-face {
|
||||
font-family: "SourceCodePro";
|
||||
src: url("/static/fonts/SourceCodePro-ExtraLight.otf") format("woff");
|
||||
@@ -37,6 +63,19 @@ body {
|
||||
font-family: "SourceCodePro";
|
||||
line-height: 1.2; }
|
||||
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
@@ -51,17 +90,37 @@ html, body {
|
||||
.terminal {
|
||||
outline: none; }
|
||||
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
.terminal {
|
||||
text-shadow: 0 0 6px rgba(255, 255, 255, 0.5);
|
||||
transition: 200ms; }
|
||||
.terminal.bell {
|
||||
-webkit-filter: blur(2px); }
|
||||
-webkit-filter: blur(2px);
|
||||
filter: blur(2px); }
|
||||
.terminal.skip {
|
||||
-webkit-filter: sepia(1); }
|
||||
-webkit-filter: sepia(1);
|
||||
filter: sepia(1); }
|
||||
.terminal.selection {
|
||||
-webkit-filter: saturate(2); }
|
||||
-webkit-filter: saturate(2);
|
||||
filter: saturate(2); }
|
||||
.terminal.alarm {
|
||||
-webkit-filter: hue-rotate(150deg);
|
||||
filter: hue-rotate(150deg); }
|
||||
.terminal.dead {
|
||||
-webkit-filter: grayscale(1); }
|
||||
-webkit-filter: grayscale(1);
|
||||
filter: grayscale(1); }
|
||||
.terminal.dead:after {
|
||||
content: "CLOSED";
|
||||
font-size: 15em;
|
||||
@@ -77,6 +136,19 @@ html, body {
|
||||
opacity: 0.2;
|
||||
font-weight: 900; }
|
||||
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
#wrapper {
|
||||
background-color: #110f13; }
|
||||
|
||||
@@ -84,6 +156,20 @@ html, body {
|
||||
background-color: #110f13;
|
||||
color: #f4ead5; }
|
||||
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* Here are the 16 "normal" colors for theming */
|
||||
.bg-color-0 {
|
||||
background-color: #2e3436; }
|
||||
.bg-color-0.reverse-video {
|
||||
@@ -260,6 +346,21 @@ html, body {
|
||||
.fg-color-15.reverse-video {
|
||||
background-color: #eeeeec !important; }
|
||||
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* Here are the 240 xterm colors */
|
||||
/* See http://upload.wikimedia.org/wikipedia/en/1/15/Xterm_256color_chart.svg */
|
||||
.bg-color-16 {
|
||||
background-color: black; }
|
||||
.bg-color-16.reverse-video {
|
||||
@@ -2922,12 +3023,38 @@ html, body {
|
||||
.fg-color-257.reverse-video {
|
||||
background-color: #f4ead5 !important; }
|
||||
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
.focus .cursor {
|
||||
transition: 300ms; }
|
||||
|
||||
.cursor.reverse-video {
|
||||
box-shadow: 0 0 0.5 #f4ead5; }
|
||||
|
||||
/* *-* coding: utf-8 *-* */
|
||||
/* This file is part of butterfly */
|
||||
/* butterfly Copyright (C) 2014 Florian Mounier */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
.bold {
|
||||
font-weight: bold; }
|
||||
|
||||
|
||||
@@ -1,7 +1,97 @@
|
||||
(function() {
|
||||
var $, Selection, State, Terminal, alt, bench, cancel, cbench, cols, ctl, ctrl, first, next_leaf, open_ts, previous_leaf, quit, rows, s, selection, send, term, virtual_input, ws, ws_url,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__slice = [].slice;
|
||||
var $, State, Terminal, cancel, cols, open_ts, quit, rows, s,
|
||||
__slice = [].slice,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
cols = rows = null;
|
||||
|
||||
quit = false;
|
||||
|
||||
open_ts = (new Date()).getTime();
|
||||
|
||||
$ = document.querySelectorAll.bind(document);
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var bench, cbench, ctl, send, term, ws, ws_url;
|
||||
send = function(data) {
|
||||
return ws.send('S' + data);
|
||||
};
|
||||
ctl = function() {
|
||||
var args, params, type;
|
||||
type = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
params = args.join(',');
|
||||
if (type === 'Resize') {
|
||||
return ws.send('R' + params);
|
||||
}
|
||||
};
|
||||
if (location.protocol === 'https:') {
|
||||
ws_url = 'wss://';
|
||||
} else {
|
||||
ws_url = 'ws://';
|
||||
}
|
||||
ws_url += document.location.host + '/ws' + location.pathname;
|
||||
ws = new WebSocket(ws_url);
|
||||
ws.addEventListener('open', function() {
|
||||
console.log("WebSocket open", arguments);
|
||||
ws.send('R' + term.cols + ',' + term.rows);
|
||||
return open_ts = (new Date()).getTime();
|
||||
});
|
||||
ws.addEventListener('error', function() {
|
||||
return console.log("WebSocket error", arguments);
|
||||
});
|
||||
ws.addEventListener('message', function(e) {
|
||||
return setTimeout(function() {
|
||||
return term.write(e.data);
|
||||
}, 1);
|
||||
});
|
||||
ws.addEventListener('close', function() {
|
||||
console.log("WebSocket closed", arguments);
|
||||
setTimeout(function() {
|
||||
term.write('Closed');
|
||||
term.skipNextKey = true;
|
||||
return term.element.classList.add('dead');
|
||||
}, 1);
|
||||
quit = true;
|
||||
if ((new Date()).getTime() - open_ts > 60 * 1000) {
|
||||
return open('', '_self').close();
|
||||
}
|
||||
});
|
||||
term = new Terminal($('#wrapper')[0], send, ctl);
|
||||
addEventListener('beforeunload', function() {
|
||||
if (!quit) {
|
||||
return 'This will exit the terminal session';
|
||||
}
|
||||
});
|
||||
bench = function(n) {
|
||||
var rnd, t0;
|
||||
if (n == null) {
|
||||
n = 100000000;
|
||||
}
|
||||
rnd = '';
|
||||
while (rnd.length < n) {
|
||||
rnd += Math.random().toString(36).substring(2);
|
||||
}
|
||||
t0 = (new Date()).getTime();
|
||||
term.write(rnd);
|
||||
return console.log("" + n + " chars in " + ((new Date()).getTime() - t0) + " ms");
|
||||
};
|
||||
cbench = function(n) {
|
||||
var rnd, t0;
|
||||
if (n == null) {
|
||||
n = 100000000;
|
||||
}
|
||||
rnd = '';
|
||||
while (rnd.length < n) {
|
||||
rnd += "\x1b[" + (30 + parseInt(Math.random() * 20)) + "m";
|
||||
rnd += Math.random().toString(36).substring(2);
|
||||
}
|
||||
t0 = (new Date()).getTime();
|
||||
term.write(rnd);
|
||||
return console.log("" + n + " chars + colors in " + ((new Date()).getTime() - t0) + " ms");
|
||||
};
|
||||
term.ws = ws;
|
||||
return window.butterfly = term;
|
||||
});
|
||||
|
||||
cancel = function(ev) {
|
||||
if (ev.preventDefault) {
|
||||
@@ -39,6 +129,7 @@
|
||||
this.element.className = 'terminal focus';
|
||||
this.element.style.outline = 'none';
|
||||
this.element.setAttribute('tabindex', 0);
|
||||
this.element.setAttribute('spellcheck', 'false');
|
||||
this.parent.appendChild(this.element);
|
||||
div = this.document.createElement('div');
|
||||
div.className = 'line';
|
||||
@@ -2405,486 +2496,7 @@
|
||||
|
||||
})();
|
||||
|
||||
selection = null;
|
||||
|
||||
previous_leaf = function(node) {
|
||||
var previous;
|
||||
previous = node.previousSibling;
|
||||
if (!previous) {
|
||||
previous = node.parentNode.previousSibling;
|
||||
}
|
||||
if (!previous) {
|
||||
previous = node.parentNode.parentNode.previousSibling;
|
||||
}
|
||||
while (previous.lastChild) {
|
||||
previous = previous.lastChild;
|
||||
}
|
||||
return previous;
|
||||
};
|
||||
|
||||
next_leaf = function(node) {
|
||||
var next;
|
||||
next = node.nextSibling;
|
||||
if (!next) {
|
||||
next = node.parentNode.nextSibling;
|
||||
}
|
||||
if (!next) {
|
||||
next = node.parentNode.parentNode.nextSibling;
|
||||
}
|
||||
while (next.firstChild) {
|
||||
next = next.firstChild;
|
||||
}
|
||||
return next;
|
||||
};
|
||||
|
||||
Selection = (function() {
|
||||
function Selection() {
|
||||
term.element.classList.add('selection');
|
||||
this.selection = getSelection();
|
||||
}
|
||||
|
||||
Selection.prototype.reset = function() {
|
||||
var fake_range, _ref, _results;
|
||||
this.selection = getSelection();
|
||||
fake_range = document.createRange();
|
||||
fake_range.setStart(this.selection.anchorNode, this.selection.anchorOffset);
|
||||
fake_range.setEnd(this.selection.focusNode, this.selection.focusOffset);
|
||||
this.start = {
|
||||
node: this.selection.anchorNode,
|
||||
offset: this.selection.anchorOffset
|
||||
};
|
||||
this.end = {
|
||||
node: this.selection.focusNode,
|
||||
offset: this.selection.focusOffset
|
||||
};
|
||||
if (fake_range.collapsed) {
|
||||
_ref = [this.end, this.start], this.start = _ref[0], this.end = _ref[1];
|
||||
}
|
||||
this.start_line = this.start.node;
|
||||
while (!this.start_line.classList || __indexOf.call(this.start_line.classList, 'line') < 0) {
|
||||
this.start_line = this.start_line.parentNode;
|
||||
}
|
||||
this.end_line = this.end.node;
|
||||
_results = [];
|
||||
while (!this.end_line.classList || __indexOf.call(this.end_line.classList, 'line') < 0) {
|
||||
_results.push(this.end_line = this.end_line.parentNode);
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Selection.prototype.clear = function() {
|
||||
return this.selection.removeAllRanges();
|
||||
};
|
||||
|
||||
Selection.prototype.destroy = function() {
|
||||
term.element.classList.remove('selection');
|
||||
return this.clear();
|
||||
};
|
||||
|
||||
Selection.prototype.text = function() {
|
||||
return this.selection.toString();
|
||||
};
|
||||
|
||||
Selection.prototype.up = function() {
|
||||
return this.go(-1);
|
||||
};
|
||||
|
||||
Selection.prototype.down = function() {
|
||||
return this.go(+1);
|
||||
};
|
||||
|
||||
Selection.prototype.go = function(n) {
|
||||
var index;
|
||||
index = term.children.indexOf(this.start_line) + n;
|
||||
if (!((0 <= index && index < term.children.length))) {
|
||||
return;
|
||||
}
|
||||
while (!term.children[index].textContent.match(/\S/)) {
|
||||
index += n;
|
||||
if (!((0 <= index && index < term.children.length))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return this.select_line(index);
|
||||
};
|
||||
|
||||
Selection.prototype.apply = function() {
|
||||
var range;
|
||||
this.clear();
|
||||
range = document.createRange();
|
||||
range.setStart(this.start.node, this.start.offset);
|
||||
range.setEnd(this.end.node, this.end.offset);
|
||||
return this.selection.addRange(range);
|
||||
};
|
||||
|
||||
Selection.prototype.select_line = function(index) {
|
||||
var line, line_end, line_start;
|
||||
line = term.children[index];
|
||||
line_start = {
|
||||
node: line.firstChild,
|
||||
offset: 0
|
||||
};
|
||||
line_end = {
|
||||
node: line.lastChild,
|
||||
offset: line.lastChild.textContent.length
|
||||
};
|
||||
this.start = this.walk(line_start, /\S/);
|
||||
return this.end = this.walk(line_end, /\S/, true);
|
||||
};
|
||||
|
||||
Selection.prototype.collapsed = function(start, end) {
|
||||
var fake_range;
|
||||
fake_range = document.createRange();
|
||||
fake_range.setStart(start.node, start.offset);
|
||||
fake_range.setEnd(end.node, end.offset);
|
||||
return fake_range.collapsed;
|
||||
};
|
||||
|
||||
Selection.prototype.shrink_right = function() {
|
||||
var end, node;
|
||||
node = this.walk(this.end, /\s/, true);
|
||||
end = this.walk(node, /\S/, true);
|
||||
if (!this.collapsed(this.start, end)) {
|
||||
return this.end = end;
|
||||
}
|
||||
};
|
||||
|
||||
Selection.prototype.shrink_left = function() {
|
||||
var node, start;
|
||||
node = this.walk(this.start, /\s/);
|
||||
start = this.walk(node, /\S/);
|
||||
if (!this.collapsed(start, this.end)) {
|
||||
return this.start = start;
|
||||
}
|
||||
};
|
||||
|
||||
Selection.prototype.expand_right = function() {
|
||||
var node;
|
||||
node = this.walk(this.end, /\S/);
|
||||
return this.end = this.walk(node, /\s/);
|
||||
};
|
||||
|
||||
Selection.prototype.expand_left = function() {
|
||||
var node;
|
||||
node = this.walk(this.start, /\S/, true);
|
||||
return this.start = this.walk(node, /\s/, true);
|
||||
};
|
||||
|
||||
Selection.prototype.walk = function(needle, til, backward) {
|
||||
var i, node, text;
|
||||
if (backward == null) {
|
||||
backward = false;
|
||||
}
|
||||
if (needle.node.firstChild) {
|
||||
node = needle.node.firstChild;
|
||||
} else {
|
||||
node = needle.node;
|
||||
}
|
||||
text = node.textContent;
|
||||
i = needle.offset;
|
||||
if (backward) {
|
||||
while (node) {
|
||||
while (i > 0) {
|
||||
if (text[--i].match(til)) {
|
||||
return {
|
||||
node: node,
|
||||
offset: i + 1
|
||||
};
|
||||
}
|
||||
}
|
||||
node = previous_leaf(node);
|
||||
text = node.textContent;
|
||||
i = text.length;
|
||||
}
|
||||
} else {
|
||||
while (node) {
|
||||
while (i < text.length) {
|
||||
if (text[i++].match(til)) {
|
||||
return {
|
||||
node: node,
|
||||
offset: i - 1
|
||||
};
|
||||
}
|
||||
}
|
||||
node = next_leaf(node);
|
||||
text = node.textContent;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
return needle;
|
||||
};
|
||||
|
||||
return Selection;
|
||||
|
||||
})();
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
var _ref, _ref1;
|
||||
if (_ref = e.keyCode, __indexOf.call([16, 17, 18, 19], _ref) >= 0) {
|
||||
return true;
|
||||
}
|
||||
if (e.shiftKey && e.keyCode === 13 && !selection && !getSelection().isCollapsed) {
|
||||
term.handler(getSelection().toString());
|
||||
getSelection().removeAllRanges();
|
||||
return cancel(e);
|
||||
}
|
||||
if (selection) {
|
||||
selection.reset();
|
||||
if (!e.ctrlKey && e.shiftKey && (37 <= (_ref1 = e.keyCode) && _ref1 <= 40)) {
|
||||
return true;
|
||||
}
|
||||
if (e.shiftKey && e.ctrlKey) {
|
||||
if (e.keyCode === 38) {
|
||||
selection.up();
|
||||
} else if (e.keyCode === 40) {
|
||||
selection.down();
|
||||
}
|
||||
} else if (e.keyCode === 39) {
|
||||
selection.shrink_left();
|
||||
} else if (e.keyCode === 38) {
|
||||
selection.expand_left();
|
||||
} else if (e.keyCode === 37) {
|
||||
selection.shrink_right();
|
||||
} else if (e.keyCode === 40) {
|
||||
selection.expand_right();
|
||||
} else {
|
||||
return cancel(e);
|
||||
}
|
||||
if (selection != null) {
|
||||
selection.apply();
|
||||
}
|
||||
return cancel(e);
|
||||
}
|
||||
if (!selection && e.ctrlKey && e.shiftKey && e.keyCode === 38) {
|
||||
selection = new Selection();
|
||||
selection.select_line(term.y - 1);
|
||||
selection.apply();
|
||||
return cancel(e);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', function(e) {
|
||||
var _ref, _ref1;
|
||||
if (_ref = e.keyCode, __indexOf.call([16, 17, 18, 19], _ref) >= 0) {
|
||||
return true;
|
||||
}
|
||||
if (selection) {
|
||||
if (e.keyCode === 13) {
|
||||
term.handler(selection.text());
|
||||
selection.destroy();
|
||||
selection = null;
|
||||
return cancel(e);
|
||||
}
|
||||
if (_ref1 = e.keyCode, __indexOf.call([37, 38, 39, 40], _ref1) < 0) {
|
||||
selection.destroy();
|
||||
selection = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
document.addEventListener('dblclick', function(e) {
|
||||
var anchorNode, anchorOffset, new_range, range, sel;
|
||||
if (e.ctrlKey || e.altkey) {
|
||||
return;
|
||||
}
|
||||
sel = getSelection();
|
||||
if (sel.isCollapsed || sel.toString().match(/\s/)) {
|
||||
return;
|
||||
}
|
||||
range = document.createRange();
|
||||
range.setStart(sel.anchorNode, sel.anchorOffset);
|
||||
range.setEnd(sel.focusNode, sel.focusOffset);
|
||||
if (range.collapsed) {
|
||||
sel.removeAllRanges();
|
||||
new_range = document.createRange();
|
||||
new_range.setStart(sel.focusNode, sel.focusOffset);
|
||||
new_range.setEnd(sel.anchorNode, sel.anchorOffset);
|
||||
sel.addRange(new_range);
|
||||
}
|
||||
range.detach();
|
||||
while (!(sel.toString().match(/\s/) || !sel.toString())) {
|
||||
sel.modify('extend', 'forward', 'character');
|
||||
}
|
||||
sel.modify('extend', 'backward', 'character');
|
||||
anchorNode = sel.anchorNode;
|
||||
anchorOffset = sel.anchorOffset;
|
||||
sel.collapseToEnd();
|
||||
sel.extend(anchorNode, anchorOffset);
|
||||
while (!(sel.toString().match(/\s/) || !sel.toString())) {
|
||||
sel.modify('extend', 'backward', 'character');
|
||||
}
|
||||
return sel.modify('extend', 'forward', 'character');
|
||||
});
|
||||
|
||||
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
||||
ctrl = false;
|
||||
alt = false;
|
||||
first = true;
|
||||
virtual_input = document.createElement('input');
|
||||
virtual_input.type = 'password';
|
||||
virtual_input.style.position = 'fixed';
|
||||
virtual_input.style.top = 0;
|
||||
virtual_input.style.left = 0;
|
||||
virtual_input.style.border = 'none';
|
||||
virtual_input.style.outline = 'none';
|
||||
virtual_input.style.opacity = 0;
|
||||
virtual_input.value = '0';
|
||||
document.body.appendChild(virtual_input);
|
||||
virtual_input.addEventListener('blur', function() {
|
||||
return setTimeout(((function(_this) {
|
||||
return function() {
|
||||
return _this.focus();
|
||||
};
|
||||
})(this)), 10);
|
||||
});
|
||||
addEventListener('click', function() {
|
||||
return virtual_input.focus();
|
||||
});
|
||||
addEventListener('touchstart', function(e) {
|
||||
if (e.touches.length === 2) {
|
||||
return ctrl = true;
|
||||
} else if (e.touches.length === 3) {
|
||||
ctrl = false;
|
||||
return alt = true;
|
||||
} else if (e.touches.length === 4) {
|
||||
ctrl = true;
|
||||
return alt = true;
|
||||
}
|
||||
});
|
||||
virtual_input.addEventListener('keydown', function(e) {
|
||||
term.keyDown(e);
|
||||
return true;
|
||||
});
|
||||
virtual_input.addEventListener('input', function(e) {
|
||||
var len;
|
||||
len = this.value.length;
|
||||
if (len === 0) {
|
||||
e.keyCode = 8;
|
||||
term.keyDown(e);
|
||||
this.value = '0';
|
||||
return true;
|
||||
}
|
||||
e.keyCode = this.value.charAt(1).charCodeAt(0);
|
||||
if ((ctrl || alt) && !first) {
|
||||
e.keyCode = this.value.charAt(1).charCodeAt(0);
|
||||
e.ctrlKey = ctrl;
|
||||
e.altKey = alt;
|
||||
if (e.keyCode >= 97 && e.keyCode <= 122) {
|
||||
e.keyCode -= 32;
|
||||
}
|
||||
term.keyDown(e);
|
||||
this.value = '0';
|
||||
ctrl = alt = false;
|
||||
return true;
|
||||
}
|
||||
term.keyPress(e);
|
||||
first = false;
|
||||
this.value = '0';
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
cols = rows = null;
|
||||
|
||||
quit = false;
|
||||
|
||||
open_ts = (new Date()).getTime();
|
||||
|
||||
$ = document.querySelectorAll.bind(document);
|
||||
|
||||
send = function(data) {
|
||||
return ws.send('S' + data);
|
||||
};
|
||||
|
||||
ctl = function() {
|
||||
var args, params, type;
|
||||
type = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
params = args.join(',');
|
||||
if (type === 'Resize') {
|
||||
return ws.send('R' + params);
|
||||
}
|
||||
};
|
||||
|
||||
if (location.protocol === 'https:') {
|
||||
ws_url = 'wss://';
|
||||
} else {
|
||||
ws_url = 'ws://';
|
||||
}
|
||||
|
||||
ws_url += document.location.host + '/ws' + location.pathname;
|
||||
|
||||
ws = new WebSocket(ws_url);
|
||||
|
||||
ws.addEventListener('open', function() {
|
||||
console.log("WebSocket open", arguments);
|
||||
ws.send('R' + term.cols + ',' + term.rows);
|
||||
return open_ts = (new Date()).getTime();
|
||||
});
|
||||
|
||||
ws.addEventListener('error', function() {
|
||||
return console.log("WebSocket error", arguments);
|
||||
});
|
||||
|
||||
ws.addEventListener('message', function(e) {
|
||||
return setTimeout(function() {
|
||||
return term.write(e.data);
|
||||
}, 1);
|
||||
});
|
||||
|
||||
ws.addEventListener('close', function() {
|
||||
console.log("WebSocket closed", arguments);
|
||||
setTimeout(function() {
|
||||
term.write('Closed');
|
||||
term.skipNextKey = true;
|
||||
return term.element.classList.add('dead');
|
||||
}, 1);
|
||||
quit = true;
|
||||
if ((new Date()).getTime() - open_ts > 60 * 1000) {
|
||||
return open('', '_self').close();
|
||||
}
|
||||
});
|
||||
|
||||
term = new Terminal($('#wrapper')[0], send, ctl);
|
||||
|
||||
addEventListener('beforeunload', function() {
|
||||
if (!quit) {
|
||||
return 'This will exit the terminal session';
|
||||
}
|
||||
});
|
||||
|
||||
bench = function(n) {
|
||||
var rnd, t0;
|
||||
if (n == null) {
|
||||
n = 100000000;
|
||||
}
|
||||
rnd = '';
|
||||
while (rnd.length < n) {
|
||||
rnd += Math.random().toString(36).substring(2);
|
||||
}
|
||||
t0 = (new Date()).getTime();
|
||||
term.write(rnd);
|
||||
return console.log("" + n + " chars in " + ((new Date()).getTime() - t0) + " ms");
|
||||
};
|
||||
|
||||
cbench = function(n) {
|
||||
var rnd, t0;
|
||||
if (n == null) {
|
||||
n = 100000000;
|
||||
}
|
||||
rnd = '';
|
||||
while (rnd.length < n) {
|
||||
rnd += "\x1b[" + (30 + parseInt(Math.random() * 20)) + "m";
|
||||
rnd += Math.random().toString(36).substring(2);
|
||||
}
|
||||
t0 = (new Date()).getTime();
|
||||
term.write(rnd);
|
||||
return console.log("" + n + " chars + colors in " + ((new Date()).getTime() - t0) + " ms");
|
||||
};
|
||||
|
||||
window.butterfly = term;
|
||||
window.Terminal = Terminal;
|
||||
|
||||
}).call(this);
|
||||
|
||||
|
||||
4
butterfly/static/main.min.js
vendored
4
butterfly/static/main.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -17,5 +17,7 @@
|
||||
<main id="wrapper"> </main>
|
||||
<script src="{{ static_url('main.%sjs' % (
|
||||
'' if options.unminified else 'min.')) }}"></script>
|
||||
<script src="{{ static_url('ext.%sjs' % (
|
||||
'' if options.unminified else 'min.')) }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -37,15 +37,9 @@ def get_style():
|
||||
if style is None:
|
||||
return
|
||||
|
||||
if style.endswith('.sass'):
|
||||
log.error('SASS syntax is not yet supported (see: '
|
||||
'https://github.com/hcatlin/libsass/issues/16'
|
||||
') please use SCSS')
|
||||
return
|
||||
|
||||
if style.endswith('.scss'):
|
||||
scss_path = os.path.join(
|
||||
os.path.dirname(__file__), 'scss')
|
||||
if style.endswith('.scss') or style.endswith('.sass'):
|
||||
sass_path = os.path.join(
|
||||
os.path.dirname(__file__), 'sass')
|
||||
try:
|
||||
import sass
|
||||
except:
|
||||
@@ -54,9 +48,11 @@ def get_style():
|
||||
return
|
||||
|
||||
try:
|
||||
return sass.compile(filename=style, include_paths=[scss_path])
|
||||
return sass.compile(filename=style, include_paths=[sass_path])
|
||||
except sass.CompileError:
|
||||
log.error('Unable to compile style.scss', exc_info=True)
|
||||
log.error(
|
||||
'Unable to compile style.scss (filename: %s, paths: %r) ' % (
|
||||
style, [sass_path]), exc_info=True)
|
||||
return
|
||||
|
||||
with open(style) as s:
|
||||
|
||||
36
coffees/ext/alarm.coffee
Normal file
36
coffees/ext/alarm.coffee
Normal file
@@ -0,0 +1,36 @@
|
||||
set_alarm = (notification) ->
|
||||
alarm = (data) ->
|
||||
butterfly.element.classList.remove 'alarm'
|
||||
note = "New activity on butterfly terminal [#{ butterfly.title }]"
|
||||
|
||||
if notification
|
||||
new Notification(
|
||||
note,
|
||||
body: data.data,
|
||||
icon: '/static/images/favicon.png')
|
||||
else
|
||||
alert(note + '\n' + data.data)
|
||||
|
||||
butterfly.ws.removeEventListener 'message', alarm
|
||||
|
||||
butterfly.ws.addEventListener 'message', alarm
|
||||
butterfly.element.classList.add 'alarm'
|
||||
|
||||
|
||||
cancel = (ev) ->
|
||||
ev.preventDefault() if ev.preventDefault
|
||||
ev.stopPropagation() if ev.stopPropagation
|
||||
ev.cancelBubble = true
|
||||
false
|
||||
|
||||
|
||||
document.addEventListener 'keydown', (e) ->
|
||||
return true unless e.altKey and e.keyCode is 65
|
||||
|
||||
if Notification and Notification.permission is 'default'
|
||||
Notification.requestPermission ->
|
||||
set_alarm(Notification.permission is 'granted')
|
||||
else
|
||||
set_alarm(Notification.permission is 'granted')
|
||||
|
||||
cancel(e)
|
||||
@@ -16,6 +16,12 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
selection = null
|
||||
|
||||
cancel = (ev) ->
|
||||
ev.preventDefault() if ev.preventDefault
|
||||
ev.stopPropagation() if ev.stopPropagation
|
||||
ev.cancelBubble = true
|
||||
false
|
||||
|
||||
previous_leaf = (node) ->
|
||||
previous = node.previousSibling
|
||||
if not previous
|
||||
@@ -38,7 +44,7 @@ next_leaf = (node) ->
|
||||
|
||||
class Selection
|
||||
constructor: ->
|
||||
term.element.classList.add('selection')
|
||||
butterfly.element.classList.add('selection')
|
||||
@selection = getSelection()
|
||||
|
||||
reset: ->
|
||||
@@ -68,7 +74,7 @@ class Selection
|
||||
@selection.removeAllRanges()
|
||||
|
||||
destroy: ->
|
||||
term.element.classList.remove('selection')
|
||||
butterfly.element.classList.remove('selection')
|
||||
@clear()
|
||||
|
||||
text: ->
|
||||
@@ -81,12 +87,12 @@ class Selection
|
||||
@go +1
|
||||
|
||||
go: (n) ->
|
||||
index = term.children.indexOf(@start_line) + n
|
||||
return unless 0 <= index < term.children.length
|
||||
index = butterfly.children.indexOf(@start_line) + n
|
||||
return unless 0 <= index < butterfly.children.length
|
||||
|
||||
until term.children[index].textContent.match /\S/
|
||||
until butterfly.children[index].textContent.match /\S/
|
||||
index += n
|
||||
return unless 0 <= index < term.children.length
|
||||
return unless 0 <= index < butterfly.children.length
|
||||
|
||||
@select_line index
|
||||
|
||||
@@ -98,7 +104,7 @@ class Selection
|
||||
@selection.addRange range
|
||||
|
||||
select_line: (index) ->
|
||||
line = term.children[index]
|
||||
line = butterfly.children[index]
|
||||
line_start =
|
||||
node: line.firstChild
|
||||
offset: 0
|
||||
@@ -170,7 +176,7 @@ document.addEventListener 'keydown', (e) ->
|
||||
# Paste natural selection too if shiftkey
|
||||
if e.shiftKey and e.keyCode is 13 and
|
||||
not selection and not getSelection().isCollapsed
|
||||
term.handler getSelection().toString()
|
||||
butterfly.handler getSelection().toString()
|
||||
getSelection().removeAllRanges()
|
||||
return cancel e
|
||||
|
||||
@@ -200,7 +206,7 @@ document.addEventListener 'keydown', (e) ->
|
||||
# Start selection mode with shift up
|
||||
if not selection and e.ctrlKey and e.shiftKey and e.keyCode == 38
|
||||
selection = new Selection()
|
||||
selection.select_line term.y - 1
|
||||
selection.select_line butterfly.y - 1
|
||||
selection.apply()
|
||||
return cancel e
|
||||
true
|
||||
@@ -210,7 +216,7 @@ document.addEventListener 'keyup', (e) ->
|
||||
|
||||
if selection
|
||||
if e.keyCode == 13
|
||||
term.handler selection.text()
|
||||
butterfly.handler selection.text()
|
||||
selection.destroy()
|
||||
selection = null
|
||||
return cancel e
|
||||
@@ -49,7 +49,7 @@ if /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
|
||||
alt = true
|
||||
|
||||
virtual_input.addEventListener 'keydown', (e) ->
|
||||
term.keyDown(e)
|
||||
butterfly.keyDown(e)
|
||||
return true
|
||||
|
||||
virtual_input.addEventListener 'input', (e) ->
|
||||
@@ -57,7 +57,7 @@ if /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
|
||||
|
||||
if len == 0
|
||||
e.keyCode = 8
|
||||
term.keyDown e
|
||||
butterfly.keyDown e
|
||||
@value = '0'
|
||||
return true
|
||||
|
||||
@@ -69,12 +69,12 @@ if /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
|
||||
e.altKey = alt
|
||||
if e.keyCode >= 97 && e.keyCode <= 122
|
||||
e.keyCode -= 32
|
||||
term.keyDown e
|
||||
butterfly.keyDown e
|
||||
@value = '0'
|
||||
ctrl = alt = false
|
||||
return true
|
||||
|
||||
term.keyPress e
|
||||
butterfly.keyPress e
|
||||
first = false
|
||||
@value = '0'
|
||||
true
|
||||
@@ -21,6 +21,8 @@ open_ts = (new Date()).getTime()
|
||||
|
||||
$ = document.querySelectorAll.bind(document)
|
||||
|
||||
document.addEventListener 'DOMContentLoaded', ->
|
||||
|
||||
send = (data) ->
|
||||
ws.send 'S' + data
|
||||
|
||||
@@ -88,5 +90,5 @@ cbench = (n=100000000) ->
|
||||
term.write rnd
|
||||
console.log "#{n} chars + colors in #{(new Date()).getTime() - t0} ms"
|
||||
|
||||
|
||||
term.ws = ws
|
||||
window.butterfly = term
|
||||
|
||||
@@ -58,6 +58,7 @@ class Terminal
|
||||
@element.className = 'terminal focus'
|
||||
@element.style.outline = 'none'
|
||||
@element.setAttribute 'tabindex', 0
|
||||
@element.setAttribute 'spellcheck', 'false'
|
||||
|
||||
@parent.appendChild(@element)
|
||||
|
||||
@@ -3011,3 +3012,5 @@ class Terminal
|
||||
Swedish: null # (H or (7
|
||||
Swiss: null # (=
|
||||
ISOLatin: null # /A
|
||||
|
||||
window.Terminal = Terminal
|
||||
|
||||
Reference in New Issue
Block a user