mort 8 лет назад
Сommit
95f85973eb
6 измененных файлов: 460 добавлений и 0 удалений
  1. 362
    0
      data/onload.js
  2. 25
    0
      install.rdf
  3. 52
    0
      lib/main.js
  4. Двоичные данные
      mouseless-plugin.xpi
  5. 9
    0
      package.json
  6. 12
    0
      test/test-main.js

+ 362
- 0
data/onload.js Просмотреть файл

@@ -0,0 +1,362 @@
var keys = {
scroll_up: {code: 84},
scroll_down: {code: 78},
scroll_up_fast: {code: 219, shiftKey: true},
scroll_down_fast: {code: 221, shiftKey: true},
blobs_show: {code: 68},
blobs_show_reload: {code: 68, ctrlKey: true},
blobs_hide: {code: 27},
blobs_click: {code: 13},
blobs_click_new_tab: {code: 13, shiftKey: true},
elem_deselect: {code: 27},
change_tab_left: {code: 72},
change_tab_right: {code: 83},
move_tab_left: {code: 72, shiftKey: true},
move_tab_right: {code: 83, shiftKey: true},
history_back: {code: 72, ctrlKey: true},
history_forward: {code: 83, ctrlKey: true}
}

var conf = {
scroll_speed: 0.4,
scroll_speed_fast: 1.1,
scroll_friction: 0.8,
chars: "SANOTEHUCP",
input_whitelist: ["checkbox", "radio", "hidden", "submit", "reset", "button", "file", "image"],
location_change_check_timeout: 2000
}

//There's a lot we don't want to do if we're not on an actual webpage, but on
//the "speed dial"-ish pages.
var onWebPage = (document.body !== undefined);

function randomChar() {
var index = Math.floor(Math.random() * conf.chars.length);
return conf.chars[index];
}

function getElemPos(elem) {
var curtop = 0;
var curleft = 0;

do {
curtop += elem.offsetTop;
curleft += elem.offsetLeft;
} while (elem = elem.offsetParent);

return {top: curtop, left: curleft};
}

var blobList = {
blobs: {},
container: null,

visible: false,
needLoadBlobs: true,

currentKey: "",

createContainer: function() {
var container = document.createElement("div");
container.style =
"pointer-events: none;"+
"display: none;"+
"position: absolute;"+
"top: 0px;"+
"left: 0px;"+
"z-index: 2147483647"; //Max z-index value in most browsers
document.body.appendChild(container);
blobList.container = container;
},

init: function() {
if (!onWebPage)
return;

blobList.createContainer();

window.addEventListener("scroll", function() {
blobList.needLoadBlobs = true;
});
},

currentIndex: 0,
loadBlobs: function() {
if (!onWebPage)
return;

var linkElems = document.querySelectorAll("a, button, input, textarea");

//Remove old container contents
blobList.container.innerHTML = ""

//Remove old blobs
blobList.blobs = {};

var i = 0;
function addBlob() {
var linkElem = linkElems[i];
i += 1;

if (i > linkElems.length)
return false;

if (linkElem === undefined)
return true;

//We don't want hidden elements
if ((linkElem === undefined)
|| (linkElem.style.display == "none")
|| (linkElem.style.visibility == "hidden")) {
return true;
}

//Get element's absolute position
var pos = getElemPos(linkElem);
//Lots of things which don't really exist have an X and Y value of 0
if (pos.top == 0 && pos.left == 0)
return true;

//We don't need to get things far above our current scroll position
if (pos.top < (window.scrollY - 100))
return true;

//We don't need things below our scroll position either
if (pos.top - 100 > (window.scrollY + window.innerHeight))
return true;

var key = randomChar();
while (blobList.blobs[key])
key += randomChar();

var blobElem = document.createElement("div");
blobElem.innerHTML = key;
blobElem.style =
"position: absolute;"+
"background-color: yellow;"+
"border: 1px solid black;"+
"border-radius: 10px;"+
"padding-left: 3px;"+
"padding-right: 3px;"+
"color: black;"+
"top: "+pos.top+"px;"+
"left: "+pos.left+"px;"+
"line-height: 12px;"+
"font-size: 8pt;";
blobList.container.appendChild(blobElem);

blobList.blobs[key] = {
blobElem: blobElem,
linkElem: linkElem
}
return true;
}

while (addBlob()) {};

},

showBlobs: function() {
blobList.visible = true;
blobList.container.style.display = "block";
},

hideBlobs: function() {
blobList.currentKey = "";
blobList.visible = false;
blobList.container.style.display = "none";
},

click: function() {
if (!blobList.visible)
return;

var blob = blobList.blobs[blobList.currentKey];
if (!blob)
return;

blob.linkElem.click();
blob.linkElem.focus();
blobList.hideBlobs();
},

clickNewTab: function() {
if (!blobList.visible)
return;

var blob = blobList.blobs[blobList.currentKey];
if (!blob)
return;

if (blob.linkElem.tagName == "A" && blob.linkElem.href) {
self.port.emit("tab_open", blob.linkElem.href);
} else {
blob.linkElem.click();
blob.linkElem.focus();
}

blobList.hideBlobs();
},

appendKey: function(c) {
blobList.currentKey += c;
}
}
blobList.init();

//Reload blobs whenever the URL changes
var currentUrl = location.href;
setInterval(function() {
if (currentUrl !== location.href) {
blobList.loadBlobs();
}
currentUrl = location.href;
}, conf.location_change_check_timeout);

function isMatch(k, evt) {
if ((k.code === evt.keyCode)
&& (!!k.ctrlKey == evt.ctrlKey)
&& (!!k.shiftKey == evt.shiftKey)
&& (!!k.altKey == evt.altKey)
&& (!!k.metaKey == evt.metaKey)) {
return true;
}

return false;
}

var pressedKeys = [];

window.addEventListener("keydown", function(evt) {

//We don't want to do anything if the user is tpying in an input field,
//unless the key is to deselect an input field
var active = document.activeElement;
if ((active.tagName === "TEXTAREA")
|| (active.tagName === "INPUT" && conf.input_whitelist.indexOf(active.type.toLowerCase) === -1)
|| (/true/i.test(active.contentEditable))) {

if (isMatch(keys.elem_deselect, evt)) {
active.blur();
} else {
return;
}
}

//User is typing a key to a blob
var c = String.fromCharCode(evt.keyCode);
if (blobList.visible && conf.chars.indexOf(c) !== -1) {
blobList.appendKey(c);
evt.preventDefault();
evt.stopPropagation();
return false;
}

//Handle other key presses
//Show/hide/reload blobs
if (onWebPage && !blobList.visible && isMatch(keys.blobs_show, evt)) {
if (blobList.needLoadBlobs)
blobList.loadBlobs();
blobList.needLoadBlobs = false;
blobList.showBlobs();
} else if (onWebPage && !blobList.visible && isMatch(keys.blobs_show_reload, evt)) {
blobList.loadBlobs();
blobList.needLoadBlobs = false;
blobList.showBlobs();
} else if (onWebPage && blobList.visible && isMatch(keys.blobs_hide, evt)) {
blobList.hideBlobs();

//Simulate clicks
} else if (onWebPage && blobList.visible && isMatch(keys.blobs_click, evt)) {
blobList.click();
} else if (onWebPage && blobList.visible && isMatch(keys.blobs_click_new_tab, evt)) {
blobList.clickNewTab();

//Scrolling
} else if (onWebPage && isMatch(keys.scroll_up, evt)) {
scroll.start(-conf.scroll_speed);
} else if (onWebPage && isMatch(keys.scroll_down, evt)) {
scroll.start(conf.scroll_speed);
} else if (onWebPage && isMatch(keys.scroll_up_fast, evt)) {
scroll.start(-conf.scroll_speed_fast);
} else if (onWebPage && isMatch(keys.scroll_down_fast, evt)) {
scroll.start(conf.scroll_speed_fast);

//Back and forwards
} else if (isMatch(keys.history_back, evt)) {
history.back();
} else if (isMatch(keys.history_forward, evt)) {
history.forward();

//Change tab
} else if (isMatch(keys.change_tab_left, evt)) {
self.port.emit("change_tab_left");
} else if (isMatch(keys.change_tab_right, evt)) {
self.port.emit("change_tab_right");

//Move tab
} else if (isMatch(keys.move_tab_left, evt)) {
self.port.emit("move_tab_left");
} else if (isMatch(keys.move_tab_right, evt)) {
self.port.emit("move_tab_right");

//We don't want to stop the event from propagating
//if it hasn't matched anything yet
} else {
return true;
}

evt.preventDefault();
evt.stopPropagation();
return false;
}, true);

window.addEventListener("keyup", function(evt) {
if ((isMatch(keys.scroll_up, evt))
|| (isMatch(keys.scroll_down, evt))
|| (isMatch(keys.scroll_up_fast, evt))
|| (isMatch(keys.scroll_down_fast, evt))) {
scroll.stop();
}
}, true);

var scroll = {
start: function(acceleration) {
scroll.acceleration = acceleration;

if (scroll.raf == null)
scroll.update();
},

stop: function() {
scroll.acceleration = 0;
},

update: function() {
var tdiff = scroll.endTime - scroll.startTime;
if (tdiff < 100) {
scroll.velocity += scroll.acceleration;
window.scrollBy(0, scroll.velocity * tdiff);
scroll.velocity *= conf.scroll_friction;
}

if (tdiff < 10 && scroll.velocity < 0.03 && scroll.velocity > -0.03) {
scroll.velocity = 0;
cancelAnimationFrame(scroll.raf);
scroll.raf = null;
} else {
scroll.startTime = scroll.endTime;
scroll.endTime = new Date().getTime();
scroll.raf = requestAnimationFrame(scroll.update);
}
},

raf: null,
acceleration: 0,
velocity: 0,
startDate: 0,
endDate: 0
}

+ 25
- 0
install.rdf Просмотреть файл

@@ -0,0 +1,25 @@

<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">

<Description about="urn:mozilla:install-manifest">
<em:id>keyless@mort.coffee</em:id>
<em:version>1.0</em:version>
<em:bootstrap>true</em:bootstrap>
<em:type>2</em:type>

<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>1.5</em:minVersion>
<em:maxVersion>4.0.*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Keyless</em:name>
<em:description>A plugin for keyless browsing.</em:description>
<em:creator>Martin Dørum Nygaard</em:creator>
<em:homepageURL>http://www.mort.coffee/</em:homepageURL>
</Description>
</RDF>

+ 52
- 0
lib/main.js Просмотреть файл

@@ -0,0 +1,52 @@
var tabs = require("sdk/tabs");
var self = require("sdk/self");

tabs.on("ready", function(tab) {
var worker = tab.attach({
contentScriptFile: self.data.url("onload.js")
});

function selectRelativeTab(n) {
var tabList = [];
var currentTabIndex;
for (let t of tabs) {
tabList[t.index] = t;
if (t.index == tab.index) {
currentTabIndex = t.index;
}
}

var newTabIndex = currentTabIndex + n;
if (newTabIndex >= tabList.length) {
newTabIndex = 0;
} else if (newTabIndex < 0) {
newTabIndex = tabList.length - 1;
}

tabList[newTabIndex].activate();
}

function moveRelativeTab(n) {
tab.index += n;
}

worker.port.on("tab_open", function(url) {
tabs.open(url);
});

worker.port.on("change_tab_left", function() {
selectRelativeTab(-1);
});

worker.port.on("change_tab_right", function() {
selectRelativeTab(1);
});

worker.port.on("move_tab_left", function() {
moveRelativeTab(-1);
});

worker.port.on("move_tab_right", function() {
moveRelativeTab(1);
});
});

Двоичные данные
mouseless-plugin.xpi Просмотреть файл


+ 9
- 0
package.json Просмотреть файл

@@ -0,0 +1,9 @@
{
"name": "mouseless-plugin",
"title": "Mouseless",
"id": "jid1-tCi0cNSM24lhnw",
"description": "For a mouseless future.",
"author": "Martin Dørum Nygaard",
"license": "MIT",
"version": "0.1"
}

+ 12
- 0
test/test-main.js Просмотреть файл

@@ -0,0 +1,12 @@
var main = require("./main");

exports["test main"] = function(assert) {
assert.pass("Unit test running!");
};

exports["test main async"] = function(assert, done) {
assert.pass("async Unit test running!");
done();
};

require("sdk/test").run(exports);

Загрузка…
Отмена
Сохранить