// ==UserScript== // @name GM Base // @fullname Greasemonkey Base // @description General Library for Greasemonkey Scripts // @name:de GM Base // @fullname:de Greasemonkey Base // @description:de Allgemeine Bibliothek für Greasemonkey Scripts // @author ollily2907 // @license Apache License, Version 2.0 // @license https://www.apache.org/licenses/LICENSE-2.0.txt // @homepageURL https://ollily.github.io/gm-common/ // @supportURL https://ollily.github.io/gm-common/ // @source https://raw.githubusercontent.com/ollily/gm-common/master/gm_base/gm_base.user.js // @icon // @compatible firefox >=52 // @compatible chrome >=57 // @namespace https://github.com/ollily // @run-at document-end // @version 0.52.001 // @include http://gmonkey.*.*/test/6/* // @include http://devzone.*.*/test/gm/* // @downloadURL https://update.greasyfork.icu/scripts/428507/GM%20Base.user.js // @updateURL https://update.greasyfork.icu/scripts/428507/GM%20Base.meta.js // ==/UserScript== // //GM-Script specific code - START // // --------------- // base-core.js - START // --------------- // noinspection JSUnusedGlobalSymbols const CURR_HOST = document.location.host; let CURR_PORT = document.location.port; /** * The URL we are currently running on. */ let currSite = CURR_HOST; if (document.location.port) { CURR_PORT = ":" + document.location.port; if (currSite.search(CURR_PORT) === -1) { currSite += ":" + document.location.port; } } const currPath = document.location.href.substring(document.location.href.indexOf(currSite) + currSite.length); const bTestMode = false; const INIT_ONLOAD = true; // - General DHTML-Lib - Start // - modified & extended dhtml.js from selfhtml.de let DHTML = false, DOM = false, MSIE4 = false, NS4 = false, OP = false; const SHORT_ID = "id"; const ATTR_SEP = ";"; if (document.getElementById) { DHTML = true; DOM = true; } else { if (document.all) { DHTML = true; MSIE4 = true; } else { if (document.layers) { DHTML = true; NS4 = true; } } } if (window.opera) { OP = true; } /** * Shorthand, tries to find one element represented by the identifier in the * page. * * @param identifier - * the key (id) of the element * @returns {Object} the found object or null */ function gmGetElI(identifier) { return gmGetEl(SHORT_ID, identifier, null); } /** * Tries to find one element represented by the identifier in the page. * * @param mode - * how the Identifier is interpreted (id, name, tagname) * @param identifier - * the key (id, name, tagname) of the element * @param elementNumber - * the index (only used for Mode=name, tagname) * @returns {Object} the found object or null */ function gmGetEl(mode, identifier, elementNumber) { let element, elementList; if (gmIsObject(identifier)) { return identifier; } if (!elementNumber) { elementNumber = 0; } if (!gmIsInstanceOf(mode, String)) { if (mode != null) { alert(mode.id + " " + identifier.constructor); } else { alert(null); } return null; } if (DOM) { if (mode.toLowerCase() === "id") { element = document.getElementById(identifier); return element; } if (mode.toLowerCase() === "name") { elementList = document.getElementsByName(identifier); element = elementList[elementNumber]; if (!element || element === "undefined") { element = null; } return element; } if (mode.toLowerCase() === "tagname") { elementList = document.getElementsByTagName(identifier); element = elementList[elementNumber]; return element; } return null; } if (MSIE4) { if (mode.toLowerCase() === "id" || mode.toLowerCase() === "name") { element = document.all(identifier); return element; } if (mode.toLowerCase() === "tagname") { elementList = document.all.tags(identifier); element = elementList[elementNumber]; return element; } return null; } if (NS4) { if (mode.toLowerCase() === "id" || mode.toLowerCase() === "name") { element = document[identifier]; if (!element) { element = document.anchors[identifier]; } return element; } if (mode.toLowerCase() === "layerindex") { element = document.layers[identifier]; return element; } return null; } return null; } /** * Tries to find all elements represented by the identifier in the page. * * @param mode * how the Identifier is interpreted (id, name, tagname) * @param identifier * the key (id, name, tagname) of the element * @returns {Object} returns the found content as array or null */ function gmGetElList(mode, identifier) { if (gmIsObject(identifier)) { return identifier; } if (identifier == null) { return null; } if (DOM) { if (mode.toLowerCase() === "name") { return document.getElementsByName(identifier); } if (mode.toLowerCase() === "tagname") { return document.getElementsByTagName(identifier); } return null; } if (MSIE4) { if (mode.toLowerCase() === "id" || mode.toLowerCase() === "name") { return document.all(identifier); } if (mode.toLowerCase() === "tagname") { return document.all.tags(identifier); } return null; } if (NS4) { return gmGetEl(mode, identifier); } return null; } /** * Shorthand, tries to find an attribute of an element in the page. * * @param identifier - * the key (id) of the element * @param attributeName - * the name of the attribute * @returns {Object} returns the found attribute or false */ function gmGetAtI(identifier, attributeName) { return gmGetAt(SHORT_ID, identifier, null, attributeName); } /** * Tries to find an attribute of an element in the page. * * @param mode - * how the Identifier is interpreted (id, name, tagname) * @param identifier - * the key (id, name, tagname) of the element * @param elementNumber - * the index (only used for Mode=name, tagname) * @param attributeName - * the name of the attribute * @returns {Object} returns the found attribute or null */ function gmGetAt(mode, identifier, elementNumber, attributeName) { let attribute = null; const element = gmGetEl(mode, identifier, elementNumber); if (element) { if (DOM || MSIE4) { try { attribute = element[attributeName]; } catch (e) { try { attribute = element.getAttribute(attributeName); } catch (e2) { // ignored } } } if (NS4) { attribute = element[attributeName]; if (!attribute) { attribute = null; } } } return attribute; } /** * Shorthand, tries to find the content of an element in the page. * * @param identifier - * the key (id) of the element * @returns {Object} returns the found content or null */ function gmGetCoI(identifier) { return gmGetCo(SHORT_ID, identifier, null); } /** * Tries to find the content of an element in the page. * * @param mode - * how the Identifier is interpreted (id, name, tagname) * @param identifier - * the key (id, name, tagname) of the element * @param elementNumber - * the index (only used for Mode=name, tagname) * @returns {Object} returns the found content or null */ function gmGetCo(mode, identifier, elementNumber) { let content = null; const element = gmGetEl(mode, identifier, elementNumber); if (element) { if (DOM && element.firstChild) { if (element.firstChild.nodeType === 3) { content = element.firstChild.nodeValue; } else { content = ""; } return content; } if (MSIE4) { content = element.innerText; return content; } } return content; } /** * Shorthand, tries to set the new content to an element in the page. * * @param identifier - * the key (id) of the element * @param text - * the new text to set to the element * @returns {Boolean} TRUE=if set was successfull, else FALSE */ function gmSetCoI(identifier, text) { return gmSetCo(SHORT_ID, identifier, null, text); } /** * Tries to set the new content to an element in the page. * * @param mode - * how the Identifier is interpreted (id, name, tagname) * @param identifier - * the key (id, name, tagname) of the element * @param elementNumber - * the index (only used for Mode=name, tagname) * @param text - * the new text to set to the element * @returns {Boolean} TRUE=if set was successfull, else FALSE */ function gmSetCo(mode, identifier, elementNumber, text) { const element = gmGetEl(mode, identifier, elementNumber); if (element) { if (DOM) { if (!element.firstChild) { element.appendChild(document.createTextNode("")); } element.firstChild.nodeValue = text; return true; } if (MSIE4) { element.innerText = text; return true; } if (NS4) { element.document.open(); element.document.write(text); element.document.close(); return true; } } return false; } // - General DHTML-Lib - End /** * Shorthand: Sets a new value for an attribute to an element, any existing * attribute value is replaced. * * @param identifier - * the key (id) of the element * @param attributeName - * the unique name of the attribute * @param attributeValue - * the new value for that attribute * @returns {Boolean} TRUE = the value could be set, else FALSE */ function gmSetAtI(identifier, attributeName, attributeValue) { return gmSetAt(SHORT_ID, identifier, null, attributeName, attributeValue); } /** * Sets a new value for an attribute to an element, any existing attribute value * is replaced. * * @param mode - * how the Identifier is interpreted (id, name, tagname) * @param identifier - * the key (id, name, tagname) of the element * @param elementNumber * @param attributeName - * the unique name of the attribute * @param attributeValue - * the new value for that attribute * @returns {Boolean} TRUE = the value could be set, else FALSE */ function gmSetAt(mode, identifier, elementNumber, attributeName, attributeValue) { //var attribute; const element = gmGetEl(mode, identifier, elementNumber); if (element) { if (DOM || MSIE4) { try { element[attributeName] = attributeValue; } catch (e) { try { element.setAttribute(attributeName, attributeValue); } catch (e2) { // ignored } } return true; // return gmGetAt(mode, identifier, elementNumber, attributeName); } if (NS4) { element[attributeName] = attributeValue; return true; // return gmGetAt(mode, identifier, elementNumber, attributeName); } } return false; } /** * Adds an additional value for an attribute to an element, the new value is * appended at the end. * * @param mode - * how the Identifier is interpreted (id, name, tagname) * @param identifier - * the key (id, name, tagname) of the element * @param attributeName - * the unique name of the attribute * @param attributeValue - * the new value for that attribute * @returns {Boolean} TRUE = the value could be set, else FALSE */ function gmAppAt(mode, identifier, attributeName, attributeValue) { const oldValue = gmGetAt(mode, identifier, attributeName); const newValue = oldValue + ATTR_SEP + attributeValue; return gmSetAt(mode, identifier, attributeName, newValue); } /** * Shorthand: Adds an additional value for an attribute to an element, the new * value is appended at the end. * * @param identifier - * the key (id, name, tagname) of the element * @param attributeName - * the unique name of the attribute * @param attributeValue - * the new value for that attribute * @returns {Boolean} TRUE = the value could be set, else FALSE */ function gmAppAtI(identifier, attributeName, attributeValue) { return gmAppAt(SHORT_ID, identifier, attributeName, attributeValue); } /** * Verifies if an instance is an array. * * @param obj - * the instance to test * @returns {Boolean} TRUE=if instance is an array, else FALSE */ function gmIsArray(obj) { return gmIsInstanceOf(obj, Array); } function gmIsFunction(obj) { return gmIsInstanceOf(obj, Function); } function gmIsUndefined(obj) { return (obj == null ? true : (typeof obj == "undefined")); } /** * Verifies if an instance is an object. * * @param obj - * the instance to test * @returns {Boolean} TRUE=if instance is an object, else FALSE */ function gmIsObject(obj) { return (obj == null ? false : (typeof obj == "object")); } /** * Tests, if an instanced object is from the requested type. * * @param obj - * an instance of an object (must not be null) * @param objType - * a name of a type, if null it will be replaced by "Object" * @returns {Boolean} TRUE = if the object is from the tested type, else FALSE */ function gmIsInstanceOf(obj, objType) { let isType = false; if (obj != null) { if (objType == null) { objType = "Object"; } try { let tObjType = eval(objType); isType = (obj.constructor === tObjType); } catch (e) { // ignore } // isType = (typeof obj) == objType; } return isType; } /** * Removes all spaces from the left. * * @param a - * the string to trim * @returns {String} the trimmed string */ function ltrim(a) { let ret = null; if (a != null) { ret = String(a); let pos = 0; while (a.charAt(pos) === " ") { pos++; } ret = a.substring(pos); } return ret; } /** * Removes all spaces from the right. * * @param a - * the string to trim * @returns {String} the trimmed string */ function rtrim(a) { let ret = null; if (a != null) { ret = String(a); let pos = a.length - 1; while (a.charAt(pos) === " ") { pos--; } ret = a.substring(0, pos + 1); } return ret; } /** * Removes all space from the left and right. * * @param a - * the string to trim * @returns {String} the trimmed string */ function trim(a) { return ltrim(rtrim(a)); } function gmCleanText(dirtyText) { let cleanText = ""; if (gmIsInstanceOf(dirtyText, String)) { cleanText = dirtyText.replace(/\s\s/g, "").replace(/\n/g, "").replace(/\r/g, "").replace(/\t/g, "").replace(/#/g, ""); } return cleanText; } /** * Extracts a numeric value from a text. Supports only "full" numbers; * * @param a * the text to parse * @returns {Number} the found numeric value or 0; */ function gmToNo(a) { let numFound = ""; if (isNaN(a)) { if (a && a.length > 0) { for (let int = 0; int < a.length; int++) { const a_ele = a[int]; if (!isNaN(a_ele)) { numFound += a_ele; } else { } } } } else { numFound = a; } let newNum = Number(numFound).valueOf(); if (typeof (newNum) != "number") { newNum = 0; } return newNum; } const SORT_NO = 0; const SORT_DEF = 1; const SORT_REV = 2; const SORT_NUM = 3; /** * Sorts an array by a specific sort order (alphanumeric). * * @param unsortedArray - the aray which should be sorted * @param sortMode - the sort order or leave null to ignore sorting * @returns {Array} the sorted array */ function gmSortArray(unsortedArray, sortMode) { const sortedArray = unsortedArray; if (sortMode == null) { sortMode = false; } if (sortMode === SORT_NUM) { sortedArray.sort(function (aE, bE) { return aE - bE; }); } else if (sortMode === SORT_REV) { sortedArray.reverse(); } else if (sortMode || sortMode === SORT_DEF) { sortedArray.sort(); } return sortedArray; } function gmSortObject(unsortedObjects, sortField) { try { if (gmIsArray(unsortedObjects)) { unsortedObjects.sort(function (aElem, bElem) { const x = aElem[sortField].toLowerCase(); const y = bElem[sortField].toLowerCase(); if (x < y) { return -1; } if (x > y) { return 1; } return 0; } ); } } catch (ex) { alert(ex); } return unsortedObjects; } function gmOnlyUnique(arrArray) { let arrUnique = []; if (gmIsArray(arrArray)) { arrUnique = arrArray.filter(function (value, index, self) { return self.indexOf(value) === index; }); } return arrUnique; } /** * The start point for all gmonkey scripts. * * @param e - * the occuring event * @returns {Boolean} TRUE = if all handler are succesfull done, else FALSE */ function gmAddHandler(e) { let isDone; lgm_addKnownSites(); lgm_addStyles(); lgm_addControls(); lgm_addInitAction(); isDone = true; return isDone; } /** * Now add the event handler. */ function gmInitEventHandler() { if (INIT_ONLOAD) { window.addEventListener("load", function (e) { gmAddHandler(e); }); } } // --------------- // base-core.js - END // --------------- // --------------- // base-object.js - START // --------------- // noinspection JSUnresolvedVariable,JSUnresolvedFunction,JSUnusedGlobalSymbols /** * Creates an DOM-Object. * * @param par - the parent object to create the new object as child * @param objtyp - the type of the new object (HTML-Tagname) * @param id - the id and name of the new object * @returns {Object} the created object or null */ function gmCreateObj(par, objtyp, id) { // var obj = $("<" + objtyp + ">"); let obj = null; if (objtyp != null && objtyp !== "") { obj = document.createElement(objtyp); if (obj) { if (id != null) { // obj.attr("id", id); // obj.attr("name", id); gmSetAtI(obj, "id", id); gmSetAtI(obj, "name", id); } if (gmIsObject(par)) { // $(par).append(obj); par.appendChild(obj); } } } return obj; } /** * Create common attributes for an DOM-Object. Leave null if not used. * * @param obj - the object to create the attributes in it * @param caption - a text which will be displayed for this object * @param tit - a W3C-conform title for that DOM * @param ro - if set != null, the DOM will be readonly * @param ev_click - a javascript-call for the click-event * @param ev_focus - a javascript-call for the focus-event * @param ev_mOver - a javascript-call for the mouseover-event * @param ev_mOut - a javascript-call for the mouseout-event * @param ev_dblClick - a javascript-call for the doubleclick-event * @returns {Object} the object with added attributes FIXME: Check */ function gmCreateObjCommon(obj, caption, tit, ro, ev_click, ev_focus, ev_mOver, ev_mOut, ev_dblClick) { if (obj) { // obj.attr("title", tit); gmSetAtI(obj, "title", tit); if (ro) { // obj.attr("readonly", "readonly"); gmSetAtI(obj, "readonly", "readonly"); } if (caption) { // obj.append(caption); gmSetCoI(obj, caption); } if (ev_click) { // obj.click(ev_click); obj.onclick = ev_click; } if (ev_dblClick) { // obj.click(ev_dblClick); obj.ondblclick = ev_dblClick; } if (ev_focus) { // obj.focus(ev_focus); obj.onfocus = ev_focus; } if (ev_mOver) { // obj.hover(ev_mOver); obj.onmouseover = ev_mOver; } if (ev_mOut) { // obj.hover(ev_mOut); obj.onmouseout = ev_mOut; } } return obj; } /** * Creates a DOM-Button. * * @param par - the parent object to create the new object as child * @param typ - the type of the new object (HTML-Tagname) * @param id - the id and name of the new object * @param caption - a text which will be displayed for this object * @param tit - a W3C-conform title for that DOM * @param initval - an initial value is set in this input * @param ev_click - a javascript-call for the click-event * @returns {Object} the created DOM-Button */ function gmCreateButton(par, typ, id, caption, tit, initval, ev_click) { let obj = gmCreateObj(par, "button", id); obj = gmCreateObjCommon(obj, caption, tit, null, ev_click); if (!typ) { typ = "button"; } gmSetAtI(obj, "type", typ); if (initval) { gmSetAtI(obj, "value", initval); } return obj; } /** * Creates a DOM-Link. * * @param par - the parent object to create the new object as child * @param id - the id and name of the new object * @param href * @param caption - a text which will be displayed for this object * @param tit - a W3C-conform title for that DOM * @param target * @param ev_click - a javascript-call for the click-event * @param ev_dblClick - a javascript-call for the doubleclick-event * @returns {Object} the created DOM-Link */ function gmCreateLink(par, id, href, caption, tit, target, ev_click, ev_dblClick) { let obj = gmCreateObj(par, "a", id); obj = gmCreateObjCommon(obj, caption, tit, null, ev_click, null, null, null, ev_dblClick); if (href) { gmSetAtI(obj, "href", href); gmSetAtI(obj, "data-href", href); } if (target) { gmSetAtI(obj, "target", target); } return obj; } /** * Creates a DOM-Input-Element. * * @param par - the parent object to create the new object as child * @param typ - the type of the new input (Type-Attribute) * @param id - the id and name of the new object * @param initval - an initial value is set in this input * @param tit - a W3C-conform title for that DOM * @param ro - if set != null, the DOM will be readonly * @param ev_click - a javascript-call for the click-event * @param ev_focus - a javascript-call for the focus-event * @returns {Object} the new DOM-Input */ function gmCreateInput(par, typ, id, initval, tit, ro, ev_click, ev_focus) { let obj = gmCreateObj(par, "input", id); if (obj) { obj = gmCreateObjCommon(obj, null, tit, ro, ev_click, ev_focus); if (!typ) { typ = "text"; } gmSetAtI(obj, "type", typ); if (initval) { // obj.val(initval); gmSetAtI(obj, "value", initval); } else { // obj.val(""); gmSetAtI(obj, "value", ""); } } return obj; } /** * Adds an object as a child node to a parent object or the document body. * * @param obj - the object to append at the end of the child list * @param parent - the parent object to append to, leave null to put it to the * document-body * @returns {Boolean} TRUE = the object could be added, else FALSE */ function gmAddObj(obj, parent) { let isSet = false; if (gmIsObject(obj)) { if (!parent) { parent = gmGetEl("tagname", "body"); } parent.appendChild(obj); isSet = true; } return isSet; } /** * Sets a new value into an object. * * @param id - the id of the object * @param initval - the new value for that element or empty * @returns {Boolean} TRUE = if the value could be set, else FALSE */ function gmSetInput(id, initval) { let isSet = false; // var obj = document.getElementById(id); const obj = gmGetElI(id); if (obj) { if (initval) { // obj.setAttribute("value", initval); gmSetAtI(obj, "value", initval); isSet = true; } else { // obj.setAttribute("value", ""); gmSetAtI(obj, "value", ""); isSet = true; } } return isSet; } /** * Selects the text in a input element. * * @param inputElem - the element containing the text * @returns {Boolean} TRUE = if the input element could be selected, else FALSE */ function gmSelectInput(inputElem) { let isSet = false; if (gmIsObject(inputElem)) { try { inputElem.select(); isSet = true; } catch (e) { } } return isSet; } /** * Constants for Selection of Text using IE. */ const SELECT_IE = 0; /** * Constants for Selection of Text using Gecko Engine. */ const SELECT_G = 1; /** * @returns {Number} which Selection of Text Modus is used */ function gmGetTextSelectMode() { if (document.selection && document.selection.createRange) { return SELECT_IE; } else if (document.createRange && window.getSelection) { return SELECT_G; } return SELECT_IE; } /** * Constants Mode which is currently used for Selection of Text. * * @see {@link #gmGetTextSelectMode()} */ const SELECT_CURR = gmGetTextSelectMode(); /** * Returns the selected Text or an empty String. * * @returns {String} the text which is currently selected. */ function gmGetSelectedText() { let selectedText = ""; if (SELECT_IE === SELECT_CURR) { selectedText = document.selection.createRange().text; } else if (SELECT_G === SELECT_CURR) { selectedText = window.getSelection(); } if (typeof selectedText == "object") { selectedText = selectedText.toString(); } return selectedText; } /** * Creates a new Range-Object for an element. * * @param elem - an element for the Range-Object or null; * @returns {Range} a new Range-Object or null */ function gmGetNewRange(elem) { let textRange = null; if (SELECT_IE === SELECT_CURR) { textRange = document.selection.createRange(); } else if (SELECT_G === SELECT_CURR) { if (gmIsObject(elem)) { textRange = document.createRange(); try { textRange.selectNode(elem); } catch (e) { // alert(e); } } } return textRange; } /** * Selects or Unselects the content inside an element (not only for input * elements. * * @param elem - the element to select the text in it * @param bForceSelect - TRUE=always select, FALSE=switch between select and unselect * @returns {String} the selected text or an empty string */ function gmSelectText(elem, bForceSelect) { let selection; let currSel = gmGetSelectedText(); if (bForceSelect == null) { bForceSelect = false; } if (!bForceSelect && (currSel && currSel !== "")) { if (SELECT_IE === SELECT_CURR) { // noinspection BadExpressionStatementJS document.selection.empty; } else if (SELECT_G === SELECT_CURR) { selection = window.getSelection(); selection.removeAllRanges(); } } else { if (gmIsObject(elem)) { const tRange = gmGetNewRange(elem); if (SELECT_IE === SELECT_CURR) { tRange.select(); } else if (SELECT_G === SELECT_CURR) { selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(tRange); } currSel = gmGetSelectedText(); } } return currSel; } /** * Removes an object from the DOM. * * @param obj the object to delete * @returns {Boolean} TRUE=the obj was deleted */ function gmDelObj(obj) { let isDel = false; let oObj = gmGetElI(obj); if (gmIsObject(oObj)) { const parent = oObj.parentNode; if (gmIsObject(parent)) { try { parent.removeChild(oObj); isDel = true; } catch (e) { alert("ERR: " + e); } } } return isDel; } /** * Removes (clears) the object from it's children. * * @param obj - the object to clear * @returns {Boolean} TRUE = always, if obj is an object, FALSE = obj is null or not an object */ function gmEmptyObj(obj) { let isEmpty = false; let oObj = gmGetElI(obj); if (gmIsObject(oObj)) { while (oObj.firstChild) { oObj.removeChild(oObj.firstChild); } isEmpty = true; } return isEmpty; } // --------------- // base-object.js - END // --------------- // --------------- // base-web.js - START // --------------- // noinspection JSUnusedGlobalSymbols class PagelinksClazz { /** * * @param {string} link * @param {string|string[]} linkText */ constructor(link, linkText) { this.link = link; if (!gmIsArray(linkText)) { this.linkText = [linkText]; } else { this.linkText = linkText; } } } class KnowSiteClazz { /** * @param {string|RegExp} site */ constructor(site) { this.site = site; } } class KnowSiteFilterClazz extends KnowSiteClazz { /** * @param {string|RegExp} site * @param {string} filter * @param {string|RegExp} path */ constructor(site, filter, path) { super(site); this.filter = filter; this.path = path; } } class KnowSiteExtClazz extends KnowSiteClazz { /** * @param {string|RegExp} site * @param {string|RegExp} url * @param {string|RegExp} search * @param {string|RegExp} replace * @param {string|RegExp} replaceLarge * @param {number} withDownload */ constructor(site, url, search, replace, replaceLarge, withDownload) { super(site); this.url = url; this.search = search; this.replace = replace; this.replace_large = replaceLarge; this.withDownload = withDownload; } } /** * List of all URLs which are known by this script. * @type {KnowSiteFilterClazz[]|KnowSiteExtClazz[]} */ let knownSite = []; /** * Minimum width of the preview. * @type {number} */ const minWidth = 240; /** * Maximum width of the preview. * @type {number} */ const maxWidth = 640; /** * Adds an url which should be known for the search. * * @param {string} filter - a predefined searchtext for that url * @param {string|RegExp} site - the hostname as regular expression * @param {string|RegExp} path - a path as regular expression (optional) */ function gmAddSite(filter, site, path) { if (gmIsArray(knownSite)) { if (site && (site.length > 0)) { knownSite.push(new KnowSiteFilterClazz( site, (filter != null ? filter : ".+"), (path != null ? path : "")) ); // const len = knownSite.length; // knownSite[len] = {}; // knownSite[len].site = site; // knownSite[len].filter = (filter != null ? filter : ".+"); // knownSite[len].path = (path != null ? path : ""); } } } /** * Adds a config for a known site. * * @param {string|RegExp} site - a site pattern * @param {string|RegExp} urlElem - a pattern for the html-element to search in the page * @param {string|RegExp} urlSearch - a pattern to search inside the url of the html-element * @param {string|RegExp} urlReplace - a literal which will used for replacing the urlSearch * @param {string|RegExp} urlReplaceLarge - a literal which will used for replacing the urlSearch with a url for large images * @param {number} withDownload - 1=will add a download-link beneath the picture, else 0 */ function gmAddSite2(site, urlElem, urlSearch, urlReplace, urlReplaceLarge, withDownload) { if (gmIsArray(knownSite)) { if (site && site.length > 0) { knownSite.push(new KnowSiteExtClazz( site, (urlElem != null ? urlElem : ".+"), (urlSearch != null ? urlSearch : ""), (urlReplace != null ? urlReplace : ""), (urlReplaceLarge != null ? urlReplaceLarge : ""), (withDownload != null ? withDownload : 0) )); // const len = knownSite.length; // knownSite[len] = {}; // knownSite[len].site = site; // knownSite[len].url = (urlElem != null ? urlElem : ".+"); // knownSite[len].search = (urlSearch != null ? urlSearch : ""); // knownSite[len].replace = (urlReplace != null ? urlReplace : ""); // knownSite[len].replace_large = (urlReplaceLarge != null ? urlReplaceLarge : ""); // knownSite[len].withDownload = (withDownload != null ? withDownload : 0); } } } /** * Searchs in the list of known sites, if this site is found and returns the * predefined searchtext. If multiple sites will match, the LAST matching * filter will be returned. * * @param {string} site - the hostname of the site to search for * @param {string} path - the path of the site to search for (optional) * @returns {string} the predefined searchtext */ function gmFoundFilter(site, path) { let retFilter = ""; if (gmIsArray(knownSite) && site) { if (!path) { path = ""; } //for (let i = 0; i < knownSite.length; i++) { let init = 0; for (let currSite of knownSite) { //let currSite = knownSite[i]; if (site.search(currSite.site) >= 0) { if (init === 0 && currSite.path === "") { retFilter = currSite.filter; init = 1; } let fIdx = path.search(currSite.path); if (path !== "" && (fIdx >= 0)) { retFilter = currSite.filter; break; } else if (path === "" && currSite.path === "") { retFilter = currSite.filter; break; } } } } return retFilter; } /** * Searchs in the list of known sites, if this site is found and returns the predefined searchtext. If multiple sites will match, the LAST matching site will be returned. * * @param {string} site - the hostname of the site to search for * @return {KnowSiteExtClazz} a found site configuration */ function gmFoundFilter2(site) { let retFilter = null; if (gmIsArray(knownSite) && site) { for (let currSite of knownSite) { if (site.search(currSite.site) >= 0) { retFilter = currSite; } } //for (let i = 0; i < knownSite.length; i++) { // if (site.search(knownSite[i].site) >= 0) { // retFilter = knownSite[i]; //} } return retFilter; } /** @type {string} */ const FL_TAG = "result-list"; /** @type {string} */ const FL_ID = "_FL"; /** * * @param {string|HTMLElement} curlink * @param {number} withDesc * @return {string[]} */ function gmPrepareLinkData(curlink, withDesc) { const linkData = []; linkData.push(gmGetAtI(curlink, "href")); if (withDesc !== 0) { linkData.push(gmGetAtI(curlink, "title")); linkData.push(gmGetAtI(curlink, "aria-label")); linkData.push(gmGetAtI(curlink, "alt")); linkData.push(gmGetAtI(curlink, "onmouseover")); linkData.push(gmGetAtI(curlink, "onclick")); linkData.push(curlink.innerHTML.replace("\\n", "").replace("#", "")); } return linkData; } /** * * @param {string|HTMLAnchorElement} curlink * @param {number} withDesc * @return {string[]} */ function gmPrepareLinkTextData(curlink, withDesc) { let linkTextData = []; try { const tmpTextData = []; tmpTextData.push(curlink.text); if (withDesc !== 0) { tmpTextData.push(gmGetAtI(curlink, "title")); tmpTextData.push(gmGetAtI(curlink, "alt")); tmpTextData.push(gmGetAtI(curlink, "aria-label")); tmpTextData.push(gmGetAtI(curlink, "onmouseover")); tmpTextData.push(gmGetAtI(curlink, "onclick")); tmpTextData.push(curlink.innerHTML); linkTextData = tmpTextData.map(function (value) { if (gmIsUndefined(value)) { return ""; } else if (gmIsObject(value)) { return value.toString(); } else if (gmIsInstanceOf(value, String)) { return gmCleanText(value); } else { return value; } }); } } catch (ex) { alert(ex); } return linkTextData; } /** * * @param {string} searchPattern * @return {RegExp} */ function gmPrepareSearchRegExp(searchPattern) { if (!searchPattern || searchPattern.length <= 0) { searchPattern = ".*"; } else if (searchPattern.charAt(0) === "/" && searchPattern.charAt(searchPattern.length - 1) === "/") { searchPattern = searchPattern.substring(1, searchPattern.length); searchPattern = searchPattern.substring(0, searchPattern.length - 1); } else { searchPattern = searchPattern.replace(/\?/g, ".").replace(/\./g, "\.").replace(/\*/g, ".*"); } //alert(searchPattern); return new RegExp(searchPattern, "i"); } /** * Search for all matching links in the page. * * @param {string} searchPattern - the search pattern or leave "" to get all * @param {number} withDesc - 0 = search only in links, * 1 = search also in link description * @returns {PagelinksClazz[]} an array with all found links */ function gmFindLinksInPage(searchPattern, withDesc) { let pagelinks = []; if (!withDesc) { withDesc = 0; } if (bTestMode) { pagelinks = gmGenTestEntries(40); } else { //searchPattern = gmPrepareSearchRegExp(searchPattern); //for (let linksIdx = 0; linksIdx < document.links.length; linksIdx++) { for (let curLink of document.links) { //let curLink = document.links[linksIdx]; const found = gmLinkMatchesPattern(curLink, searchPattern, withDesc); if (found && gmGetAtI(curLink.id, FL_TAG) !== FL_ID) { let htmlLink = gmGetAtI(curLink, "href"); let htmlText = gmLinkGenerateLinkText(curLink, withDesc); let bFound = false; for (let foundLinksIdx = 0; foundLinksIdx < pagelinks.length; foundLinksIdx++) { let currPageLink = pagelinks[foundLinksIdx]; if (htmlLink === currPageLink.link) { bFound = true; currPageLink.linkText.push(htmlText); pagelinks[foundLinksIdx].linkText = gmOnlyUnique(currPageLink.linkText); //alert(pagelinks[foundLinksIdx].linkText); break; } } if (!bFound) { pagelinks.push(new PagelinksClazz(htmlLink, htmlText)); } } } } return pagelinks; } /** * DON'T USE DIRECTLY * * @param {string|Object} curLink * @param {string} searchPattern - a search text (might be a regular expression) * @param {number} withDesc * @returns {boolean} TRUE= the search text is found in the array, or FALSE */ function gmLinkMatchesPattern(curLink, searchPattern, withDesc) { let cleanSearchPattern = gmPrepareSearchRegExp(searchPattern); const arrText = gmPrepareLinkData(curLink, withDesc); let bFound = false; if (gmIsArray(arrText)) { for (let i = 0; i < arrText.length; i++) { const searchText = arrText[i]; try { bFound = searchText.search(cleanSearchPattern) !== -1; } catch (e) { // ignored } if (bFound) { break; } } } return bFound; } /** * DON'T USE DIRECTLY * * @param {string|Object} curLink * @param {number} withDesc * @returns {string} the final link description */ function gmLinkGenerateLinkText(curLink, withDesc) { let arrText = gmPrepareLinkTextData(curLink, withDesc); let searchTextClean = []; let htmlText = ""; if (gmIsArray(arrText)) { for (let idxST = 0; idxST < arrText.length; idxST++) { searchTextClean[idxST] = trim(gmCleanText(arrText[idxST])); } htmlText = gmOnlyUnique(searchTextClean).join(""); } return htmlText; } /** * Adds a javascript block into the page. * * @param {Object|Function|Array|string} scc - a string, a function or an array with the javascript code or a function-list * @returns {boolean} TRUE = if the script block could be set, else FALSE */ function gmAddScriptGlobal(scc) { let isSet = false; if (gmIsObject(scc) || gmIsFunction(scc) || (scc && scc.length > 0)) { const head = gmGetHead(); if (head) { const script = gmCreateObj(head, "script"); script.type = "text/javascript"; let allscc = ""; if (gmIsArray(scc)) { for (let i = 0; i < scc.length; i++) { allscc += scc[i] + " \n"; } } else { allscc = scc; } gmSetCoI(script, "\n" + allscc + "\n"); isSet = true; } } return isSet; } /** * Adds a link to a javascript file into the page. * * @param {string|string[]} scLink - a string or an array with the url of the javascript-file * FIXME: Check */ function gmAddScriptLinkGlobal(scLink) { let isSet = false; const head = gmGetHead(); if (head && scLink && scLink.length > 0) { let allScLink; if (gmIsArray(scLink)) { allScLink = scLink; } else { allScLink = [scLink]; } for (let i = 0; i < allScLink.length; i++) { const newScript = gmCreateObj(head, "script"); newScript.type = "text/javascript"; newScript.src = allScLink[i]; } isSet = true; } return isSet; } /** * Adds a style block into the page. * * @param {string|string[]} scc - a string or an array with the css code * @returns {boolean} TRUE = if the style block could be set, else FALSE */ function gmAddStyleGlobal(scc) { let isSet = false; if (gmIsObject(scc) || (scc && scc.length > 0)) { const head = gmGetHead(); if (head) { const style = gmCreateObj(head, "style"); style.type = "text/css"; let allscc = ""; if (gmIsArray(scc)) { for (let i = 0; i < scc.length; i++) { allscc += scc[i] + " \n"; } } else { allscc = scc; } gmSetCoI(style, "\n" + allscc + "\n"); isSet = true; } } return isSet; } /** * Generates some sample entries for testing. * * @param {number} maxEntries - number of entries to generate * @returns {PagelinksClazz[]} array of entries */ function gmGenTestEntries(maxEntries) { if (isNaN(maxEntries)) { maxEntries = 1; } if (maxEntries < 0) { maxEntries = 0; } else if (maxEntries > 100) { maxEntries = 100; } let testArray = []; for (let i = 1; i <= maxEntries; i++) { let htmlLink = "https://" + currSite + currPath + "/link-" + i; let htmlText = "linktext-" + i; testArray.push(new PagelinksClazz(htmlLink, htmlText)); } return testArray; } /** * Calculate the offset of an element relating to the elemnt at the most top. * * @param {Object|HTMLInputElement} element - the element to check the offeset * @returns {number[]} an array with the leftOffset, topOffset * FIXME:TEST */ function gmCumulativeOffset(element) { let valueT = 0; let valueL = 0; if (element) { valueL = element.width || 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; element = element.offsetParent; } while (element); } return [valueL, valueT]; } /** * Calculates the horizontal offset to the right in relation to it's parent element. * * @param {Object} parentElem - the element to calculate the offset from * @param {Array} iPoint - a screen point, to add an additional offset * @param {number} iZoom - the zoom factor 1= Originalsize * @returns {number} the horizontal offset or 0 FIXME:Test */ function gmCalcOffsetH(parentElem, iPoint, iZoom) { if (isNaN(iZoom)) { iZoom = 1; } let offsetH = 0; if (parentElem) { offsetH = gmCumulativeOffset(parentElem)[1]; if (!isNaN(offsetH) && gmIsArray(iPoint)) { if (!isNaN(iPoint[1])) { offsetH = offsetH - (iPoint[1] * iZoom) + (parentElem.height || 0); } } } return offsetH; } /** * Searches the url for a pattern and replace the text. * * @param {string} searchForPattern - the pattern to search for * @param {string} replaceWithText - the text what will be inserted instead * @param {string} oldUrl - the URL to search in * @returns {string} the url with replaced text */ function gmGetReplaceUrl(searchForPattern, replaceWithText, oldUrl) { let newUrl = oldUrl; if (oldUrl != null) { if (searchForPattern !== "") { // there is something to replace if (replaceWithText == null) { replaceWithText = ""; } const patternReplace = new RegExp(searchForPattern); newUrl = oldUrl.replace(patternReplace, replaceWithText); } } return newUrl; } /** * Returns the actual server of the running page. * * @returns {string} a server name */ function gmGetCurrentSite() { let currSite = document.location.host; if (document.location.port) { currSite += ":" + document.location.port; } return currSite; } /** * Determine the metrics of that image. * * @param {Object} newImage - the image to inspect * @returns {number[]} the image metrics [width, height] in px */ function gmGetImageSize(newImage) { let imageObjectWidth = 0; let imageObjectHeight = 0; if (newImage && newImage.width && newImage.width > 0) { imageObjectWidth = newImage.width; imageObjectHeight = newImage.height; } if (imageObjectWidth <= 0) { imageObjectWidth = minWidth; } else if (imageObjectWidth > maxWidth) { imageObjectWidth = maxWidth; imageObjectHeight = 0; } if (imageObjectHeight <= 0) { imageObjectHeight = "auto"; } return [imageObjectWidth, imageObjectHeight]; } /** * Add a default style to a div-element. * * @param {Object} hDiv - the div-element * @param {number[]} iPoint - metrics of the image [width, height] in px * @param {number} offsetW - an offset (number) for the width (optional), default is 5px * @param {number} offsetH - an offset (number) for the height (optional), default is 5px * @param {number} ratio - the aspect ratio of the image, only is used, if we have no image height * @param {number} iZoom - * @returns {boolean} TRUE = the layout could be added, else FALSE */ function gmSetDivLayout(hDiv, iPoint, offsetW, offsetH, ratio, iZoom) { let w = "auto"; let h = "auto"; let oH = "5px"; let oW = "5px"; let isSet = false; if (gmIsObject(hDiv)) { if (isNaN(iZoom)) { iZoom = 1; } if (gmIsArray(iPoint)) { if (!isNaN(iPoint[0])) { w = iPoint[0] + 2; } if (isNaN(iPoint[1])) { h = (gmToNo(hDiv.style.width) / ratio) + 2;// + picTextHeight; } else { h = (iPoint[1] + 2); // + picTextHeight; } } if (!isNaN(offsetH)) { oH = offsetH + "px"; } if (!isNaN(offsetW)) { oW = offsetW + "px"; } // noinspection JSCheckFunctionSignatures if (!isNaN(w)) { w *= iZoom; w = w + "px"; } // noinspection JSCheckFunctionSignatures if (!isNaN(h)) { h *= iZoom; h = h + "px"; } let css = gmGetAtI(hDiv, "style"); if (css === false) { css = ""; } css += ";width:" + w + ";height:" + h + ";top:" + oH + ";left:" + oW; gmSetAtI(hDiv, "style", css); isSet = true; } return isSet; } /** * Add a default style to an img-element. * * @param {Object} hDiv - the div-element * @param {Object} hImg - the img-element * @param {number[]} iPoint - metrics of the image [width, height] in px * @param {number} iZoom - the zoom factor (1 = Originalsize) * @returns {boolean} TRUE = the layout could be added, else FALSE */ function gmSetImgLayout(hDiv, hImg, iPoint, iZoom) { let h = "auto"; let w = "auto"; let isSet = false; if (gmIsObject(hDiv)) { if (isNaN(iZoom)) { iZoom = 1; } if (gmIsArray(iPoint)) { if (!isNaN(iPoint[0])) { w = iPoint[0]; } if (isNaN(iPoint[1])) { if (gmIsObject(hImg)) { let ratio = gmToNo(hImg.style.width) / gmToNo(hImg.style.height); h = (gmToNo(hDiv.style.width) / ratio);// + picTextHeight; } } else { h = iPoint[1]; // + picTextHeight; } } // noinspection JSCheckFunctionSignatures if (!isNaN(w)) { w *= iZoom; w = w + "px"; } // noinspection JSCheckFunctionSignatures if (!isNaN(h)) { h *= iZoom; h = h + "px"; } let css = gmGetAtI(hDiv, "style"); if (css === false) { css = ""; } css += ";width:" + w + ";height:" + h; gmSetAtI(hDiv, "style", css); isSet = true; } return isSet; } /** * * @returns {Object} the object for the head in the page, or null */ function gmGetHead() { return gmGetEl("tagname", "head"); } /** * * @returns {Object} the object for the body in the page, or null */ function gmGetBody() { return gmGetEl("tagname", "body"); } /** * @param {string|HTMLElement} obj - the object from which to get the css-style * @returns {HTMLStyleElement} the css-style-object from that object */ function gmGetStyle(obj) { let res = null; const oObj = gmGetElI(obj); if (oObj) { try { res = oObj.style; } catch (e) { // ignored } } return res; } /** * * @param {string|HTMLElement} obj - the object to set the css-style * @param {string} styleName - the name of style * @param {*} styleValue - the new value for this style * @return {HTMLElement} the modified object */ function gmSetStyle(obj, styleName, styleValue) { let oStyle = gmGetStyle(obj); if (oStyle) { try { gmSetAtI(oStyle, styleName, styleValue); } catch (e) { // ignored } } return obj; } /** * @returns {number} height of the document body */ function gmGetBodyHeight() { let D = gmGetBody(); let Dh = 0; let Eh = 0; if (D) { // noinspection JSCheckFunctionSignatures Dh = Math.max(isNaN(D.style.height) ? 0 : D.style.height, D.scrollHeight, D.offsetHeight, D.clientHeight); } if (D.documentElement) { D = D.documentElement; // noinspection JSCheckFunctionSignatures Eh = Math.max(isNaN(D.style.height) ? 0 : D.style.height, D.scrollHeight, D.offsetHeight, D.clientHeight); } return Math.max(Dh, Eh); } /** * * @param {string} url * @return {boolean} */ function gmOpenInTab(url) { if (url) { window.open(url, "_blank"); } return true; } // --------------- // base-web.js - END // --------------- // --------------- // base-clipboard.js - START // --------------- // noinspection JSUnresolvedVariable,JSUnresolvedFunction,JSUnusedGlobalSymbols /** * @returns {Object} the reference to the base window, might be the greasemonkey * unsafeWindow */ function gmClipRef() { let refWindow = window; if (!refWindow && unsafeWindow != null) { refWindow = unsafeWindow; } return refWindow; } /** * @returns {Object} the reference to the the Security Manager or null * @depricated since FF 16 */ function gmPrivsManager() { let privsMan = null; const wdw = gmClipRef(); if (gmIsObject(wdw)) { try { if (gmIsObject(wdw.netscape.security.PrivilegeManager)) { privsMan = wdw.netscape.security.PrivilegeManager; } } catch (e) { // ignored } } return privsMan; } /** * Copies a text to clipboard. * * @param {string|Object} text - the text to copy to the clipboard * @param {boolean} bQuite - don't schow any alerts * @param {Object} refWindow - a reference on the page (optional) * @returns {string|null} text = if set to clipboard, else null */ function gmCopy2clipboard(text, bQuite, refWindow) { const resultText = text; let wdw = gmClipRef(); if (wdw.clipboardData) { wdw.clipboardData.setData("text", text); return resultText; } else { try { wdw.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); } catch (ex) { if (!bQuite) { alert("Internet Security settings do not allow copying to clipboard!"); } return null; } try { let e = wdw.Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(wdw.Components.interfaces.nsIClipboard); if (!e) return null; } catch (ex) { if (!bQuite) { alert("1:" + ex); } return null; } try { let b = wdw.Components.classes["@mozilla.org/widget/transferable;1"] .createInstance(wdw.Components.interfaces.nsITransferable); if (!b) return null; } catch (ex) { if (!bQuite) { alert("2:" + ex); } return null; } b.addDataFlavor("text/unicode"); try { let o = wdw.Components.classes["@mozilla.org/supports-string;1"] .createInstance(wdw.Components.interfaces.nsISupportsString); if (!o) return null; o.data = text; } catch (ex) { if (!bQuite) { alert("3:" + ex); } return null; } b.setTransferData("text/unicode", o, (text == null ? 0 : text.length * 2)); try { let t = wdw.Components.interfaces.nsIClipboard; e.setData(b, null, t.kGlobalClipboard); } catch (ex) { if (!bQuite) { alert("4:" + ex); } return null; } if (!bQuite) { alert("Copy doesn't work!"); } return text; } } /** * Same as {@link #gmCopy2clipboard(text, bQuite, refWindow)}, but customized only for use in a webpage. * Not usable for a greasemonkey script. * * @param {*} text - the text to copy to the clipboard */ function copyPostToClipboard(text) { let clipboard = null, transferable = null, clipboardID = null; try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); } catch (e) { alert(e); } try { clipboard = Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(Components.interfaces.nsIClipboard); } catch (e) { alert(e); } try { transferable = Components.classes["@mozilla.org/widget/transferable;1"] .createInstance(Components.interfaces.nsITransferable); } catch (e) { alert(e); } if (transferable) { transferable.addDataFlavor("text/unicode"); const str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString); str.data = text; transferable.setTransferData("text/unicode", str, str.data.length * 2); try { clipboardID = Components.interfaces.nsIClipboard; if (clipboard) { clipboard.setData(transferable, null, clipboardID.kGlobalClipboard); } } catch (e) { alert(e); } } } /** * Adds the functions for using a "copy to clipboard" in a web-page. */ function gmAddClipboardSupport() { gmAddScriptGlobal([gmClipRef, gmCopy2clipboard, gmIsClipboardSupported]); } /** * @returns {boolean} TRUE=using clipboard is supported, else FALSE */ function gmIsClipboardSupported() { let isOK = false; try { let privsMan = gmPrivsManager(); if (gmIsObject(privsMan)) { privsMan.enablePrivilege("UniversalXPConnect"); isOK = true; } } catch (ex) { alert("ERR: " + ex); } return isOK; } // --------------- // base-clipboard.js - END // --------------- // //GM-Script specific code - END // // noinspection JSUnusedGlobalSymbols /** * Adds site which should be known by this script. * Can be left empty. */ function lgm_addKnownSites() { } /** * Adds CSS-Styles for this script. * Can be left empty. * * @return {boolean} always true */ function lgm_addStyles() { return true; } /** * Adds HTML-Objects for this script. * Can be left empty. */ function lgm_addControls() { } /** * The first action which should be excecuted in this script. * Can be left empty. * * @return {boolean} always true */ function lgm_addInitAction() { return true; } // //GM-Script - END //