Skip to content

Commit

Permalink
Merge pull request flippidippi#86 from luchenyuxx/issue68
Browse files Browse the repository at this point in the history
  • Loading branch information
abrookins authored Mar 17, 2018
2 parents 5118268 + 4e54ebe commit d28b814
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 53 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ The functionality of sVim will mostly follow the Chrome extension [cVim](https:/
| **Link Hints** | | |
| "f" | open link in current tab | createHint |
| "shift+f" | open link in new background tab | createTabbedHint |
| "ctrl+shift+f" | open link in new foreground tab | createForegroundHint |
| **Clipboard** | | |
| "y y" | copy current URL to clipboard | yankDocumentUrl |

## sVimrc
- The sVimrc page is where you can customize sVim settings and css.
Expand Down
19 changes: 18 additions & 1 deletion sVim.safariextension/sVimGlobal.html
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,20 @@
}
},

// Close other tabs
closeOtherTabs: function() {
var tab = safari.application.activeBrowserWindow.activeTab;
var tabs = safari.application.activeBrowserWindow.tabs;
var tabIndex = tabs.indexOf(tab);

for (var j = 0; j < tabs.length; j++) {
if(j != tabIndex) {
tabs[j].close();
}
}
},


// Close all tabs to the right of the current tab
closeTabsToRight: function() {
var tab = safari.application.activeBrowserWindow.activeTab;
Expand Down Expand Up @@ -385,6 +399,7 @@
"g x t" : "closeTabRight",
"g x 0" : "closeTabsToLeft",
"g x $" : "closeTabsToRight",
"g x o" : "closeOtherTabs",
"shift+x" : "lastClosedTab",
"ctrl+shift+x" : "lastClosedTabBackground",
"t" : "newTab",
Expand All @@ -407,7 +422,9 @@
// Link Hints
"f" : "createHint",
"shift+f" : "createTabbedHint",
//FIXX "ctrl+shift+f" : "createActiveTabbedHint"
"ctrl+shift+f" : "createForegroundHint",
// Clipboard
"y y" : "yankDocumentUrl"
};

// Other private settings
Expand Down
18 changes: 18 additions & 0 deletions sVim.safariextension/sVimHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,21 @@ sVimHelper.matchLocation = function(location, pattern) {

return true;
};

// Copy text to clipboard
sVimHelper.copyToClipboard = function(text) {
var input = document.createElement('input');
input.setAttribute('value', text);
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
};

sVimHelper.inIframe = function () {
try{
return window.self !== window.top;
}catch(e) {
return true;
}
}
160 changes: 110 additions & 50 deletions sVim.safariextension/sVimHint.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,52 @@ var sVimHint = {};
// Start hint
sVimHint.start = function(newTab) {
var hintKeys = new String(sVimTab.settings.hintcharacters).toUpperCase();
var xpath = "//a|//input[not(@type=\x22hidden\x22)]|//textarea|//select|//img[@onclick]|//button|//div[@role=\x22button\x22]|//summary";
/**Given a HTML element, we use following criteria to decide if to generate
* hint for it.
* 1. we check if it has invalid style. If so, return false.
* 2. we check if it has invalid attributes. If so, return false.
* 3. we check if it is the tag we want. If so, return true.
* 4. we check if it has some style we want. If so, return true.
* 5. return false.
*/
var invalidStyle = {
'display': 'none',
'visibility': 'hidden'
}
var invalidAttr = {
'type': 'hidden',
'hidden': true,
'disabled': true
};
var validTagNames = {
'a': true,
'input': true,
'textarea': true,
'select': true,
'img':{'onclick':true},
'button': true,
'div': {'role':'button'},
'summary': true,
'iframe': true
};
var validStyle = {
'cursor': 'pointer'
}
var keyMap = {"8": "Bkspc", "46": "Delete", "32": "Space", "13":"Enter", "16": "Shift", "17": "Ctrl", "18": "Alt"};

var hintKeysLength;
var hintContainerId = "hintContainer";
var hintElements = {};
var inputKey = "";
var lastMatchHint = null;
var k=0;
var hintStrings = [];
var elemCount = 0;

function getAbsolutePosition( elem, html, body, inWidth, inHeight ){
function getAbsolutePosition(elem, html, body, inWidth, inHeight){
var style = getComputedStyle(elem,null);
if(style.visibility === "hidden" || style.opacity === "0" ) return false;
//var rect = rectFixForOpera( elem, getComputedStyle(elem,null)) || elem.getClientRects()[0];
var rect = elem.getClientRects()[0];
if( rect && rect.right - rect.left >=0 && rect.left >= 0 && rect.top >= -5 && rect.bottom <= inHeight + 5 && rect.right <= inWidth ){
var rect = elem.getBoundingClientRect();
// Check if the element is visible on the view.
if( rect && (rect.top+rect.bottom+rect.left+rect.right >0) && rect.right - rect.left >= 0 && rect.bottom-rect.top>=0 && rect.left >= 0 && rect.top >= -5 && rect.top <= inHeight + 5 && rect.left <= inWidth ){
return {
top: (body.scrollTop || html.scrollTop) - html.clientTop + rect.top,
left: (body.scrollLeft || html.scrollLeft ) - html.clientLeft + rect.left
Expand Down Expand Up @@ -52,7 +80,7 @@ sVimHint.start = function(newTab) {
return hintString.join("");
}

function buildHintStrings()
function buildHintStrings(elemCount)
{
var digitsNeeded = Math.ceil(Math.log(elemCount+1) / Math.log(hintKeysLength));
var shortHintCount = Math.floor((Math.pow(hintKeysLength, digitsNeeded) - elemCount) / hintKeysLength);
Expand Down Expand Up @@ -87,42 +115,78 @@ sVimHint.start = function(newTab) {
return result;
}

function createText(num){
function createText(num, elemCount){
if (hintStrings.length == 0) {
buildHintStrings();
buildHintStrings(elemCount);
}
return hintStrings[num];
}

function getXPathElements(win){
function resolv(p){ if (p == "xhtml") return "http://www.w3.org/1999/xhtml"; }
var result = win.document.evaluate(xpath, win.document, resolv, 7, null);
for (var i = 0, arr = [], len = result.snapshotLength; i < len; i++){
arr[i] = result.snapshotItem(i);
// A Depth First Search to iterate through all HTML elements from body. We
// ignore the its children when we find an element meeting our criteria. In
// this way, we avoid generating duplicated hints.
function getValidElements(win) {
var body = win.document.body
var toProcess = [body];
var result = [];
while (toProcess.length > 0){
var elem = toProcess.pop();
if(isValidStyle(elem) && isValidAttr(elem)){
if(isValidTag(elem)) result.push(elem);
else {
var children = elem.children;
for(i=0;i<children.length;i++){
toProcess.push(children[i]);
}
}
}
}
return result;
}
function isValidStyle(elem){
for(s in invalidStyle){
if(getComputedStyle(elem)[s] == invalidStyle[s]) return false;
}
return true;
}
function isValidAttr(elem){
for(attr in invalidAttr){
if(elem.getAttribute(attr) == invalidAttr[attr]) return false;
}
return true;
}
function isValidTag(elem) {
var tagValid = validTagNames[elem.tagName.toLowerCase()]
if(tagValid) {
for(attr in tagValid) {
if(elem.getAttribute(attr) != tagValid[attr]) return false;
}
return true;
} else {
for(s in validStyle) {
if(getComputedStyle(elem)[s] == validStyle[s]) return true;
}
return false;
}
return arr;
}

function countElements(win)
{
function getElemPositions(win) {
var html = win.document.documentElement;
var body = win.document.body;
var inWidth = win.innerWidth;
var inHeight = win.innerHeight
var elems = getXPathElements(win);
elems.forEach(function(elem) {

var elems = getValidElements(win);
var elemPositions = [];
elems.forEach(function(elem){
var pos = getAbsolutePosition(elem, html, body, inWidth, inHeight );
if( pos == false ) return;
elemCount++;
});
elemPositions.push({"elem":elem, "pos":pos});
}, this);
return elemPositions;
}

function start(win){
var html = win.document.documentElement;
var body = win.document.body;
var inWidth = win.innerWidth;
var inHeight = win.innerHeight

var df = document.createDocumentFragment();
var div = df.appendChild(document.createElement("div"));
div.id = hintContainerId;
Expand All @@ -133,11 +197,13 @@ sVimHint.start = function(newTab) {
"margin": "0px"
};

var elems = getXPathElements(win);
elems.forEach(function(elem){
var pos = getAbsolutePosition(elem, html, body, inWidth, inHeight );
if( pos == false ) return;
var hint = createText(k);
var elemPositions = getElemPositions(win);
var k = 0;
var elemCount = elemPositions.length;
elemPositions.forEach(function(elemPosition){
var elem = elemPosition.elem;
var pos = elemPosition.pos;
var hint = createText(k, elemCount);
var span = win.document.createElement("span");
span.appendChild(document.createTextNode(hint));
span.className = "sVim-hint";
Expand All @@ -148,8 +214,8 @@ sVimHint.start = function(newTab) {
if (elem.hasAttribute("href") !== true) {
span.classList.add("sVim-hint-form");
}
st.left = Math.max(0,pos.left-8) + "px";
st.top = Math.max(0,pos.top-8) + "px";
st.left = Math.max(0,pos.left-4) + "px";
st.top = Math.max(0,pos.top-4) + "px";
hintElements[hint] = span;
span.element = elem;
div.appendChild(span);
Expand Down Expand Up @@ -214,8 +280,8 @@ sVimHint.start = function(newTab) {
resetInput();
removeHints();
lastElement.focus();
if (!newTab && /https?:\/\//.test(lastElement.href)) {
sVimTab.commands.newTabBackground(lastElement.href);
if (newTab && /https?:\/\//.test(lastElement.href)) {
newTab(lastElement.href);
}
else {
fireEvent(lastElement, 'click');
Expand All @@ -235,15 +301,16 @@ sVimHint.start = function(newTab) {
}

function removeHints(){
var frame = top.frames;
var frame = window.frames;
if( !document.getElementsByTagName("frameset")[0]){
end(top);
end(window);
}else {
Array.prototype.forEach.call(frame, function(elem){
try{
end(elem);
}catch(e){ }
}, this);
}
Array.prototype.forEach.call(frame, function(elem){
try{
end(elem);
}catch(e){ }
}, this);
}

function resetInput(){
Expand All @@ -269,15 +336,8 @@ sVimHint.start = function(newTab) {

var frame = window.frames;
if(!document.getElementsByTagName("frameset")[0]){
countElements(window);
start(window);
}else{
Array.prototype.forEach.call(frame, function(elem){
try{
countElements(window);
}catch(e){
}
},this);
Array.prototype.forEach.call(frame, function(elem){
try{
start(elem);
Expand Down
29 changes: 27 additions & 2 deletions sVim.safariextension/sVimTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ sVimTab.commands = {
safari.self.tab.dispatchMessage("closeTabsToRight");
},

closeOtherTabs: function() {
safari.self.tab.dispatchMessage("closeOtherTabs");
},

// Open the last closed tab
lastClosedTab: function() {
safari.self.tab.dispatchMessage("lastClosedTab");
Expand Down Expand Up @@ -271,13 +275,17 @@ sVimTab.commands = {

// Enter normal mode
normalMode: function() {
var previousMode = sVimTab.mode
var element = document.activeElement;
if (element != null) {
element.blur();
}
sVimTab.mode = "normal";
sVimTab.commandDiv.innerHTML = "-- NORMAL --";
sVimTab.commandDiv.style.display = "none";
if(previousMode == "normal" && sVimHelper.inIframe()){
window.top.focus();
}
},

// Enter insert mode
Expand All @@ -289,12 +297,29 @@ sVimTab.commands = {

// Open link in current tab
createHint: function() {
sVimHint.start(true);
sVimHint.start();
},

// Open link in new background tab
createTabbedHint: function() {
sVimHint.start(false);
var openUrl = function(url) {
safari.self.tab.dispatchMessage("newTabBackground", url);
};
sVimHint.start(openUrl);
},

// Open link in new foreground tab
createForegroundHint: function() {
var openUrl = function(url) {
safari.self.tab.dispatchMessage("newTab", url);
};
sVimHint.start(openUrl);
},

// Copy current URL to clipboard
yankDocumentUrl: function() {
var text = window.location.href;
sVimHelper.copyToClipboard(text);
}
};

Expand Down

0 comments on commit d28b814

Please sign in to comment.