Online IDE Support JS

Injects elements into a codepen IDE for demonstration purposes

目前為 2022-11-01 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/438329/1112000/Online%20IDE%20Support%20JS.js

// import moment from 'moment.min.js';

// Math.seedrandom
!function (f, a, c) { var s, l = 256, p = "random", d = c.pow(l, 6), g = c.pow(2, 52), y = 2 * g, h = l - 1; function n(n, t, r) { function e() { for (var n = u.g(6), t = d, r = 0; n < g;)n = (n + r) * l, t *= l, r = u.g(1); for (; y <= n;)n /= 2, t /= 2, r >>>= 1; return (n + r) / t } var o = [], i = j(function n(t, r) { var e, o = [], i = typeof t; if (r && "object" == i) for (e in t) try { o.push(n(t[e], r - 1)) } catch (n) { } return o.length ? o : "string" == i ? t : t + "\0" }((t = 1 == t ? { entropy: !0 } : t || {}).entropy ? [n, S(a)] : null == n ? function () { try { var n; return s && (n = s.randomBytes) ? n = n(l) : (n = new Uint8Array(l), (f.crypto || f.msCrypto).getRandomValues(n)), S(n) } catch (n) { var t = f.navigator, r = t && t.plugins; return [+new Date, f, r, f.screen, S(a)] } }() : n, 3), o), u = new m(o); return e.int32 = function () { return 0 | u.g(4) }, e.quick = function () { return u.g(4) / 4294967296 }, e.double = e, j(S(u.S), a), (t.pass || r || function (n, t, r, e) { return e && (e.S && v(e, u), n.state = function () { return v(u, {}) }), r ? (c[p] = n, t) : n })(e, i, "global" in t ? t.global : this == c, t.state) } function m(n) { var t, r = n.length, u = this, e = 0, o = u.i = u.j = 0, i = u.S = []; for (r || (n = [r++]); e < l;)i[e] = e++; for (e = 0; e < l; e++)i[e] = i[o = h & o + n[e % r] + (t = i[e])], i[o] = t; (u.g = function (n) { for (var t, r = 0, e = u.i, o = u.j, i = u.S; n--;)t = i[e = h & e + 1], r = r * l + i[h & (i[e] = i[o = h & o + t]) + (i[o] = t)]; return u.i = e, u.j = o, r })(l) } function v(n, t) { return t.i = n.i, t.j = n.j, t.S = n.S.slice(), t } function j(n, t) { for (var r, e = n + "", o = 0; o < e.length;)t[h & o] = h & (r ^= 19 * t[h & o]) + e.charCodeAt(o++); return S(t) } function S(n) { return String.fromCharCode.apply(0, n) } if (j(c.random(), a), "object" == typeof module && module.exports) { module.exports = n; try { s = require("crypto") } catch (n) { } } else "function" == typeof define && define.amd ? define(function () { return n }) : c["seed" + p] = n }("undefined" != typeof self ? self : this, [], Math);

chrome.storage.sync.get("global", (data) => {
    let global = data.global;

    var areTmClassesAdded = false;
    var isTamperClickAdded = false;
    var defaultTamperlabelBkgd = 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(0,0,0,0) 50%)';
    const LogStyle = {
        base: [
            "color: #fff",
            "background-color: #444",
            "padding: 2px 4px",
            "border-radius: 2px"
        ],
        warning: [
            "color: #eee",
            "background-color: red"
        ],
        success: [
            "background-color: green"
        ]
    };
    var monkey = {
        addGlobalStyle: function (css) {
            var head, style;
            head = document.getElementsByTagName('head')[0];
            if (!head) { return; }
            style = document.createElement('style');
            style.type = 'text/css';
            style.innerHTML = css;
            head.appendChild(style);
        },
        log: (msg, extra = []) => {
            let style = LogStyle.base.join(';') + ';';
            style += extra.join(';'); // Add any additional styles
            if (typeof msg === 'string') {
                console.log(`%c${msg}`, style);
            } else {
                console.log(msg);
            }
        },
        selectText: function (targetClass) {
            var textToCopy, range;
            try {
                if (document.selection) {
                    range = document.body.createTextRange();
                    range.moveToElementText(document.getElementsByClassName(targetClass)[0]);
                    range.select();
                } else if (window.getSelection) {
                    var selection = window.getSelection();
                    range = document.createRange();
                    range.selectNode(document.getElementsByClassName(targetClass)[0]);
                    selection.removeAllRanges();
                    selection.addRange(range);
                }
            } catch (err) {
                monkey.log('Failed to select text');
            }
        },
        ping: function (ip, callback) { // via http://jsfiddle.net/GSSCD/203/
            if (!this.inUse) {
                this.status = 'unchecked';
                this.inUse = true;
                this.callback = callback;
                this.ip = ip;
                var _that = this;
                this.img = new Image();

                this.img.onload = function () {
                    _that.inUse = false;
                    _that.callback('responded');
                };

                this.img.onerror = function (e) {
                    if (_that.inUse) {
                        _that.inUse = false;
                        _that.callback('error', e);
                    }
                };

                this.start = new Date().getTime();
                this.img.src = "http://" + this.ip;
                this.timer = setTimeout(function () {
                    if (_that.inUse) {
                        _that.inUse = false;
                        _that.callback('timeout');
                    }
                }, 1500);
            }
        },
        isTagIn: function (targetString, tags) {
            var isFound = false;
            _.each(tags, function scanTarget(tag) {
                if (targetString.toLowerCase().indexOf(tag.toLowerCase()) > -1) {
                    isFound = true;
                }
            });
            return isFound;
        },
        copyTextToClipboard: function (text) {
            var copyFrom = document.createElement("textarea");
            copyFrom.textContent = text;
            var body = document.getElementsByTagName('body')[0];
            body.appendChild(copyFrom);
            copyFrom.select();
            document.execCommand('copy');
            body.removeChild(copyFrom);
        },
        waitTimers: [],
        getElementsByText: function (str, tag) {
            var tagIndex = 0,
                foundElements,
                tagRange = ['a', 'button', 'input', 'div', 'span'],
                checkTag = function (checkStr, checkTag) {
                    utils.log('checkTag( ' + checkStr + ', ' + checkTag + ')');
                    return Array.prototype.slice.call(document.getElementsByTagName(checkTag)).filter(el => el.textContent.trim() === checkStr.trim());
                };
            if (tag != null) {
                return checkTag(str, tag);
            } else {
                while ((foundElements == null || foundElements.length === 0) && tagIndex < tagRange.length) {
                    foundElements = checkTag(str, tagRange[tagIndex]);
                    tagIndex++;
                };
                return foundElements;
            }
        },
        initNotes: function (global) {
            if (global.notes == null) {
                global.notes = {
                    messages: [],
                    notifiedCount: 0
                };
            }
        },
        getContainer: function (opts) {
            var options = {
                id: opts.id ? opts.id : opts.el.replace(/[ (:)]/g, ''),
                el: opts.el,
                try: 0,
                max: opts.max ? opts.max : 20,
                spd: opts.spd ? opts.spd : 500
            };
            clearTimeout(monkey.waitTimers[options.id]);
            return new Promise(function (resolve, reject) {
                monkey.waitForContainerElement(resolve, options);
            });
        },
        waitForContainerElement: function (resolve, options) {
            var $configElement = document.querySelector(options.el);
            if ($configElement.length === 0) {
                options.try++;
                if (options.try < options.max) {
                    monkey.waitTimers[options.id] = setTimeout(monkey.waitForContainerElement.bind(this, resolve, options), options.spd);
                } else {
                    console.error(`maximum searches reached - monkey.waitForContainerElement`);
                }
            } else {
                resolve($configElement);
            }
        },
    };
    let debugPassFail = [];
    const avatarHost = global.prefs.users.avatars.hostUrl;
    const pingPhoto = global.prefs.users.avatars.noImageName;
    const imageExt = global.prefs.users.avatars.imageExtension;

    (function () {
        const observers = {
            /**
             * handler to process user-defined callback when element is located in DOM
             * @param {NodeList} elements - All elements found matching the user-defined selector in "observerDefs"
             * @return {void}
             */
            handleFound: (elements, observerDef) => {
                elements.forEach((element) => {
                    observerDef.callback(element, observerDef);
                    // if (!foundEls.includes(element.id)) {
                    //     foundEls.push(element.id);
                    // }
                });
            },
            /**
             * Creates a single-use observer to await the existance of a DOM element
             * @param {Array<ObserverDef>} observerDefs - an array of elements to await
             * @return {Array<observer} observers - an array of all MutationObservers created
             */
            create: (observerDefs) => {
                // As assistance for delayed initialization, define an observer to watch for changes
                var observerList = [];
                observerDefs.forEach(function (obd) {
                    if (obd.single == null) {
                        obd.single = true;
                    }
                    observerList.push(
                        new MutationObserver(function (mutations, me) {
                            var targetElems = document.querySelectorAll(obd.selector);
                            if (targetElems.length > 0) {
                                observers.handleFound(targetElems, obd);
                                if (obd.single) {
                                    me.disconnect(); // stop observing
                                }
                                return;
                            }
                        })
                    );
                });
                // start observing
                observerList.forEach(function (observer) {
                    observer.observe(document, {
                        childList: true,
                        subtree: true
                    });
                });
                return observerList;
            }
        }
        const report = {
            start: (id) => {
                if (debugPassFail.length > 0) {
                    utils.debug([`Reporting Error!`, debugPassFail, id]);
                }
                debugPassFail = [];
                debugPassFail.push({
                    id: id,
                    step: `start`,
                    pass: true,
                });
            },
            abort: () => {
                report.step(`not needed`);
                report.finish();
            },
            step: (step, pass = true) => {
                let thisId = debugPassFail[0] ? debugPassFail[0].id : `???`;
                debugPassFail.push({
                    id: thisId,
                    step: step,
                    pass: pass,
                });
            },
            finish: () => {
                const pass = [];
                const fail = [];
                if (debugPassFail.length === 0) {
                    return;
                }
                if (debugPassFail.length === 1) {
                    report.step(`did nothing`, false);
                }
                debugPassFail.forEach(pf => {
                    if (pf.pass) { pass.push(pf); };
                    if (!pf.pass) { fail.push(pf); };
                });
                if (fail.length > 0) {
                    utils.debug({
                        iAm: fail[0].id,
                        ...fail
                    });
                } else if (pass.length > 0 && !global.prefs.account.debug.debugIgnores.includes(`pass`)) {
                    utils.debug({
                        iAm: pass[0].id,
                        ...pass
                    });
                }
                debugPassFail = [];
            },
        };
        const utils = {
            acronym: (str = '') => {
                const strArr = str.split(' ');
                let res = '';
                strArr.forEach(el => {
                    const [char] = el;
                    if (char === char.toUpperCase() && char !== char.toLowerCase()) {
                        res += char;
                    };
                });
                return res;
            },
            capitalize: (cap) => {
                if (!cap) {
                    return;
                }
                return cap.charAt(0).toUpperCase() + cap.slice(1);
            },
            createElement: (a, b) => {
                var d = document.createElement(a);
                if (b && "object" == typeof b) {
                    var e;
                    for (e in b) {
                        if ("innerHTML" === e) {
                            d.innerHTML = b[e];
                        } else if ("innerText" === e) {
                            d.innerText = b[e];
                        } else {
                            if (e.slice(0, 5) === "aria_" || e.slice(0, 5) === "data_") {
                                var attr = e.slice(0, 4) + "-" + e.slice(5);
                                d.setAttribute(attr, b[e]);
                            } else {
                                d.setAttribute(e, b[e]);
                            }
                        }
                    }
                }
                return d;
            },
            debug: (msg) => {
                const iAm = msg.iAm ? msg.iAm : debugPassFail[0] ? debugPassFail[0].id : `undefined`;
                let shouldIgnore = false;
                let userSelectedIgnore = [iAm].filter(function (iAmCheck) {
                    return global.prefs.account.debug.debugIgnores.some(function (match) {
                        return iAmCheck.toLowerCase().indexOf(match.toLowerCase()) !== -1;
                    });
                });
                shouldIgnore = userSelectedIgnore.length > 0;
                if (utils.stringToBoolean(global.prefs.account.debug.useDebug) && !shouldIgnore) {
                    if (typeof msg === 'object') {
                        let hasSource = false;
                        if (msg.constructor === Array) {
                            msg.includes("iAm") && (hasSource = true);
                            shouldIgnore = global.prefs.account.debug.debugIgnores.includes(msg.find(id => id === "iAm"));
                        } else {
                            const keys = Object.keys(msg);
                            keys.forEach((key) => {
                                !hasSource && (hasSource = key === "iAm");
                            });
                            shouldIgnore = global.prefs.account.debug.debugIgnores.includes(msg.iAm);
                        }
                        if (hasSource) {
                            !shouldIgnore && monkey.log(msg);
                        } else {
                            monkey.log({ 'iAm': iAm, ...msg });
                        }
                    } else {
                        monkey.log({ 'iAm': iAm, 're': msg });
                    }
                }
            },
            delayUntil: function (id, condition, callback) {
                const repeat = (id, co, ca) => {
                    setTimeout(() => {
                        utils.delayUntil(id, co, ca);
                    }, global.constants.TIMEOUT * 2);
                };
                if (!global.states.delays.find((x) => x.delayId === id)) {
                    global.states.delays.push({
                        delayId: id,
                        delayCount: 0
                    });
                }
                global.states.delays.find((x) => x.delayId === id).delayCount++;
                if (global.states.delays.find((x) => x.delayId === id).delayCount > 20) {
                    global.states.delays.find((x) => x.delayId === id).delayCount === 0;
                    return;
                }
                try {
                    if (!condition()) {
                        utils.debug('delay WAIT called by ' + id);
                        repeat(id, condition, callback);
                    } else {
                        utils.debug('delay PASS for ' + id);
                        callback();
                    }
                } catch (e) {
                    utils.debug('delay WAIT called by ' + id + ' TRIED\n   ' + e);
                    repeat(id, condition, callback);
                }
            },
            getElementByTextContent: (text, tagName = `span`) => {
                var spanList = document.getElementsByTagName(tagName);
                for (var i = 0, len = spanList.length; i < len; i++) {
                    if (spanList[i].textContent === text) {
                        return spanList[i];
                    }
                }
            },
            getElementsByTextContent: (text, tagName = `span`) => {
                const spanList = document.getElementsByTagName(tagName);
                const returnList = [];
                for (var i = 0, len = spanList.length; i < len; i++) {
                    if (spanList[i].textContent === text) {
                        returnList.push(spanList[i]);
                    }
                }
                return returnList;
            },
            initScript: () => {
                Object.keys(global.constants.initalizeOnElements).forEach((key) => {
                    const trigger = global.constants.initalizeOnElements[key];
                    monkey.getContainer({
                        'el': trigger,
                        'max': 100,
                        'spd': 1000
                    }).then(function () {
                        page.initialize();
                    });
                });
            },
            isEmpty: (obj) => {
                return Object.keys(obj).every(key => obj[key] === undefined);
            },
            isNumeric: (n) => {
                return !isNaN(parseFloat(n)) && isFinite(n);
            },
            listenOnce: (element, event, handler) => {
                element.addEventListener(
                    event,
                    function tempHandler(e) {
                        handler(e);
                        element.removeEventListener(event, tempHandler, false);
                    },
                    false
                );
            },
            shuffle: (array) => {
                var date = new Date();
                var dateString =
                    "" + date.getFullYear() + date.getMonth() + date.getDate() + "1";
                var p = new Math.seedrandom(dateString);

                var currentIndex = array.length,
                    temporaryValue,
                    randomIndex;

                // While there remain elements to shuffle...
                while (0 !== currentIndex) {
                    // Pick a remaining element...
                    randomIndex = Math.floor(p() * currentIndex);
                    currentIndex -= 1;

                    // And swap it with the current element.
                    temporaryValue = array[currentIndex];
                    array[currentIndex] = array[randomIndex];
                    array[randomIndex] = temporaryValue;
                }

                return array;
            },
            stringToBoolean: (thisState) => {
                if (typeof thisState === `boolean`) {
                    return thisState;
                }
                if (thisState === `1` || thisState === `true`) {
                    return true;
                }
                return false;
            },
            user: {
                chat: (idName, messageName, email) => {
                    const chatLink = 'https://teams.microsoft.com/l/chat/0/0?users=';
                    idName = email || utils.user.renameIndexedToEmail(idName) + '@dell.com';
                    var message = messageName ? '&message=' + messageName : '';
                    window.open(chatLink + idName + message);
                },
                renameIndexedToEmail: (userName) => {
                    const commaIndex = userName.indexOf(`, `);
                    if (commaIndex <= 0) {
                        return;
                    }
                    const lName = userName.substr(0, commaIndex);
                    const fName = userName.replace(lName, ``).replace(`, `, ``);
                    return `${fName}_${lName}`;
                },
                properName: (thisName) => {
                    if (!thisName) {
                        return;
                    }
                    var firstName = '',
                        lastName = '',
                        midName = '';

                    thisName = thisName
                        .replace('https://gitlab.dell.com/', '')
                        .replace(' - Dell Team', '')
                        .replace('User profile for ', '')
                        .replace('\'s avatar', '')
                        .replace('Assigned to ', '')
                        .replace('Avatar for ', '')
                        .replace('Assignee:', '')
                        .replace(`Uploaded image for project: `, ``)
                        .replace(/(<table>).*(<\/table>)/gi, '')
                        .replace(/dell\.com/gi, '')
                        .replace(/dellteam\.com/gi, '')
                        .replace(/'/g, '')
                        .replace('@', '')
                        .replace(/@/g, '')
                        .replace(/\//g, '')
                        .replace(/Review requested from /g, '')
                        .replace(/_/g, '-');
                    firstName = thisName.substring(0, thisName.indexOf('-'));
                    lastName = thisName.substring(thisName.indexOf('-') + 1, thisName.length);
                    if ((firstName.length === 0 && lastName.length === 0)) {
                        return;
                    }
                    if (firstName.length > 0 && lastName.length > 0 && thisName.indexOf(',') < 0) {
                        thisName = lastName + ', ' + firstName;
                    }
                    if (thisName.indexOf('-') > 0) {
                        midName = thisName.substring(0, thisName.indexOf('-'));
                        thisName = thisName.substring(thisName.indexOf('-') + 1, thisName.length);
                        thisName = thisName + ' ' + midName;
                    }
                    while (thisName.indexOf('  ') > 0) {
                        thisName = thisName.replace(/\s\s/, ''); // no double spaces
                    }
                    thisName = thisName
                        .replace(/(\r\n\t|\n|\r\t)/gm, '') // no line breaks or tabs
                        .replace(/ ,/, ',') // no spaces before commas
                        .replace(/\%20/, '') // no %20 characters
                        .replace(/Americas/g, '')
                        .trim(); // seriously, no extra spaces
                    thisName = thisName.replace(',', ', ').replace('  ', ' '); // there's probably a less-stupid way of REALLY making sure it's always "COMMA SPACE"
                    return thisName;
                },
                updateImg: function (img, thisName) {
                    if (thisName && thisName.length > 0 && thisName !== ', ') {
                        const src = img.getAttribute(`src`);
                        if (src.match(/(!none|jira|gravatar)/gi)) {
                            img.setAttribute("data-previous", src);
                            const nameDivider = (thisName.indexOf(',') < 0) ? ' ' : ', ';
                            const thisNameSpaceIndex = thisName.indexOf(' ') + 1;
                            let thisNameFirst = thisName.indexOf(' ') < 0 ? thisName : nameDivider === ' ' ? thisName.substr(0, thisName.indexOf(thisNameSpaceIndex) - 1) : thisName.substr(thisNameSpaceIndex, thisName.length - thisNameSpaceIndex);
                            if (thisNameFirst.length === 0) {
                                thisNameFirst = thisName;
                            }
                            if (!global.states.avatarPingFailed && global.prefs.users.avatars.avatarsSources[0] === 'localhost') {
                                // look at why there's a difference handling localhost vs. the other avatar servers
                                img.setAttribute('src', 'http://' + avatarHost + thisName + imageExt);
                            } else if (global.prefs.users.avatars.avatarsSources[0] !== 'localhost') {
                                let templateSrc = global.templates.default.replace(/IMGID/g, thisName).replace(/IMGALT/g, thisName);
                                if (global.prefs.users.avatars.avatarsType === `gender`) { // aw, this won't work for free for more than 1000
                                    const finishUpdating = (genRes) => {
                                        genRes = JSON.parse(genRes);
                                        const result = genRes.gender ? genRes.gender : `initials`;
                                        templateSrc = templateSrc.replace(global.prefs.users.avatars.avatarsType, result);
                                        templateSrc = encodeURI(templateSrc.substr(templateSrc.indexOf('http'), (templateSrc.indexOf('alt=') - templateSrc.indexOf('http')) - 2));
                                        img.setAttribute('src', templateSrc);
                                    };
                                    utils.xhrAction(`updateImg`, null, `GET`, `https://api.genderize.io/?name=${thisNameFirst}`, null, finishUpdating);
                                } else {
                                    templateSrc = encodeURI(templateSrc.substr(templateSrc.indexOf('http'), (templateSrc.indexOf('alt=') - templateSrc.indexOf('http')) - 2));
                                    img.setAttribute('src', templateSrc);
                                }
                            }
                        }
                    } else {
                        utils.debug('updateImg: invalid user name for ' + img.src + ': ' + thisName + '(' + thisName.length + ' chars)');
                    }
                },
            },
            xhrRequest: (method, url, data = {}, token) => {
                const xhr = new XMLHttpRequest();
                return new Promise(resolve => {
                    xhr.open(method, url, true);
                    xhr.onload = () => resolve({
                        status: xhr.status,
                        response: xhr.responseText
                    });
                    xhr.onerror = () => resolve({
                        status: xhr.status,
                        response: xhr.responseText
                    });
                    if (method != 'GET') {
                        xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
                    }
                    if (token != null) {
                        xhr.setRequestHeader('X-Jsio-Token', token);
                    }
            
                    data != {} ? xhr.send(JSON.stringify(data)) : xhr.send();
                })
            },
            xhrAction: (iAm, token, xhrType, urlPath, params, callback, alwaysCallback) => { // deprecate?
                if (!iAm || !xhrType || !urlPath || !callback) {
                    utils.debug('improper xhr setup');
                    return;
                }
                utils.debug({ 'id': iAm, 'xhrType': xhrType, 'urlPath': urlPath, 'params': params, 'callback': callback, 'alwaysCallback': alwaysCallback });

                // Set up our HTTP request
                const xhr = new XMLHttpRequest();

                // Setup our listener to process completed requests
                xhr.onload = function () {
                    global.states.xhrBusy = false;

                    // Process our return data
                    if (xhr.status >= 200 && xhr.status < 300) {
                        // What do when the request is successful
                        const resp = xhr.response;
                        if (resp) {
                            // resp = JSON.parse(resp).slice().reverse();
                            utils.debug({ 'id': 'xhrAction', 're': { 'urlPath': urlPath, ...JSON.parse(resp) } });
                            callback(resp);
                        }
                    } else {
                        // What do when the request fails
                        utils.debug('XHR Call for ' + iAm + ' failed!');
                    }
                    if (alwaysCallback) {
                        alwaysCallback();
                    }
                };
                xhr.open(xhrType, urlPath);
                if (token) {
                    xhr.setRequestHeader('Authorization', 'Bearer ' + token);
                }
                if (params != null) {
                    xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
                    xhr.send(JSON.stringify(params));
                } else {
                    xhr.send();
                }
            },
        };
        global = {
            ...global,
            constants: {
                TIMEOUT: 750,
                initalizeOnElements: ['body'], // .content
            },
            ids: {
                scriptName: 'JiraMods',
                prefsName: 'JiraPrefs',
                memsName: 'JiraMems',
            },
            states: {
                areClassesAdded: false,
                avatarPingFailed: false,
                isMouseMoved: false,
                areStepsAnnounced: false,
                observingAvatars: false,
                observingChatAdds: false,
            },
            mems: {},
            templates: {
                default: null,
                localhost: `<img src="http://' + avatarHost + 'IMGID.png" alt="IMGALT" title="IMGALT" class="avatar s40" data-email="">`,
                dicebear: `<img src="https://avatars.dicebear.com/api/${global.prefs.users.avatars.avatarsType}/IMGID.svg" alt="IMGALT" title="IMGALT" class="avatar s40" data-email="">`,
                boring: `https://source.boringavatars.com/beam/120/IMGID?colors=264653,2a9d8f,e9c46a,f4a261,e76f51`,
            }
        };
        switch (global.prefs.users.avatars.avatarsSources[0]) {
            case 'boring':
                global.templates.default = global.templates.boring;
                break;
            case 'dicebear':
                global.templates.default = global.templates.dicebear;
                break;
            default:
                global.templates.default = global.templates.localhost;
                break;
        }
        const page = {
            initialize: function () {
                page.setMems();
                const stepsRun = [];
                const steps = [
                    page.addClasses,
                ];
                Object.keys(mods).forEach(catkey => {
                    Object.keys(mods[catkey]).forEach(stepkey => {
                        steps.push(mods[catkey][stepkey]);
                    });
                });
                steps.forEach((step, index) => {
                    if (!global.states.areStepsAnnounced) {
                        stepsRun.push(step);
                    }
                    setTimeout(() => {
                        step();
                    }, index * 10);
                });
                if (!global.states.areStepsAnnounced) {
                    utils.debug([`INIT`, stepsRun]);
                    global.states.areStepsAnnounced = true;
                }
            },
            setMems: function () {
                var currentMems; // = GM_getValue(global.ids.memsName);
                if (currentMems == null || utils.isEmpty(JSON.parse(currentMems))) {
                    global.mems = {};
                    // monkey.savePreferences(global.ids.memsName, global.mems);
                } else {
                    global.mems = JSON.parse(currentMems);
                }
            },
            addClasses: function () {
                report.start(`page.addClasses`);
                if (!global.states.areClassesAdded) {
                    global.states.areClassesAdded = true;
                    report.step(`classes added`);

                    const ddsCss = [
                        `https://dds.dell.com/components/2.13.0/css/dds-reboot.min.css`,
                        `https://dds.dell.com/components/2.13.0/css/dds-fonts.min.css`,
                        `https://dds.dell.com/components/2.13.0/css/dds-icons.min.css`,
                        `https://dds.dell.com/components/2.13.0/css/dds-helpers.min.css`,
                        `https://dds.dell.com/components/2.13.0/css/dds-main.min.css`
                    ];
                    ddsCss.forEach(cssUrl => {
                        report.step(`adding ${cssUrl}`);
                        const link = utils.createElement(`link`, {
                            href: cssUrl,
                            type: `text/css`,
                            rel: `stylesheet`,
                        });
                        document.getElementsByTagName("head")[0].appendChild(link);
                    });

                    if (global.prefs.customize.comments.commentsMove) {
                        monkey.addGlobalStyle(`
                .changehistory td {
                    display: flex;
                    line-height: 0.8rem;
                    font-family: monospace;
                    font-size: 0.8rem;
                }`);
                    }

                    if (global.prefs.customize.lists.listsShorten) {
                        monkey.addGlobalStyle(`
                span.issue-link-key {
                    margin-right: 0.625rem;
                }
                .issue-content-container {
                    display: flex;
                    font-size: 0.8rem;
                }`);
                    }

                    // Make certain sections pop
                    if (global.prefs.customize.highlights.highlightsDetails) {
                        monkey.addGlobalStyle(`
                #detail-repro,
                #customfield_10208-val,
                #description-val,
                #customfield_10217-val {
                    background: aliceblue;
                    border: 0.2rem dotted;
                    border-radius: 0.625rem;
                }`);
                    }

                    // add custom hide-the-button-on-dds__notifications classes
                    monkey.addGlobalStyle(`
                    .ddsc__notification--button-primary-none .dds__notification__body .dds__button--primary
                    {
                        display: none !important;
                    }
                    `)
                    monkey.addGlobalStyle(`
                    .ddsc__notification--button-secondary-none .dds__notification__body .dds__button--secondary 
                    {
                        display: none !important;
                    }
                    `)

                    // add custom class for template modal
                    monkey.addGlobalStyle(`
                    .ddsc__modal
                    {
                        z-index: 3050;
                    }
                    `)


                    // header removed buttons
                    monkey.addGlobalStyle(`
            .removedLink {
                display:none !important;
            }`);

                    // header favorite buttons
                    monkey.addGlobalStyle(`
            .faveLink {
                margin-left: 0.625rem;
                margin-right: 0 !important;
                padding: 0.5rem;
            }`);

                    // dashboard days-remaining
                    monkey.addGlobalStyle(`
            #gadget-11556 {
                height: inherit !important;
            }
            .sprint-days-remaining-dashboard-item .ghx-remaining-value {
                font-family: fantasy;
                opacity: 0.5;
                top: 1.3rem;
                position: relative;
            }`);

                    // fit images to size
                    monkey.addGlobalStyle(`
            .activity-comment img {
                max-width: 100%;
            }`);

                    // adjustments for DDS classes
                    monkey.addGlobalStyle(`
            #structure .celldiv {
                height: inherit;
                max-height: inherit;
            }
            .aui-button-primary {
                font-weight: inherit;
            }
            .dds__button {
                margin-right: 0.625rem;
            }
            h1 {
                font-size: 2rem;
                line-height: 2rem;
            }
            h2 {
                font-size: 1.5rem;
                line-height: 1.5rem;
            }
            h3 {
                font-size: 1rem;
                line-height: 1rem;
            }
            blockquote {
                font-size: 1.05rem;
                line-height: 130%;
            }`);

                    // Copy Issue as Markdown styling
                    monkey.addGlobalStyle(`
            .copyLink {
                margin-left: 0.625rem;
                cursor: pointer;
            }
            .ghx-column .copyLink {
                position: absolute;
                bottom: 1rem;
                right: 1rem;
            }`);

                    if (global.prefs.customize.backlog.backlogMove) {
                        // Adjusting Backlog to be alongside TODO list
                        monkey.addGlobalStyle(`
                #ghx-content-group {
                    display: flex !important;
                }
                .ghx-backlog-container {
                    margin-top: 0px !important;
                    margin-left: 1rem;
                }
                .ghx-sprint-group, .ghx-backlog-group {
                    width: 50%;
                    max-height: 72vh !important;
                    overflow-y: scroll;
                    border-bottom: 2px dotted dodgerblue;
                }
                .js-sprint-container:not(:first-child) {
                    margin-top: 2rem !important;
                }`);
                    }

                    if (global.prefs.append.scrumButtons.scrumButtonsAdd) {
                        monkey.addGlobalStyle(`
                        .nextPeepButton {
                            padding-right: 0.7rem;
                            padding-left: 0.3rem;
                            padding-bottom: 0.3rem;
                            position: relative;
                            left: -0.4rem;
                            height: 2.15rem;
                        }
                        `)
                    }

                    if (global.prefs.account.user.customCss) {
                        monkey.addGlobalStyle(global.prefs.account.user.customCss);
                    }

                } else {
                    report.step(`already added`);
                }
                report.finish();
            },
        };
        const mods = {
            user: {
                addChat: () => {
                    report.start(`user.addChat`);
                    if (!global.prefs.users.chatLinks.chatAdd) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    if (global.states.observingChatAdds) {
                        report.step(`already observing`);
                        report.abort();
                        return;
                    }
                    global.states.observingChatAdds = true;
                    const obdSelector = `.user-hover-details`;
                    const obdCallback = () => {
                        const chatEl = document.querySelector(`.user-chat-link`);
                        const userEl = document.querySelector(`.user-hover-details`);
                        const avatarEl = document.querySelector(`#avatar-full-name-link`);
                        const summaryEl = document.getElementById(`summary-val`);
                        const subnavEl = document.getElementById(`subnav-title`);
                        const emailEl = document.getElementById(`user-hover-email`);
                        let chatRef = summaryEl ? summaryEl.innerText : subnavEl ? subnavEl.innerText : ``;
                        if (chatRef) {
                            chatRef = `[${chatRef}](${window.location.href})`;
                        }
                        if (!userEl) {
                            // report.abort();
                            return;
                        }
                        if (!chatEl) {
                            const displayName = utils.user.properName(avatarEl.innerText);
                            const chatLink = utils.createElement(`a`, {
                                href: `sip:${emailEl.innerText}`,
                                class: `user-chat-link`
                            });
                            chatLink.innerText = `Chat with ${displayName}`;
                            // chatLink.addEventListener(`click`, () => {
                            //     utils.user.chat(displayName, chatRef, emailEl.innerText);
                            // });
                            userEl.appendChild(chatLink);
                            // report.step(`added chat link`);
                        }let demo;
                        let sources;
                        let initCounter = 10;
                        const deprecatedEvents = {
                          dropdown: [`getValue`]
                        };
                         
                        const pen = {
                          actions: {
                            log: (msg) => {
                              if (demo.log !== `off`) {
                                const logEl = pen.elements.logContent;
                                pen.elements.logLabel.classList.add(`highlight`);
                                setTimeout(() => {
                                  pen.elements.logLabel.classList.remove(`highlight`);
                                }, 500);
                                logEl.innerText = `${msg}\n${logEl.innerText}`;
                                if (msg) console.log(msg);
                              }
                            },
                            api: {
                              addButton: (lText, lAction) => {
                                if (typeof lText === `string`) {
                                  const newButton = pen.utils.createElement(`button`, {
                                    role: `button`,
                                    type: `button`,
                                    class: `dds__btn dds__btn-primary dds__btn-sm dds__button dds__button--mini dds__text-truncate`,
                                    text: lText
                                  });
                                  newButton.addEventListener(`click`, (e) => {
                                    let actionResponse;
                                    setTimeout(() => {
                                      if (lAction.length > 0) {
                                        try {
                                          actionResponse = lAction(document.querySelector(`.dds__side-nav__item`));
                                        } catch (e) {
                                          try {
                                            actionResponse = lAction("0");
                                          } catch (e) {
                                            try {
                                              actionResponse = lAction(["0"]);
                                            } catch (e) {
                                              try {
                                                actionResponse = lAction([0]);
                                              } catch (e) {
                                                try {
                                                  actionResponse = lAction(new Date());
                                                } catch (e) {
                                                  try {
                                                    actionResponse = lAction({ "alignment": "end" });
                                                  } catch (e) {
                                                    try {
                                                      actionResponse = lAction(0);
                                                    } catch (e) {
                                                      try {
                                                        actionResponse = lAction(0, `descending`);
                                                      } catch (e) {
                                                        try {
                                                          actionResponse = lAction("1");
                                                        } catch (e) {
                                                          console.error(e);
                                                          actionResponse = lAction();
                                                        }
                                                      }
                                                    }
                                                  }
                                                }
                                              }
                                            }
                                          }
                                        }
                                      } else {
                                        actionResponse = lAction();
                                      }
                                    });
                                    if (actionResponse) pen.actions.log(actionResponse);
                                  });
                                  pen.elements.apiContent.appendChild(newButton);
                                } else { // presume we are moving an existing element to the pen nav
                                  pen.elements.apiContent.appendChild(lText);
                                }
                              }
                            },
                          },
                          elements: {
                            logId: `log`,
                            apiId: `api`,
                          },
                          utils: {
                            addStyle: (styles) => {
                              /* Create style document */
                              var css = document.createElement('style');
                              css.type = 'text/css';
                              if (css.styleSheet)
                                css.styleSheet.cssText = styles;
                              else
                                css.appendChild(document.createTextNode(styles));
                              /* Append style to the tag name */
                              document.getElementsByTagName("head")[0].appendChild(css);
                            },
                            /**
                            * converts camelCased words into dashed-cased ones
                            * @param {string} key - a string with some number of capitalized letters
                            * @return {string} a dashed version of whatever string was entered
                            */
                            camelDash: (key) => {
                                return key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
                            },
                            /**
                            * converts kebab-case words into camelCase ones
                            * @param {string} key - a string with some number of dashes
                            * @return {string} a camelCase version of whatever string was entered
                            */
                            dashCamel: function (key) {
                              return key.replace(/-[a-z]/g, (m) => m.toUpperCase().replace(/-/gi, ""));
                            },
                            capitalize: (what) => {
                              return what.charAt(0).toUpperCase() + what.slice(1);
                            },
                            createElement: (nodeType, props) => {
                              const domNode = document.createElement(nodeType);
                              if (props && "object" === typeof props) {
                                for (const prop in props) {
                                  if (prop === "html") {
                                    domNode.innerHTML = props[prop];
                                  } else if (prop === "text") {
                                    domNode.textContent = props[prop];
                                  } else {
                                    if (prop.slice(0, 5) === "aria_" || prop.slice(0, 4) === "data_") {
                                      const attr = prop.slice(0, 4) + "-" + prop.slice(5);
                                      domNode.setAttribute(attr, props[prop]);
                                    } else {
                                      domNode.setAttribute(prop, props[prop]);
                                    }
                                  }
                                  // Set attributes on the element if passed
                                  if (["role", "aria-label"].includes(prop)) domNode.setAttribute(prop, props[prop]);
                                }
                              }
                              return domNode;
                            },
                            random: (min = 100000000, max = 999999999) => {
                              min = Math.ceil(min);
                              max = Math.floor(max);
                              return Math.floor(Math.random() * (max - min) + min);
                            },
                            load: (script) => {
                              document.write('<'+'script src="'+script+'" type="text/javascript"><' + '/script>');
                            },
                          },
                          initialize: () => {
                            if (demo == null) {
                              setTimeout(() => {
                                initCounter--;
                                if (initCounter > 0) {
                                  pen.initialize();
                                }
                              }, 50);
                              if (initCounter === 1) {
                                document.getElementById(`penlay`).remove();
                                demo = {
                                  version: `2.14.1`
                                };
                                sources = pen.addLinks();
                              }
                              return;
                            }
                            if (demo.version) {
                              sources = pen.addLinks();
                            }
                            setTimeout(() => {
                              document.getElementById(`penlay`).remove();
                              demo.components = [];
                              const method = pen.utils.capitalize(pen.utils.dashCamel(demo.selector));
                              document.querySelectorAll(`[data-dds="${demo.selector}"]`).forEach((element) => {
                                if (!element[method]) {
                                    demo.components.push(DDS[method](element, demo.options));
                                } else {
                                    demo.components.push(element[method]);
                                }
                              });
                              Object.keys(pen.elements).forEach(key => {
                                if (key.indexOf(`Id`) > 0) {
                                  const elString = key.replace(`Id`, ``);
                                  pen.elements[elString] = pen.utils.createElement(`div`, {
                                    id: elString,
                                  });
                                  if (elString === 'log' && (demo.log == null || demo.log !== `open`)) {
                                    pen.elements[elString].classList.add(`closed`);
                                  }
                                  if (elString === 'log' && (demo.log === `off` || demo.log === false)) {
                                    pen.elements[elString].classList.add(`pen__none`);
                                  }
                                  if (elString === 'api' && (demo.api == null || demo.api !== `open`)) {
                                    pen.elements[elString].classList.add(`closed`);
                                  }
                                  pen.elements[`${elString}Label`] = pen.utils.createElement(`div`, {
                                    class: `label`
                                  });
                                  pen.elements[`${elString}Label`].innerText = elString;
                         
                                  pen.elements[`${elString}Content`] = pen.utils.createElement(`div`, {
                                    class: `content`
                                  });
                                }
                              });
                              Object.keys(pen.elements).forEach(key => {
                                if (!key.match(/(Id|Label|Content)/g)) {
                                  document.querySelector(`body`).appendChild(pen.elements[key]);
                                  pen.elements[key].appendChild(pen.elements[`${key}Label`]);
                                  pen.elements[key].appendChild(pen.elements[`${key}Content`]);
                                }
                                if (key.indexOf(`Label`) > 0) {
                                  pen.elements[key].addEventListener(`click`, () => {
                                    pen.elements[key.replace(`Label`, ``)].classList.toggle(`closed`);
                                  });
                                }
                              });
                         
                              let hasDispose = false;
                              const comp = demo.components[0];
                              if (!comp) {
                                return;
                              }
                              Object.keys(comp).forEach(key => {
                                const method = pen.utils.capitalize(pen.utils.dashCamel(demo.selector));
                                const selectorScript = `document.querySelector('[data-dds="${demo.selector}"]').${method}`;
                                if (typeof comp[key] === `function`) {
                                  if (key !== `dispose`) {
                                    if (!deprecatedEvents[demo.selector] || !deprecatedEvents[demo.selector].includes(key)) {
                                      const parameterCount = comp[key].length;
                                      let comment = parameterCount > 0 ? ` // takes ${parameterCount} parameters` : ``;
                                      pen.actions.api.addButton(key, comp[key]);
                                      pen.actions.log(`${selectorScript}.${key}();${comment}`);
                                    }
                                  } else {
                                    hasDispose = true;
                                    pen.actions.log(`${selectorScript}.dispose()`);
                                  }
                                } else {
                                  pen.actions.log(`${selectorScript}.${key} = ${comp[key]}`);
                                }
                              });
                              pen.actions.log(`:::::::::::::::::::::::::::::::::::::::::::::::::::`);
                              pen.actions.log(`\n\n${demo.selector} properties / methods:::::::::::::::::::::::`);
                         
                              hasDispose && (pen.actions.api.addButton(`dispose`, () => {
                                comp[`dispose`]();
                                pen.elements.api.querySelectorAll(`button`).forEach(b => b.disabled = `true`);
                              }));
                         
                         
                              demo.events.forEach((ev) => {
                                pen.actions.log(`
                        document.addEventListener('${ev}', (event) => {
                            console.log(event.detail);
                        })`);
                                document.addEventListener(ev, (e) => {
                                  let output;
                                  pen.actions.log(`${ev} was fired with {event}.detail = ${JSON.stringify(e.detail)}`);
                                  try {
                                    output = JSON.stringify(e);
                                    pen.actions.log(output);
                                  } catch (error) {
                                    output = e;
                                    console.error(ev, output);
                                  }
                                });
                              });
                              pen.actions.log(`::::::::::::::::::::::::::::::::::::::`);
                              pen.actions.log(`\n${demo.selector} events:::::::::::::::::::::::`);
                              
                              // BEGIN LOGGING INITIALIZATION
                              pen.actions.log(`
                        let components = [];
                        document.querySelectorAll('[data-dds="${demo.selector}"]').forEach((element) => {
                          components.push(DDS.${method}(element));
                        });
                              `);
                              sources.scripts.forEach(scrp => {        
                                pen.actions.log(`<script src="${scrp}"></script>`);
                              })
                              sources.styles.forEach(styl => {        
                                pen.actions.log(`<link rel="stylesheet" crossorigin href="${styl}" />`);
                              })
                              pen.actions.log(`::::::::::::::::::::::::::::::::::::::`);
                              pen.actions.log(`\n INITIALIZATION :::::::::::::::::::::::`);
                            }, 500);
                          },
                          addLinks: () => {
                            const links = [
                              `https://dds.dell.com/components/${demo.version}/css/dds-reboot.min.css`,
                              `https://dds.dell.com/components/${demo.version}/css/dds-fonts.min.css`,
                              `https://dds.dell.com/components/${demo.version}/css/dds-icons.min.css`,
                              `https://dds.dell.com/components/${demo.version}/css/dds-helpers.min.css`,
                              `https://dds.dell.com/components/${demo.version}/css/dds-main.min.css`,
                            ];
                            links.forEach((href) => {
                              let link = pen.utils.createElement(`link`, {
                                rel: 'stylesheet',
                                crossorigin: '',
                                href: href,
                                'data-dds': 'stylesheet',
                              });
                              document.querySelector(`head`).appendChild(link);
                            });
                            // DOESN'T WORK pen.utils.load(`https://dds.dell.com/components/${demo.version}/js/index.min.js`);
                         
                            const scripts = [`https://dds.dell.com/components/${demo.version}/js/index.min.js`];
                            scripts.forEach((href) => {
                              let scrp = pen.utils.createElement(`script`, {
                                type: `text/javascript`,
                                src: href,
                                'data-dds': 'script',
                              });
                              document.querySelector(`head`).appendChild(scrp);
                            });
                            pen.ready = true;
                            return {
                              styles: links,
                              scripts: scripts,
                            }
                          },
                          removeLinks: () => {
                            document.querySelectorAll(`[data-dds="stylesheet"]`).forEach(stylesheet => {
                                stylesheet.remove(); 
                            });
                            document.querySelectorAll(`[data-dds="script"]`).forEach(script => {
                                script.remove(); 
                            });
                          },
                          addCss: () => {
                            pen.utils.addStyle(`
                        body {
                          max-width: 1900px !important;
                          padding: 5rem 4rem !important;
                        }
                        #log,
                        #api {
                            transition: all 0.5s ease-in-out;
                            position: absolute;
                            z-index: 9999;
                            height: 49vh;
                            width: 80%;
                            top: 50%;
                            margin-left: 5%;
                            color: white;
                            background: black;
                            font-size: 0.7rem;
                            font-family: monospace;
                            line-height: 0.9rem;
                            padding: 1rem;
                            border-bottom-right-radius: 0.625rem;
                        }
                         
                        #log .label,
                        #api .label {
                            font-family: Roboto;
                            background: black;
                            color: white;
                            position: relative;
                            float: right;
                            top: .7rem;
                            left: 4rem;
                            max-width: 6rem;
                            min-width: 5.25rem;
                            text-align: center;
                            padding: 0.5rem 0.625rem;
                            transform: rotate(-90deg);
                            cursor: pointer;
                            border-bottom-right-radius: 0.625rem;
                            border-bottom-left-radius: 0.625rem;
                            white-space: nowrap;
                        }
                        #log .content {
                          position: relative;
                          top: -2rem;
                          width: 98%;
                          max-height: 46vh;
                          overflow: auto;
                        }
                         
                        .highlight {
                          background: rgb(2,0,36) !important;
                          background: linear-gradient(180deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%) !important;
                        }
                         
                        #api {
                            top: 0;
                            height: unset;
                            min-height: 4.55rem;
                            background: aliceblue;
                            padding: 0.625rem;
                        }
                         
                        #api button {
                          margin-left: 0.3rem;
                          margin-bottom: 0.1rem;
                        }
                         
                        #api .label {
                            top: 0;
                            background: aliceblue;
                            color: black;
                            font-weight: bold;
                        }
                         
                        .closed {
                            transform: translateX(-110%);
                        }
                        .pen__none {
                            display: none;
                        }
                         
                        .content::-webkit-scrollbar {
                            width: 15px;
                        }
                         
                        .content::-webkit-scrollbar-track {
                            background: rgba(255, 255, 255, 0.1);
                            border-radius: 15px;
                        }
                         
                        .content::-webkit-scrollbar-thumb {
                            border-radius: 15px;
                            background: rgba(255, 255, 255, 0.15);
                        }
                         
                        `);
                          },
                          addButton: (options = {
                            label: `label`,
                            callback: () => {console.log(`callback`);},
                            target: undefined,
                            class: undefined
                          },
                          deprecated1,
                          deprecated2,
                          ) => {
                            if (typeof options == `string`) {
                                options = {
                                    label: options,
                                    callback: deprecated1,
                                    target: deprecated2,
                                }
                            }
                            if (typeof options.target === 'string') {
                                options.target = document.querySelector(options.target);
                            }
                            if (!options.class) {
                                options.class = `dds__button--mini`;
                            }
                            const btnId = options.label.replace(/[^0-9a-zA-Z]+/, ``);
                            const newBtn = pen.utils.createElement(`button`, {
                                id: btnId,
                              role: `button`,
                              type: `button`,
                              class: `dds__btn dds__btn-primary dds__btn-sm dds__button ${options.class} dds__text-truncate`,
                              text: options.label,
                              style: `margin-right: 0.625rem; margin-bottom: 0.625rem;`,
                            });
                            newBtn.addEventListener(`click`, options.callback);
                            if (!document.getElementById(btnId)) {
                                if (options.target) {
                                    options.target.appendChild(newBtn);
                                } else {
                                  document.querySelector(`body`).prepend(newBtn);
                                }
                            }
                          },
                          whenReady: (callback) => {
                            if (!pen.ready) {
                                setTimeout(() => {
                                    pen.whenReady(callback);
                                }, 100);
                            } else {
                                callback();
                            }
                          },
                          reset: () => {
                            const method = pen.utils.capitalize(pen.utils.dashCamel(demo.selector));
                         
                            // remove element properties
                            document.querySelectorAll(`[data-dds="${demo.selector}"]`).forEach((element) => {
                                element[method] = undefined;
                            });
                         
                            // dispose of components
                            demo.components.forEach(cmp => {
                                cmp.dispose();
                            });
                            demo.components = [];
                         
                            // dispose of library
                            DDS = undefined;
                         
                            // remove pen elements
                            document.getElementById("log").remove();
                            document.getElementById("api").remove();
                         
                            pen.removeLinks();
                            pen.ready = false;
                            pen.showLoader();
                          },
                          showLoader: () => {
                            const penlay = pen.utils.createElement(`div`, {
                              id: `penlay`,
                              style: `
                              background-color: white;
                              overflow:hidden;
                              position:absolute;
                              top:0px;
                              right:0px;
                              bottom:0px;
                              left:0px;
                              z-index: 99999999;
                              `
                            });
                            penlay.innerHTML = `<div class="dds__loading-indicator">
                              <div class="dds__loading-indicator__spinner"></div>
                            </div>`;
                            document.querySelector(`body`).appendChild(penlay);
                          }
                        };
                         
                        pen.showLoader();
                         
                        (() => {
                          pen.addCss();
                          setTimeout(() => {
                            pen.initialize();
                          }, 1000)
                        })();
                    };
                    observers.create([{
                        single: false,
                        selector: obdSelector,
                        callback: obdCallback
                    }]);

                    report.finish();
                },
                avatars: () => {
                    report.start(`user.avatars`);
                    if (!global.prefs.users.avatars.avatarsMod) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    if (global.states.observingAvatars) {
                        report.step(`already observing`);
                        report.abort();
                        return;
                    }
                    global.states.observingAvatars = true;
                    if (!global.states.avatarPingFailed && pingPhoto != null && global.prefs.users.avatars.avatarsSources[0] === `localhost`) {
                        report.step(`Using localhost avatars`);
                        monkey.ping(avatarHost + pingPhoto + imageExt, function callback(response) {
                            if (response === `responded`) {
                                global.states.avatarPingFailed = false;
                            } else {
                                report.step(`avatar ping failed`, false);
                                global.states.avatarPingFailed = true;
                            }
                            // global.mems.avatarPingTimer = moment();
                            // monkey.savePreferences(global.ids.memsName, global.mems);
                        });
                    }

                    let thisName = `none`;

                    const swapAvatar = (element, avatar) => {
                        let inst = element.tagName === `IMG` ? element : avatar.getImgSource(avatar.element);
                        thisName = utils.user.properName(avatar.getNameSource(inst));
                        thisName && utils.user.updateImg(inst, thisName);

                        inst.onerror = () => {
                            const prevSrc = inst.getAttribute(`data-previous`);
                            const noSrc = `http://${avatarHost}!none${imageExt}`
                            inst.src = prevSrc || noSrc;
                        }
                        inst.src = inst.src;
                    };

                    const guessName = (el) => {
                        if (!el) return null;
                        if (el.alt) return el.alt;
                        if (el.getAttribute(`alt`)) return el.getAttribute(`alt`);
                        if (el.getAttribute(`title`)) return el.getAttribute(`title`);
                        if (el.getAttribute(`data-tooltip`)) return el.getAttribute(`data-tooltip`);
                        if (el.innerText) return el.innerText;
                        if (el.parentElement.innerText) return el.parentElement.innerText;
                        if (document.getElementById(`up-user-title-name`)) return document.getElementById(`up-user-title-name`).innerText;
                    };

                    const observerDefinitions = [{
                        single: false,
                        meta: `Hover menu avatars (ie those who voted)`,
                        selector: `.user-hover img`,
                        getImgSource: null,
                        getNameSource: guessName,
                        callback: swapAvatar,
                    }, {
                        single: false,
                        meta: `Releases Page`,
                        selector: `.author-avatar-wrapper img`,
                        getImgSource: null,
                        getNameSource: guessName,
                        callback: swapAvatar,
                    }, {
                        single: false,
                        meta: `Backlog Avatars`,
                        selector: `.ghx-avatar-img`,
                        getImgSource: null,
                        getNameSource: guessName,
                        callback: swapAvatar,
                    }, {
                        single: false,
                        meta: `Item small avatars`,
                        selector: `.aui-avatar-inner img`,
                        getImgSource: null,
                        getNameSource: guessName,
                        callback: swapAvatar,
                    }, {
                        single: false,
                        meta: `Profile Page Edit Avatar`,
                        selector: `#avatar-owner-id`,
                        getImgSource: (el) => { return document.querySelector(`${el} img`); },
                        getNameSource: guessName,
                        callback: swapAvatar,
                    }, {
                        single: false,
                        meta: `User details page, User Icon in Activity Feed`,
                        selector: `span.user-icon img`,
                        getImgSource: null,
                        getNameSource: guessName,
                        callback: swapAvatar,
                    }, {
                        single: false,
                        meta: `User Avatar in Tooltip`,
                        selector: `.avatar-image`,
                        getImgSource: null,
                        getNameSource: guessName,
                        callback: swapAvatar,
                    }];

                    observerDefinitions.forEach(obd => {
                        report.step(`Observing ${obd.selector}`);
                        document.querySelectorAll(obd.selector).forEach(el => {
                            swapAvatar(el, obd);
                        })
                    });

                    observers.create(observerDefinitions);

                    report.finish();
                },
            },
            customize: {
                addFaves: () => {
                    report.start(`customize.addFaves`);
                    if (!global.prefs.customize.mainMenu.menuAdd || !global.prefs.customize.mainMenu.menuAdditions || global.prefs.customize.mainMenu.menuAdditions.length === 0) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const header = document.querySelector(`.aui-header-inner`);
                    if (header && header.querySelectorAll(`.faveLink`).length > 0) {
                        report.step(`already performed`);
                        report.abort();
                        return;
                    }
                    global.prefs.customize.mainMenu.menuAdditions.forEach(fave => {
                        const fButton = utils.createElement(`button`, {
                            'class': `dds__button dds__button--sm faveLink ${fave.class}`,
                            'aria-label': fave.name,
                            'title': fave.name,
                        });
                        fButton.innerHTML = fave.icon ? `<i class="dds__icon dds__icon--${fave.icon}"></i>` : fave.name;
                        fButton.addEventListener(`click`, (e) => {
                            document.location.href = fave.url;
                        });
                        if (header) {
                            header.appendChild(fButton);
                        }
                    })
                    report.finish();
                },
                modMainLogo: () => {
                    report.start(`customize.modMainLogo`);
                    if (!global.prefs.customize.mainLogo.logoMod || global.prefs.customize.mainLogo.logoSrc.length === 0) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const logoEl = document.querySelector(`#logo`);
                    const logoImg = document.querySelector(`#logo img`);
                    if (logoEl.classList.contains(`modModified`)) {
                        report.step(`already performed`);
                        report.abort();
                        return;
                    }
                    logoEl.classList.add(`modModified`);
                    logoEl.style.marginRight = `0`;
                    logoImg.style.width = `32px`;
                    logoImg.src = global.prefs.customize.mainLogo.logoSrc;
                    report.step(`modified main logo`);
                    report.finish();
                },
                modMainMenu: () => {
                    report.start(`customize.modMainMenu`);
                    if (!global.prefs.customize.mainMenu.menuMod || (global.prefs.customize.mainMenu.menuModifications && global.prefs.customize.mainMenu.menuModifications.length < 1)) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const moddedMenus = document.querySelectorAll(`.aui-header-primary a.aui-dropdown2-trigger .modModified`);
                    const menus = document.querySelectorAll(`.aui-header-primary a.aui-dropdown2-trigger:not(.modModified)`);
                    if (menus.length === 0 || moddedMenus.length > 0) {
                        report.step(`already performed`);
                        report.abort();
                        return;
                    }
                    menus.forEach(ahref => {
                        ahref.classList.add(`modModified`);
                        global.prefs.customize.mainMenu.menuModifications.forEach(iconSwap => {
                            if (ahref.innerText.trim().toLowerCase() === iconSwap.text.toLowerCase()) {
                                const newIcon = utils.createElement(`i`, {
                                    class: `dds__icon dds__icon--${iconSwap.icon}`,
                                    title: ahref.innerText,
                                });
                                ahref.innerText = ``;
                                ahref.appendChild(newIcon);
                                report.step(`added icon for ${iconSwap.text}`)
                            }
                        });
                    })
                    report.finish();
                },
                moveComments: () => {
                    report.start(`customize.moveComments`);
                    if (!global.prefs.customize.comments.commentsMove) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }

                    const activityId = `activitymodule`;
                    const addCommentId = `addcomment`;
                    const sidebarId = `viewissuesidebar`;
                    const peopleModuleId = `peoplemodule`;

                    const addComment = document.querySelector(`#${addCommentId}`);
                    const activity = document.querySelector(`#${activityId}`);
                    const sidebar = document.querySelector(`#${sidebarId}`);
                    const peopleModule = document.querySelector(`#${peopleModuleId}`);

                    if (sidebar && !sidebar.querySelector(`#${addCommentId}`)) {
                        sidebar.prepend(addComment);
                        sidebar.prepend(activity);
                        sidebar.prepend(peopleModule);
                        report.step(`comments/activity moved`);
                    } else if (sidebar && sidebar.querySelector(`#${addCommentId}`)) {
                        report.step(`already successful`);
                    } else if (!sidebar) {
                        report.step(`nothing to rearrange`);
                    }
                    report.finish();
                },
                removeLinks: () => {
                    report.start(`customize.removeLinks`);
                    if (!global.prefs.customize.mainMenu.menuRemove || !global.prefs.customize.mainMenu.menuRemovals || global.prefs.customize.mainMenu.menuRemovals.length === 0) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const header = document.querySelector(`.aui-header-inner`);
                    if (header && header.querySelectorAll(`.removedLink`).length > 0) {
                        report.step(`already performed`);
                        report.abort();
                        return;
                    }
                    global.prefs.customize.mainMenu.menuRemovals.forEach(fave => {
                        const removalEl = utils.getElementByTextContent(fave, `a`);
                        if (removalEl) {
                            removalEl.classList.add(`removedLink`);
                            report.step(`removed ${fave}`);
                        }
                    })
                    report.finish();
                },
                shortLists: () => {
                    report.start(`customize.shortLists`);
                    if (!global.prefs.customize.lists.listsShorten) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    if (!global.prefs.account.project.code) {
                        report.step(`Project Code not set up in option`, false);
                        report.abort();
                        return;
                    }
                    const listEls = document.querySelectorAll(`span.issue-link-key`);
                    if (listEls.length > 0) {
                        report.step(`adjusting list display`);
                        listEls.forEach(il => {
                            il.innerText = il.innerText.replace(`${global.prefs.account.project.code}-`, ``);
                        });
                    } else {
                        report.step(`no lists to modify`);
                    }
                    report.finish();
                },
                collapseBotPosts: () => {
                    report.start(`customize.collapseBotPosts`);
                    if (!global.prefs.customize.posts.postsCloseBots) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const posts = document.querySelectorAll(`.activity-comment.expanded`);
                    report.step(`Found ${posts.length} posts`);
                    let moddedCount = 0;
                    posts.forEach(p => {
                        if (!p.classList.contains(`autoCollapsed`) && p.innerText.indexOf(`svc_prddeveloperexp`) > -1) {
                            try {
                                p.querySelector(`button`).click();
                                report.step(`collapsing botPost`);
                                p.classList.add(`autoCollapsed`);
                                moddedCount++;
                            } catch (e) {
                                report.step(`Couldn't collapse. ${e}`);
                            }
                        }
                    })
                    report.step(`Modded ${moddedCount} posts`);
                    report.finish();
                },
                filterTitles: () => {
                    report.start(`customize.filterTitles`);
                    if (!global.prefs.customize.posts.filterTitles) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const filterTitle = (nModule) => {
                        const iconsToAdd = [];
                        let customColor = `#e31c79`; // a hot pink because yes
                        global.prefs.customize.posts.filterTitlesPairs.forEach(pair => {
                            if (pair.color) {
                                customColor = pair.color;
                            }
                            if (nModule.innerText.indexOf(pair.match) > -1) {
                                if (pair.icon) {
                                    nModule.innerText = nModule.innerText.replace(pair.match, ``);
                                    iconsToAdd.push(utils.createElement(`i`, {
                                        class: `dds__icon dds__icon--${pair.icon} dds__mr-1`,
                                        title: pair.match,
                                        style: `color: ${customColor};`,
                                    }));
                                } else {
                                    nModule.innerText = nModule.innerText.replace(pair.match, pair.replace);
                                }
                            }
                        });
                        iconsToAdd.forEach(icon => {
                            nModule.prepend(icon);
                        })
                    };
                    const selectors = [
                        `.ghx-summary`,
                        `.ghx-detail-description`,
                        `#summary-val`,
                        `.ghx-description`,
                    ];
                    selectors.forEach(selector => {
                        document.querySelectorAll(selector).forEach(nModule => {
                            filterTitle(nModule);
                        });
                    });
                    report.finish();
                },
                epicPills: () => {
                    report.start(`customize.epicPills`);
                    if (!global.prefs.customize.posts.epicPills) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    document.querySelectorAll(`.ghx-label:not(.pill)`).forEach(nodeMod => {
                        let acro = utils.acronym(nodeMod.innerText);
                        let version = nodeMod.innerText.match(/(\d{1,}).*(\d{1,})/g);
                        if (!version) version = ``;
                        if (acro.length > 3) {
                            acro = acro.substring(0, 3);
                        }
                        nodeMod.classList.add(`pill`);
                        nodeMod.innerText = acro + version;
                    })
                    report.finish();
                },
                keyLinkPills: () => {
                    report.start(`customize.keyLinkPills`);
                    if (!global.prefs.customize.posts.keyLinkPills) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const notPills = document.querySelectorAll(`.js-key-link:not(.pill)`);
                    notPills.forEach(nodeMod => {
                        let inrText = nodeMod.innerText;
                        nodeMod.classList.add(`pill`);
                        const badge = utils.createElement(`span`, {
                            class: `dds__badge dds__badge--sm dds__badge--gray`,
                        });
                        const label = utils.createElement(`span`, {
                            class: `dds__badge__label`,
                        });
                        label.innerText = inrText.replace(`${global.prefs.account.project.code}-`, ``);
                        nodeMod.innerText = ``;
                        badge.appendChild(label)
                        nodeMod.appendChild(badge)
                    })
                    report.finish();
                },
            },
            autofill: {
                defect: () => {
                    report.start(`autofill.defect`);
                    let fields = [];
                    if (!global.prefs.automation.forms.formsFill || global.prefs.automation.forms.formsSelectors.length === 0) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    global.prefs.automation.forms.formsSelectors.forEach(afill => {
                        if (!afill.identifier || document.querySelector(afill.identifier)) {
                            fields = [
                                ...fields,
                                ...afill.fields,
                            ]
                        } else {
                            report.step(`${afill.identifier} not open at this time`);
                        }
                    });
                    if (fields.length > 0) {
                        fields.forEach(field => {
                            const fieldEl = document.getElementById(field.id);
                            if (fieldEl && (fieldEl.value.length === 0 || fieldEl.value === `-1`)) {
                                fieldEl.value = field.value;
                            } else {
                                report.step(`already done for ${field.id}`);
                            }
                        });
                    }
                    report.finish();
                },
                searchbar: () => {
                    report.start(`autofill.searchbar`);
                    if (!global.prefs.automation.searchbar.searchFill) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    if (!global.prefs.account.project.name) {
                        report.step(`Project Name not set up in option`, false);
                        report.abort();
                        return;
                    }
                    if (window.location.href.indexOf(`issues/`) < 0) {
                        report.step(`No need to run on this page`);
                        report.abort();
                        return;
                    }
                    const projectEl = document.querySelector(`div[data-id="project"]:not(.active)`);
                    if (projectEl && projectEl.innerText.match(/All/g)) {
                        report.step(`populating project selection`);
                        projectEl.click();
                        setTimeout(() => {
                            const projectOptionEl = document.querySelector(`label[title="${global.prefs.account.project.name}"]`);
                            projectOptionEl.click();
                            projectEl.click();
                        }, 500);
                    } else {
                        report.step(`Unable to set project selection`, false);
                    }
                    report.finish();
                },
            },
            append: {
                addKeys: () => {
                    report.start(`interface.addKeys`);
                    if (!global.prefs.append.keys.useKeys) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    if (global.states.keysAdded) {
                        report.step(`already added`);
                        report.abort();
                        return;
                    }
                    global.states.keysAdded = true;
                    let modalApi;
                    let dropdownApi;

                    const handleDropdownChange = (e) => {
                        if (!e.detail.value) {
                            return;
                        }
                        monkey.copyTextToClipboard(e.detail.value);
                        dropdownApi.clearSelection();
                        modalApi.close();
                    };
                    const defineShortcut = (userDefinedShortcut) => {
                        let keyMeta = {
                            ctrl: undefined, // only used to parse, later
                            control: false,
                            shift: false,
                            alt: false,
                        };

                        let shortcut = userDefinedShortcut;
                        if (shortcut.match(/control/gi)) {
                            keyMeta.control = true;
                        }
                        if (shortcut.match(/ctrl/gi)) {
                            keyMeta.control = true;
                        }
                        if (shortcut.match(/shift/gi)) {
                            keyMeta.shift = true;
                        }
                        if (shortcut.match(/alt/gi)) {
                            keyMeta.alt = true;
                        }
                        Object.keys(keyMeta).forEach(key => {
                            // const regcheck = new RegExp(key, 'gi');
                            // if (shortcut.match(regcheck)) {
                            //     keyMeta[key] = true;
                            // }
                            shortcut = shortcut.toUpperCase().replace(key.toUpperCase(), ``);
                        });
                        shortcut = shortcut.replace(/-/gi, ``);
                        shortcut = shortcut.replace(/\+/gi, ``).trim();
                        shortcut = shortcut.charCodeAt(0);
                        return {
                            value: shortcut,
                            ...keyMeta,
                        };
                    };
                    const showTemplateMenu = () => {
                        modalApi.open();
                    };
                    const addTemplateModal = () => {
                        const sId = `templatesModal`;
                        const sModal = utils.createElement(`div`, {
                            id: sId,
                            role: `dialog`,
                            class: `dds__modal ddsc__modal`,
                            'aria-labelledby': `${sId}-header`
                        });
                        const sContent = utils.createElement(`div`, {
                            class: `dds__modal__content`,
                        });
                        const sHeader = utils.createElement(`div`, {
                            class: `dds__modal__header`,
                        });
                        const sH3 = utils.createElement(`h3`, {
                            id: `${sId}-header`,
                            class: `dds__modal__title`,
                            innerText: `Select a template to copy`,
                        });
                        const sBody = utils.createElement(`div`, {
                            id: `${sId}-body`,
                            class: `dds__modal__body`,
                            style: `min-height: 50vh;`,
                        });
                        addTemplateDropdown().then((newDropdown) => {
                            sModal.appendChild(sContent);
                            sContent.appendChild(sHeader);
                            sContent.appendChild(sBody);
                            sHeader.appendChild(sH3);
                            sBody.appendChild(newDropdown);
                            document.querySelector(`body`).appendChild(sModal);
                            setTimeout(() => {
                                const el = document.getElementById(sId).querySelector(`[data-dds="dropdown"]`);
                                dropdownApi = DDS.Dropdown(el);
                                modalApi = DDS.Modal(document.getElementById(sId));
                                dropdownApi.element.addEventListener(`ddsDropdownSelectionChangeEvent`, handleDropdownChange)
                            });
                        });
                    };
                    const addTemplateDropdown = async () => {
                        const dId = `templateDropdown`;
                        const dDropdown = utils.createElement(`div`, {
                            id: dId,
                            class: `dds__dropdown`,
                            'data-dds': `dropdown`,
                            'data-selection': `single`,
                        });
                        const dContainer = utils.createElement(`div`, {
                            class: `dds__dropdown__input-container`,
                        });
                        const dLabel = utils.createElement(`label`, {
                            id: `${dId}-label`,
                            for: `${dId}-input`,
                            innerText: `Select a template`,
                        });
                        const dInputWrapper = utils.createElement(`div`, {
                            class: `dds__dropdown__input-wrapper`,
                            autocomplete: `off`,
                            role: `combobox`,
                            'aria-haspopup': `listbox`,
                            'aria-controls': `${dId}-popup`,
                            'aria-expanded': `false`,
                        });
                        const dInput = utils.createElement(`input`, {
                            id: `${dId}-input`,
                            name: `${dId}-name`,
                            type: `text`,
                            class: `dds__dropdown__input-field`,
                            autocomplete: `off`,
                            'aria-labelledby': `${dId}-label ${dId}-helper`,
                            'aria-expanded': `false`,
                            'aria-controls': `${dId}-list`,
                        });
                        const dSmall = utils.createElement(`small`, {
                            id: `${dId}-helper`,
                            class: `dds__input-text__helper`,
                        });
                        const dPopup = utils.createElement(`div`, {
                            id: `${dId}-popup`,
                            class: `dds__dropdown__popup dds__dropdown__popup--hidden`,
                            role: `presentation`,
                            tabindex: `-1`,
                        });
                        const templateDropdown = addTemplateOptions(dId).then((responseEl) => {
                            dDropdown.appendChild(dContainer);
                            dContainer.appendChild(dLabel);
                            dContainer.appendChild(dInputWrapper);
                            dInputWrapper.appendChild(dInput);
                            dInputWrapper.appendChild(dSmall);
                            // dInputWrapper.appendChild(dFeedback);
                            dContainer.appendChild(dPopup);
                            dPopup.appendChild(responseEl);
                            return dDropdown;
                        });
                        return templateDropdown;
                    };
                    const addTemplateOptions = async (dropdownId) => {
                        const dUl = utils.createElement(`ul`, {
                            id: `${dropdownId}-list`,
                            class: `dds__dropdown__list`,
                            role: `listbox`,
                            tabindex: `-1`,
                        });
                        const dansJsonServerToken = `3579afef7ef98a51d0c37ac6de3e2c93`;
                        const urlPart = `jiradork`;
                        const xhrRequest = await utils.xhrRequest(`GET`, `https://api.jsonserver.io/${urlPart}`, undefined, dansJsonServerToken);
                        JSON.parse(xhrRequest.response).templates.forEach(template => {
                            let templateOption = template.option.replace(`INVALID_EXPRESSION: `, ``);
                            let templateSnippet = template.snippet.replace(`INVALID_EXPRESSION: `, ``);
                            const dLi = utils.createElement(`li`, {
                                class: `dds__dropdown__item`,
                                role: `none`,
                            });
                            const dButton = utils.createElement(`button`, {
                                type: `button`,
                                class: `dds__dropdown__item-option`,
                                role: `option`,
                                tabindex: `-1`,
                                'data-selected': `false`,
                                'data-value': templateSnippet,
                            });
                            const dSpan = utils.createElement(`span`, {
                                class: `dds__dropdown__item-label`,
                                innerText: templateOption,
                            });
                            dUl.appendChild(dLi);
                            dLi.appendChild(dButton);
                            dButton.appendChild(dSpan);
                        });

                        /*
                            const finishUpdating = (genRes) => {
                                genRes = JSON.parse(genRes);
                                const result = genRes.gender ? genRes.gender : `initials`;
                                templateSrc = templateSrc.replace(global.prefs.users.avatars.avatarsType, result);
                                templateSrc = encodeURI(templateSrc.substr(templateSrc.indexOf('http'), (templateSrc.indexOf('alt=') - templateSrc.indexOf('http')) - 2));
                                img.setAttribute('src', templateSrc);
                            };
                        */
                        return dUl;
                    };
                    const handleKeyUp = (e) => {
                        const thisKey = defineShortcut(global.prefs.append.keys.templateMenu);
                        var keyCodes = [
                            {
                                value: thisKey.value,
                                ctrl: thisKey.control,
                                alt: thisKey.alt,
                                shift: thisKey.shift,
                                method: showTemplateMenu,
                            }
                        ];

                        keyCodes.forEach(code => {
                            if (e.keyCode === code.value && e.ctrlKey === code.ctrl && e.altKey === code.alt) {
                                code.method.call();
                            };
                        });
                    };

                    addTemplateModal();
                    document.removeEventListener(`keyup`, handleKeyUp);
                    document.addEventListener(`keyup`, handleKeyUp);

                    report.finish();
                },
                copyLink: () => {
                    report.start(`append.copyLink`);
                    if (!global.prefs.append.copyLinks.copyLinksAdd) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const linkSelectors = [
                        `.aui-nav-breadcrumbs .issue-link`,
                        `.issuerow .summary .issue-link`,
                        `a.js-key-link`,
                        `a[title="View this issue"]`
                    ];
                    const links = [];
                    linkSelectors.forEach(linkSelector => {
                        const linkElement = document.querySelectorAll(linkSelector);
                        links.push(linkElement);
                    });
                    const appendLink = (ilink) => {
                        if (ilink.parentElement.querySelectorAll(`.copyLink`).length > 0) {
                            report.step(`Already added for ${ilink.classList}`);
                            return;
                        }
                        const cicon = utils.createElement(`i`, {
                            class: `dds__icon dds__icon--copy-alt`
                        });
                        const clink = utils.createElement(`span`, {
                            class: `copyLink`,
                            title: `Copy link as Markdown. Double-click to copy for Gitlab TEXT`,
                        });
                        clink.appendChild(cicon);
                        clink.addEventListener(`click`, (e) => {
                            const liEl = e.target.closest(`li`);
                            const breadcrumbEl = e.target.closest(`.aui-nav-breadcrumbs`);
                            const summaryEl = document.getElementById(`summary-val`);
                            const sidebarEl = e.target.closest(`#ghx-detail-contents`);
                            const cardEl = e.target.closest(`.js-issue`);
                            const inSidebar = sidebarEl != null;
                            const inDraggable = cardEl != null;
                            const onDetailPage = window.location.href.indexOf(`browse`) > -1 || window.location.href.indexOf(`issues/`) > -1;
                            let linkText;
                            if ((onDetailPage && liEl && liEl.querySelector(`#parent_issue_summary`)) && ilink.getAttribute(`title`)) {
                                linkText = ilink.getAttribute(`title`); // parent issue (ie we're looking at a Task of a Story)
                            } else if (summaryEl && (onDetailPage || inSidebar)) {
                                linkText = summaryEl.innerText;
                            } else if (inDraggable) {
                                linkText = cardEl.querySelector(`.ghx-summary`).innerText;
                            } else {
                                linkText = ilink.innerText;
                            }
                            linkText = linkText.replace(/\[/g, `#`).replace(/]/g, ``); // convert bracketed text to #tags
                            const textToCopy = `[${linkText}](${ilink.href})`;
                            monkey.copyTextToClipboard(textToCopy);
                        });
                        clink.addEventListener(`dblclick`, () => {
                            let linkText = `JIRA#${ilink.innerText};`;
                            const linkTextSpaceIndex = linkText.indexOf(` `);
                            if (linkTextSpaceIndex > -1) {
                                linkText = linkText.substring(0, linkTextSpaceIndex) + `;`;
                            }
                            const textToCopy = linkText; // `[${linkText}](${ilink.href})`;
                            monkey.copyTextToClipboard(textToCopy);
                        });
                        ilink.parentElement.appendChild(clink);
                        report.step(`added copyLink for ${ilink}`);
                    };
                    links.forEach(nodeList => {
                        nodeList.forEach(ilink => appendLink(ilink));
                    });
                    if (links.length === 0) {
                        report.step(`no link modification needed`);
                    }
                    report.finish();
                },
                swimButtons: () => {
                    report.start(`append.swimButtons`);
                    if (!global.prefs.append.collapseButtons.collapseButtonsAdd) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    document.querySelectorAll(`.monkeyButton`).forEach(mb => {
                        mb.classList.remove(`dds__d-none`);
                    });
                    if (window.location.href.indexOf(`chart=`) > 0) {
                        document.querySelectorAll(`.monkeyButton`).forEach(mb => {
                            mb.classList.add(`dds__d-none`);
                        });
                        report.step(`not needed on chart page`);
                        report.abort();
                        return;
                    }
                    if (document.querySelector(`.collapseAllLanes`)) {
                        report.step(`swimlane controls already created`);
                        report.abort();
                        return;
                    }

                    // ADD COLLAPSE BUTTON
                    const filterControlEl = document.querySelector(`#ghx-operations`);
                    if (!filterControlEl) {
                        report.step(`nothing to attach controls`);
                        report.abort();
                        return
                    }
                    const btnCollapseLanes = utils.createElement(`button`, {
                        class: `dds__button dds__button--sm monkeyButton collapseAllLanes`,
                    });
                    btnCollapseLanes.innerText = `Collapse`;
                    const handleCollapse = () => {
                        document.querySelectorAll(`.ghx-swimlane:not(.ghx-closed)`).forEach(lane => {
                            lane.querySelector(`.ghx-heading-expander`).click();
                        });
                        document.querySelectorAll(`.ghx-expander:not([aria-expanded="false"]`).forEach(sprint => {
                            sprint.click();
                        });
                    };
                    btnCollapseLanes.addEventListener(`click`, handleCollapse);
                    filterControlEl.prepend(btnCollapseLanes);

                    // ADD EXPAND BUTTON
                    const btnExpandLanes = utils.createElement(`button`, {
                        class: `dds__button dds__button--sm monkeyButton expandAllLanes`,
                    });
                    btnExpandLanes.innerText = `Expand`;
                    const handleExpand = () => {
                        document.querySelectorAll(`.ghx-closed`).forEach(lane => {
                            lane.querySelector(`.ghx-heading-expander`).click();
                        });
                        document.querySelectorAll(`.ghx-expander:not([aria-expanded="true"]`).forEach(sprint => {
                            sprint.click();
                        });
                    };
                    btnExpandLanes.addEventListener(`click`, handleExpand);
                    filterControlEl.prepend(btnExpandLanes);

                    report.finish();
                },
                voteButtons: () => {
                    report.start(`append.voteButtons`);
                    if (!global.prefs.append.voteButtons.voteButtonsAdd) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const issueRows = document.querySelectorAll(`.issuerow`);
                    if (issueRows.length === 0) {
                        report.step(`no rows to add vote buttons to`);
                        report.abort();
                        return;
                    }
                    issueRows.forEach(row => {
                        const rowActionCell = row.querySelector(`.qrf-issue-actions`);
                        const issueId = row.getAttribute(`data-issue-key`);
                        if (rowActionCell && !rowActionCell.querySelector(`.addVoteBtn`)) {
                            const btnIcon = utils.createElement(`i`, {
                                class: `dds__icon dds__icon--add-cir`,
                                style: `pointer-events: none;`,
                                title: `Add your vote for this issue`,
                            });
                            const btn = utils.createElement(`button`, {
                                class: `dds__button dds__button--mini addVoteBtn`,
                            });
                            btn.appendChild(btnIcon);
                            btn.addEventListener(`click`, (e) => {
                                const dotMenu = e.target.parentElement.querySelector(`.qrf-issue-actions-trigger`);
                                dotMenu.click();
                                setTimeout(() => {
                                    monkey.getContainer({
                                        'el': `.qrf-ia-vote-issue`,
                                        'max': 100,
                                        'spd': 1000
                                    }).then(function (el) {
                                        el.click();
                                    });
                                }, 500);
                            });
                            rowActionCell.appendChild(btn);
                        } else {
                            report.step(`already added vote buttons`);
                        }
                    });
                    report.finish();
                },
                scrumButtons: () => {
                    report.start(`append.scrumButtons`);
                    if (!global.prefs.append.scrumButtons.scrumButtonsAdd) {
                        report.step(`declined via prefs`);
                        report.abort();
                        return;
                    }
                    const filterControlEl = document.querySelector(`#ghx-operations`);
                    if (!filterControlEl) {
                        report.step(`nothing to attach controls`);
                        report.abort();
                        return
                    }
                    if (document.querySelector(`.scrumButton`)) {
                        report.step(`already added`);
                        report.abort();
                        return;
                    }

                    let currentPeep = 0;
                    let peeps = [];
                    let todaysPeeps = [];

                    const selectNextPeep = () => {
                        if (currentPeep === todaysPeeps.length && todaysPeeps.length > 0) {
                            stopScrum();
                            return;
                        }
                        if (document.querySelectorAll(`[data-scrum="${currentPeep}"]`).length === 0) {
                            clearOtherFilters();
                        }
                        let finalDelay = 0;
                        let msDelay = 400;
                        const peepEls = document.querySelectorAll(`[title^="assignee"]`);
                        const activePeepEls = document.querySelectorAll(`.ghx-active[title^="assignee"]`);
                        if (peeps.length === 0) {
                            peepEls.forEach(el => {
                                peeps.push(el.innerText.trim());
                            });
                        }
                        if (todaysPeeps.length === 0) {
                            todaysPeeps = utils.shuffle(peeps);
                            todaysPeeps.forEach((peep, i) => {
                                peepEls.forEach(el => {
                                    if (el.innerText.trim() === peep) {
                                        const numspan = utils.createElement(`span`, {
                                            innerText: ` ${i + 1}`,
                                            class: `peepNumber`,
                                        });
                                        el.appendChild(numspan);
                                        el.setAttribute(`data-scrum`, i);
                                    }
                                });
                            })
                        }
                        activePeepEls.forEach((b, i) => { // deselect all active
                            setTimeout(() => {
                                b.click();
                            }, i * msDelay);
                            finalDelay++;
                        });

                        setTimeout(() => {
                            const currentPeepButton = document.querySelector(`[data-scrum="${currentPeep}"]`)
                            addCurrentPeepNextButton(currentPeep);
                            currentPeepButton.click();
                            currentPeep++;
                        }, finalDelay * msDelay);
                    };

                    const addCurrentPeepNextButton = (currentPeep) => {
                        const currentPeepButton = document.querySelector(`[data-scrum="${currentPeep}"]`);
                        const isAtEnd = currentPeep === document.querySelectorAll(`[title^="assignee"]`).length - 1;
                        const btnIcon = isAtEnd ? `close-x` : `chevron-right`;
                        document.querySelectorAll(`.nextPeepButton`).forEach(npb => npb.remove());
                        const newNextPeepButton = utils.createElement(`button`, {
                            class: `dds__button dds__button--sm nextPeepButton`,
                        });
                        const npbIcon = utils.createElement(`i`, {
                            class: `dds__icon dds__icon--${btnIcon}`,
                            style: `min-height: 1.5rem; padding: 0.3rem 0.3rem 0 0.3rem;`
                        });
                        newNextPeepButton.appendChild(npbIcon);
                        currentPeepButton.parentElement.appendChild(newNextPeepButton);
                        if (!isAtEnd) {
                            newNextPeepButton.addEventListener(`click`, selectNextPeep);
                        } else {
                            newNextPeepButton.addEventListener(`click`, stopScrum);
                        }
                    };

                    const clearOtherFilters = () => {
                        let msDelay = 40;
                        const activeFilters = document.querySelectorAll(`.ghx-active:not([title^="assignee"])`);
                        activeFilters.forEach((el, i) => {
                            setTimeout(() => {
                                el.click();
                            }, i * msDelay);
                        });
                    };

                    const stopScrum = () => {
                        document.querySelector(`.nextPeepButton`).remove();
                        document.querySelectorAll(`.peepNumber`).forEach(el => el.remove());
                        document.querySelectorAll(`[title^="assignee"]`).forEach(el => {
                            if (el.classList.contains(`ghx-active`)) {
                                el.click();
                            }
                            el.removeAttribute(`data-scrum`);
                        });
                        currentPeep = 0;
                        peeps = [];
                        todaysPeeps = [];
                    }

                    const btnScrum = utils.createElement(`button`, {
                        class: `dds__button dds__button--sm scrumButton`,
                    });
                    btnScrum.innerText = `Scrum`;
                    const iconScrum = utils.createElement(`i`, {
                        class: `dds__icon dds__icon--refresh-spin`,
                        style: `min-height: 1.5rem; padding: 0.3rem 0.3rem 0 0.3rem;`
                    });
                    btnScrum.appendChild(iconScrum);

                    btnScrum.addEventListener(`click`, selectNextPeep);
                    filterControlEl.prepend(btnScrum);

                    report.finish();
                },
            }
        };

        (function () { // Global Functions
            let runOnThisSite = false;
            global.prefs.account.urls.runOn.forEach(runUrl => {
                if (window.location.href.indexOf(runUrl) > -1) {
                    runOnThisSite = true;
                }
            });
            if (runOnThisSite) {
                document.onmousemove = function () {
                    if (!global.states.isMouseMoved) {
                        global.states.isMouseMoved = true;
                        setTimeout(function () {
                            global.states.isMouseMoved = false;
                        }, global.constants.TIMEOUT * 2);
                        if (global.prefs.account.enabled.isEnabled) {
                            utils.initScript();
                        }
                    }
                };
            }
        })(); // Global Functions
    })();

});