您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Small script to be @require-d, providing useful functions and extensions I like to regularly refer to
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/447081/1199231/BP%20Funcs.js
// ==UserScript== // @name BP Funcs // @description Small script to be @require-d, providing useful functions and extensions I like to regularly refer to // @version 1.1.0 // @namespace BP // @author Benjamin Philipp <dev [at - please don't spam] benjamin-philipp.com> // ==/UserScript== /* BP Funcs, as of 2023-06-02 17:22:41 (GMT +02:00) */ class SelectorDef{ constructor(selectors="", refineFunc=null, within=null){ this.selectors = []; if(selectors){ switch(typeof selectors){ case "object": if(selectors instanceof Array){ this.selectors = [...selectors]; } else{ try{ Object.assign(this, selectors); return this; }catch(e){ console.warn("Could not assign Object to SelectorDef with", selectors); } } break; case "string": this.selectors = [selectors]; break; default: console.warn("Could not construct SelectorDef with selector of type '" + (typeof selectors) + "':", selectors); } } this.refineFunc = refineFunc; this.within = within; return this; } add(selectors="", refineFunc=null, within=null){ this.selectors.push(new SelectorDef(selectors, refineFunc, within)); return this; } find(within=null, separate=false){ if(!within) within = document; var r; if(separate) r = { length: 0, hasEMpty: false }; else r = $(); var i = 0; for(let sel of this.selectors){ let f = null; if(sel instanceof SelectorDef){ f = sel.find(within); } else{ f = $(within).find(sel); if(this.refineFunc) f = this.refineFunc(f); } if(separate){ if(f && f.length) r[i] = f; else{ r[i] = null; r.hasEMpty = true; } r.length++; } else if(f && f.length) r = r.add(f); i++; } return r; } get(separate = false){ return this.find(this.within, separate); } waitFor(cb, stopForAny = false, requireAll = false, cbFail=null, doneClass="", interval=200, maxTries=50){ console.log("waiting, maxTries = " + maxTries); var res = this.get(requireAll); var runAgain = true; if(res.length>0){ runAgain = maxTries <= -1 && !stopForAny; if(requireAll){ if(!res.hasEMpty){ var results = res[0]; console.log("got all?", res); for(let i = 1; i<res.length; i++) results = results.add(res[i]); cb(results); } else runAgain = true; } else cb(res); } else runAgain = true; if(runAgain){ var t = this; if(maxTries>0){ maxTries--; } else if(maxTries==0){ if(typeof cbFail == "function") cbFail(); return; } this.__timer = setTimeout(function(){ t.waitFor(cb, stopForAny, requireAll, cbFail, doneClass, interval, maxTries); }, interval); } } stop(){ if(this.__timer) clearTimeout(this.__timer); } } String.prototype.after = function(str, fromRight, returnAll){ if(fromRight === undefined) fromRight = false; if(returnAll === undefined) returnAll = false; var os = this.indexOf(str); if(fromRight) os = this.lastIndexOf(str); if(os<0) return returnAll?this:""; return this.substring(os + str.length); }; function arraySortRelevance(arr, q, selectRelevantProperty = (item)=>item){ q = q.toLowerCase().replace(/&/g, " and "); var words = { q: q.split(/[\b\s./\\'-]/) }; var matchFunc = function(a, b){ return a.localeCompare(b, undefined, {sensitivity: "accent", usage: "sort" })===0; }; arr.sort(function(itemA, itemB){ var items = { a: selectRelevantProperty(itemA).toLowerCase().replace(/&/g, " and "), b: selectRelevantProperty(itemB).toLowerCase().replace(/&/g, " and ") }; if(matchFunc(items.a, items.b)){ return 1; } if(matchFunc(items.a, q)) return -1; if(matchFunc(items.b, q)) return 1; var score = { a: 0, b: 0, last: { a: 0, b: 0 } }; var exactMult = 2; var runMult = 2; var missingMult = 1; words.a = items.a.split(/[\b\s./\\'-]/); words.b = items.b.split(/[\b\s./\\'-]/); for(let k of["a", "b"]){ for(let w of words.q){ if(w.trim()==="") continue; missingMult = 1; var mi = items[k].indexOf(w); if(mi>=0){ var s = w.length; var ni = mi + s; var prev = items[k].substring(mi-1, mi); var next = items[k].substring(ni, ni+1); if(! /\w/.test(prev + next)){ s *= exactMult; } score[k] += score.last[k] + s; score.last[k] = w.length * (runMult - 1); } else{ score[k] -= w.length * missingMult; score.last[k] = 0; } } missingMult = 0.5; for(let wk of words[k]){ if(!q.indexOf(wk)>=0){ score[k] -= wk.length * missingMult; } } } // log(score, items, q); if(score.a>score.b) return -1; if(score.a<score.b) return 1; if(items.a.length<items.b.length) return -1; else return 1; }); return arr; } String.prototype.before = function(str, fromRight, returnAll){ if(fromRight === undefined) fromRight = false; if(returnAll === undefined) returnAll = false; var os = this.indexOf(str); if(fromRight) os = this.lastIndexOf(str); if(os<0) return returnAll?this:""; return this.substr(0, os); }; function bpMenu(style="light"){ var r = {}; r.obj = null; r.items = {}; const styles = { light: { colors : { background : "#fff", color : "#333", item : "#222", itemHover : "#268", itemBack : "transparent", frame : "#eee", button:{ background: "#bbb", color: "#333", background_hover: "#157", color_hover: "#fff", } }, fontSize: "14px" }, dark: { colors : { background : "#000", color : "#ccc", item : "#ddd", itemHover : "#6ce", itemBack : "transparent", frame : "#111", button:{ background: "#444", color: "#ccc", background_hover: "#7cf", color_hover: "#000", } }, fontSize: "14px" } }; r.style = styles[style]; if(!r.style) r.style = styles.light; r.css = ` .bpbutton{ padding: 5px; display: inline-block; background: ${r.style.colors.button.background}; color: ${r.style.colors.button.color}; cursor: pointer; font-weight: 600; line-height: 1em; } .bpbutton:hover, .bpbutton.on{ background: ${r.style.colors.button.background_hover}; color: ${r.style.colors.button.color_hover}; } #bpMenu{ position: fixed; z-index: 99999; top: -50px; right: 0px; height: 70px; display: inline-block; transition: top 0.5s; padding: 0px 0px 10px; } #bpMenu .inner{ display: inline-block; background-color: ${r.style.colors.background}; color: ${r.style.colors.color}; padding: 0px 10px 5px; border-radius: 0 0 10px 10px; box-shadow: 0 0 10px rgba(0,0,0,0.5); } #bpMenu:hover{ top: 0px; } #bpMenu .bp{ display: inline-block; padding: 5px; font-size: ${r.style.fontSize}; } #bpMenu .bp.item{ color: ${r.style.colors.item}; background: ${r.style.colors.itemBack}; font-weight: bold; cursor: pointer; } #bpMenu .bp.item:hover{ color: ${r.style.colors.itemHover}; } #bpMenu .bp+.bp{ margin-left: 10px; } `; r.setup = function(override=false){ var head, script; head = document.getElementsByTagName("head")[0]; if(typeof $ !== "function"){ return console.error("bpMenu: No jQuery '$'; can't continue"); // console.log("jQuery not available?\nTrying to insert & load...", typeof $); // script = document.createElement("script"); // script.type = "text/javascript"; // script.onload = function(){ // r.setup(); // }; // script.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"; // head.appendChild(script); // return; } if(typeof bpModal !== "function"){ return console.error("bpMenu: No bpModal; can't continue"); // console.warn("BP Modal not available?\nTrying to insert & load...", typeof bpModal); // script = document.createElement("script"); // script.type = "text/javascript"; // script.onload = function(){ // r.setup(); // }; // script.src = "https://benjamin-philipp.com/js/gm/funcs.js?funcs=bpModal"; // head.appendChild(script); // return; } // console.log("Setup BP Menu"); if(override){ $("body>#bpMenu").remove(); } r.injectStyles(override); if(!$("body>#bpMenu").length) $("body").append("<div id='bpMenu'><div class='inner'></div></div>"); r.obj = $("body>#bpMenu"); }; r.injectStyles = function(override=false){ if("undefined" != typeof GM_addStyle) return GM_addStyle(r.css); if(override) $("#bpMenuStyle").remove(); if($("#bpMenuStyle").length<=0) $("head").append(`<style id="bpMenuStyle">${r.css}</style>`); }; r.add = function(id, html, cb=null, title="", override=false, sel=""){ let l = $("body>#bpMenu>.inner #" + id); let add = true; if(l.length >0){ add = false; if(override){ l.remove(); add = true; } } if(add){ if(title) title = " title='" + title + "'"; $("body>#bpMenu .inner").append("<div id='" + id + "' class='bp" + (cb?" item":"") + "'" + title + ">" + html + "</div>"); r.items[id] = $("#bpMenu #" + id); if(cb) $("#bpMenu #" + id).click(function(e){ cb(e); }); } }; r.changeStyle = function(obj){ mergeDeep(r.style, obj); r.injectStyles(true); }; r.setup(); return r; } //if("undefined" === typeof bpMenuHelper){ // jshint ignore:line // var bpMenuHelper = bpMenu(); // jshint ignore:line //} class BP_Color{ constructor(colorOrRed, green, blue){ this.color = { red: 0, green: 0, blue: 0 }; if(colorOrRed instanceof Color){ this.color = colorOrRed.color; } else if(colorOrRed instanceof Array){ this.color = { red: colorOrRed[0], green: colorOrRed[1], blue: colorOrRed[2] }; } else if(typeof colorOrRed == "object"){ this.color = { red: colorOrRed.red || colorOrRed.Red || colorOrRed.r || colorOrRed.R, green: colorOrRed.green || colorOrRed.Green || colorOrRed.g || colorOrRed.G, blue: colorOrRed.blue || colorOrRed.Blue || colorOrRed.b || colorOrRed.B }; } else if(typeof colorOrRed == "string" && green === undefined){ var hex = []; if(colorOrRed.startsWith("#")){ colorOrRed = colorOrRed.replace(/^#?(\w)(\w)(\w)$/, "#$1$1$2$2$3$3"); var c = Color.hexToRgb(colorOrRed); if(c){ this.color.red = c[0]; this.color.green = c[1]; this.color.blue = c[2]; } } } else{ this.color.red = colorOrRed; this.color.green = green; this.color.blue = blue; } return this; } toRGB(){ return [this.color.red, this.color.green, this.color.blue]; } toHex(){ return Color.rgbToHex(this.color.red, this.color.green, this.color.blue); } get(){ return this.color; } static componentToHex(c) { var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; } static rgbToHex(r, g, b) { return "#" + Color.componentToHex(r) + Color.componentToHex(g) + Color.componentToHex(b); } static hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null; } } function colorString(str, colorOrRed, green, blue){ var color = [0,0,0]; if([colorOrRed, green, blue].every(v => typeof v == "number")) color = [colorOrRed, green, blue]; else if(colorOrRed instanceof Array) color = colorOrRed; else{ color = new BP_Color(colorOrRed, green, blue).toRGB(); } return `\x1B[38;2;${color[0]};${color[1]};${color[2]}m${str}\x1B[0m`; } function convolve(func, ...partArrays){ if(partArrays.length == 1 && !(partArrays[0] instanceof Array) && (typeof partArrays[0]) == "object") partArrays = Object.values(partArrays[0]); if(! partArrays?.length) return; var funcArgArrays = []; var firstParts = partArrays[0]; var nextArgs = []; if(partArrays.length == 1){ if(typeof func == "function"){ for(let p of firstParts) func(p); } return firstParts; } var lastArray = false; if(partArrays.length > 2){ nextArgs = convolve(false, ...partArrays.slice(1)); } else{ nextArgs = partArrays[1]; lastArray = true; } for(let arg of firstParts){ for(let otherArgs of nextArgs){ if(lastArray) funcArgArrays.push([arg, otherArgs]); else funcArgArrays.push([arg, ...otherArgs]); if(typeof func == "function") func(arg, ...otherArgs); } } return funcArgArrays; } function copyToClipboard(text) { if (window.clipboardData && window.clipboardData.setData) { // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible. return window.clipboardData.setData("Text", text); } else if (document.queryCommandSupported && document.queryCommandSupported("copy")) { var textarea = document.createElement("textarea"); textarea.textContent = text; textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in Microsoft Edge. document.body.appendChild(textarea); textarea.select(); try { document.execCommand("copy"); // Security exception may be thrown by some browsers. msg("Copied.", "success", "", 1000); return; } catch (ex) { console.warn("Copy to clipboard failed.", ex); return prompt("Copy to clipboard: Ctrl+C, Enter", text); } finally { document.body.removeChild(textarea); } } } if("undefined" == typeof bp_iObject){ bp_iObject = { style: `<style id="bp_iO_style"> div.bp_iObject{ color: #fff; } div.bp_iObject div.bp_iObject{ display: inline; vertical-align: text-top; } .bp_iObject div.bp_iO_inner{ white-space: pre; border: 1px solid transparent; transition: all 0.5s; } .bp_iO_pseudoContent{ margin: 0.4em; } .bp_iO_pseudoContent + .bp_iO_pseudoContent{ margin-left: 0; } .bp_iO_pseudoContent:before{ content: attr(data-value); } span.bp_iO_meta{ opacity: 0.7; background: #333; border: 1px solid #888; border-radius: 0.3em; font-size: 66%; } span.bp_iO_indent:before{ content: " "; white-space: pre; } span.bp_iO_comma{ display: inline; vertical-align: bottom; } div.bp_iObject>.bp_iO_content{ color: #bbb; white-space: pre; } div.bp_iObject[data-type="string"]>.bp_iO_content, div.bp_iObject span.quotes{ color: #888; } span.bp_iO_key{ font-style: italic; } .collapsible>span.bp_iO_key{ cursor: pointer; } div.bp_iObject[data-type="string"]>.bp_iO_content>span.text{ color: #fe8; } div.bp_iObject[data-type="number"]>.bp_iO_content{ color: #5cf; } div.bp_iObject[data-type="RegExp"]>.bp_iO_content{ color: #f48; } div.bp_iObject[data-type="boolean"]>.bp_iO_content{ color: #08f; } div.bp_iObject.collapsed>.bp_iO_content{ white-space: normal; } div.bp_iObject.collapsed .bp_iO_inner{ display: inline-block; width: 1px; height: 1px; overflow: hidden; margin-left: -5px; opacity: 0.01; clip: rect(0 0 0 0); } div.bp_iObject.collapsible .bracket.closing{ cursor: pointer; pointer-events: all; } div.bp_iObject.collapsed .bracket.closing:before{ content: "... "; } .bp_iObject{ position: relative; } .bp_iObject.collapsible>.bp_iO_key:before{ content: "â–² "; position: absolute; display: inline-block; right: 100%; font-size: 66%; pointer-events: all; } .bp_iObject.collapsible.collapsed>.bp_iO_key:before{ content: "â–¼ "; } .bp_iO_inner.editing{ border: 1px inset #888; background: #000; color: #fff; padding: 0.5em; margin: 0.2em; } .bp_iO_copy{ position: absolute; right: 0; top:0; opacity: 0; padding: 0.75em 1em; background: #666; color: #fff; border: 1px solid #fff; border-radius: 0.5em; cursor: pointer; transition: all 0.5s; } /* .bp_iO_copy::before{ content: "\\002398 Copy"; } .bp_iObject:hover>.bp_iO_copy{ opacity: 0.3; } .bp_iObject:hover>.bp_iO_copy:hover{ opacity: 1; background: #000; }*/ /* TODO: make palatable */ .zeroHeight{ line-height: 0; margin:0; padding:0; display: none; } .collapsed br.zeroHeight{ display: inline; } .collapsedIndent{ display: none; } .collapsed>.bp_iO_content>.bp_iO_inner>.collapsedIndent{ display: inline; } </style>`, createInteractiveObject: function(obj, onlyOwnProperties=true, indentString="\t", objKey="", level=0, includeLabel = false){ if("undefined" == typeof bp_iO) bp_iO = bp_iObject.setup(); var indent = ""; for(i=0; i<level; i++) indent += indentString; var q = "<span class='quotes'>"</span>"; var ell = "<span class='bp_iO_ellipsis'>"</span>"; var r = ""; var t = typeof obj; var typeString = t.replace(/^\w/, (a)=> a.toUpperCase()); var inner = ""; var count=0; var isArray = obj instanceof Array; switch(typeof obj){ case "object": var pt = ""; // + obj.toString(); // TODO: prototype class name? if(isArray) typeString = "Array"; else if(obj instanceof RegExp){ typeString = t = "RegExp"; inner = obj.toString(); break; } typeString += " " + pt; if(!obj || !obj.keys){ inner = obj.toString(); break; } var keys = Object.keys(obj); if(onlyOwnProperties){ var okeys = keys; keys = []; for(let k of okeys){ if(obj.hasOwnProperty(k)) keys.push(k); } } count = keys.length; for(let i=0; i<keys.length; i++){ let k = keys[i]; inner += (isArray?"":`${indent + indentString}`) + createInteractiveObject(obj[k], onlyOwnProperties, indentString, k, level+1, isArray?false:true) + (i<keys.length-1?"<span class='bp_iO_comma'>, </span>" + (isArray?"":"<br />"):""); } if(!isArray && count>0) inner = "<div class='bp_iO_inner'><br class='zeroHeight' />" + inner + "<br /><span class='collapsedIndent'>" + indent + "</span></div>"; if(isArray) inner = "[" + inner + "]"; else{ inner = "{" + inner + (inner?indent:"") + "<span class='bracket closing'>}</span>"; } break; case "string": inner += q + "<span class='text bp_iO_inner'>" + htmlEntities(obj) + "</span>" + q; break; default: inner += "<span class='bp_iO_inner'>" + htmlEntities(obj.toString()) + "</span>"; } r = (objKey && includeLabel?`<span class='bp_iO_key'>${q + objKey + q}</span><span class='bp_iO_type bp_iO_meta bp_iO_pseudoContent' data-value='${typeString}'></span>` + (t=="object"?`<span class='bp_iO_count bp_iO_meta bp_iO_pseudoContent' data-value='(${count})'></span>`:"") + ": " :"") + "<span class='bp_iO_content'>" + inner + "</span>"; r = `<div class="bp_iObject${t=="object" && !(obj instanceof Array) && count>0?" collapsible":""}${level<=0?" root editable":""}" data-type="${t}" data-level="${level}" data-key="${objKey}"><div class="bp_iO_copy"></div>${r}</div>`; if(level <= 0){ r = $(r)[0]; r.bp_iO = { ref: obj, onlyOwnProperties, indentString, element: r, redraw: function(){ var e = createInteractiveObject(this.ref, this.onlyOwnProperties, this.indentString); this.element.innerHTML = e.innerHTML; $(e).remove(); // console.log(this.element); } }; } return r; }, setup: function(){ if("undefined" == typeof bp_iO){ bp_iO = this; $("head").append(this.style); $("body").on("click", ".bp_iO_copy", function(e){ e.stopImmediatePropagation(); // console.log(e, e.target); var r = $(e.target).parent().text(); copyToClipboard(r); }); $("body").on("click", ".collapsible>.bp_iO_key, .collapsible>.bp_iO_content>.bracket.closing", function(e){ e.stopImmediatePropagation(); // console.log(e, e.target); var p = $(e.target).parent(); if(p.hasClass("bp_iO_content")) p = p.parent(); p.toggleClass("collapsed"); }); $("body").on("dblclick", ".bp_iObject.root.editable .bp_iO_inner", function(e){ // if(!$(e.target).is(e.currentTarget)) // return; e.stopImmediatePropagation(); // console.log(e, e.target); var o = $(e.currentTarget); o.addClass("editing"); // console.log(e.target, o); o.attr("contenteditable", "true"); o[0].beforeEdit = o.text().trim(); o[0].focus(); return false; }); $(document).on("keydown", ".bp_iO_inner.editing", function(e){ // console.log(e); var o = $(e.target); if(e.key=="Enter" && !e.shiftKey){ // console.log("enter!"); var val_inner = o.text().trim(); var val_container = o.closest(".bp_iObject").first(); var val_outer = o.parent().text().trim(); // console.log("new value?", val_outer, val_inner); var path = []; var obj = o.parents(".bp_iObject.root"); if(!obj || obj.length<=0 || !obj[0].bp_iO){ console.error("Dang, no root found for", o); } else{ try{ obj = obj[0].bp_iO; var ref = obj.ref; var v = eval(`(function(){return ${val_outer};})();`); if(val_container.is(o.parents(".bp_iObject.root").first())){ // console.log("yup, modifying root"); for(let k in ref) // TODO: accommodate different types delete ref[k]; for(let k in v) ref[k] = v[k]; obj.redraw(); } else{ var ancestry = o.parentsUntil(".bp_iObject.root", ".bp_iObject").get(); ancestry = ancestry.reverse(); for(let i = 0; i<ancestry.length; i++){ var p = ancestry[i]; // var key = p.attr("data-key"); var k = p.dataset.key; // console.log(k, p); if(i>=ancestry.length-1){ // console.log(`(function(){return ${val_outer};})();`); ref[k] = v; var h = createInteractiveObject(ref[k], obj.onlyOwnProperties, obj.indentString, k, i+1, i+1>0); val_container.html(h); break; } ref = ref[k]; path.push(k); } // console.log(obj, path) } }catch(e){ console.error(e); } } } else if(e.key=="Escape"){ // console.log("Esc!"); o.text(o[0].beforeEdit); } else return; e.preventDefault(); o.removeClass("editing"); o.attr("contenteditable", "false"); }); createInteractiveObject = this.createInteractiveObject; return this; } } }; } function dateString(date, format){ if(date===undefined || date===null || date === "") date = new Date(); if(format===undefined) format="YYYY-MM-DD HH:mm:SS"; else if(format==="file") format="YYYY-MM-DD HH-mm-SS"; date = new Date(date); var year = pad(date.getFullYear(), 4); var months = pad(date.getMonth() + 1); var days = pad(date.getDate()); var hours = pad(date.getHours()); var minutes = pad(date.getMinutes()); var seconds = pad(date.getSeconds()); return format.replace("YYYY", year) .replace("MM", months) .replace("DD", days) .replace("HH", hours) .replace("mm", minutes) .replace("SS", seconds); } function pad(num, digits=2){ var r = String(num); if(r.padStart) return r.padStart(digits, "0"); return ("0000000000" + r).slice(-digits); } function escapeHtml(str){ return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); } String.prototype.escapeHtml = function(){ return escapeHtml(this); }; filterTables = {count: 0, hasInit: false}; filterTables.init = function(filter="", override=false){ if($("head #bp_filtertable").length<=0) $("head").append(` <style id="bp_filtertable"> .filtertable.table, .filtertable .table { display: table; } .filtertable .tr { display: table-row; } .filtertable .td, .filtertable .th { display: table-cell; } .filtertable th, .filtertable .th { white-space: nowrap; word-break: keep-all; padding: 3px; text-align: left; font-weight: bold; } .filtertable .filterhide, .simplehide{ display: none; } .filtertable th.filtering{ background-color: rgba(200,20,0,0.3); } .filtertable .columnFilter{ display: inline-block; vertical-align: middle; width: calc(100% - 20px); } .filtertable .columnFilter::placeholder{ font-weight: 300; } .filtertable .sorter{ display: inline-block; vertical-align: middle; margin-left: 3px } .filtertable .sort{ padding: 1px 4px; line-height: 9px; font-size: 10px; background-color: rgba(140,140,140,0.5); color: #ccc; cursor: pointer; opacity: 0.5; } .filtertable .sort+.sort{ margin-top:2px; } .filtertable .sort.desc:after{ content: "â–¼"; } .filtertable .sort.asc:after{ content: "â–²"; } .filtertable .sort:hover{ opacity: 1; } .filtertable .sort.active{ background-color: #160; color: #fff; } .filtertable .clearfilter{ display: inline-block; vertical-align: middle; margin-left: -15px; padding: 0 3px 4px; background: rgba(120,10,0,1); color: #fff; opacity: 0.5; cursor: pointer; } .filtertable .clearfilter:hover{ opacity: 1; } </style> `); $(".filtertable" + filter + ":not(.hasSetup)").each(function(){ makeTableSortable(this, true); }); if(filterTables.hasInit && !override) return; $("body").on("keyup", ".filtertable .columnFilter", function(e){ // console.log(this, e); var t = $(this).closest("table, .table"); if(!t || t.length<1) return console.error("Table not found filtering"); var headers = t[0].filterHeaders; if(!headers) headers = t; var cont = t.children("tbody, .tbody"); if(cont && cont.length>0) t = cont.first(); var n = $(this).attr("data-colnum"); var o = this; clearTimeout(filterTables.tuFilter); filterTables.tuFilter = setTimeout(function(){ // console.log("filtering", n, t); var f = o.value; var fheader = $(headers).find("th:nth-child(" + n + "), .th:nth-child(" + n + ")"); if(f == ""){ $(t).find("tr, .tr").each(function(){ if(filterTables.setColumnFilter(this, n, false)) filterTables.showhideFilter(this); }); $(fheader).removeClass("filtering"); return; } $(fheader).addClass("filtering"); $(t).find("tr, .tr").each(function(){ if(this == fheader || $(this).hasClass("sortHeaderRow")) return; var c = $(this).find("td:nth-child(" + n + "), .td:nth-child(" + n + ")"); // console.log("got cells?", c); if(c.length > 0){ var s = c.text(); // console.log("compare", f, "against", s, "in", n+" -> nth child"); var fs = f.substr(0, 1); var fc = true; if(fs == "<" && f.length > 1){ // console.log("is <"); fc = s * 1 >= f.substr(1) * 1; } else if(fs == ">" && f.length > 1){ // console.log("is >"); fc = s * 1 <= f.substr(1) * 1; } else { fc = !s.toLowerCase().includes(f.toLowerCase()); } if(filterTables.setColumnFilter(this, n, fc)){ filterTables.showhideFilter(this); } } }); }, 100); }); $("body").on("click", ".filtertable .sort", function(){ var isactive = !$(this).hasClass("active"); filterTables.intable(this, ".sort").removeClass("active"); $(this).toggleClass("active", isactive); var sortcol = 0; var asc = true; if(isactive){ sortcol = $(this).parent().attr("data-colnum"); asc = $(this).hasClass("asc"); } filterTables.sortTable($(this).closest("table, .table").first(), sortcol, asc); }); $("body").on("click", ".clearfilter", function(){ var fi = $(this).parent().find(".columnFilter"); // console.log("clear", fi); // if(fi.length>0){ $(fi).val(""); $(fi).keyup(); // } }); $("body").on("click", "table.minimized tr, .table.minimized .tr", function(){ $(this).parent().find("tr.active, .tr.active").removeClass("active"); $(this).addClass("active"); }); setTimeout(filterTables.resizeFilters, 1000); setInterval(filterTables.resizeFilters, 5000); filterTables.hasInit = true; }; filterTables.tuFilter = null; filterTables.setColumnFilter = function(e, c, onoff){ var cols = []; var ec = $(e).attr("filteredCols"); if(undefined !== ec && ec != "") cols = ec.split(","); var i = cols.indexOf(c); // console.log("Set filter for column " + c + " to " + onoff.toString() + " on: ", e); if(i < 0 == onoff){ if(onoff){ cols.push(c); // console.log("column filter added"); } else { // console.log("remove column filter:"); var a = cols.splice(i, 1); // console.log("a:", a); // console.log("cols:", cols); } $(e).attr("filteredCols", cols.join(",")); // console.log("Needed change, is now: " + $(e).attr("filteredCols")); return true; } // console.log("no change"); return false; }; filterTables.showhideFilter = function(e){ var ec = $(e).attr("filteredCols"); if(undefined !== ec && ec != "") $(e).addClass("filterhide"); else $(e).removeClass("filterhide"); }; filterTables.sortTable = function(table, col, asc){ var tbody = $(table).children("tbody, .tbody"); if(tbody.length>0) table = tbody; var tosort = $(table).children("tr.sortable, .tr.sortable"); var ifasc = asc ? 1 : -1; // console.log("sorting:", table, tosort); tosort.sort(function(a, b){ var ca, cb; if(col === 0){ ca = $(a).attr("nosort"); cb = $(b).attr("nosort"); } else { ca = $(a).find("td:nth-child(" + col + "), .td:nth-child(" + col + ")").text(); cb = $(b).find("td:nth-child(" + col + "), .td:nth-child(" + col + ")").text(); } ca = ca.trim().toLowerCase(); cb = cb.trim().toLowerCase(); if((ca*1).toString() == ca.toString() && (cb*1).toString() == cb.toString()){ // console.log("is numeric"); return (ca*1 - cb*1) * ifasc; } else{ var rex = /\s*(\d+)(.*)/; var ma = ca.match(rex); var mb = cb.match(rex); if(ma && mb){ // console.log("oh, numbers!", ma, mb); var diff = ma[1] - mb[1]; if(diff!=0) return diff * ifasc; } } if(ca > cb) return 1 * ifasc; if(ca < cb) return -1 * ifasc; return 0; }); // $(table).children("tr.sortable, .tr.sortable").remove(); $(table).append(tosort); }; filterTables.intable = function(el, s){ return $(el).closest("table, .table").first().find(s); }; filterTables.resizeFilters = function(){ $(".shrink .columnFilter").each(function(){ var o = this; filterTables.shrinkifbigger(o); }); }; filterTables.shrinkifbigger = function(o){ var p = $(o).parent().parent(); var w = p.width() - 22; if(w<40) w = 40; if(w > $(p).width()-20) return; $(o).css("width", w+"px"); // setTimeout(function(){ if(w > $(p).width()-20){ filterTables.shrinkifbigger(o); // console.log("shrink: " + p.text()); } else{ // console.log("is max: " + p.text); } // }, 1); }; function makeTableSortable(element, hasInit=false){ var table = $(element); var headers; if(table.is("tr, .tr")){ headers = table; table = headers.closest("table, .table"); } else{ let cont = table.children("thead, .thead, tbody, .tbody").first(); if(!cont || cont.length<=0) cont = table; headers = cont.find(">tr>th, >.tr>th, >tr>.th, >.tr>.th"); if(headers && headers.length>0) headers = headers.parent(); else headers = cont.find(">tr, >.tr").first(); } headers.addClass("sortHeaderRow").children("td, .td").addClass("th"); // in case they're just td, .td table.addClass("filtertable sortable"); table[0].filterHeaders = headers; filterTables.count++; var c = 0; $(headers).children("th, .th").each(function(){ var h = $(this).html(); c++; var w = $(this).width() - 16; if(w<40) w = 40; $(this).html("<div class='filterable'>" + h + "</div><div class='filters'><input class='columnFilter' style='widdth: " + w + "px;' name='columnFilter-" + c + "' value='' data-colnum='" + c + "' placeholder='Filter \"" + h + "\"' /><div class='clearfilter'>x</div><div class='sorter' data-colnum='" + c + "'><div class='sort asc'></div><div class='sort desc'></div></div></div>").addClass("sortHeader"); }); c = 0; var cont = table.children("tbody, .tbody"); if(!cont || cont.length<1) cont = table; $(cont).children("tr:not(.sortHeaderRow), .tr:not(.sortHeaderRow)").each(function(){ // if(c > 0){ $(this).addClass("sortable"); $(this).attr("nosort", c); // } c++; }); table.addClass("hasSetup"); if(!filterTables.hasInit && !hasInit) filterTables.init(); } function filteredDeepCopy(obj, func, maxDepth=10, circ=[]){ var out = {}; if(obj instanceof Array) out = []; for(let k in obj){ if(!Object.hasOwn(obj, k)) continue; let v = obj[k]; if(typeof func == "function"){ let o = func(k, v); if(!o) continue; if(o instanceof Array){ k = o[0]; v = o[1]; } } if(typeof v == "object"){ if(maxDepth<=0 || circ.includes(v)) continue; maxDepth--; out[k] = filteredDeepCopy(v, func, maxDepth, [...circ, v]); continue; } out[k] = v; } return out; } var bpTitleFormats = { movies: [ "[title] [[year]]", "[title] ([year])", "[title] - [year]" ], series: [ "[name] - Season [season] Episode [episode] - [title]", "[name] - Season [season] Episode [episode]", "[name] - S[lzseason]E[lzepisode] - [title]", "[name] - S[lzseason]E[lz3episode] - [title]" ] }; var bpMediaTitleRegex = { movies: [ /(.+) \[(\d{4})\]$/mi, /(.+) \((\d{4})\)$/mi, /(.+) - (\d{4})$/mi, ], series: [ // /(.+?) ?-? (?:(?:season |S)?0?0?(\d+))? ?(?:episode |E|x)0*(\d+(?:-\d+)?)(?: ?[:-]? (.+))?/mi /(.+?) ?[:,-]? (?:(?:season |S)?0*(\d+))?\s*[,-]?\s*(?:episode |E|x)0*(\d+(?:-\d+)?)(?: ?[:, -]* (.+))?/mi ] }; function testEpisodeTitleRex(rex){ var values = { name: ["Some series name", "Test? <^> \"Toast\""], season: ["0", "03", "102"], episode: ["0", "03", "102", "13-14"], title: ["and a title", "\"and a title\"", " - and a title"], joiners: [",", ":", " :", "-", " -", ""], altjoiners: [",", "-", " -"], sseason: ["season ", "s"], sepisode: ["episode ", "e"] }; if(!rex) rex = bpMediaTitleRegex.series[0]; var testStrings = []; convolve((name, season, episode, title, joiner, altjoiner, sseason, sepisode)=>{ testStrings.push(`${name}${joiner} ${sseason}${season}${altjoiner} ${sepisode}${episode}${joiner} ${title}`); testStrings.push(`${name}${joiner} ${sseason}${season}${altjoiner}${sepisode}${episode}${joiner} ${title}`); testStrings.push(`NoTitle${name}${joiner} ${sseason}${season}${altjoiner} ${sepisode}${episode}`); testStrings.push(`NoTitle${name}${joiner} ${sseason}${season}${altjoiner}${sepisode}${episode}`); testStrings.push(`${name} ${season}x${episode}${joiner} ${title}`); testStrings.push(`NoTitle${name} ${season}x${episode}`); }, values ); // log(testStrings); var colorOk = "#66ff55"; var colorWarn = "#cccc55"; var colorBad = "#ff6655"; var testOk = []; var testFail = []; for(let s of testStrings){ s = s.replace(/\s+/g, " "); var m = s.match(rex); // log(r); if(!m || !m.length){ log(colorString(s, colorWarn), colorString("Failed RegEx test", colorBad)); testFail.push([s, "Failed to match RegEx"]); } else{ // log(colorString(s, colorOk)); var name = m[1]; var season = m[2]; var episode = m[3]; var title = m[4]; if(title){ title = title.trim().replace(/^[,: -]+/g, ""); title = title.trim().replace(/^"(.+)"$/, "$1"); } var isNoTitle = false; if(name.startsWith("NoTitle")){ name = name.replace(/^NoTitle/, ""); isNoTitle = true; } if(!values.name.includes(name)){ testFail.push([s, "Failed NAME"]); continue; } if(!["0", "3", "102"].includes(season)){ testFail.push([s, "Failed SEASON"]); continue; } if(!["0", "3", "102", "13-14"].includes(episode)){ testFail.push([s, "Failed EPISODE"]); continue; } if(!["and a title"].includes(title) && !isNoTitle){ testFail.push([s, "Failed TITLE"]); continue; } testOk.push(s); } } for(let f of testFail) log(f[0], colorString(f[1], colorBad)); log(colorString("Test OK: " + testOk.length, colorOk), colorString("Test Failed: " + testFail.length, testFail.length?colorBad:colorOk)); } function guessMovieOrTV(title){ var tit = title.replace(/[—–]/g, "-"); // em-dash, en-dash for(let rex of bpMediaTitleRegex.series){ if(rex.test(tit)) return "TV"; } for(let rex of bpMediaTitleRegex.movies){ if(rex.test(tit)) return "Movie"; } return false; } function formatMovieTV(tit, templateSeries, templateMovie){ switch(guessMovieOrTV(tit)){ case "TV": return formatEpisodeTitle(tit, templateSeries); case "Movie": return formatMovieTitle(tit, templateMovie); default: console.log("Could not identify TV or Movie title"); return tit; } } function formatMovieTitle(tit, template){ if(!template) template = bpTitleFormats.movies[0]; // console.log("preferred format: " + template); var match = false; for(let rex of bpMediaTitleRegex.movies){ match = tit.match(rex); if(match){ // match = rex.exec(tit); console.log("title matches format " + rex.toString(), match); break; } } if(!match){ console.log("Title format not recognized", tit); return tit; } var name = match[1]; var year = match[2]; tit = template.replace("[title]", name) .replace("[year]", year); console.log("formatted title:", tit); return tit; } function formatEpisodeTitle(tit, template){ if(!template) template = bpTitleFormats.series[0]; // console.log("preferred format: " + template); tit = tit.replace(/[—–]/g, "-"); // em-dash, en-dash var match = false; for(let rex of bpMediaTitleRegex.series){ match = tit.match(rex); if(match){ // match = rex.exec(tit); console.log("title matches format " + rex.toString(), match); break; } } if(!match){ console.log("Title format not recognized", tit); return tit; } var name = match[1]; var season = match[2]; if(!season) season = 1; var episode = match[3]; var title = (match.length>=5 && match[4] !== undefined)? match[4] : ""; if((/(Episode #? ?\d+|S\d+ ?E\d+)/i).test(title)) title = ""; // console.log({"name" : name, "season" : season, "episode" : episode, "title" : title}); if(title===""){ template = template.replace(/ ?-? \[title\]/, ""); console.log("no title:", template); } tit = template.replace("[name]", name) .replace("[season]", season) .replace("[episode]", episode) .replace(/\[lz(\d*)season]/i, function(_,p){ if(p==="") p = 2; return lz(season, p); }) .replace(/\[lz(\d*)episode]/i, function(_,p){ if(p==="") p = 2; return lz(episode, p); }) .replace("[title]", title); console.log("formatted title:", tit); return tit; } function sanitize(str){ str = str.replace(/[\\]/g, "-") .replace(/["]/g, "'") .replace(/\?\?/g, "â‡") .replace(/\?/g, "︖") .replace(/\s*[/:]\s*/g, " - ") .replace(/\s+-\s*(?:-+\s+)+/g, " - ") .replace(/\s+/g, " "); return str.trim(); } function lz(num, places = 2){ return ("0000000000" + num).slice(-places); } function varToPretty(str, casing="title", isSecond=false){ str = str .replace(/(?:([^A-Z])([A-Z]))|(?:([a-zA-Z])([^a-zA-Z]))/g, "$1$3 $2$4") .replace(/_/g, " ") .replace(/\s\s+/g, " ") .replace(/(max|min)/gi, "$1imum"); if(casing == "title") str = str.replace(/(^|\s+)([a-z])/g, (_, a, b) => a + b.toUpperCase()); if(isSecond) return str; return varToPretty(str, casing, true); } function toTitleCase(str, preserveCaps=false, preserveAllCaps=false){ return str.replace(/\w[^\s_:-]*/g, function(txt){ var rest = txt.substr(1); if(!preserveCaps){ if(preserveAllCaps){ if(txt.charAt(0) != txt.charAt(0).toUpperCase()|| rest != rest.toUpperCase()) rest = rest.toLowerCase(); } else rest = rest.toLowerCase(); } return txt.charAt(0).toUpperCase() + rest; }); } const mimeTypes = { ".aac": "audio/aac", ".abw": "application/x-abiword", ".arc": "application/x-freearc", ".avif": "image/avif", ".avi": "video/x-msvideo", ".azw": "application/vnd.amazon.ebook", ".bin": "application/octet-stream", ".bmp": "image/bmp", ".bz": "application/x-bzip", ".bz2": "application/x-bzip2", ".cda": "application/x-cdf", ".csh": "application/x-csh", ".css": "text/css", ".csv": "text/csv", ".doc": "application/msword", ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".eot": "application/vnd.ms-fontobject", ".epub": "application/epub+zip", ".gz": "application/gzip", ".gif": "image/gif", ".htm": "text/html", ".html": "text/html", ".ico": "image/vnd.microsoft.icon", ".ics": "text/calendar", ".jar": "application/java-archive", ".jpeg.jpg": "image/jpeg", ".js": "text/javascript", ".json": "application/json", ".jsonld": "application/ld+json", ".mid.midi": "audio/midi", ".mjs": "text/javascript", ".mp3": "audio/mpeg", ".mp4": "video/mp4", ".mpeg": "video/mpeg", ".mpkg": "application/vnd.apple.installer+xml", ".odp": "application/vnd.oasis.opendocument.presentation", ".ods": "application/vnd.oasis.opendocument.spreadsheet", ".odt": "application/vnd.oasis.opendocument.text", ".oga": "audio/ogg", ".ogv": "video/ogg", ".ogx": "application/ogg", ".opus": "audio/opus", ".otf": "font/otf", ".png": "image/png", ".pdf": "application/pdf", ".php": "application/x-httpd-php", ".ppt": "application/vnd.ms-powerpoint", ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", ".rar": "application/vnd.rar", ".rtf": "application/rtf", ".sh": "application/x-sh", ".svg": "image/svg+xml", ".swf": "application/x-shockwave-flash", ".tar": "application/x-tar", ".tif": "image/tiff", ".tiff": "image/tiff", ".ts": "video/mp2t", ".ttf": "font/ttf", ".txt": "text/plain", ".vsd": "application/vnd.visio", ".wav": "audio/wav", ".weba": "audio/webm", ".webm": "video/webm", ".webp": "image/webp", ".woff": "font/woff", ".woff2": "font/woff2", ".xhtml": "application/xhtml+xml", ".xls": "application/vnd.ms-excel", ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xml": "application/xml", ".xul": "application/vnd.mozilla.xul+xml", ".zip": "application/zip", ".3gp": "video/3gpp", ".3g2": "video/3gpp2", ".7z": "application/x-7z-compressed" }; function getContentTypeByExtension(ext){ var defaultType = "application/octet-stream"; if(typeof ext != "string") return console.warn('[getContentTypeByExtension] Invalid type supplied, please supply string', ext) || defaultType; if(ext.indexOf(".")<0) ext = "." + ext; else ext = ext.replace(/^.*(?=[.]\w+$)/, ''); var mime = mimeTypes[ext.toLowerCase()]; if(!mime) return console.warn('[getContentTypeByExtension] Failed to resolve for content name: %s', ext) || defaultType; return mime; } function getParam(s){ return getParamFromString(location.href, s); } function getParamFromString(u, s){ var url = new URL(u); return url.searchParams.get(s); } function getProperties(obj, filter=false, skipNative = true, _stringify=false){ var r = {}; var i = 0; for(let k in obj){ let v = obj[k]; let include = true; switch(typeof filter){ case "function": include = filter(v); break; case "object": if(filter instanceof RegExp) include = filter.test(k); break; case "string": let types = filter.split(/[, ]/); include = false; let inverted = false; for(let t of types){ t = t.trim(); if(t == "") continue; if(t.substr(0,1)=="!"){ inverted = true; t = t.substr(1); } if((t == "function" && v && v.call)){ if(!inverted){ v = v.toString(); include = !skipNative || v.indexOf("[native code]")<0; } else include = false; break; } else if((t == typeof v) ^ inverted){ include = true; break; } } break; } if(include){ if(_stringify && (typeof v != "string")){ let maybeRemoveQuotes = false; try{ v = JSON.stringify(v, function(k, v){ if(typeof v == "function" || (v && v.call)){ v = v.toString(); maybeRemoveQuotes = true; return v; } return v; }, "\t"); }catch(e){ maybeRemoveQuotes = false; v = v.toString(); } if(maybeRemoveQuotes && typeof v == "string" && v.startsWith("\"") && v.endsWith("\"")) v = v.substring(1, v.length-1); } r[k] = v; } } return r; } function getSelectedElements(){ var allSelected = []; try{ var selection = window.getSelection(); var range = selection.getRangeAt(0); if(range.startOffset == range.endOffset) return allSelected; var cont = range.commonAncestorContainer; if(!cont){ // console.log("no parent container?"); return range.startContainer; } if(!cont.nodeName || cont.nodeName == "#text" || !cont.getElementsByTagName){ var p = cont.parentElement; // console.log("weird container or text node; return parent", cont, p); if(!p){ // console.log("actually, never mind; has no parent. Return element instead"); return [cont]; } return [p]; } var allWithinRangeParent = cont.getElementsByTagName("*"); for (var i=0, el; el = allWithinRangeParent[i]; i++){ // jshint ignore:line // The second parameter says to include the element // even if it's not fully selected if (selection.containsNode(el, true)) allSelected.push(el); } }catch(e){ console.log(e); } return allSelected; } function htmlEntities(str, nl2br=false){ str = str.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { return '&#' + i.charCodeAt(0) + ';'; }); if(nl2br) str = str.replace(/\r?\n/g, "<br />"); return str; } function isNativeFunction(value) { // Used to resolve the internal `[[Class]]` of values var toString = Object.prototype.toString; // Used to resolve the decompiled source of functions var fnToString = Function.prototype.toString; // Used to detect host constructors (Safari > 4; really typed array specific) var reHostCtor = /^\[object .+?Constructor\]$/; // Compile a regexp using a common native method as a template. // We chose `Object#toString` because there's a good chance it is not being mucked with. var reNative = RegExp('^' + // Coerce `Object#toString` to a string String(toString) // Escape any special regexp characters .replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&') // Replace mentions of `toString` with `.*?` to keep the template generic. // Replace thing like `for ...` to support environments like Rhino which add extra info // such as method arity. .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); var type = typeof value; return type == 'function' // Use `Function#toString` to bypass the value's own `toString` method // and avoid being faked out. ? // jshint ignore:line reNative.test(fnToString.call(value)) // Fallback to a host object check because some environments will represent // things like typed arrays as DOM methods which may not conform to the // normal native pattern. : (value && type == 'object' && reHostCtor.test(toString.call(value))) || false; } function isPrimitive(obj){ if(typeof obj == "undefined" || obj === null) return true; if(typeof obj == "object" || typeof obj == "function") return false; return true; } function hasJQuery(){ if(typeof $ == "function" && typeof $.prototype == "object" && typeof $.fn != "undefined") return $; if(typeof jQuery == "function" && typeof jQuery.prototype == "object" && typeof jQuery.fn != "undefined") return jQuery; return false; } function isJQuery(obj){ if(!obj || typeof obj != "object") return false; var jq = hasJQuery(); if(!jq) return false; return obj instanceof jq; } function jqExtend(jQ=false){ if(!jQ) jQ = hasJQuery(); if(jQ){ var fn = jQ.fn; fn.selectText = function(){ var doc = document; for(var i = 0; i<this.length; i++){ var element = this[i]; var range; if (doc.body.createTextRange){ range = document.body.createTextRange(); range.moveToElementText(element); range.select(); } else if (window.getSelection){ var selection = window.getSelection(); range = document.createRange(); range.selectNodeContents(element); selection.removeAllRanges(); selection.addRange(range); } } }; fn.fare = function(){ $(this).fadeOut(function(){ $(this).remove(); }); }; fn.textOnly = function(trim=true){ var c = this.clone(); c.children().remove(); if(trim) return c.text().trim(); return c.text(); }; } // else // console.log("no jQuery, no extensions :("); } jqExtend(); class BPLogger { constructor(name = false, prefix = false, debugging = false, level = 1){ if(!name && typeof GM_info !== "undefined") name = GM_info.script.name; if(prefix === false && name) prefix = name; if(!name) name = "Logger"; this.name = name; this.prefix = prefix; this.debugging = debugging; this.level = level; this.colors = { default: [180, 100], warn: [60, 100], error: [0, 100], success: [150, 100] }; this.history = []; this.keepHistory = false; if (typeof name == "object"){ Object.assign(this, name); } return this; } writeLog(args, type = "default", level = 1) { if (this.keepHistory) this.history.push([Date.now(), type, level, args]); if (level > this.level) return; if(this.prefix) args = ["%c" + this.prefix + ":", `color: hsl(${this.colors[type][0]},${this.colors[type][1]}%,80%); background-color: hsl(${this.colors[type][0]},${this.colors[type][1]}%,15%); font-weight: 900!important`, ...args]; if (this.debugging) args = [...args, new Error().stack.replace(/^\s*(Error|Stack trace):?\n/gi, "").replace(/^([^\n]*\n)/, "\n")]; if(["warn", "error"].includes(type)) console[type](...args); else console.log(...args); } log(...args){ this.writeLog(args); } warn(...args){ this.writeLog(args, "warn"); } error(...args){ this.writeLog(args, "error"); } success(...args){ this.writeLog(args, "success"); } } function BPLogger_default(...args){ if(args.length<=0) args = ""; var logger = new BPLogger(args); log = function(...args){ logger.log(...args); }; warn = function(...args){ logger.warn(...args); }; error = function(...args){ logger.error(...args); }; success = function(...args){ logger.success(...args); }; return logger; } String.prototype.matches = function(rex){ if(!(rex instanceof RegExp)) return log("Not a regular Expression:", rex); return rex.exec(this); }; function mergeDeep(target, source, mutate=true){ let output = mutate ? target : Object.assign({}, target); if(typeof target == "object"){ if(typeof source != "object") source = {source}; Object.keys(source).forEach(key => { if(typeof source[key] == "object"){ if(!(key in target)) Object.assign(output, { [key]: source[key] }); else output[key] = mergeDeep(target[key], source[key]); }else{ Object.assign(output, { [key]: source[key] }); } }); } return output; } function bpModal(style="light"){ var r = {}; r.messages = []; r.count = 0; const styles = { light: { colors: { msg: { border: "#666", background: "#eee", color: "#333" }, error: { border: "#a10", background: "#ffcabf", color: "#610" }, warn: { border: "#cc4", background: "#fec", color: "#880" }, success: { border: "#190", background: "#bf9", color: "#070" }, modal: { border: "#fff", background: "#eee", color: "#333" }, modalClose: { border: "#fff", background: "#000", color: "#fff" } } }, dark: { colors: { msg: { border: "#aaa", background: "#222", color: "#ddd" }, error: { border: "#722", background: "#300", color: "#c66" }, warn: { border: "#cc4", background: "#330", color: "#dd6" }, success: { border: "#190", background: "#040", color: "#6d6" }, modal: { border: "#333", background: "#000", color: "#ccc" }, modalClose: { border: "#fff", background: "#000", color: "#fff" } } }, }; r.style = styles[style]; if(!r.style) r.style = styles.light; r.css = `#messageoverlays{ position:fixed; top:20vh; z-index:1000; text-align:left; margin: 0 auto; left:50%; transform: translate(-50%, 0px); } #messageoverlays>.table{ margin: 0px auto; } #messageoverlays .msg{ display:inline-block; width:auto; margin: 5px auto; position:relative; padding: 10px; box-sizing: border-box; border-radius: 5px; } #messageoverlays .msg{ border: 1px solid ${r.style.colors.msg.border}; background-color: ${r.style.colors.msg.background}; color: ${r.style.colors.msg.color}; } #messageoverlays .msg.error{ border: 1px solid ${r.style.colors.error.border}; background-color: ${r.style.colors.error.background}; color: ${r.style.colors.error.color}; } #messageoverlays .msg.warn{ border: 1px solid ${r.style.colors.warn.border}; background-color: ${r.style.colors.warn.background}; color: ${r.style.colors.warn.color}; } #messageoverlays .msg.success{ border: 1px solid ${r.style.colors.success.border}; background-color: ${r.style.colors.success.background}; color: ${r.style.colors.success.color}; } .closebutton{ font-weight: 900; font-size: 12px; cursor: pointer; z-index: 20; opacity: 0.75; color: #fff; background-color: #a10; padding: 0 5px 1px; border-radius: 100%; position: absolute; right: -5px; top: -2px; line-height: 16px; } .closebutton:hover, .bpModback .modclose:hover{ opacity: 1; } .bpModback{ position:fixed; width:100%; height:100%; display:table; left:0; top:0; z-index:99000; } .bpModback.tint{ background-color:rgba(0,0,0,0.5); } .bpModback.nomodal{ display:block; width: auto; height: auto; left: 50%; top: 20px; transform: translateX(-50%); } .bpModback .modcent{ display:table-cell; vertical-align:middle; height:100%; max-height:100%; min-height:100%; } .bpModback.nomodal .modcent{ height:auto; max-height:auto; } .bpModback .modtable{ display:table; margin:auto; position:relative; left:0; } .bpModback .modframe{ border-radius: 6px; border:10px solid ${r.style.colors.modal.border}; display:block; background-color: ${r.style.colors.modal.background}; box-shadow: 0 0 20px rgba(0,0,0,0.5); max-height: 90vh!important; max-width: 90vw!important; overflow-y:auto; } .bpModback .modclose{ display:block; background-color: ${r.style.colors.modalClose.background}; color: ${r.style.colors.modalClose.color}; opacity:0.7; position:absolute; right:-12px; top:-12px; -webkit-border-radius: 20px; -moz-border-radius: 20px; border-radius: 20px; border:4px solid ${r.style.colors.modalClose.border}; font-weight:900; font-size:12pt; padding:0px 7px; cursor:pointer; z-index:400; } .bpModback .modbox{ position:relative; display: table; padding:20px 20px 10px; color: ${r.style.colors.modal.color}; overflow: unset; display:block; text-align: left; } .bpModback .table{ display:table; } .bpModback .tr{ display:table-row; } .bpModback .td{ display: table-cell; } #watch #player{ height: 200px; }`; r.setup = function(){ // TODO: retrofill if(typeof $ !== "function"){ return console.error("bpMenu: No jQuery '$'; can't continue"); // console.log("jQuery not available?\nTrying to insert & load...", typeof $); // script = document.createElement("script"); // script.type = "text/javascript"; // script.onload = function(){ // r.setup(); // }; // script.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"; // head.appendChild(script); // return; } // console.log("Actual setup with jQuery"); if($("head #bpModalStyle").length<=0){ $("head").append(`<style id="bpModalStyle"> ${r.css} </style>`); } }; r.msg = function(instr, id="", modal=true, callback=null){ r.count++; if(!id){ id = "bpmod" + r.count; } var noclose = false; if(typeof(modal)=="string" && modal == "noclose"){ noclose = true; modal = true; } var m = { id : id, msg: instr, callback: callback, modal: modal, obj: $("<div class='bpModback " + (modal?"tint":"nomodal") + (noclose?" noclose":"") + "' id='" + id + "'><div class='tr'><div class='modcent'><div class='modtable'><div class='modclose'>X</div><div class='modframe'><div class='modbox'>" + instr + "</div></div></div></div></div></div>"), close: function(){ this.obj.remove(); delete r.messages[this.id]; if(this.callback) this.callback(this); } }; $("body").append(m.obj); $("#" + id + ":not('.noclose') .modcent").click(function(e){ if(e.target == this) m.close(this); }); $("#" + id + " .modclose").click(function(e){ m.close(this); }); r.messages[id] = m; return m; }; r.close = function(el="all"){ if(el=="all"){ } }; r.setup(); return r; } //if("undefined" === typeof bpModHelper){ // jshint ignore:line // var bpModHelper = bpModal(); // jshint ignore:line //} function message(content, classname, id, expirein, closable){ expirein = typeof expirein !== 'undefined' ? expirein : 0; if(closable===undefined) closable = true; var expires = expirein !== 0 ? true : false; if (id === undefined || id === ""){ for (var i = 0; i < 512; i++){ if ($(document).find("#message-" + i)[0] !== undefined){} else { this.id = "message-" + i; break; } } } else { this.id = id; } var fid = this.id; this.expire = function(){ if (expirein > 0){ if(this.extimer) window.clearTimeout(this.extimer); this.extimer = window.setTimeout(function(){ $("#" + fid).fadeOut(function(){ $("#" + fid).remove(); }); }, expirein); } }; this.html = "<div id='" + this.id + "' class='table'><div class='msg " + classname + "'>" + content + (closable?"<div class='closebutton' id='c-" + this.id + "'>x</div>":"") + "</div></div>"; } function overlaymessage(content, classname, id, expirein, closable){ expirein = typeof expirein !== 'undefined' ? expirein : 5000; classname = classname || "hint"; id = id || ""; var curmes = new message(content, classname, id, expirein, closable); //console.log(curmes); if($("#messageoverlays").length<=0) $("body").append("<div id='messageoverlays'></div>"); $("#messageoverlays").append(curmes.html); $(".msg .closebutton").off("click").on("click", function(){ console.log("close", $(this).parent().parent()); $(this).parent().parent().fare(); }); curmes.expire(); } function msg(content, classname, id, expirein){ overlaymessage(content, classname, id, expirein); } function msgbox(content, classname="", id=""){ if (id === undefined || id === ""){ for (var i = 0; i < 512; i++){ if ($(document).find("#message-" + i)[0] !== undefined){} else { id = "message-" + i; break; } } } return "<div id='" + id + "' class='msg " + classname + "'>" + content + "</div>"; } String.prototype.rIndexOf = function(regex, startpos) { var indexOf = this.substring(startpos || 0).search(regex); return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf; }; var regEsc = regEsc ? regEsc : function(str) { if (typeof str != "string") { console.warn("called regEsc with non-string:", str); return str; } return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); }; String.prototype.replaceAllInsensitive = function(str1, str2) { return this.replace(new RegExp(regEsc(str1), "gi"),(typeof str2 == "string")?str2.replace(/\$/g,"$$$$"):str2); }; function scrollIntoView(object, offsetTop = 20, offsetLeft = 20){ object = $(object); if(object.length<=0) return; object = object[0]; var offset = $(object).offset(); $('html, body').animate({ scrollTop: offset.top - offsetTop, scrollLeft: offset.left - offsetLeft }); object.scrollIntoView(); } class elementSelector{ constructor(element=false, attributes=true){ this.tagName = this.id = this.className = this.sibling = ""; this._attribs = {}; if(element){ if(isJQuery(element)){ if(element.length<=0) return; element = element[0]; } if(element instanceof HTMLElement){ var t = this; this.element = element; this.tagName = element.tagName.toLowerCase(); if(element.id) this.id = "#" + element.id; if(element.className){ this.className = "." + element.className.trim().replace(/\s+/g, "."); } if(attributes) for(let attr of element.attributes){ if(["id", "class"].includes(attr.name)) continue; if(typeof attributes == "string") attributes = [attributes]; if(attributes instanceof Array && !attributes.includes(attr.name)) continue; if(attr.value) this._attribs[attr.name] = attr.value; } var p = element.parentElement; if(p && p.childElementCount>1){ var sibs = p.querySelectorAll(":scope > " + this.tagName); if(sibs && sibs.length>1){ for(let i = 0; i<sibs.length; i++){ let sib = sibs[i]; if(sib == element){ // console.log("found in siblings:", el); if(i==0) t.sibling = ":first-of-type"; else if(i==sibs.length-1) t.sibling = ":last-of-type"; else t.sibling = ":nth-of-type(" + (i+1) + ")"; return false; } // console.log("not same:", sib, el); } } } } } } attribs(filter=false){ var r = ""; if(typeof filter == "string") filter = [filter]; for(let k in this._attribs){ if(filter instanceof Array && !filter.includes(k)) continue; let v = this._attribs[k]; r += `[${k}="${v}"]`; } return r; } get attributes(){ return this.attribs(); } clone(){ var r = new elementSelector(); Object.assign(r, this); r._attribs = {}; for(let k in this._attribs) r._attribs[k] = this._attribs[k]; return r; } toString(includeAttribs = true, includeSibling = true){ return this.tagName + this.id + this.className + (includeAttribs?this.attribs(includeAttribs):"") + (includeSibling?this.sibling:""); } countMatches(descendants = null, includeAttribs = true, includeSibling = true){ if(descendants) return descendants.add(this, true).toString(includeAttribs, includeSibling); var s = this.toString(includeAttribs, includeSibling); if(s) return document.querySelectorAll(s).length; return 0; } } class elementSelectorChain{ constructor(elements = false, reverse = false, attributes=true){ this.selectors = []; if(elements){ if(isJQuery(elements)) elements = elements.get(); if(elements instanceof Array){ if(reverse) elements = elements.reverse(); for(let e of elements){ if(!e){ this.add(null); continue; } if(e instanceof elementSelector) this.add(e); else this.add(new elementSelector(e, attributes)); } } else if(elements instanceof HTMLElement) this.add(new elementSelector(elements, attributes)); else console.warn("elementSelectorChain: Could not construct with", elements, ", starting empty"); } } add(selector, toStart=false){ if(toStart){ this.selectors = [selector, ...this.selectors]; return this; } this.selectors.push(selector); return this; } each(func, reverse=false){ var l = this.selectors.length-1; for(let i = 0; i<=l; i++){ let j = reverse?l-i:i; let e = this.selectors[j]; let r = func(e, j); if(r===false) break; if(r === null || r instanceof elementSelector) this.selectors[j] = r; } } length(includeEmpty=false){ if(includeEmpty) return this.selectors.length; var c = 0; for(let s of this.selectors) if(s) c++; return c; } get(ix){ return this.selectors[ix]; } getElements(){ var r = []; var s = this.toString(); if(s) r = document.querySelectorAll(s); return r; } clone(){ var r = new elementSelectorChain(); this.each(function(s){ if(!s) r.add(null); else r.add(s.clone()); }); return r; } toString(includeAttribs = true, includeSibling = true){ var r = ""; var isDirect = true; var isFirst = true; for(let e of this.selectors){ if(!e){ isDirect = false; continue; } r += (isFirst?"":(isDirect?" > ":" ")) + e.toString(includeAttribs, includeSibling); isDirect = true; isFirst = false; } return r; } countMatches(includeAttribs = true, includeSibling = true){ var s = this.toString(includeAttribs, includeSibling); if(s) return document.querySelectorAll(s).length; return 0; } } /** * @name getSelector * @description Get a matching CSS selector for the given HTML Element * @param {(HTMLElement|jQuery)} element - The object (instance of HTMLElement or jQuery/$) for which to compose the selector * @param {boolean} [minimal=true] - Try to trim selectors that don't narrow down the search on this page? (default: true) * @param {(boolean|string|string[]|"auto")} [attributes="auto"] - Should attributes be included? (default: "auto") * - string/string[]: Filter by attribute name[list] * - \"auto\": include if it narrows down the search * @param {boolean} [preferTopDown=true] - Try to trim unnecessary selectors closest to the element. Otherwise, trim unnecessary selectors closest to the document root. Only significant when minimal==true (default: true) * @returns {string} CSS Selector */ function getSelector(element, minimal = true, attributes = "auto", preferTopDown = true){ return traverseAncestryForSelectors(element, minimal, attributes, preferTopDown).toString(); } function getSelectors(elements, minimal = true, attributes = "auto", preferTopDown = true){ var r = []; for(let element of elements) r.push(traverseAncestryForSelectors(element, minimal, attributes, preferTopDown).toString()); return r; } function getSelectorChoices(element, attributes = "auto"){ var r = []; r.push(traverseAncestryForSelectors(element, true, attributes, true).toString()); r.push(traverseAncestryForSelectors(element, true, attributes, false).toString()); r.push(traverseAncestryForSelectors(element, false, attributes).toString()); return r; } function traverseAncestryForSelectors(element, minimal = true, attributes = "auto", preferTopDown = true){ if(!element) throw new Error("traverseAncestryForSelectors: Not a valid element"); if(isJQuery(element)){ if(element.length<=0) throw new Error("traverseAncestryForSelectors: No element in jQuery object"); element = element[0]; } var ancestry = []; if(typeof $ == "function" && typeof $.prototype == "object" && typeof $(element) == "object" && typeof $(element).parents == "function"){ ancestry = $(element).add($(element).parents()); } else{ var p = element; while(p && p != document){ ancestry.push(p); p = p.parentElement; } ancestry = ancestry.reverse(); } var fullChain = new elementSelectorChain(ancestry, false, attributes); var tmpChain = fullChain.clone(); var chain = new elementSelectorChain(); var cs = fullChain.toString(attributes); if(!cs) throw new Error("Selector empty"); var bestCount = document.querySelectorAll(cs).length; if(bestCount<=0){ console.warn("Selector chain too restrictive/broken?", cs, fullChain); } // console.log("Complete chain:", cs, fullChain, bestCount==1?"UNIQUE":bestCount); function applyIfViable(tmpSelector, ix){ if(fullChain.selectors[ix] === null || fullChain.selectors[ix] == tmpSelector) return fullChain.selectors[ix]; if(tmpSelector && (tmpSelector.tagName + tmpSelector.id + tmpSelector.className) == "") return fullChain.selectors[ix]; tmpChain.selectors[ix] = tmpSelector; let els = tmpChain.getElements(); // console.log(els.length, bestCount, els.is(element), els, element); if(els.length>bestCount || els[0] != element){ tmpChain.selectors[ix] = fullChain.selectors[ix].clone(); } else{ if(tmpSelector) fullChain.selectors[ix] = tmpSelector.clone(); else fullChain.selectors[ix] = null; // console.log("set to ", tmpSelector, fullChain.selectors[ix]); } return fullChain.selectors[ix]; } // console.log("going DOWN"); fullChain.each(function(sel, ix){ // console.log(ix, sel.toString(), tmpChain.selectors[ix]); if(!sel || !sel.sibling) return; var c = sel.clone(); c.sibling = ""; // console.log("before sib check", ix, c, tmpChain.selectors[ix]); applyIfViable(c, ix); // console.log("after sib check", tmpChain.selectors[ix]); }); // console.log("going UP", fullChain.toString()); if(!minimal) return fullChain; fullChain.each(function(sel, ix){ // console.log(ix, sel.toString()); var c = sel; if(minimal && ix<=fullChain.length()-1) c = applyIfViable(null, ix); if(c){ c = c.clone(); if(attributes=="auto") for(let k in c._attribs){ // console.log("before attrib remove", k, c._attribs); delete c._attribs[k]; // console.log("before attrib check", k, c._attribs); c = applyIfViable(c, ix).clone(); // console.log("after attrib check", k, c._attribs); } for(let k of ["tagName", "className"]){ // console.log("before attrib remove", ix, k, c); c[k] = ""; // console.log("before attrib check", k, c); c = applyIfViable(c, ix).clone(); // console.log("after attrib check", k, c); } } else{ // console.log("skipped", sel.toString()); } chain.add(c, preferTopDown); //console.log("currently at", s); if(minimal && c && document.querySelector(chain.toString()) == element) return false; }, preferTopDown); return chain; } // TODO: option to filter attribs by specific values also, not just keys /* globals isElement, isNativeFunction, uneval */ function stringify(obj, forHTML, onlyOwnProperties, completeFunctions, level, maxLevel, skipEmpty){ if(!level) level = 0; var r = ""; if(obj===undefined) r = "[undefined]"; else if(obj === null) r = "[null]"; else if(obj === false) r = "FALSE"; else if(obj === true) r = "TRUE"; else if(obj==="") r = "[empty]"; else if(typeof obj == "object"){ var isDOMElement = isElement(obj); if(onlyOwnProperties === undefined) onlyOwnProperties = true; if(completeFunctions === undefined) completeFunctions = false; if(maxLevel === undefined) maxLevel = 5; if(skipEmpty === undefined) skipEmpty = false; r = "[object] "; var level_padding = ""; var padString = " "; for(var j = 0; j < level; j++) level_padding += padString; if(isDOMElement){ r = "[DOMElement " + obj.nodeName + "] "; skipEmpty = true; completeFunctions = false; } if(level<maxLevel){ r += "{\n"; if(isDOMElement){ r += level_padding + padString + "HTML => " + obj.outerHTML.replace(/\r?\n/g, "\\n").replace(/\s+/g, " ") + "\n"; } for(var item in obj){ try{ var value = obj[item]; if(onlyOwnProperties && obj.hasOwnProperty && !obj.hasOwnProperty(item) || isNativeFunction(value) || skipEmpty && (value===undefined || value === null || value==="")) continue; if(typeof(value) == 'object'){ r += level_padding + padString + "'" + item + "' => "; r += stringify(value, forHTML, onlyOwnProperties, completeFunctions, level+1, maxLevel, skipEmpty) + "\n"; }else if(typeof(value) == 'undefined'){ r += level_padding + padString + "'" + item + "' => [undefined]\n"; }else{ if(typeof(value.toString)=="function") value = value.toString(); if(!completeFunctions){ let m = value.match(/function\s*\(([^\)]*)\)\s*\{/i); if(m) value = "function(" + m[1] + ")"; } r += level_padding + padString + "'" + item + ("' => \"" + value).replace(/\r?\n/g, "\\n").replace(/\s+/g, " ") + "\"\n"; } }catch(e){ console.log(e); } } r += level_padding + "}"; }else r += "[Max depth of " + maxLevel + " exceeded]"; } else if(typeof obj == "function"){ if(typeof(obj.toString)=="function") r = obj.toString(); else r = uneval(obj); if(!completeFunctions){ let m = r.match(/function\s*\(([^\)]*)\)\s*\{/i); if(m) r = "function(" + m[1] + ")"; } } else r = obj + ""; if(level===0){ if(!!forHTML){ r = r.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); r = "<pre>" + r + "</pre>"; } } return r; } function isNode(o){ return ( typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string" ); } /* globals HTMLDocument */ function isElement(o){ return ( ((typeof HTMLElement === "object" && o instanceof HTMLElement) || (typeof Element === "object" && o instanceof Element) || (typeof HTMLDocument === "object" && o instanceof HTMLDocument))? true : //DOM2 o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string" ); } function stringifyWithFuncs(val, withTabs = true){ return JSON.stringify(val, function(key, value){ if (typeof value === 'function') { return value.toString(); } return value; }, withTabs?"\t":" "); } function toText(str, maxBreaks=2){ // if($ === undefined){ // $ = jQuery = require( "jquery" )(new JSDOM("").window); // } var hack = "__break god dammit__"; str = str.replace(/(<(?:br|hr) ?\/?>|<\/(?:p|li|div|td|h\d)>)/gi, hack + "$1"); str = $("<div/>").append(str).text(); var rex = new RegExp(hack, "gi"); str = str.replace(rex, "\n"); rex = new RegExp("(\\n{" + maxBreaks + "})\\n+", "g"); str = str.replace(rex, "$1"); return str.trim(); } function toVarName(str){ return str .replace(/\s+(\w)/g, function(_, a){ return a.toUpperCase(); }) .replace(/[^a-zA-Z0-9]/g, "_") .replace(/^([0-9])/, "_$1"); } function trim(s, what="\\s"){ var rex = new RegExp("^(?:[" + what + "])*((?:[\\r\\n]|.)*?)(?:[" + what + "])*$"); var m = s.match(rex); // log(m); if(m !== null && m.length>=2) return m[1]; return ""; } function varToPretty(str){ return str.replace(/(.+?)([A-Z])/g, "$1 $2").replace(/_|-/g, " ").replace(/\s\s+/g, " ").replace(/\b([a-z])/g, function(v,i){return v.toUpperCase();}); } class eleWaiter{ constructor(sel, cb, cbFail=null, findIn=null, delay=500, maxTries=50, alwaysOn=false, autoStart=true, debug = false, logFunc = null){ this.sel = ""; this.cb = null; this.cbFail = null; this.findIn = null; this.delay = 500; this.maxTries = 50; this.alwaysOn = false; this.autoStart = true; this.debug = false; this.logFunc = null; this.__running = false; this.__tries = 0; this.__timer = 0; this.__jqo = {}; if(typeof sel == "object" && !(sel instanceof Array)){ // 2022-04-16 : Now allowing array of selectors // log("got object"); Object.assign(this, sel); } else{ this.sel = sel; this.cb = cb; if(cbFail!== undefined || cbFail!== null) this.cbFail = cbFail; if(findIn) this.findIn = findIn; this.delay = delay; this.maxTries = maxTries; this.alwaysOn = alwaysOn; this.autoStart = autoStart; this.debug = debug; this.logFunc = logFunc; } if(!(this.sel instanceof Array)){ // 2022-04-16 : Now allowing array of selectors this.sel = [this.sel]; } if(this.debug){ if(typeof this.debug == "string"){ this.debug = { prefix: this.debug, level: 1 }; } else if(typeof this.debug == "number"){ this.debug = { prefix: "", level: this.debug }; } else if(typeof this.debug == "object"){ if(!this.debug.prefix) this.debug.prefix = ""; if(!this.debug.level) this.debug.level = 1; } else{ this.debug = { prefix: "", level: 1 }; } } if(!this.logFunc){ var prefix = ""; if(this.debug) prefix = this.debug.prefix; if(typeof BPLogger != "undefined"){ var logger = new BPLogger(prefix ? prefix + " EleWaiter" : "EleWaiter"); this.logFunc = logger.log.bind(logger); // this.debug.prefix = false; } else{ this.logFunc = function(...args){ console.log("EleWaiter:", ...args); }; } } this.log(this, 3); if(this.autoStart) this.__wait(); } log(...args){ if(!this.debug) return; if(typeof args == "object" && args instanceof Array && args.length>=2 && typeof args[args.length-1] == "number"){ var level = args[args.length-1]; if(level>this.debug.level){ return; } args.pop(); } this.logFunc(...args); } start(){ if(!this.__running){ this.log("Start waiting", this.findIn, this.sel, 1); this.__wait(); } } stop(){ clearTimeout(this.__timer); this.__running = false; } __wait(){ if(!this.findIn || this.findIn == "document"){ if(!!document) this.findIn = document; else this.findIn = $(":root"); } this.__running = true; if(this.maxTries!=-1) this.__tries++; var triesLeft = this.alwaysOn?1:(this.maxTries - this.__tries); this.log("tries left:", triesLeft, 3); this.__jqo = $(); for(let sel of this.sel){ if(typeof sel == "function"){ // 2022-07-11: predicate style this.log("sel is func:", this.sel, 3); jqo = $(this.findIn); var res = sel(jqo); if(!res){ if(!this.alwaysOn) this.log("Not true:", sel.toString(), "for", this.findIn, 3); if(triesLeft!==0){ this.__timer = setTimeout(function(){this.__wait();}.bind(this), this.delay); if(this.alwaysOn) this.__result(false); } else this.__result(false); return; } else{ this.__jqo = this.__jqo.add(res); this.log("Found something, is now:", this.__jqo, 3); } continue; } var jqo = $(this.findIn).find(sel); if(jqo.length<=0){ if(!this.alwaysOn) this.log("Not found: " + sel, "in", this.findIn, 3); if(triesLeft!==0){ this.__timer = setTimeout(function(){this.__wait();}.bind(this), this.delay); if(this.alwaysOn) this.__result(false); } else this.__result(false); return; } else{ this.__jqo = this.__jqo.add(jqo); this.log("Found something, is now:", this.__jqo, 3); } } this.__result(this.__jqo); if(this.alwaysOn){ this.log("Always on, repeat", 3); this.__timer = setTimeout(function(){this.__wait();}.bind(this), this.delay); } } __result(success=false){ if(!this.alwaysOn){ this.__running = false; this.log("Result:", success, 2); }else if(this.debug.level>2) this.log("Result:", success, 1); if(success){ if(this.cb!==undefined && typeof this.cb == "function") this.cb(this.__jqo); else console.log("Warning: callback cb not function", this.cb); } else{ if(this.cbFail!==undefined && typeof this.cbFail == "function") this.cbFail(this.__jqo); } } } if("undefined" === typeof eleWaiters){ // jshint ignore:line var eleWaiters ={}; // jshint ignore:line } function waitFor(sel, cb, cbFail=null, findIn="document", delay=500, maxTries=50, alwaysOn=false, debug = false){ // 2021-01-29 return new eleWaiter(sel, cb, cbFail, findIn, delay, maxTries, alwaysOn, true, debug); }