| 1 | // ==UserScript==
|
|---|
| 2 | // @name OpenContextually
|
|---|
| 3 | // @description open link contextually (selected text, selected link, cite attribute,,,)
|
|---|
| 4 | // @author snj14
|
|---|
| 5 | // @namespace http://white.s151.xrea.com/
|
|---|
| 6 | // @include chrome://browser/content/browser.xul
|
|---|
| 7 | // ==/UserScript==
|
|---|
| 8 |
|
|---|
| 9 | (function OpenContextually() {
|
|---|
| 10 | const MIN_LENGTH = 8;
|
|---|
| 11 | const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|---|
| 12 | var contextMenu = document.getElementById("contentAreaContextMenu");
|
|---|
| 13 | var separator = document.getElementById("context-sep-properties");
|
|---|
| 14 | var urls = [];
|
|---|
| 15 | var command = function(){
|
|---|
| 16 | // for TreeStyleTab http://piro.sakura.ne.jp/xul/_treestyletab.html
|
|---|
| 17 | if ('TreeStyleTabService' in window) TreeStyleTabService.readyToOpenChildTab(gBrowser.selectedTab, true);
|
|---|
| 18 | urls.forEach(function(a){
|
|---|
| 19 | gBrowser.addTab(a);
|
|---|
| 20 | });
|
|---|
| 21 | if ('TreeStyleTabService' in window) TreeStyleTabService.stopToOpenChildTab(gBrowser.selectedTab);
|
|---|
| 22 | }
|
|---|
| 23 | var menu = document.createElementNS(kXULNS, "menuitem");
|
|---|
| 24 | menu.id = "OpenContextually";
|
|---|
| 25 | menu.setAttribute("label", "Open");
|
|---|
| 26 | menu.setAttribute("accesskey", "O");
|
|---|
| 27 | menu.addEventListener("command", command, false);
|
|---|
| 28 | menu.hidden = true;
|
|---|
| 29 | contextMenu.insertBefore(menu, separator);
|
|---|
| 30 |
|
|---|
| 31 | // selected text url
|
|---|
| 32 | function selectTextUrl(selection){
|
|---|
| 33 | if(!gContextMenu.isTextSelected) return;
|
|---|
| 34 | var selection_string = selection.toString();
|
|---|
| 35 | var lst = selection_string.split(/\s/);
|
|---|
| 36 | var res = [];
|
|---|
| 37 | if(!lst.length) return;
|
|---|
| 38 | lst.forEach(function(a){
|
|---|
| 39 | a = a.replace(/[^-.$,;:&=?!*~@#()%\w/]+/g,'');
|
|---|
| 40 | a = a.replace(/^\s*(.+?)\s*$/,'$1');
|
|---|
| 41 | a = a.replace(/^(?:h?t)?tp(s?):[/][/]/,'http$1://');
|
|---|
| 42 | var reg = new RegExp("h?ttps?://");
|
|---|
| 43 | if(a.length > MIN_LENGTH && a.match(reg)) res.push(a);
|
|---|
| 44 | // /https?:\/\//
|
|---|
| 45 | });
|
|---|
| 46 | if(res.length){
|
|---|
| 47 | return ["Open Text ( " + res.length + " )", res];
|
|---|
| 48 | }
|
|---|
| 49 | }
|
|---|
| 50 | // selected links
|
|---|
| 51 | function selectLink(selection){
|
|---|
| 52 | if(!gContextMenu.isTextSelected) return;
|
|---|
| 53 | var URIHash={}, URIList=[], node=null;
|
|---|
| 54 | function check(node){
|
|---|
| 55 | if(!selection.containsNode(node, true)){
|
|---|
| 56 | return NodeFilter.FILTER_SKIP;
|
|---|
| 57 | }else if(node.tagName.toLowerCase() == 'a' && node.href){
|
|---|
| 58 | return NodeFilter.FILTER_ACCEPT;
|
|---|
| 59 | }else{
|
|---|
| 60 | return NodeFilter.FILTER_SKIP;
|
|---|
| 61 | }
|
|---|
| 62 | }
|
|---|
| 63 | for(var i=0,len=selection.rangeCount; i<len; i++){
|
|---|
| 64 | var range = range=selection.getRangeAt(i);
|
|---|
| 65 | if((range.endContainer === range.startContainer) &&
|
|---|
| 66 | (check(node=range.commonAncestorContainer.parentNode) == NodeFilter.FILTER_ACCEPT)){
|
|---|
| 67 | URIHash[node.href] = true;
|
|---|
| 68 | }else{
|
|---|
| 69 | var walker = document.createTreeWalker(range.commonAncestorContainer, NodeFilter.SHOW_ELEMENT, check, false);
|
|---|
| 70 | if(range.startContainer.nodeValue.length == range.startOffset)
|
|---|
| 71 | walker.currentNode = range.startContainer;
|
|---|
| 72 | while(walker.nextNode())
|
|---|
| 73 | URIHash[walker.currentNode.href] = true;
|
|---|
| 74 | }
|
|---|
| 75 | }
|
|---|
| 76 | for(var tmp in URIHash) URIList.push(tmp);
|
|---|
| 77 | if(URIList.length){
|
|---|
| 78 | return ["Open Links ( " + URIList.length + " )", URIList];
|
|---|
| 79 | }
|
|---|
| 80 | }
|
|---|
| 81 | // blockquote's cite attribute
|
|---|
| 82 | function blockquoteCite(selection){
|
|---|
| 83 | var element = gContextMenu.target;
|
|---|
| 84 | while(element.parentNode){
|
|---|
| 85 | if(element.localName.toLowerCase() == "q" ||
|
|---|
| 86 | element.localName.toLowerCase() == "blockquote"){
|
|---|
| 87 | return ["Open original document"
|
|---|
| 88 | ,[element.getAttribute("cite")]];
|
|---|
| 89 | }else{
|
|---|
| 90 | element = element.parentNode;
|
|---|
| 91 | }
|
|---|
| 92 | }
|
|---|
| 93 | }
|
|---|
| 94 | // twitter's @username
|
|---|
| 95 | function selectTwitterUserName(selection){
|
|---|
| 96 | if(!gContextMenu.isTextSelected) return;
|
|---|
| 97 | if(selection.toString().match(/@(\w+)/))
|
|---|
| 98 | return ["Open Twitter ( " + RegExp.$1 + " )",
|
|---|
| 99 | ['http://twitter.com/' + RegExp.$1]];
|
|---|
| 100 | }
|
|---|
| 101 | function setContextMenu(aEvent){
|
|---|
| 102 | if(aEvent.target != aEvent.currentTarget) return;
|
|---|
| 103 | urls = [];
|
|---|
| 104 | menu.hidden = true;
|
|---|
| 105 | var getter = [selectTextUrl
|
|---|
| 106 | , selectLink
|
|---|
| 107 | , blockquoteCite
|
|---|
| 108 | , selectTwitterUserName
|
|---|
| 109 | ];
|
|---|
| 110 | var label;
|
|---|
| 111 | var selection = content.getSelection();
|
|---|
| 112 | for(var i=0; i<getter.length && menu.hidden; i++){
|
|---|
| 113 | var res = getter[i](selection);
|
|---|
| 114 | if(!!res){
|
|---|
| 115 | [label, urls] = res;
|
|---|
| 116 | if(urls.length){
|
|---|
| 117 | menu.hidden = false;
|
|---|
| 118 | menu.setAttribute("label", label);
|
|---|
| 119 | }
|
|---|
| 120 | }
|
|---|
| 121 | }
|
|---|
| 122 | }
|
|---|
| 123 | contextMenu.addEventListener("popupshowing", setContextMenu, false);
|
|---|
| 124 | })();
|
|---|