frisch's UserScript Extender

Extends the document with a new namespace for user script crosswide functions to utilize. Has no use alone but can be accessed by other scripts using document.fExt

当前为 2017-01-17 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name frisch's UserScript Extender
  3. // @namespace http://null.frisch-live.de/
  4. // @version 0.70
  5. // @description Extends the document with a new namespace for user script crosswide functions to utilize. Has no use alone but can be accessed by other scripts using document.fExt
  6. // @author frisch
  7. // @include *
  8. // @require http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js
  9. // @grant GM_setClipboard
  10. // ==/UserScript==
  11. if(document.fExt === undefined) {
  12. console.log("Initializing frisch's UserScript Extender...");
  13.  
  14. // Initialization
  15. fExt = {};
  16. fExt.jq = $;
  17. // Overwrites the contains expression to be case-insensitive
  18. fExt.jq.expr[':'].contains = function(a, i, m) {
  19. return fExt.jq(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
  20. };
  21. fExt.settings = {
  22. minShowTime: 3000, // in milliseconds
  23. maxShowTime: 9000, // in milliseconds
  24. position: "BottomRight", // Position for the popup menu: TopRight, TopLeft, BottomRight, BottomLeft
  25. animationType: "fade", // Animation for the popup menu: slide, fade, none
  26. animationSpeed: 500,
  27. customContextMenu: true,
  28. hideContextMenuOnLeave: true, // automatically fades out the custom context menu when it loses focus, otherwhise only hides when you click somewhere
  29. progressType: "progressbar", // Possible values: hourglass, progressbar
  30. toleranceX: 20, // pixels as tolerance for the context menu, adjust the position slightly
  31. toleranceY: 30, // pixels as tolerance for the context menu, adjust the position slightly
  32. ctxActorMark: true, // set to true to highlight the actor of the context menu when active
  33. ctxActorMarkThickness: 2, // set the thickness of the highlighting border as pixel
  34. };
  35. fExt.popupQueue = [];
  36. fExt.popping = false;
  37.  
  38. var hourGlass0 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhkICgkAYfvBAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAKpJREFUSMetlcEOwyAMQ+39/z97h0nrOkjihPZAAYlXVPALcPgQgE7WEzhA8LODKYLfZoTg7dVGcOm0ENx2bQTDgYVgOiwRLCdSBK2pEEHAB6wI2pvaIgj0AReC87SpOtbXEz5Ivq97Zrs/UWvsO8eovTnci6RYPs5VVu6vKkyqFZjFWZ5FI6EYOkiEIlNIgdLUUOJGqmpK+U/rGpSFn8KiYWHiA8X1NM14AxEBKx1JZtGVAAAAAElFTkSuQmCC";
  39. var hourGlass25 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhkICgR+0Id8AAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAKhJREFUSMetlUkOxCAMBLv4/589h5koG3hjckgAydVtR8bS5oMk24lH+iKsoSx+3zqC++tA1C1cAQ0LT0AewXSZRrDcpBC42xBBeOAiSB0tEUh5wBtB2tQUgVQHnAj63WbRbx3/uA8cfbv3bLWIlxJQT+FQDy4LInUFLkYUHl1ZI6PuIciGrxIZidxdF+TV5y6GH05YC2rqbxc0p8JjsFi/k7aH62436wPGSjUSU11z+AAAAABJRU5ErkJggg==";
  40. var hourGlass50 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhkICTqUnMkUAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAKpJREFUSMe1lMsOgzAMBHfy///sHopEQsGv0BxAMtpZJ8ErbS4k2Y4e6YuwhrM43nUE66OKmJVM9SRiNWb5lkBc+r4ACi08AZIteAAfQVhwEaRKjwikPOAXQbqpWwRSHXAi6E+bRdc63sgDx9/Wma0e4nQE1LdwyAlmjMhdQRcjkkeRRcbd62L48jhvRuzupx7lPHIizTo/IC35Taxbdwxoy6d7tb8NY2Z9AJxfMxVU9/UgAAAAAElFTkSuQmCC";
  41. var hourGlass75 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhkICTPtQHGwAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAK1JREFUSMetlUkSgzAMBKf5/5/FIUnFG9ZiOEAVaHqE8Qjp8ECSneiRPggrOIvvNY+gP2URrZLmfhDRG9M9CyCGvgeAi2DSMNVsECwULOoeECzrWVptW4gANi1EATOCcFNLBFIe8EdQT5t5O+t6Yx5s/K3PbHYRmyUg/wo/dycgeO5yurh8+b4LEkkIhsly342k3Bkolt89FOQPQ9Vqe5iifJhxVk/S8c/1NM26AScHLxnJI48dAAAAAElFTkSuQmCC";
  42. var hourGlass100 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhkICSxgSHxFAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAKpJREFUSMetlUsShDAIBem5/52ZxbhIIhA+40Kr9L0GCSEiwwsR0YkfkR9CG5GF51lHsN+qiNXJ8j6J2AOzfUsgjrwPwBXBy8NLEyAwHBg6B4GpxwxlIHDUONkeCFwtbr0WBIGSoOQPglBHuOiaUl0Ql876/GMejH6BhL1ZRM0pSdqLjaT5fCnYk5tJa+tG0X4ZKFrvHhp2Z6hqr4dp2o8xof2dND5cp7tZvgvYKh27llJDAAAAAElFTkSuQmCC";
  43.  
  44. var pbar0 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AkTCSsaEe8UvgAAALZJREFUSMfdll0OxCAIhAfDjfT+J5Az0adumo1FUXSTnacmVj8ZflpSVQAAEaGlnPPnWUTg1X0+v73wBFjro/C0AvGIIwAj0bF1yHNjD+KKqAWZsam1h98gMwDrYrya7BF7Tes8kJGLpRMQACBVBREpNkpVKeGQvhuWIqtQRHQ5R16FgXrDNQzUs/aYdf9X3nR/akspZqJnh2yt9UflPfPj4Sl59vTCSl/xzii29ZHlSIoaMb31C3teW2ty8diEAAAAAElFTkSuQmCC";
  45. var pbar25 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AkTCSsrQDEUhAAAALZJREFUSMfdlssNxCAMRMfIHUH/FcQ1zZ6yyoEYzG+lnVMkAg+PP4mQBACICGrKOX+fzQxR3efr2wtPgLfeC08zkIh0BaAnOvUOeW5sQUIR1SAjNtX26BtkBOBdTGeT3WOva10E0nOxdAICAEISIkJsFElJOCTtqCIZrUIz43SOoloGag3XZaCWtces+7/ylvtTW0pxEz06ZK/r+lF5j/x4REpeI70w01e6M4ptfeQ5klaNmNb6B29WW2uHr2HMAAAAAElFTkSuQmCC";
  46. var pbar50 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AkTCSs7XYYE4AAAALVJREFUSMfdlssNxCAMRMfIHZn+Kwg1zZ6yyoEYzG+lnVMkAg+PP4mQBACICGoys+9zKQVR3efr2wtPgLfeC08zkIh0BaAnOvUOeW5sQUIR1SAjNtX26BtkBOBdTGeT3WOva10E0nOxdAICAEISIkJsFElJOKThHJmZdNjL6RxFtQzUGq7LQK3qO2bd/5W33J/anLOb6NEhe13Xj8p75McjUvIa6YWZvtKdUWzrI8+RtGrEtNY/mktYa4XsqhcAAAAASUVORK5CYII=";
  47. var pbar75 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AkTCSwJ2hDDpwAAAK1JREFUSMftllEOAyEIRAfDjfD+J9AzsV/bbBpFQWuTpvNlgvJwNCipKgCAiNCSiLzGtVZ4defn3oQnwIrPwtMKxCPeAZjZHVtJngtHENeOWpCITa013INEAFZhvHrYM/aa1nkgM4WlExAAIFUFESk+KFWlhEPinclEhN7s1eUz8uoPCuv3rjfdT23O2ewC0SZbSvnSZYh8PDydnXuBnZDtLcgqNp2AuEAjW0fxC+YyVWuwau4lAAAAAElFTkSuQmCC";
  48. var pbar100 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AkTCTAYVte+CAAAAKJJREFUSMftllEOxCAIRAfDjfT+J9AzzX510w+loLabbDpfJigPR4MKSQCAiKCnnPN33FpDVEd+HU04A6y4F55WIBHpDoBnd2olOS+8goR21IPM2NRboyPIDMAqTFcP22OvaV0E4iksPQEBACEJESFuFElJeEgv6AX9MUiOp7aUYnaB2SZba/2RdTMfj0hn11FgJ2T5mfAAbrl1liNpRxJP/APHYkptRNVvhwAAAABJRU5ErkJggg==";
  49.  
  50. var loader0, loader25, loader50, loader75, loader100;
  51.  
  52. switch(fExt.settings.progressType){
  53. case "progressbar":
  54. loader0 = pbar0;
  55. loader25 = pbar25;
  56. loader50 = pbar50;
  57. loader75 = pbar75;
  58. loader100 = pbar100;
  59. break;
  60. case "hourglass":
  61. loader0 = hourGlass0;
  62. loader25 = hourGlass25;
  63. loader50 = hourGlass50;
  64. loader75 = hourGlass75;
  65. loader100 = hourGlass100;
  66. break;
  67. default:
  68. loader0 = hourGlass0;
  69. loader25 = hourGlass25;
  70. loader50 = hourGlass50;
  71. loader75 = hourGlass75;
  72. loader100 = hourGlass100;
  73. break;
  74. }
  75.  
  76. var topZIndex = 0;
  77. $("[z-index!='']").each(function(){
  78. var thisZIndex = parseInt($(this).css('z-index'));
  79. if(!isNaN(thisZIndex) && thisZIndex > topZIndex)
  80. topZIndex = thisZIndex + 1;
  81. });
  82.  
  83. // Custom Elements
  84. fExt.fExtPopup = $('<div id="fExtPopup" class="fExtElement" style="display:none; position:fixed; width:auto; height:auto; background-color:#545454; padding:10px; border-color:white; border-style:groove; color:white; z-index: ' + topZIndex + '; text-align:center; font-size:12px;"></div>');
  85. $("body").append(fExt.fExtPopup);
  86.  
  87. fExt.fExtMessage = $('<div id="fExtMessage" class="fExtElement" style="display:none; position:fixed; width:auto; height:auto; background-color:#545454; padding:25px; border-color:white; border-style:groove; color:white; z-index: ' + topZIndex + '; text-align:center; font-size:large; padding:20px;"></div>');
  88. fExt.fExtMessageText = $('<span style="display: inline-block;"></span>');
  89. fExt.fExtMessageText.appendTo(fExt.fExtMessage);
  90. fExt.fExtMessage.appendTo("body");
  91.  
  92. // Functions
  93. fExt.setLoading = function(percentage){
  94. if(percentage >= 100){
  95. $("link[rel='icon']").attr("href", loader100);
  96. $("link[rel='shortcut icon']").attr("href", loader100);
  97.  
  98. setTimeout(function(){ fExt.setLoading(-1); },fExt.settings.minShowTime);
  99. }
  100. else if(percentage >= 75){
  101. $("link[rel='icon']").attr("href", loader75);
  102. $("link[rel='shortcut icon']").attr("href", loader75);
  103. }
  104. else if(percentage >= 50){
  105. $("link[rel='icon']").attr("href", loader50);
  106. $("link[rel='shortcut icon']").attr("href", loader50);
  107. }
  108. else if(percentage >= 25){
  109. $("link[rel='icon']").attr("href", loader25);
  110. $("link[rel='shortcut icon']").attr("href", loader25);
  111. }
  112. else if(percentage >= 0){
  113. $("link[rel='icon']").attr("href", loader0);
  114. $("link[rel='shortcut icon']").attr("href", loader0);
  115. }
  116. else {
  117. $("link[rel='icon']").attr("href", siteFavIconHref);
  118. $("link[rel='shortcut icon']").attr("href", siteFavIconHref);
  119. }
  120. };
  121.  
  122. fExt.enqueuePopup = function(msg) {
  123. fExt.popupQueue.push(msg);
  124. };
  125.  
  126. fExt.dequeuePopup = function() {
  127. var rv;
  128. if(fExt.popupQueue.length > 0){
  129. rv = fExt.popupQueue[0];
  130. fExt.popupQueue.splice(0, 1);
  131. }
  132. return rv;
  133. };
  134.  
  135. fExt.createStyle = function(newClass) {
  136. $( "<style>" + newClass + "</style>" ).appendTo("head");
  137. };
  138.  
  139. fExt.center = function (element,w) {
  140. if(w !== undefined)
  141. element.width(w);
  142.  
  143. element.css("position", "fixed")
  144. .css("top", (($(window).height() - $(element).outerHeight()) / 2) + "px")
  145. .css("left", (($(window).width() - $(element).outerWidth()) / 2) + "px");
  146. };
  147.  
  148. fExt.popup = function(msg) {
  149. if(msg !== undefined){
  150. if(fExt.popping){
  151. fExt.enqueuePopup(msg);
  152. return;
  153. }
  154. fExt.popping = true;
  155. fExt.fExtPopup.html(msg);
  156.  
  157. fExt.show(fExt.fExtPopup);
  158.  
  159. var showTime = msg.length * 50;
  160.  
  161. if(showTime < fExt.settings.minShowTime)
  162. showTime = fExt.settings.minShowTime;
  163. else if(showTime > fExt.settings.maxShowTime)
  164. shotTime = fExt.settings.maxShowTime;
  165.  
  166. setTimeout(function(){
  167. fExt.hide(fExt.fExtPopup);
  168. setTimeout(function(){
  169. fExt.fExtPopup.text('');
  170. fExt.popping = false;
  171. fExt.popup(fExt.dequeuePopup());
  172. },fExt.settings.animationSpeed);
  173. },showTime);
  174. }
  175. };
  176.  
  177. fExt.clipboard = function(action, text) {
  178. var clipboardCopy, retVal;
  179. action = action.toLowerCase();
  180.  
  181. clipboardCopy = document.createElement('textarea');
  182. document.body.appendChild(clipboardCopy);
  183. clipboardCopy.value = text;
  184. clipboardCopy.select();
  185.  
  186. var msg;
  187. try {
  188. var successful = document.execCommand(action);
  189. if(successful)
  190. msg = action + " successful";
  191. else
  192. msg = "Could not perform Clipboard-Action " + action;
  193.  
  194. if(clipboardCopy.value.length <= 50)
  195. msg += " (" + clipboardCopy.value + ")";
  196.  
  197. document.fExt.popup(msg);
  198. }
  199. catch (err) {
  200. document.fExt.popup("Error on Clipboard-Action " + action +": " + err);
  201. }
  202.  
  203. if(clipboardCopy)
  204. clipboardCopy.remove();
  205. };
  206.  
  207. fExt.message = function(msg) {
  208. if(msg !== undefined && msg.length > 0){
  209. fExt.fExtMessageText.text(msg);
  210.  
  211. if(!fExt.fExtMessage.is(":visible"))
  212. fExt.show(fExt.fExtMessage);
  213.  
  214. fExt.center($("div#fExtMessage"));
  215. }
  216. else {
  217. fExt.hide(fExt.fExtMessage);
  218. setTimeout(function(){
  219. fExt.fExtMessageText.text('');
  220. },fExt.settings.animationSpeed);
  221. }
  222. };
  223.  
  224. fExt.rotate = function(element, rotation) {
  225. var jqEl = $(element);
  226. var degree = jqEl.data('rotation');
  227.  
  228. if (!degree)
  229. degree = 0;
  230.  
  231. degree += rotation;
  232.  
  233. jqEl.css('-webkit-transform','rotate(' + degree + 'deg)');
  234. jqEl.data('rotation',degree);
  235. };
  236.  
  237. fExt.zoom = function(element, zoom){
  238. var jqEl = $(element);
  239. var zValue = parseFloat(jqEl.css('zoom'));
  240. var zAdd = parseFloat(zoom) / 100;
  241.  
  242. if(!zValue)
  243. zValue = 1.0;
  244.  
  245. var zNew = zValue + zAdd;
  246. jqEl.css('zoom', zNew);
  247. };
  248.  
  249. fExt.zoomIn = function(element, zoom){
  250. fExt.zoom(element, zoom);
  251. };
  252.  
  253. fExt.zoomOut = function(element, zoom){
  254. fExt.zoom(element, zoom * -1);
  255. };
  256.  
  257. switch(fExt.settings.animationType) {
  258. case "fade":
  259. fExt.show = function(element, speed, callback) {
  260. $(element).fadeIn(fExt.settings.animationSpeed, callback);
  261. };
  262. fExt.hide = function(element, speed, callback) {
  263. $(element).fadeOut(fExt.settings.animationSpeed, callback);
  264. };
  265. break;
  266. case "slide":
  267. fExt.show = function(element, speed, callback) {
  268. $(element).slideDown(fExt.settings.animationSpeed, callback);
  269. };
  270. fExt.hide = function(element, speed, callback) {
  271. $(element).slideUp(fExt.settings.animationSpeed, callback);
  272. };
  273. break;
  274. default:
  275. fExt.show = function(element, speed, callback) {
  276. $(element).show(fExt.settings.animationSpeed, callback);
  277. };
  278. fExt.hide = function(element, speed, callback) {
  279. $(element).hide(fExt.settings.animationSpeed, callback);
  280. };
  281. break;
  282. }
  283.  
  284. switch(fExt.settings.position) {
  285. case "TopRight":
  286. fExt.fExtPopup.attr("style", fExt.fExtPopup.attr("style") + "top:50px; right:50px;");
  287. break;
  288. case "TopLeft":
  289. fExt.fExtPopup.attr("style", fExt.fExtPopup.attr("style") + "top:50px; left:50px;");
  290. break;
  291. case "BottomRight":
  292. fExt.fExtPopup.attr("style", fExt.fExtPopup.attr("style") + "bottom:50px; right:50px;");
  293. break;
  294. case "BottomLeft":
  295. fExt.fExtPopup.attr("style", fExt.fExtPopup.attr("style") + "bottom:50px; left:50px;");
  296. break;
  297. default:
  298. break;
  299. }
  300.  
  301. var siteFavIcon = $("link[rel='icon']");
  302. if(siteFavIcon === undefined) {
  303. $("head").append('<link id="favIcon" rel="icon" href="');
  304. siteFavIcon = $("link[rel='icon']");
  305. }
  306. else {
  307. siteFavIcon.attr("id","favIcon");
  308. }
  309. var siteFavIconHref = siteFavIcon.attr("href");
  310.  
  311. fExt.getSelection = function() {
  312. return window.getSelection().toString();
  313. };
  314.  
  315. fExt.getSource = function(element) {
  316. var retVal;
  317.  
  318. var jqTarget = $(element);
  319. var ucTagName = element.tagName.toUpperCase();
  320.  
  321. switch(ucTagName) {
  322. case "IMG":
  323. retVal = element.src;
  324. break;
  325. case "A":
  326. retVal = element.href;
  327. break;
  328. case "VIDEO":
  329. var videoSource = jqTarget.find("source");
  330. retVal = (videoSource.length > 0) ? videoSource.get(0).src : undefined;
  331. break;
  332. case "INPUT":
  333. return jqTarget.val();
  334. default:
  335. jqTarget = $(element).closest("a");
  336. if(jqTarget.length > 0)
  337. retVal = jqTarget.attr("href");
  338. break;
  339. }
  340.  
  341. if(retVal) {
  342. if(!retVal.match("http.*")) {
  343. retVal = window.location.origin + retVal;
  344. }
  345. }
  346. else {
  347. retVal = $(element).text();
  348. }
  349.  
  350. return retVal;
  351. };
  352.  
  353. fExt.createStyle(".fExtLoader { background:url('" + loader50 + "') #EFF7FF no-repeat top center; }");
  354.  
  355. // ContextMenu
  356. if(fExt.settings.customContextMenu) {
  357. fExt.ctxMenu = [];
  358.  
  359. // Styles
  360. fExt.createStyle("#fExtContextMenu,#fExtContextMenu * { text-align: left !important; text-decoration: none !important; color: #fff; }");
  361. fExt.createStyle("#fExtContextMenu { position: fixed; z-index: " + (topZIndex + 10) + "; }");
  362. fExt.createStyle("#fExtContextMenu,.ctxSubList { font-size: 14px; background-color: #263238; width: 300px; height: auto; padding: 0;}");
  363. fExt.createStyle("#fExtContextMenu a,#fExtContextMenu hr,#fExtContextMenu li { width: 100%;}");
  364. fExt.createStyle("#fExtContextMenu .ctxElement { float: left; clear: left;}");
  365. fExt.createStyle("#fExtContextMenu li>a,#fExtContextMenu li>div,.ctxSubList li>a,.ctxSubList li>div { padding: 8px;}");
  366. fExt.createStyle("#fExtContextMenu li hr { margin: 0; border-style: solid; border-color: #666; border-width: 1px 0 0 0;}");
  367. fExt.createStyle("#fExtContextMenu li.ctxSub { cursor: default; font-weight: bold;}");
  368. fExt.createStyle("#fExtContextMenu li.ctxSub * { cursor: pointer; font-weight: normal;}");
  369. fExt.createStyle("#fExtContextMenu li { list-style-type: none; margin: 0 !important;}");
  370. fExt.createStyle("#fExtContextMenu li.ctxSub:hover>div.ctxSubLabel,");
  371. fExt.createStyle("#fExtContextMenu li.ctxSub:hover>div.ctxArrow {}");
  372. fExt.createStyle("#fExtContextMenu li.ctxItem { padding-left: 5px;}");
  373. fExt.createStyle("#fExtContextMenu li.ctxItem { border: none; position: initial; box-sizing: border-box; transition: all 250ms ease; }");
  374. fExt.createStyle("#fExtContextMenu li.ctxItem:hover { background: #3a7999; color: #3a7999; box-shadow: inset 0 0 0 3px #3a7999; }");
  375. fExt.createStyle("#fExtContextMenu li.ctxSub.disabled div.ctxSubLabel:hover,");
  376. fExt.createStyle("#fExtContextMenu li.ctxItem.disabled:hover a,#fExtContextMenu li.disabled div.ctxSubLabel,#fExtContextMenu li.disabled a { opacity: 0.70; font-style: italic; padding-left: 7px !important;}");
  377. fExt.createStyle("#fExtContextMenu li.ctxSeparator { display: inline-block;}");
  378. fExt.createStyle("#fExtContextMenu li.ctxSub div.ctxSubLabel { padding-left: 10px !important; float: left; clear: left;}");
  379. fExt.createStyle("#fExtContextMenu li.ctxSub div.ctxArrow { float: right; clear: right;}");
  380. fExt.createStyle("#fExtContextMenu li.ctxSub ul.ctxSubList { position: absolute; height: auto;}");
  381. fExt.createStyle(".fExtCtxHighlighter { position: absolute; background-color: red; display: block;}");
  382.  
  383. // Default variables
  384. fExt.ctxMenu.uniqueID = 1;
  385. fExt.ctxMenu.allItems = [];
  386. fExt.ctxMenu.actor = undefined;
  387. fExt.ctxMenu.actorStyles = '';
  388. fExt.ctxMenu.html = $('<ul id="fExtContextMenu" class="ctxElement" style="display: none;"></ul>');
  389. fExt.ctxMenu.html.appendTo("body");
  390. fExt.ctxMenu.Show = function() { fExt.show(fExt.ctxMenu.html, 0); };
  391. fExt.ctxMenu.Hide = function() {
  392. fExt.hide(fExt.ctxMenu.html, undefined, function(){
  393.  
  394. if(fExt.ctxMenu.actor) {
  395. if (fExt.settings.ctxActorMark)
  396. $(".fExtCtxHighlighter").remove();
  397. //fExt.ctxMenu.actor = undefined;
  398. }
  399. });
  400. };
  401.  
  402. // Private Methods
  403. ctxCtor = function(ret) {
  404. ret.ItemText = function(value) {
  405. if(value === undefined)
  406. return this.label.text();
  407.  
  408. this.label.text(value);
  409. };
  410. ret.Toggle = function(enabled) {
  411. if(enabled === true || (enabled === undefined && this.hasClass("disabled")))
  412. this.removeClass("disabled");
  413. else
  414. this.addClass("disabled");
  415. };
  416. ret.IsDisabled = function() {
  417. return this.hasClass("disabled");
  418. };
  419. ret.ID = function() {
  420. return this.data("Item-ID");
  421. };
  422. };
  423.  
  424. getParentFrom = function(sub) {
  425. if(sub !== undefined)
  426. return $(sub).find("ul:first");
  427. else
  428. return fExt.ctxMenu.html;
  429. };
  430.  
  431. setZIndex = function(item, parent) {
  432. var parentzIndex = parent.css("z-index");
  433. parentzIndex++;
  434. item.css("z-index", parentzIndex);
  435. item.children().each(function() { $(this).css("z-index", parentzIndex); });
  436. };
  437.  
  438. // Public Methods
  439. fExt.ctxMenu.addItem = function(label, unused, sub) {
  440. console.log("The method 'addItem' is obsolete, please use 'addCtxItem(label, sub)'!");
  441. return fExt.ctxMenu.addCtxItem(label, sub);
  442. };
  443.  
  444. fExt.ctxMenu.addCtxItem = function(label, sub) {
  445. var ret = $('<li class="ctxItem ctxElement"></li>');
  446. var retObj = $('<a href="#" class="ctxElement">' + label + '</a>');
  447. retObj.appendTo(ret);
  448. ret.label = retObj;
  449.  
  450. if(sub)
  451. sub.Add(ret);
  452. else {
  453. ret.appendTo(fExt.ctxMenu.html);
  454. setZIndex(ret, fExt.ctxMenu.html);
  455. }
  456.  
  457. ctxCtor(ret);
  458.  
  459. ret.Attribute = function(attribute, value) {
  460. if(value === undefined)
  461. return ret.attr(attribute);
  462. else
  463. ret.attr(attribute, value);
  464. };
  465.  
  466. ret.Action = undefined;
  467. fExt.ctxMenu.assignID(ret);
  468. return ret;
  469. };
  470.  
  471. fExt.ctxMenu.addSub = function(label, orientation, sub) {
  472. console.log("The method 'addSub' is obsolete, please use 'addCtxSub(label, sub, orientation)'!");
  473. return fExt.ctxMenu.addCtxSub(label, sub, orientation);
  474. };
  475.  
  476. fExt.ctxMenu.addCtxSub = function(label, sub, orientation) {
  477. if(orientation !== undefined)
  478. console.log("The orientation parameter is obsolete, please use addCtxSub(label, sub).");
  479.  
  480. var ret = $('<li class="ctxElement ctxSub"><div class="ctxElement ctxArrow">&gt;</div></li>');
  481. var list = $('<ul class="ctxElement ctxSubList" style="display: none"></ul>');
  482. list.appendTo(ret);
  483. ret.list = list;
  484. var element = $('<div class="ctxElement ctxSubLabel">' + label + ' </div>');
  485. element.appendTo(ret);
  486. ret.label = element;
  487.  
  488. ctxCtor(ret);
  489.  
  490. ret.Items = [];
  491.  
  492. var parentMenu = getParentFrom(sub);
  493.  
  494. if(sub)
  495. sub.Add(ret);
  496. else {
  497. ret.appendTo(parentMenu);
  498. setZIndex(ret, parentMenu);
  499. }
  500.  
  501. ret.Add = function(item) {
  502. ret.Items.push(item);
  503. item.appendTo(ret.list);
  504. setZIndex(item, parentMenu);
  505. };
  506.  
  507. ret.Clear = function() {
  508. while(ret.Items.length > 0) {
  509. ret.Items[0].remove();
  510. ret.Items.splice(0, 1);
  511. }
  512. };
  513.  
  514. return ret;
  515. };
  516.  
  517. fExt.ctxMenu.addSeparator = function(sub) {
  518. var ret = $('<li class="ctxElement ctxSeparator"><hr/></li>');
  519.  
  520. sub.Add(ret);
  521.  
  522. return ret;
  523. };
  524.  
  525. fExt.ctxMenu.assignID = function(item) {
  526. if(item.ID() !== undefined)
  527. console.log("Item already has an ID: " + item.ID());
  528.  
  529. item.data("Item-ID", fExt.ctxMenu.uniqueID);
  530. fExt.ctxMenu.uniqueID++;
  531. fExt.ctxMenu.allItems.push(item);
  532. };
  533.  
  534. fExt.ctxMenu.getItem = function(id) {
  535. if(id !== undefined) {
  536. for(i = 0;i < fExt.ctxMenu.allItems.length; i++) {
  537. var item = fExt.ctxMenu.allItems[i];
  538. if(id === item.ID())
  539. return item;
  540. }
  541. }
  542.  
  543. return undefined;
  544. };
  545.  
  546. // Events
  547. fExt.ctxMenu.html.on("click", "li", function( event ) {
  548. var sender = fExt.ctxMenu.getItem($(this).data("Item-ID"));
  549. if(sender === undefined || sender.Action === undefined)
  550. return true;
  551.  
  552. event.preventDefault();
  553. if(!sender.IsDisabled())
  554. sender.Action(event, sender, fExt.ctxMenu.actor);
  555.  
  556. fExt.ctxMenu.Hide();
  557. return false;
  558. });
  559.  
  560. fExt.ctxMenu.html.on("mouseenter", "li.ctxSub", function(event) {
  561. var sub = $(this);
  562. var subOffs = sub.offset();
  563. var ul = sub.children("ul.ctxSubList:first");
  564.  
  565. if(ul.children("li.ctxItem, li.ctxSub").length === 0)
  566. return;
  567.  
  568. fExt.show(ul, 0);
  569.  
  570. var height = ul.height();
  571. var width = ul.width();
  572. var y = subOffs.top;
  573. var x = subOffs.left + sub.width();
  574. if ((x + width) >= window.screen.availWidth)
  575. x = subOffs.left - ul.width();
  576. if ((y + height + 100 - window.scrollY) >= window.screen.availHeight)
  577. y = y - height + fExt.settings.toleranceY;
  578.  
  579. if ((y - window.scrollY) < 0)
  580. y = window.scrollY;
  581. if ((x - window.scrollX) < 0)
  582. x = window.scrollX;
  583.  
  584. if (y < event.clientY && (y + height) <= event.clientY)
  585. y += fExt.settings.toleranceY;
  586.  
  587. ul.offset({ top: y, left: x});
  588. });
  589.  
  590. fExt.ctxMenu.html.on("mouseleave", "li.ctxSub", function(event) {
  591. var ul = $(this).children("ul.ctxSubList:first");
  592. fExt.hide(ul, 0);
  593. });
  594.  
  595. fExt.ctxMenu.html.on("mouseenter", "li.ctxSub", function(event) {
  596. var ul = $(this).children("ul.ctxSubList:first");
  597. ul.finish();
  598.  
  599. if($(this).find("li.ctxItem").length > 0)
  600. fExt.show(ul, 0);
  601. });
  602.  
  603. fExt.customContextMenuHandler = function (event) {
  604. event.preventDefault();
  605.  
  606. fExt.ctxMenu.actor = $(event.target);
  607.  
  608. if (fExt.settings.ctxActorMark) {
  609. $(".fExtCtxHighlighter").remove();
  610.  
  611. var offs = fExt.ctxMenu.actor.offset();
  612. var coords = [{
  613. name: "top",
  614. x: offs.left,
  615. y: offs.top - fExt.settings.ctxActorMarkThickness,
  616. height: fExt.settings.ctxActorMarkThickness,
  617. width: fExt.ctxMenu.actor.width(),
  618. },{
  619. name: "left",
  620. x: offs.left - fExt.settings.ctxActorMarkThickness,
  621. y: offs.top,
  622. height: fExt.ctxMenu.actor.height(),
  623. width: fExt.settings.ctxActorMarkThickness,
  624. },{
  625. name: "bottom",
  626. x: offs.left,
  627. y: offs.top + fExt.ctxMenu.actor.height() + fExt.settings.ctxActorMarkThickness,
  628. height: fExt.settings.ctxActorMarkThickness,
  629. width: fExt.ctxMenu.actor.width(),
  630. },{
  631. name: "right",
  632. x: offs.left + fExt.ctxMenu.actor.width() + fExt.settings.ctxActorMarkThickness,
  633. y: offs.top,
  634. height: fExt.ctxMenu.actor.height(),
  635. width: fExt.settings.ctxActorMarkThickness,
  636. },];
  637.  
  638. var zIndex = fExt.ctxMenu.actor.css("z-index");
  639. if(zIndex != "auto")
  640. zIndex++;
  641.  
  642. for(var ind in coords){
  643. $("<div class='fExtCtxHighlighter' style='position: absolute; z-index: " + zIndex + "; width: " + coords[ind].width + "px; height: " + coords[ind].height + "px; left: " + coords[ind].x + "px; top: " + coords[ind].y + "px;'></div>").appendTo("body");
  644. }
  645. }
  646.  
  647. var y = event.clientY - fExt.settings.toleranceY,
  648. x = event.clientX - fExt.settings.toleranceX;
  649. if ((x + fExt.ctxMenu.html.width()) >= window.screen.availWidth)
  650. x = x - fExt.ctxMenu.html.width() + (fExt.settings.toleranceX * 1.5);
  651. if ((y + fExt.ctxMenu.html.height() + 100) >= window.screen.availHeight)
  652. y = y - fExt.ctxMenu.html.height() + (fExt.settings.toleranceY * 1.5);
  653.  
  654. if (y < 0)
  655. y = 0;
  656. if (x < 0)
  657. x = 0;
  658.  
  659. fExt.ctxMenu.html.css({
  660. top: y,
  661. left: x
  662. });
  663.  
  664. fExt.ctxMenu.html.trigger("fExtContextMenuOpening", [fExt.ctxMenu.actor]);
  665.  
  666. fExt.ctxMenu.Show();
  667.  
  668. return false;
  669. };
  670.  
  671. if(fExt.settings.hideContextMenuOnLeave) {
  672. fExt.ctxMenu.html.mouseleave(function() {
  673. fExt.ctxMenu.Hide();
  674. });
  675. fExt.ctxMenu.html.mouseenter(function() {
  676. fExt.ctxMenu.html.finish();
  677. fExt.ctxMenu.Show(0);
  678. });
  679. }
  680. else {
  681. $("body").click(function(e) {
  682. if(!$(this).hasClass("ctxElement") && fExt.ctxMenu.html.is(":visible"))
  683. fExt.ctxMenu.Hide();
  684. });
  685. }
  686.  
  687. $(document).on('contextmenu', function(event) {
  688. if(!$(event.target).hasClass("ctxElement")) {
  689. if(!event.shiftKey) {
  690. fExt.customContextMenuHandler(event);
  691. }
  692. }
  693. else
  694. fExt.ctxMenu.Hide();
  695. });
  696.  
  697.  
  698. $(document).on('keyup', function(event) {
  699. if((event.which === 17 && event.altKey) || (event.which === 18 && event.ctrlKey)) {
  700. fExt.settings.customContextMenu = !fExt.settings.customContextMenu;
  701. fExt.popup("Context-Menu has been toggled " + (fExt.settings.customContextMenu ? "on" : "off"));
  702. }
  703. });
  704. }
  705. else {
  706. fExt.ctxMenu.uniqueID = 1;
  707. fExt.ctxMenu.allItems = [];
  708. fExt.ctxMenu.actor = undefined;
  709. fExt.ctxMenu.Show = function() {
  710. console.log("ContextMenu is turned off");
  711. };
  712. fExt.ctxMenu.Hide = function() {
  713. console.log("ContextMenu is turned off");
  714. };
  715. fExt.ctxMenu.addItem = function() {
  716. console.log("ContextMenu is turned off");
  717. };
  718. fExt.ctxMenu.addCtxItem = function() {
  719. console.log("ContextMenu is turned off");
  720. };
  721. fExt.ctxMenu.addSub = function() {
  722. console.log("ContextMenu is turned off");
  723. };
  724. fExt.ctxMenu.addCtxSub = function() {
  725. console.log("ContextMenu is turned off");
  726. };
  727. fExt.ctxMenu.addSeparator = function() {
  728. console.log("ContextMenu is turned off");
  729. };
  730. fExt.ctxMenu.assignID = function() {
  731. console.log("ContextMenu is turned off");
  732. };
  733. fExt.ctxMenu.getItem = function() {
  734. console.log("ContextMenu is turned off");
  735. };
  736. fExt.customContextMenuHandler = function () {
  737. console.log("ContextMenu is turned off");
  738. };
  739. }
  740.  
  741.  
  742. // Finalize
  743. document.fExt = fExt;
  744.  
  745. fExt.fExtPopup.click(function(e){
  746. fExt.popupQueue = [];
  747. fExt.hide($(this));
  748. });
  749.  
  750. fExt.fExtMessage.click(function(e){
  751. fExt.hide($(this));
  752. });
  753.  
  754. console.log("frisch's UserScript Extender initialized!");
  755. }