mirror of
https://github.com/linuxserver/core.git
synced 2026-02-20 05:07:19 +08:00
389 lines
13 KiB
JavaScript
389 lines
13 KiB
JavaScript
define(function(require, exports, module) {
|
|
main.consumes = [
|
|
"Plugin", "c9", "settings", "ui",
|
|
"anims", "menus", "commands", "util", "tabManager"
|
|
];
|
|
main.provides = ["ace.gotoline"];
|
|
return main;
|
|
|
|
// @todo add commands for list navigation and bookmarking
|
|
// @todo fix pasting of line numbers
|
|
|
|
function main(options, imports, register) {
|
|
var c9 = imports.c9;
|
|
var Plugin = imports.Plugin;
|
|
var settings = imports.settings;
|
|
var ui = imports.ui;
|
|
var anims = imports.anims;
|
|
var menus = imports.menus;
|
|
var commands = imports.commands;
|
|
var tabs = imports.tabManager;
|
|
|
|
var skin = require("text!./skin.xml");
|
|
var markup = require("text!./gotoline.xml");
|
|
|
|
/***** Initialization *****/
|
|
|
|
var plugin = new Plugin("Ajax.org", main.consumes);
|
|
var emit = plugin.getEmitter();
|
|
|
|
var originalLine, originalColumn, control, lastLine, lineControl;
|
|
var nohide, originalPath;
|
|
var win, input, list, lines; // ui elements
|
|
|
|
var loaded = false, changed = false;
|
|
function load() {
|
|
if (loaded) return false;
|
|
loaded = true;
|
|
|
|
menus.addItemByPath("Goto/Goto Line...", new ui.item({
|
|
caption: "Goto Line...",
|
|
hint: "enter a line number and jump to it in the active document",
|
|
command: "gotoline"
|
|
}), 200, plugin);
|
|
|
|
commands.addCommand({
|
|
name: "gotoline",
|
|
bindKey: { mac: "Command-L", win: "Ctrl-G" },
|
|
isAvailable: function(editor) {
|
|
return editor && editor.type == "ace";
|
|
},
|
|
exec: function() {
|
|
gotoline();
|
|
}
|
|
}, plugin);
|
|
|
|
commands.addCommand({
|
|
name: "hideGotoLine",
|
|
group: "ignore",
|
|
bindKey: { mac: "ESC", win: "ESC" },
|
|
isAvailable: function(editor) { return win && win.visible; },
|
|
exec: function() {
|
|
hide();
|
|
var tab = tabs.focussedTab;
|
|
tab && tabs.focusTab(tab);
|
|
|
|
if (originalLine) {
|
|
execGotoLine(originalLine, originalColumn, true);
|
|
originalPath = originalColumn = originalLine = undefined;
|
|
}
|
|
}
|
|
}, plugin);
|
|
|
|
settings.on("read", function() {
|
|
lines = settings.getJson("state/gotoline") || [];
|
|
if (!Array.isArray(lines))
|
|
lines = [];
|
|
}, plugin);
|
|
|
|
settings.on("write", function() {
|
|
if (changed) {
|
|
settings.setJson("state/gotoline", lines);
|
|
changed = false;
|
|
}
|
|
}, plugin);
|
|
}
|
|
|
|
var drawn = false;
|
|
function draw() {
|
|
if (drawn) return;
|
|
drawn = true;
|
|
|
|
// Import Skin
|
|
ui.insertSkin({
|
|
name: "gotoline",
|
|
data: skin,
|
|
}, plugin);
|
|
|
|
// Create UI elements
|
|
ui.insertMarkup(null, markup, plugin);
|
|
|
|
win = plugin.getElement("window");
|
|
input = plugin.getElement("input");
|
|
list = plugin.getElement("list");
|
|
|
|
list.$ext.textContent = "";
|
|
list.drawContents = function() {
|
|
var ch = list.$ext.children;
|
|
for (var i = 0; i < lines.length; i++) {
|
|
var el = ch[i];
|
|
if (!el) el = document.createElement("div");
|
|
el.setAttribute("index", i);
|
|
el.textContent = lines[i];
|
|
el.className = i == list.selected ? "selected" : "";
|
|
if (el.parentNode != list.$ext)
|
|
list.$ext.appendChild(el);
|
|
}
|
|
while (ch.length > lines.length)
|
|
ch[lines.length].remove();
|
|
};
|
|
list.selected = -1;
|
|
|
|
list.$ext.addEventListener("mouseup", function(e) {
|
|
var i = parseInt(e.target.getAttribute("index"), 10);
|
|
if (i >= 0) {
|
|
list.selected = i;
|
|
input.setValue(lines[list.selected]);
|
|
list.drawContents();
|
|
execGotoLine(null, null, e.detail == 1);
|
|
if (e.detail != 1) hide();
|
|
}
|
|
});
|
|
|
|
list.$altExt.addEventListener("mousedown", function(e) {
|
|
input.focus();
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
});
|
|
|
|
input.addEventListener("keydown", function(e) {
|
|
var NotANumber = (e.keyCode > 57 || e.keyCode == 32)
|
|
&& (e.keyCode < 96 || e.keyCode > 105);
|
|
|
|
if (e.keyCode == 13) {
|
|
execGotoLine();
|
|
return false;
|
|
}
|
|
else if (e.keyCode == 40) {
|
|
if (list.selected == -1)
|
|
list.cache = input.getValue();
|
|
if (list.selected < lines.length -1)
|
|
list.selected++;
|
|
input.setValue(lines[list.selected]);
|
|
list.drawContents();
|
|
}
|
|
else if (e.keyCode == 38) {
|
|
if (list.selected > -1)
|
|
list.selected--;
|
|
input.setValue(lines[list.selected] || list.cache);
|
|
list.drawContents();
|
|
}
|
|
else if (NotANumber && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
return false;
|
|
}
|
|
|
|
// Numbers & Cmd-V / Cmd-C
|
|
if (!NotANumber || (e.metaKey || e.ctrlKey)
|
|
&& (e.keyCode == 86 || e.keyCode == 88)) {
|
|
setTimeout(function() {
|
|
execGotoLine(null, null, true);
|
|
}, 10);
|
|
}
|
|
});
|
|
|
|
win.addEventListener("blur", function(e) {
|
|
if (!ui.isChildOf(win, e.toElement))
|
|
hide();
|
|
});
|
|
|
|
input.addEventListener("blur", function(e) {
|
|
if (!ui.isChildOf(win, e.toElement))
|
|
hide();
|
|
});
|
|
|
|
emit("draw");
|
|
}
|
|
|
|
/***** Methods *****/
|
|
|
|
function show(noanim) {
|
|
var tab = tabs.focussedTab;
|
|
var editor = tab && tab.editor;
|
|
if (!editor || editor.type != "ace") return;
|
|
|
|
var ace = editor.ace;
|
|
var aceHtml = ace.container;
|
|
var cursor = ace.getCursorPosition();
|
|
|
|
originalLine = cursor.row + 1;
|
|
originalColumn = cursor.column;
|
|
originalPath = tab.path;
|
|
|
|
//Set the current line
|
|
input.setValue(input.getValue() || cursor.row + 1);
|
|
|
|
list.drawContents();
|
|
|
|
//Determine the position of the window
|
|
var pos = ace.renderer.textToScreenCoordinates(cursor.row, cursor.column);
|
|
var epos = ui.getAbsolutePosition(aceHtml.parentNode);
|
|
var maxTop = aceHtml.offsetHeight - 100;
|
|
var top = Math.max(0, Math.min(maxTop, pos.pageY - epos[1] - 5));
|
|
var left = 0;
|
|
|
|
ace.container.parentNode.appendChild(win.$ext);
|
|
|
|
win.show();
|
|
win.$ext.style.top = top + "px";
|
|
|
|
//Animate
|
|
if (!noanim && settings.getBool('user/general/@animateui')) {
|
|
win.setWidth(0);
|
|
|
|
anims.animate(win, {
|
|
width: "60px",
|
|
duration: 0.15,
|
|
timingFunction: "cubic-bezier(.11, .93, .84, 1)"
|
|
}, function() {
|
|
win.$ext.style.left = left + "px";
|
|
});
|
|
}
|
|
else {
|
|
win.setWidth(60);
|
|
}
|
|
input.focus();
|
|
}
|
|
|
|
function hide() {
|
|
if (nohide) return;
|
|
|
|
if (settings.getBool('user/general/@animateui')) {
|
|
anims.animate(win, {
|
|
width: "0px",
|
|
duration: 0.15,
|
|
timingFunction: "cubic-bezier(.10, .10, .25, .90)"
|
|
}, function() {
|
|
win.hide();
|
|
});
|
|
}
|
|
else {
|
|
win.hide();
|
|
}
|
|
}
|
|
|
|
function gotoline(force) {
|
|
draw();
|
|
|
|
if (control && control.stop)
|
|
control.stop();
|
|
|
|
var tab = tabs.focussedTab;
|
|
var editor = tab && tab.editor;
|
|
if (!editor || editor.type != "ace")
|
|
return;
|
|
|
|
if (force != 2 && !win.visible || force == 1)
|
|
show();
|
|
else
|
|
hide();
|
|
|
|
return false;
|
|
}
|
|
|
|
function execGotoLine(line, column, preview) {
|
|
var tab = tabs.focussedTab && tabs.focussedTab;
|
|
var editor = tab && tab.editor;
|
|
if (!editor || editor.type != "ace") return;
|
|
|
|
var ace = editor.ace;
|
|
var aceHtml = ace.container;
|
|
|
|
if (typeof line != "number")
|
|
line = parseInt(input.getValue(), 10) || 0;
|
|
|
|
// I don't know why this if was here. It caused a bug where if the
|
|
// line target was already in view, it wouldn't jump to it.
|
|
// if (!lastLine || lastLine != line || !ace.isRowFullyVisible(line)) {
|
|
ace.gotoLine(line, column);
|
|
lastLine = line;
|
|
// }
|
|
|
|
if (typeof preview != "undefined") {
|
|
var animate = settings.getBool("user/ace/@animatedScroll");
|
|
if (!animate)
|
|
return;
|
|
|
|
var cursor = ace.getCursorPosition();
|
|
var renderer = ace.renderer;
|
|
var pos = renderer.textToScreenCoordinates(cursor.row, cursor.column);
|
|
var maxTop = renderer.$size.height - win.getHeight() - 10;
|
|
var epos = ui.getAbsolutePosition(aceHtml);
|
|
var sm = renderer.scrollMargin;
|
|
var scrollTop = ace.session.getScrollTop();
|
|
scrollTop = Math.max(-sm.top, Math.min(scrollTop,
|
|
renderer.layerConfig.maxHeight - renderer.$size.scrollerHeight + sm.v));
|
|
var top = Math.min(pos.pageY - epos[1] - 2
|
|
+ renderer.scrollTop - scrollTop, maxTop);
|
|
|
|
if (lineControl)
|
|
lineControl.stop();
|
|
|
|
//Animate
|
|
anims.animate(win, {
|
|
top: top + "px",
|
|
duration: 0.25,
|
|
timingFunction: "cubic-bezier(.11, .93, .84, 1)"
|
|
}, function() {
|
|
win.$ext.style.left = "0px";
|
|
});
|
|
}
|
|
else {
|
|
hide();
|
|
|
|
var i = lines.indexOf(line);
|
|
if (i != -1)
|
|
lines.splice(i, 1);
|
|
|
|
if (i) {
|
|
lines.unshift(line);
|
|
changed = true;
|
|
settings.save();
|
|
}
|
|
|
|
tabs.focusTab(tab);
|
|
}
|
|
}
|
|
|
|
/***** Lifecycle *****/
|
|
|
|
plugin.on("load", function() {
|
|
load();
|
|
});
|
|
plugin.on("enable", function() {
|
|
|
|
});
|
|
plugin.on("disable", function() {
|
|
|
|
});
|
|
plugin.on("unload", function() {
|
|
loaded = false;
|
|
});
|
|
|
|
/***** Register and define API *****/
|
|
|
|
/**
|
|
* The goto line dialog for ace editors. The goto line dialog allows
|
|
* users to jump to a line in a file. It has a history of all lines
|
|
* that were jumped to before. Users can navigate this list and press
|
|
* ESC to return to their original position.
|
|
*
|
|
* @singleton
|
|
**/
|
|
plugin.freezePublicAPI({
|
|
/**
|
|
* Jump to a line and column in the focussed tab.
|
|
* @param {Number} line The line to jump to.
|
|
* @param {Number} column The column to jump to.
|
|
* @param {Boolean} preview Whether to keep the original location in memory.
|
|
*/
|
|
gotoline: function(line, column, preview) {
|
|
gotoline(1);
|
|
return execGotoLine(line, column, preview);
|
|
},
|
|
|
|
/**
|
|
* Show the goto line dialog
|
|
*/
|
|
show: function() { gotoline(1); },
|
|
|
|
/**
|
|
* Hide the goto line dialog
|
|
*/
|
|
hide: function() { gotoline(2); }
|
|
});
|
|
|
|
register(null, {
|
|
"ace.gotoline": plugin
|
|
});
|
|
}
|
|
}); |