mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-21 23:57:59 +01:00
906 lines
33 KiB
JavaScript
906 lines
33 KiB
JavaScript
/***** BEGIN LICENSE BLOCK ***** {{{
|
|
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
The contents of this file are subject to the Mozilla Public License Version
|
|
1.1 (the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
for the specific language governing rights and limitations under the
|
|
License.
|
|
|
|
(c) 2006-2007: Martin Stubenschrott <stubenschrott@gmx.net>
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
of those above. If you wish to allow use of your version of this file only
|
|
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
use your version of this file under the terms of the MPL, indicate your
|
|
decision by deleting the provisions above and replace them with the notice
|
|
and other provisions required by the GPL or the LGPL. If you do not delete
|
|
the provisions above, a recipient may use your version of this file under
|
|
the terms of any one of the MPL, the GPL or the LGPL.
|
|
}}} ***** END LICENSE BLOCK *****/
|
|
|
|
/*
|
|
* This class is used for prompting of user input and echoing of messages
|
|
*
|
|
* it consists of a prompt and command field
|
|
* be sure to only create objects of this class when the chrome is ready
|
|
*/
|
|
function CommandLine() //{{{
|
|
{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////{{{
|
|
|
|
const UNINITIALIZED = -2; // notifies us, if we need to start history/tab-completion from the beginning
|
|
const HISTORY_SIZE = 500;
|
|
|
|
var completionlist = new InformationList("vimperator-completion", { min_items: 2, max_items: 10 });
|
|
var completions = [];
|
|
|
|
var history = [];
|
|
var history_index = UNINITIALIZED;
|
|
var history_start = "";
|
|
|
|
// for the example command "open sometext| othertext" (| is the cursor pos):
|
|
var completion_start_index = 0; // will be 5 because we want to complete arguments for the :open command
|
|
var completion_prefix = "" // will be: "open sometext"
|
|
var completion_postfix = ""; // will be: " othertext"
|
|
|
|
var wild_index = 0; // keep track how often we press <Tab> in a row
|
|
var completion_index = UNINITIALIZED;
|
|
|
|
// The prompt for the current command, for example : or /. Can be blank
|
|
var prompt_widget = document.getElementById('vimperator-commandline-prompt');
|
|
// The command bar which contains the current command
|
|
var command_widget = document.getElementById('vimperator-commandline-command');
|
|
|
|
// The widget used for multiline output
|
|
var multiline_output_widget = document.getElementById("vimperator-multiline-output");
|
|
multiline_output_widget.contentDocument.body.setAttribute("style", "margin: 0px; font-family: -moz-fixed;"); // get rid of the default border
|
|
multiline_output_widget.contentDocument.body.innerHTML = "";
|
|
// we need this hack, or otherwise the first use of setMultiline() will have a wrong height
|
|
setTimeout(function() {
|
|
multiline_output_widget.collapsed = false;
|
|
var content_height = multiline_output_widget.contentDocument.height;
|
|
multiline_output_widget.collapsed = true;
|
|
}, 100);
|
|
|
|
// The widget used for multiline intput
|
|
var multiline_input_widget = document.getElementById("vimperator-multiline-input");
|
|
|
|
// we need to save the mode which were in before opening the command line
|
|
// this is then used if we focus the command line again without the "official"
|
|
// way of calling "open"
|
|
var cur_extended_mode = null; // the extended mode which we last openend the command line for
|
|
var cur_prompt = null;
|
|
var cur_command = null;
|
|
var old_mode = null; // when we leave the command prompt this mode is restored
|
|
var old_extended_mode = null;
|
|
|
|
// save the arguments for the inputMultiline method which are needed in the event handler
|
|
var multiline_regexp = null;
|
|
var multiline_callback = null;
|
|
|
|
// load the commandline history
|
|
var hist = Options.getPref("commandline_history", "");
|
|
history = hist.split("\n");
|
|
|
|
// TODO: these styles should be moved to the .css file
|
|
function setNormalStyle()
|
|
{
|
|
command_widget.inputField.setAttribute("style","font-family: monospace;");
|
|
}
|
|
function setMessageStyle()
|
|
{
|
|
prompt_widget.setAttribute("style", "font-family: monospace; color:magenta; font-weight: bold");
|
|
command_widget.inputField.setAttribute("style", "font-family: monospace;");
|
|
}
|
|
function setErrorStyle()
|
|
{
|
|
command_widget.inputField.setAttribute("style", "font-family: monospace; color:white; background-color:red; font-weight: bold");
|
|
}
|
|
|
|
// Sets the prompt - for example, : or /
|
|
function setPrompt(prompt)
|
|
{
|
|
if (typeof prompt != "string")
|
|
prompt = "";
|
|
|
|
prompt_widget.value = prompt;
|
|
if (prompt)
|
|
{
|
|
// Initially (in the xul) the prompt is 'collapsed', this makes
|
|
// sure it's visible, then we toggle the display which works better
|
|
prompt_widget.style.visibility = 'visible';
|
|
prompt_widget.style.display = 'inline';
|
|
prompt_widget.size = prompt.length;
|
|
}
|
|
else
|
|
{
|
|
prompt_widget.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// Sets the command - e.g. 'tabopen', 'open http://example.com/'
|
|
function setCommand(cmd)
|
|
{
|
|
command_widget.value = cmd;
|
|
}
|
|
|
|
function setMultiline(cmd)
|
|
{
|
|
// TODO: we should retain any previous command output like Vim
|
|
if (!multiline_output_widget.collapsed)
|
|
multiline_output_widget.collapsed = true;
|
|
|
|
multiline_input_widget.collapsed = true;
|
|
|
|
// vimperator.log(content_height);
|
|
cmd = cmd.replace(/\n|\\n/g, "<br/>") + "<br/><span style=\"color: green;\">Press ENTER or type command to continue</span>";
|
|
multiline_output_widget.contentDocument.body.innerHTML = cmd;
|
|
|
|
// TODO: resize upon a window resize
|
|
var available_height = getBrowser().mPanelContainer.boxObject.height;
|
|
var content_height = multiline_output_widget.contentDocument.height;
|
|
// vimperator.log(content_height);
|
|
var height = content_height < available_height ? content_height : available_height;
|
|
|
|
//multiline_output_widget.style.height = height + "px";
|
|
multiline_output_widget.height = height + "px";
|
|
multiline_output_widget.collapsed = false;
|
|
//vimperator.log(content_height);
|
|
multiline_output_widget.contentWindow.scrollTo(0, content_height); // scroll to the end when 'nomore' is set
|
|
multiline_output_widget.contentWindow.focus();
|
|
}
|
|
|
|
function autosizeMultilineInputWidget()
|
|
{
|
|
// XXX: faster/better method?
|
|
|
|
var lines = 0;
|
|
var str = multiline_input_widget.value;
|
|
for (var i = 0; i < str.length; i++)
|
|
{
|
|
if (str[i] == "\n")
|
|
lines++;
|
|
}
|
|
if (lines == 0)
|
|
lines = 1;
|
|
multiline_input_widget.setAttribute("rows", lines.toString());
|
|
}
|
|
|
|
function addToHistory(str)
|
|
{
|
|
if (str.length < 1)
|
|
return;
|
|
|
|
// first remove all old history elements which have this string
|
|
history = history.filter(function(elem) {
|
|
return elem != str;
|
|
});
|
|
// add string to the command line history
|
|
if (history.push(str) > HISTORY_SIZE) //remove the first 10% of the history
|
|
history = history.slice(HISTORY_SIZE / 10);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////}}}
|
|
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////{{{
|
|
|
|
this.getCommand = function()
|
|
{
|
|
return command_widget.value;
|
|
};
|
|
|
|
/**
|
|
* All arguments can be ommited and will be defaulted to "" or null
|
|
*/
|
|
this.open = function(prompt, cmd, ext_mode)
|
|
{
|
|
// save the current prompts, we need it later if the command widget
|
|
// receives focus without calling the this.open() method
|
|
cur_prompt = prompt || "";
|
|
cur_command = cmd || "";
|
|
cur_extended_mode = ext_mode || null;
|
|
|
|
setNormalStyle();
|
|
history_index = UNINITIALIZED;
|
|
completion_index = UNINITIALIZED;
|
|
|
|
// save the mode, because we need to restore it
|
|
[old_mode, old_extended_mode] = vimperator.getMode();
|
|
vimperator.setMode(vimperator.modes.COMMAND_LINE, cur_extended_mode, true);
|
|
setPrompt(cur_prompt);
|
|
setCommand(cur_command);
|
|
|
|
command_widget.focus();
|
|
};
|
|
|
|
// FIXME: flags not yet really functional --mst
|
|
this.echo = function(str, flags)
|
|
{
|
|
var focused = document.commandDispatcher.focusedElement;
|
|
if (/*!echo_allowed && focused && */focused == command_widget.inputField)
|
|
return false;
|
|
|
|
if (typeof str != "string")
|
|
str = "";
|
|
|
|
setNormalStyle();
|
|
if (flags || str.indexOf("\n") > -1 || str.indexOf("\\n") > -1 || str.indexOf("<br>") > -1 || str.indexOf("<br/>") > -1)
|
|
{
|
|
setMultiline(str);
|
|
}
|
|
else
|
|
{
|
|
setPrompt("");
|
|
setCommand(str);
|
|
}
|
|
cur_extended_mode = null;
|
|
return true;
|
|
};
|
|
|
|
this.echoErr = function(str)
|
|
{
|
|
var focused = document.commandDispatcher.focusedElement;
|
|
if (/*!echo_allowed && focused && */focused == command_widget.inputField)
|
|
return false;
|
|
|
|
setErrorStyle();
|
|
setPrompt("");
|
|
setCommand(str);
|
|
cur_extended_mode = null;
|
|
return true;
|
|
};
|
|
|
|
// this will prompt the user for a string
|
|
// vimperator.commandline.input("(s)ave or (o)pen the file?")
|
|
this.input = function(str)
|
|
{
|
|
// TODO: unfinished, need to find out how/if we can block the execution of code
|
|
// to make this code synchronous or at least use a callback
|
|
setMessageStyle();
|
|
setPrompt(str);
|
|
setCommand("");
|
|
return "not implemented";
|
|
};
|
|
|
|
// reads a multi line input and returns the string once the last line matches
|
|
// @param until_regexp
|
|
this.inputMultiline = function(until_regexp, callback_func)
|
|
{
|
|
// save the mode, because we need to restore it on blur()
|
|
[old_mode, old_extended_mode] = vimperator.getMode();
|
|
vimperator.setMode(vimperator.modes.COMMAND_LINE, vimperator.modes.READ_MULTILINE, true);
|
|
|
|
// save the arguments, they are needed in the event handler onEvent
|
|
multiline_regexp = until_regexp;
|
|
multiline_callback = callback_func;
|
|
|
|
multiline_input_widget.collapsed = false;
|
|
multiline_input_widget.value = "";
|
|
autosizeMultilineInputWidget();
|
|
|
|
setTimeout(function() {
|
|
multiline_input_widget.focus();
|
|
}, 10);
|
|
};
|
|
|
|
this.clear = function()
|
|
{
|
|
multiline_input_widget.collapsed = true;
|
|
multiline_output_widget.collapsed = true;
|
|
|
|
setPrompt(" "); // looks faster than an empty string as most prompts are 1 char long
|
|
setCommand("");
|
|
setNormalStyle();
|
|
};
|
|
|
|
this.onEvent = function(event)
|
|
{
|
|
var command = this.getCommand();
|
|
|
|
if (event.type == "blur")
|
|
{
|
|
// prevent losing focus, there should be a better way, but it just didn't work otherwise
|
|
if (vimperator.hasMode(vimperator.modes.COMMAND_LINE))
|
|
setTimeout(function() { command_widget.inputField.focus(); }, 0);
|
|
}
|
|
else if (event.type == "focus")
|
|
{
|
|
if (!cur_extended_mode)
|
|
event.target.blur();
|
|
}
|
|
else if (event.type == "input")
|
|
{
|
|
vimperator.triggerCallback("change", cur_extended_mode, command);
|
|
}
|
|
else if (event.type == "keypress")
|
|
{
|
|
var key = vimperator.events.toString(event);
|
|
|
|
/* user pressed ENTER to carry out a command */
|
|
if (vimperator.events.isAcceptKey(key))
|
|
{
|
|
var mode = cur_extended_mode; // save it here, as setMode() resets it
|
|
addToHistory(command);
|
|
vimperator.setMode(old_mode, old_extended_mode);
|
|
vimperator.focusContent();
|
|
completionlist.hide();
|
|
vimperator.statusline.updateProgress(""); // we may have a "match x of y" visible
|
|
return vimperator.triggerCallback("submit", mode, command);
|
|
}
|
|
|
|
/* user pressed ESCAPE to cancel this prompt */
|
|
else if (vimperator.events.isCancelKey(key))
|
|
{
|
|
var res = vimperator.triggerCallback("cancel", cur_extended_mode);
|
|
addToHistory(command);
|
|
vimperator.setMode(old_mode, old_extended_mode);
|
|
vimperator.focusContent();
|
|
completionlist.hide();
|
|
vimperator.statusline.updateProgress(""); // we may have a "match x of y" visible
|
|
this.clear();
|
|
return res;
|
|
}
|
|
|
|
/* user pressed UP or DOWN arrow to cycle history completion */
|
|
else if (key == "<Up>" || key == "<Down>")
|
|
{
|
|
//always reset the tab completion if we use up/down keys
|
|
completion_index = UNINITIALIZED;
|
|
|
|
/* save 'start' position for iterating through the history */
|
|
if (history_index == UNINITIALIZED)
|
|
{
|
|
history_index = history.length;
|
|
history_start = command;
|
|
}
|
|
|
|
while (history_index >= -1 && history_index <= history.length)
|
|
{
|
|
key == "<Up>" ? history_index-- : history_index++;
|
|
if (history_index == history.length) // user pressed DOWN when there is no newer history item
|
|
{
|
|
setCommand(history_start);
|
|
return;
|
|
}
|
|
// cannot go past history start/end
|
|
if (history_index <= -1)
|
|
{
|
|
history_index = 0;
|
|
vimperator.beep();
|
|
break;
|
|
}
|
|
if (history_index >= history.length + 1)
|
|
{
|
|
history_index = history.length;
|
|
vimperator.beep();
|
|
break;
|
|
}
|
|
|
|
if (history[history_index].indexOf(history_start) == 0)
|
|
{
|
|
setCommand(history[history_index]);
|
|
return;
|
|
}
|
|
}
|
|
vimperator.beep();
|
|
}
|
|
|
|
/* user pressed TAB to get completions of a command */
|
|
else if (key == "<Tab>" || key == "<S-Tab>")
|
|
{
|
|
//always reset our completion history so up/down keys will start with new values
|
|
history_index = UNINITIALIZED;
|
|
|
|
// we need to build our completion list first
|
|
if (completion_index == UNINITIALIZED)
|
|
{
|
|
completion_start_index = 0;
|
|
|
|
completion_index = -1;
|
|
wild_index = 0;
|
|
|
|
completion_prefix = command.substring(0, command_widget.selectionStart);
|
|
completion_postfix = command.substring(command_widget.selectionStart);
|
|
var res = vimperator.triggerCallback("complete", cur_extended_mode, completion_prefix);
|
|
if (res)
|
|
[completion_start_index, completions] = res;
|
|
|
|
// Sort the completion list
|
|
if (vimperator.options["wildoptions"].search(/\bsort\b/) > -1)
|
|
{
|
|
completions.sort(function(a, b) {
|
|
if (a[0] < b[0])
|
|
return -1;
|
|
else if (a[0] > b[0])
|
|
return 1;
|
|
else
|
|
return 0;
|
|
});
|
|
}
|
|
}
|
|
|
|
if (completions.length == 0)
|
|
{
|
|
vimperator.beep();
|
|
// prevent tab from moving to the next field
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
|
|
var wim = vimperator.options["wildmode"].split(/,/);
|
|
var has_list = false;
|
|
var longest = false;
|
|
var full = false;
|
|
var wildtype = wim[wild_index++] || wim[wim.length - 1];
|
|
if (wildtype == 'list' || wildtype == 'list:full' || wildtype == 'list:longest')
|
|
has_list = true;
|
|
if (wildtype == 'longest' || wildtype == 'list:longest')
|
|
longest = true;
|
|
else if (wildtype == 'full' || wildtype == 'list:full')
|
|
full = true;
|
|
|
|
// show the list
|
|
if (has_list)
|
|
{
|
|
if (completion_index < 0)
|
|
completionlist.show(completions);
|
|
else
|
|
completionlist.show();
|
|
}
|
|
|
|
if (full)
|
|
{
|
|
if (event.shiftKey)
|
|
{
|
|
completion_index--;
|
|
if (completion_index < -1)
|
|
completion_index = completions.length -1;
|
|
}
|
|
else
|
|
{
|
|
completion_index++;
|
|
if (completion_index >= completions.length)
|
|
completion_index = -1;
|
|
}
|
|
|
|
vimperator.statusline.updateProgress("match " + (completion_index + 1) + " of " + completions.length);
|
|
// if the list is hidden, this function does nothing
|
|
completionlist.selectItem(completion_index);
|
|
}
|
|
|
|
|
|
if (completion_index == -1 && !longest) // wrapped around matches, reset command line
|
|
{
|
|
if (full && completions.length > 1)
|
|
{
|
|
setCommand(completion_prefix + completion_postfix);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (longest && completions.length > 1)
|
|
var compl = vimperator.completion.get_longest_substring();
|
|
else if (full)
|
|
var compl = completions[completion_index][0];
|
|
else if (completions.length == 1)
|
|
var compl = completions[0][0];
|
|
if (compl)
|
|
{
|
|
setCommand(command.substring(0, completion_start_index) + compl + completion_postfix);
|
|
command_widget.selectionStart = command_widget.selectionEnd = completion_start_index + compl.length;
|
|
|
|
// Start a new completion in the next iteration. Useful for commands like :source
|
|
// RFC: perhaps the command can indicate whether the completion should be restarted
|
|
// Needed for :source to grab another set of completions after a file/directory has been filled out
|
|
if (completions.length == 1 && !full)
|
|
completion_index = UNINITIALIZED;
|
|
}
|
|
}
|
|
|
|
// prevent tab from moving to the next field
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
else if (key == "<BS>")
|
|
{
|
|
// reset the tab completion
|
|
completion_index = history_index = UNINITIALIZED;
|
|
|
|
// and blur the command line if there is no text left
|
|
if (command.length == 0)
|
|
{
|
|
this.clear();
|
|
vimperator.focusContent();
|
|
}
|
|
}
|
|
else // any other key
|
|
{
|
|
// reset the tab completion
|
|
completion_index = history_index = UNINITIALIZED;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.onMultilineInputEvent = function(event)
|
|
{
|
|
if (event.type == "keypress")
|
|
{
|
|
var key = vimperator.events.toString(event);
|
|
if (vimperator.events.isAcceptKey(key))
|
|
{
|
|
//var lines = multiline_input_widget.value.substr(0, multiline_input_widget.selectionStart).split(/\n/);
|
|
var text = multiline_input_widget.value.substr(0, multiline_input_widget.selectionStart);
|
|
if (text.match(multiline_regexp))
|
|
{
|
|
text = text.replace(multiline_regexp, "");
|
|
vimperator.setMode(old_mode, old_extended_mode);
|
|
multiline_input_widget.collapsed = true;
|
|
multiline_callback.call(this, text);
|
|
}
|
|
}
|
|
else if (vimperator.events.isCancelKey(key))
|
|
{
|
|
vimperator.setMode(old_mode, old_extended_mode);
|
|
multiline_input_widget.collapsed = true;
|
|
}
|
|
}
|
|
else if (event.type == "blur")
|
|
{
|
|
if (vimperator.hasMode(vimperator.modes.READ_MULTILINE))
|
|
setTimeout(function() { multiline_input_widget.inputField.focus(); }, 0);
|
|
}
|
|
else if (event.type == "input")
|
|
{
|
|
autosizeMultilineInputWidget();
|
|
}
|
|
}
|
|
|
|
this.onMultilineOutputEvent = function(event)
|
|
{
|
|
var key = vimperator.events.toString(event);
|
|
if (vimperator.events.isAcceptKey(key) || vimperator.events.isCancelKey(key))
|
|
{
|
|
multiline_output_widget.collapsed = true;
|
|
vimperator.focusContent();
|
|
}
|
|
}
|
|
|
|
// it would be better if we had a destructor in javascript ...
|
|
this.destroy = function()
|
|
{
|
|
Options.setPref("commandline_history", history.join("\n"));
|
|
}
|
|
//}}}
|
|
} //}}}
|
|
|
|
/**
|
|
* The list which is used for the completion box, the preview window and the buffer preview window
|
|
*
|
|
* @param id: the id of the the XUL widget which we want to fill
|
|
* @param options: an optional hash which modifies the behavior of the list
|
|
*/
|
|
function InformationList(id, options) //{{{
|
|
{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////{{{
|
|
|
|
const CONTEXT_LINES = 3;
|
|
var max_items = 10;
|
|
var min_items = 1;
|
|
var incremental_fill = true; // make display faster, but does not show scrollbar
|
|
|
|
if (options)
|
|
{
|
|
if (options.max_items) max_items = options.max_items;
|
|
if (options.min_items) min_items = options.min_items;
|
|
if (options.incremental_fill) incremental_fill = options.incremental_fill;
|
|
}
|
|
|
|
var widget = document.getElementById(id);
|
|
var completions = null; // a reference to the Array of completions
|
|
var list_offset = 0; // how many items is the displayed list shifted from the internal tab index
|
|
var list_index = 0; // list_offset + list_index = completions[item]
|
|
|
|
// add a single completion item to the list
|
|
function addItem(completion_item, at_beginning)
|
|
{
|
|
var item = document.createElement("listitem");
|
|
var cell1 = document.createElement("listcell");
|
|
var cell2 = document.createElement("listcell");
|
|
|
|
cell1.setAttribute("label", completion_item[0]);
|
|
cell2.setAttribute("label", completion_item[1]);
|
|
cell2.setAttribute("style", "color:green; font-family: sans");
|
|
|
|
item.appendChild(cell1);
|
|
item.appendChild(cell2);
|
|
if (at_beginning == true)
|
|
{
|
|
var items = widget.getElementsByTagName("listitem");
|
|
if (items.length > 0)
|
|
widget.insertBefore(item, items[0]);
|
|
else
|
|
widget.appendChild(item);
|
|
}
|
|
else
|
|
widget.appendChild(item);
|
|
}
|
|
|
|
/**
|
|
* uses the entries in completions to fill the listbox
|
|
*
|
|
* @param startindex: start at this index and show max_items
|
|
* @returns the number of items
|
|
*/
|
|
function fill(startindex)
|
|
{
|
|
var complength = completions.length;
|
|
|
|
// remove all old items first
|
|
var items = widget.getElementsByTagName("listitem");
|
|
while (items.length > 0)
|
|
{
|
|
widget.removeChild(items[0]);
|
|
}
|
|
|
|
if (!incremental_fill)
|
|
{
|
|
for (i in completions)
|
|
addItem(completions[i], false);
|
|
return complength;
|
|
}
|
|
|
|
// find start index
|
|
if (startindex + max_items > complength)
|
|
startindex = complength - max_items;
|
|
if (startindex < 0)
|
|
startindex = 0;
|
|
|
|
list_offset = startindex;
|
|
list_index = -1;
|
|
|
|
for (var i = startindex; i < complength && i < startindex + max_items; i++)
|
|
{
|
|
addItem(completions[i], false);
|
|
}
|
|
|
|
return (i-startindex);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////}}}
|
|
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////{{{
|
|
|
|
/**
|
|
* Show the completion list window
|
|
*
|
|
* @param compl: if null, only show the list with current entries, otherwise
|
|
* use entries of 'compl' to fill the list.
|
|
* Required format: [["left", "right"], ["another"], ["completion"]]
|
|
*/
|
|
this.show = function(compl)
|
|
{
|
|
//max_items = vimperator.options["previewheight"];
|
|
|
|
if (compl)
|
|
{
|
|
completions = compl;
|
|
fill(0);
|
|
}
|
|
|
|
var length = completions.length;
|
|
if (length > max_items)
|
|
length = max_items;
|
|
if (length >= min_items)
|
|
{
|
|
widget.setAttribute("rows", length.toString());
|
|
widget.hidden = false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
widget.hidden = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
this.hide = function()
|
|
{
|
|
widget.hidden = true;
|
|
}
|
|
|
|
this.visible = function()
|
|
{
|
|
return !widget.hidden;
|
|
}
|
|
|
|
/**
|
|
* select index, refill list if necessary
|
|
*/
|
|
this.selectItem = function(index)
|
|
{
|
|
if (widget.hidden)
|
|
return;
|
|
|
|
if (!incremental_fill)
|
|
{
|
|
widget.selectedIndex = index;
|
|
return;
|
|
}
|
|
|
|
// find start index
|
|
var new_offset = 0;
|
|
if (index >= list_offset + max_items - CONTEXT_LINES)
|
|
new_offset = index - max_items + CONTEXT_LINES + 1;
|
|
else if (index <= list_offset + CONTEXT_LINES)
|
|
new_offset = index - CONTEXT_LINES;
|
|
else
|
|
new_offset = list_offset;
|
|
|
|
if (new_offset + max_items > completions.length)
|
|
new_offset = completions.length - max_items;
|
|
if (new_offset < 0)
|
|
new_offset = 0;
|
|
|
|
// for speed reason: just remove old item, and add the new one at the end of the list
|
|
var items = widget.getElementsByTagName("listitem");
|
|
if (new_offset == list_offset + 1)
|
|
{
|
|
widget.removeChild(items[0]);
|
|
addItem(completions[index + CONTEXT_LINES], false);
|
|
}
|
|
else if (new_offset == list_offset - 1)
|
|
{
|
|
widget.removeChild(items[items.length-1]);
|
|
addItem(completions[index - CONTEXT_LINES], true);
|
|
}
|
|
else if (new_offset == list_offset)
|
|
{
|
|
// do nothing
|
|
}
|
|
else
|
|
fill(new_offset);
|
|
|
|
list_offset = new_offset;
|
|
widget.selectedIndex = index - list_offset;
|
|
}
|
|
|
|
this.onEvent = function(event)
|
|
{
|
|
var listcells = document.getElementsByTagName("listcell");
|
|
// 2 columns for now, use the first column
|
|
var index = (widget.selectedIndex * 2) + 0;
|
|
var val = listcells[index].getAttribute("label");
|
|
if (val && event.button == 0 && event.type == "dblclick") // left double click
|
|
vimperator.open(val);
|
|
else if (val && event.button == 1) // middle click
|
|
vimperator.open(val, vimperator.NEW_TAB);
|
|
else
|
|
return false;
|
|
}
|
|
//}}}
|
|
} //}}}
|
|
|
|
function StatusLine() //{{{
|
|
{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////{{{
|
|
|
|
// our status bar fields
|
|
var statusline_widget = document.getElementById("vimperator-statusline");
|
|
var url_widget = document.getElementById("vimperator-statusline-field-url");
|
|
var inputbuffer_widget = document.getElementById("vimperator-statusline-field-inputbuffer");
|
|
var progress_widget = document.getElementById("vimperator-statusline-field-progress");
|
|
var tabcount_widget = document.getElementById("vimperator-statusline-field-tabcount");
|
|
var bufferposition_widget = document.getElementById("vimperator-statusline-field-bufferposition");
|
|
|
|
/////////////////////////////////////////////////////////////////////////////}}}
|
|
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////{{{
|
|
|
|
// use names for the color or "transparent" to remove color information
|
|
this.setColor = function(color)
|
|
{
|
|
if (!color)
|
|
color = "transparent";
|
|
statusline_widget.setAttribute("style", "background-color: " + color);
|
|
};
|
|
this.setClass = function(type)
|
|
{
|
|
statusline_widget.setAttribute("class", "status_" + type);
|
|
};
|
|
|
|
this.updateUrl = function(url)
|
|
{
|
|
if (!url || typeof url != "string")
|
|
url = vimperator.buffer.location;
|
|
|
|
url_widget.value = url;
|
|
};
|
|
|
|
this.updateInputBuffer = function(buffer)
|
|
{
|
|
if (!buffer || typeof buffer != "string")
|
|
buffer = "";
|
|
|
|
inputbuffer_widget.value = buffer;
|
|
};
|
|
|
|
this.updateProgress = function(progress)
|
|
{
|
|
if (!progress)
|
|
progress = "";
|
|
|
|
if (typeof progress == "string")
|
|
progress_widget.value = progress;
|
|
else if (typeof progress == "number")
|
|
{
|
|
var progress_str = "";
|
|
if (progress <= 0)
|
|
progress_str = "[ Loading... ]";
|
|
else if (progress < 1)
|
|
{
|
|
progress_str = "[";
|
|
var done = Math.floor(progress * 20);
|
|
for (i=0; i < done; i++)
|
|
progress_str += "=";
|
|
|
|
progress_str += ">";
|
|
|
|
for (i=19; i > done; i--)
|
|
progress_str += " ";
|
|
|
|
progress_str += "]";
|
|
}
|
|
progress_widget.value = progress_str;
|
|
}
|
|
};
|
|
|
|
// you can omit either of the 2 arguments
|
|
this.updateTabCount = function(cur_index, total_tabs)
|
|
{
|
|
if (!cur_index || typeof cur_index != "number")
|
|
cur_index = vimperator.tabs.index() + 1;
|
|
if (!total_tabs || typeof cur_index != "number")
|
|
total_tabs = vimperator.tabs.count();
|
|
|
|
tabcount_widget.value = "[" + cur_index + "/" + total_tabs + "]";
|
|
};
|
|
|
|
// percent is given between 0 and 1
|
|
this.updateBufferPosition = function(percent)
|
|
{
|
|
if (!percent || typeof percent != "number")
|
|
{
|
|
var win = document.commandDispatcher.focusedWindow;
|
|
percent = win.scrollMaxY == 0 ? -1 : win.scrollY / win.scrollMaxY;
|
|
}
|
|
|
|
var bufferposition_str = "";
|
|
percent = Math.round(percent*100);
|
|
if (percent < 0) bufferposition_str = "All";
|
|
else if (percent == 0) bufferposition_str = "Top";
|
|
else if (percent < 10) bufferposition_str = " " + percent + "%";
|
|
else if (percent >= 100) bufferposition_str = "Bot";
|
|
else bufferposition_str = percent + "%";
|
|
|
|
bufferposition_widget.value = bufferposition_str;
|
|
};
|
|
//}}}
|
|
} //}}}
|
|
|
|
// vim: set fdm=marker sw=4 ts=4 et:
|