aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--background_page.html9
-rw-r--r--bookmarks.js137
-rw-r--r--completionDialog.js178
-rw-r--r--lib/keyboardUtils.js22
-rw-r--r--manifest.json5
-rw-r--r--vimiumFrontend.js1
6 files changed, 350 insertions, 2 deletions
diff --git a/background_page.html b/background_page.html
index abb91677..8a6ade8a 100644
--- a/background_page.html
+++ b/background_page.html
@@ -65,7 +65,8 @@
getCurrentTabUrl: getCurrentTabUrl,
getZoomLevel: getZoomLevel,
saveZoomLevel: saveZoomLevel,
- getSetting: getSetting
+ getSetting: getSetting,
+ getBookmarks: getBookmarks
};
var sendRequestHandlers = {
@@ -282,6 +283,12 @@
returnPort.postMessage({ key: args.key, value: value });
}
+ function getBookmarks(args, port) {
+ chrome.bookmarks.search(args.query, function(bookmarks) {
+ port.postMessage({bookmarks:bookmarks})
+ })
+ }
+
/*
* Persists the current zoom level for a given domain
*/
diff --git a/bookmarks.js b/bookmarks.js
new file mode 100644
index 00000000..1ec0c3d1
--- /dev/null
+++ b/bookmarks.js
@@ -0,0 +1,137 @@
+
+function activateBookmarkFindModeToOpenInNewTab() {
+ BookmarkMode.openInNewTab(true)
+ BookmarkMode.enable()
+}
+
+function activateBookmarkFindMode() {
+ BookmarkMode.openInNewTab(false)
+ BookmarkMode.enable()
+}
+
+(function() {
+ // so when they let go of shift after hitting capital "B" it won't
+ // untoggle it
+ var shiftWasPressedWhileToggled = false;
+
+ var BookmarkMode = {
+ isEnabled: function() {
+ return this.enabled
+ },
+ openInNewTab: function(newTab) {
+ this.newTab = newTab
+ },
+ invertNewTabSetting: function() {
+ this.newTab = !this.newTab;
+ if(this.isEnabled()) {
+ this.renderHUD()
+ }
+ },
+ enable: function() {
+ this.enabled = true;
+
+ if(!this.initialized) {
+ initialize.call(this)
+ }
+
+ this.renderHUD();
+ this.completionDialog.show();
+
+ this.keyPressListener.enable();
+ },
+ disable: function() {
+ this.enabled = false;
+ this.keyPressListener.disable();
+ this.completionDialog.hide()
+ HUD.hide();
+ },
+ renderHUD: function() {
+ if (this.newTab)
+ HUD.show("Open bookmark in new tab");
+ else
+ HUD.show("Open bookmark in current tab");
+ }
+
+ }
+
+ // private method
+ var initialize = function() {
+ var self = this;
+ self.initialized = true;
+
+ self.completionDialog = new CompletionDialog({
+ source: findBookmarks,
+ onSelect: function(selection) {
+ var url = selection.url
+ var isABookmarklet = function(url) {
+ return url.indexOf("javascript:")===0
+ }
+
+ if(!self.newTab || isABookmarklet(url)) {
+ window.location=url
+ }
+ else {
+ window.open(url)
+ }
+
+ self.disable();
+ },
+ renderOption: function(searchString, selection) {
+
+ var displaytext = selection.title + " (" + selection.url + ")"
+
+ if(displaytext.length>70) {
+ displaytext = displaytext.substr(0, 70)+"..."
+ }
+
+ return displaytext.split(new RegExp(searchString, "i")).join("<strong>"+searchString+"</strong>")
+ },
+ initialSearchText: "Type a bookmark name or URL"
+ })
+
+ this.keyPressListener = new KeyPressListener({
+ keyDown: function(event) {
+ // shift key will toggle between new tab/same tab
+ if (event.keyCode == keyCodes.shiftKey) {
+ self.invertNewTabSetting();
+ shiftWasPressedWhileToggled = true
+ return
+ }
+
+ var keyChar = getKeyChar(event);
+ if (!keyChar)
+ return;
+
+ // TODO(philc): Ignore keys that have modifiers.
+ if (isEscape(event)) {
+ self.disable();
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+ },
+ keyUp: function(event) {
+ // shift key will toggle between new tab/same tab
+ if (event.keyCode == keyCodes.shiftKey && shiftWasPressedWhileToggled) {
+ self.invertNewTabSetting();
+ shiftWasPressedWhileToggled = false
+ }
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ })
+ }
+
+ var findBookmarks = function(searchString, callback) {
+ var port = chrome.extension.connect({ name: "getBookmarks" })
+ port.onMessage.addListener(function(msg) {
+ callback(msg.bookmarks)
+ port = null
+ })
+ port.postMessage({query:searchString})
+ };
+
+ //export global
+ window.BookmarkMode = BookmarkMode;
+
+}())
diff --git a/completionDialog.js b/completionDialog.js
new file mode 100644
index 00000000..c96ce92c
--- /dev/null
+++ b/completionDialog.js
@@ -0,0 +1,178 @@
+(function(window, document) {
+
+ var CompletionDialog = function(options) {
+ this.options = options
+ }
+
+ CompletionDialog.prototype = {
+ show: function() {
+ if(!this.isShown) {
+ this.isShown=true;
+ this.query = [];
+ if(!this.initialized) {
+ initialize.call(this);
+ this.initialized=true;
+ }
+ this.keyPressListener.enable();
+ render.call(this)
+ clearInterval(this._tweenId);
+ this._tweenId = Tween.fade(this.container, 1.0, 150);
+ }
+ },
+ hide: function() {
+ if(this.isShown) {
+ this.keyPressListener.disable();
+ this.isShown=false;
+ this.currentSelection=0;
+ clearInterval(this._tweenId);
+ this._tweenId = Tween.fade(this.container, 0, 150);
+ }
+ },
+ getDisplayElement: function() {
+ if(!this.container) {
+ this.container = createDivInside(document.body)
+ }
+ return this.container
+ },
+ getQueryString: function() {
+ return this.query.join("")
+ }
+ }
+
+ var initialize = function() {
+ var self = this
+ addCssToPage(completionCSS)
+
+ self.currentSelection=0;
+
+ self.keyPressListener = new KeyPressListener({
+ keyDown: function(event) {
+ var keyChar = getKeyChar(event);
+ if(keyChar==="up") {
+ if(self.currentSelection>0) {
+ self.currentSelection-=1;
+ }
+ render.call(self,self.getQueryString(), self.completions)
+ }
+ else if(keyChar==="down") {
+ if(self.currentSelection<self.completions.length-1) {
+ self.currentSelection+=1;
+ }
+ render.call(self,self.getQueryString(), self.completions)
+ }
+ else if(event.keyCode == keyCodes.enter) {
+ self.options.onSelect(self.completions[self.currentSelection])
+ }
+ else if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
+ if (self.query.length > 0) {
+ self.query.pop();
+ self.options.source(self.getQueryString(), function(completions) {
+ render.call(self, self.getQueryString(), completions)
+ })
+ }
+ }
+ else if(keyChar!=="left" && keyChar!="right") {
+ self.query.push(keyChar);
+ self.options.source(self.getQueryString(), function(completions) {
+ render.call(self, self.getQueryString(), completions)
+ })
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ })
+ }
+
+ var render = function(searchString, completions) {
+ if(this.isShown) {
+ this.searchString = searchString;
+ this.completions = completions;
+ var container = this.getDisplayElement()
+ clearChildren(container);
+
+ if(searchString===undefined) {
+ this.container.className = "vimium-dialog";
+ createDivInside(container).innerHTML=this.options.initialSearchText || "Begin typing"
+ }
+ else {
+ this.container.className = "vimium-dialog vimium-completions";
+ var searchBar = createDivInside(container)
+ searchBar.innerHTML=searchString
+ searchBar.className="vimium-searchBar"
+
+ searchResults = createDivInside(container)
+ searchResults.className="vimium-searchResults"
+ if(completions.length<=0) {
+ var resultDiv = createDivInside(searchResults)
+ resultDiv.className="vimium-noResults"
+ resultDiv.innerHTML="No results found"
+ }
+ else {
+ for(var i=0;i<completions.length;i++) {
+ var resultDiv = createDivInside(searchResults)
+ if(i===this.currentSelection) {
+ resultDiv.className="vimium-selected"
+ }
+ resultDiv.innerHTML=this.options.renderOption(searchString, completions[i])
+ }
+ }
+ }
+
+ container.style.top=(window.innerHeight/2-container.clientHeight/2) + "px";
+ container.style.left=(window.innerWidth/2-container.clientWidth/2) + "px";
+ }
+ };
+ var createDivInside = function(parent) {
+ var element = document.createElement("div");
+ parent.appendChild(element);
+ return element
+ }
+
+ var clearChildren = function(elem) {
+ if (elem.hasChildNodes()) {
+ while (elem.childNodes.length >= 1) {
+ elem.removeChild(elem.firstChild);
+ }
+ }
+ }
+
+ var completionCSS = ".vimium-dialog {"+
+ "position:fixed;"+
+ "background-color: #ebebeb;" +
+ "z-index: 99999998;" +
+ "border: 1px solid #b3b3b3;" +
+ "font-size: 12px;" +
+ "text-align:left;"+
+ "color: black;" +
+ "padding:10px;"+
+ "border-radius: 4px;" +
+ "font-family: Lucida Grande, Arial, Sans;" +
+ "}"+
+ ".vimium-completions {"+
+ "width:400px;"+
+ "}"+
+ ".vimium-completions .vimium-searchBar {"+
+ "height: 15px;"+
+ "border-bottom: 1px solid #b3b3b3;"+
+ "}"+
+ ".vimium-completions .vimium-searchResults {"+
+ "}"+
+ ".vimium-completions .vimium-searchResults .vimium-selected{"+
+ "background-color:#aaa;"+
+ "border-radius: 4px;" +
+ "}"+
+ ".vimium-completions div{"+
+ "padding:4px;"+
+ "}"+
+ ".vimium-completions div strong{"+
+ "color: black;" +
+ "font-weight:bold;"+
+ "}"+
+ ".vimium-completions .vimium-noResults{"+
+ "color:#555;"+
+ "}";
+
+ window.CompletionDialog = CompletionDialog;
+
+}(window, document))
diff --git a/lib/keyboardUtils.js b/lib/keyboardUtils.js
index fe3dcd59..5b8b4c84 100644
--- a/lib/keyboardUtils.js
+++ b/lib/keyboardUtils.js
@@ -61,3 +61,25 @@ function isEscape(event) {
return event.keyCode == keyCodes.ESC ||
(event.ctrlKey && getKeyChar(event) == '['); // c-[ is mapped to ESC in Vim by default.
}
+
+var KeyPressListener = function(handlers) {
+ this.handlers = handlers;
+}
+
+KeyPressListener.prototype = {
+ enable: function() {
+ var handlers = this.handlers;
+ var wrapper = function(callback){
+ return function(event) {
+ callback(event)
+ }
+ }
+ (handlers.keyDown && document.addEventListener("keydown", handlers.keyDown, true));
+ (handlers.keyUp && document.addEventListener("keyup", handlers.keyUp, true));
+ },
+ disable: function() {
+ var handlers = this.handlers;
+ (handlers.keyDown && document.removeEventListener("keydown", handlers.keyDown, true));
+ (handlers.keyUp && document.removeEventListener("keyup", handlers.keyUp, true));
+ }
+}
diff --git a/manifest.json b/manifest.json
index 181c8739..e100f42d 100644
--- a/manifest.json
+++ b/manifest.json
@@ -9,6 +9,7 @@
"options_page": "options.html",
"permissions": [
"tabs",
+ "bookmarks",
"http://*/*",
"https://*/*"
],
@@ -19,7 +20,9 @@
"lib/keyboardUtils.js",
"lib/clipboard.js",
"linkHints.js",
- "vimiumFrontend.js"
+ "vimiumFrontend.js",
+ "completionDialog.js",
+ "bookmarks.js"
],
"run_at": "document_start",
"all_frames": true
diff --git a/vimiumFrontend.js b/vimiumFrontend.js
index e0b6eb6a..6877be5f 100644
--- a/vimiumFrontend.js
+++ b/vimiumFrontend.js
@@ -266,6 +266,7 @@ function scrollFullPageDown() { window.scrollBy(0, window.innerHeight); }
function scrollLeft() { window.scrollBy(-1 * settings.get("scrollStepSize"), 0); }
function scrollRight() { window.scrollBy(settings.get("scrollStepSize"), 0); }
+
function focusInput(count) {
var results = document.evaluate(textInputXPath,
document.documentElement, null,