2018-01-09 01:00:44 +04:00

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
});
}
});