brobada / My CPQ Tweaks

// ==UserScript==
// @name         My CPQ Tweaks
// @version      0.736
// @description  My CPQ Tweaks
// @author       Obada Kadri
// @match        *://*.bigmachines.com/*
// @grant        GM_addStyle
// @license      MIT
// @require     https://code.jquery.com/jquery-3.4.1.min.js
// @require     https://momentjs.com/downloads/moment.js
// ==/UserScript==

// Extend jQuery contains to have case insensitive option (conaintsi)
$.extend($.expr[':'], {
  'containsi': function(elem, i, match, array)
  {
    return (elem.textContent || elem.innerText || '').toLowerCase()
    .indexOf((match[3] || "").toLowerCase()) >= 0;
  }
});

var iframe;
var bodyHasNoText = (document.body.innerText.length === 0);

// Auto refresh
// Only for deployment center
var dCenters = ["schedule_deployment.jsp", "schedule_events.jsp"];
if (dCenters.indexOf(document.URL.split("/").pop().split("?").shift()) >= 0) {
	var refreshBtn = document.querySelector("[name='refresh']");
	var timer = 10; // in seconds
	var autoRefresh = true;
	var note, btn, refreshInterval, timeout;

	var toggleAutoRefresh = function () {
		autoRefresh = !autoRefresh;

		if (autoRefresh) {
			startAutoRefresh();
		} else {
			btn.innerHTML = "Auto Refresh Off";
			timer = 30;
			clearInterval(refreshInterval);
			clearTimeout(timeout);
		}
	};

	var startAutoRefresh = function () {
		btn.innerHTML = "Auto refreshing in " + timer + "...";
		refreshInterval = setInterval(function () {
			var text;
			if (--timer > 0) {
				text = "Auto refreshing in " + timer + "...";
			} else {
				text = "Auto refreshing now...";
			}
			btn.innerHTML = text;
		}, 1000);

		timeout = setTimeout(function () {
			refreshBtn.click();
		}, 1000 * timer);
	};


	if (refreshBtn) {
		note = document.createTextNode("Auto refreshing in " + timer + "...");
		btn = document.createElement("button");
		btn.className += " auto-refresh";
		btn.addEventListener("click", toggleAutoRefresh);

		btn.appendChild(note);

		document.body.appendChild(btn);

		if (autoRefresh) {
			startAutoRefresh();
		}
	}

	// Delete a history entry in deployment center if more than 5 entries
	var KEEP_ENTRIES = 5;
	var historyEntries = document.querySelectorAll("tr[class^='bgcolor-list-']");
	Array.from(historyEntries).forEach(function (el, i) {
		if (i < historyEntries.length - KEEP_ENTRIES) {
			el.querySelector("input[type=\"checkbox\"]").click();
		}
	});
	if (historyEntries.length > KEEP_ENTRIES) {
		document.getElementById("delete").click();
	}
}

// Append SSO link
if (document.querySelector("#login-form .login-links")) {
	var sspSpan = document.createElement("span");
	var ssoLink = document.createElement("a");
	var ssoText = document.createTextNode("SSO Login");
	sspSpan.classList.add("login-link");
	ssoLink.classList.add("commerce-sidebar-item");
	ssoLink.setAttribute("href", "/sso/saml_request.jsp");
	ssoLink.appendChild(ssoText);
	sspSpan.appendChild(ssoLink);
	document.querySelector("#login-form .login-links").appendChild(sspSpan);
}

// Konami Kickass
if (window.addEventListener) {
	var kkeys = [],
		konami = "38,38,40,40,37,39,37,39,66,65";
	window.addEventListener("keydown", function (e) {
		kkeys.push(e.keyCode);
		if (kkeys.toString().indexOf(konami) >= 0) {
			var s = document.createElement("script");
			s.type = "text/javascript";
			document.body.appendChild(s);
			s.src = "//hi.kickassapp.com/kickass.js";

			kkeys = [];
		}
	}, true);
}

// Prevent Timeout (auto clicks on 'Continue Working' button)
setInterval(function () {
	var timeOutPopup = document.getElementById("session-timeout-popup");
	var timeOutBtn = document.getElementById("reset-session");
	if (timeOutPopup && timeOutPopup.style.display != "none") {
		timeOutBtn.click();
	}
}, 30000);


// Hightlight Config util functions in migration tool
// Set listeners everytime the DOM has something added or removed
const migrationTree = document.body; //document.getElementById("nav-tree");
if (migrationTree) {
    let mutationObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.type === "childList"
                && $("#nav-tree_HLD-0-0 .x-tree3-node") // The utils folder is visible
                && $("#nav-tree_HLD-0-0 .x-item-expanded").length > 0 // The utils folder is expanded
                && !$("#nav-tree_HLD-0-0 .x-item-expanded").hasClass("x-item-checkbox-gone") // The utils folder has a checkbox (Usually no checkbox if no mode)
               ) {
                // Other util functions
                $("#nav-tree_HLD-0-0 .x-tree3-node .x-tree3-node-text")
                    .filter((i, el) => $(el).text().indexOf("HWSW") < 0 && $(el).text().indexOf("C:") < 0)
                    .closest(".x-tree3-node").addClass("other-util");
                // In-sync utils
                $("#nav-tree_HLD-0-0 .x-tree3-node .x-tree3-node-text")
                    .filter((i, el) => $(el).siblings(".x-tree3-node-icon").attr("style").indexOf("tick.png") >= 0)
                    .closest(".x-tree3-node").addClass("in-sync-util");
                // HWSW
                $("#nav-tree_HLD-0-0 .x-tree3-node .x-tree3-node-text")
                    .filter((i, el) => $(el).text().indexOf("HWSW") >= 0)
                    .closest(".x-tree3-node").addClass("hwsw-util");
                // CC:
                $("#nav-tree_HLD-0-0 .x-tree3-node .x-tree3-node-text")
                    .filter((i, el) => $(el).text().indexOf("CC:") >= 0)
                    .closest(".x-tree3-node").addClass("cc-util");
                // C:
                $("#nav-tree_HLD-0-0 .x-tree3-node .x-tree3-node-text")
                    .filter((i, el) => $(el).text().indexOf("C:") >= 0 && !$(el).closest(".x-tree3-node").hasClass("cc-util"))
                    .closest(".x-tree3-node").addClass("c-util");
            }
        });
    });

    mutationObserver.observe(migrationTree, {
                             attributes: true,
                             characterData: true,
                             childList: true,
                             subtree: true,
                             attributeOldValue: true,
                             characterDataOldValue: true
                             });
}

// Show Hidden Grid
const hiddenGridInterval = setInterval(() => {
    const hiddenGrid = document.querySelector('.hidden-grid');
    if (hiddenGrid) {
        clearInterval(hiddenGridInterval);
        const showHiddenGrid = () => {
            hiddenGrid.style.display = "block";
        }

        let hdnGridBtn = document.createElement("button");
        hdnGridBtn.classList.add("btn-show-hidden-grid");
        let hdnGridTxt = document.createTextNode("Show Hidden Grid");
        hdnGridBtn.appendChild(hdnGridTxt);
        document.body.appendChild(hdnGridBtn);

        hdnGridBtn.addEventListener("click", (e) => {
            showHiddenGrid();
            e.target.style.display = "none";
        });

    }
}, 500);

// Attributes Serializations + JSON pretty print
// Only for pipeline viewer
var pipelineViewerUrls = ["pipeline_viewer_config.jsp"];
if (pipelineViewerUrls.indexOf(document.URL.split("/").pop().split("?").shift()) >= 0) {
    const serializeState = (el, label) => {
        if (el.length) {
            const stateTxt = el.closest("tr").children().eq(1).text();
            if (stateTxt.indexOf("$$$") >= 0) {
                let allParts = {};
                stateTxt.split("$$$")
                    .forEach(apt => {
                    const apa = apt.split(":::");
                    let apVal = apa[1] || '';
                    if (apVal.indexOf("@@") >= 0) {
                        apVal = apVal.split("@@").map(val => {
                            if (val.indexOf("+++") >= 0) {
                                const objVal = {};
                                val.split("+++").forEach(tva => {
                                    const subValArr = tva.split("===");
                                    objVal[subValArr[0]] = subValArr[1];
                                });
                                return objVal;
                            }
                            return val;
                        });
                    } else if (apVal.indexOf("+++") >= 0) {
                        const objVal = {};
                        apVal.split("+++").forEach(tva => {
                            const subValArr = tva.split("===");
                            objVal[subValArr[0]] = subValArr[1];
                        });
                        apVal = objVal;
                    }
                    if (apa[0].trim()) allParts[apa[0].trim()] = apVal;
                });
                console.log(label, allParts);
            }
        }
    };

    const stateLabels = ['All Parts State', 'Cache Date State', 'Quote State', 'Selected Parts State', 'Filters State', 'Search State', 'Messages State'];
    stateLabels.forEach(stateLabel => serializeState($(`tr td.form-label:first-child:contains("${stateLabel}")`), stateLabel));

    // pretty print
    Array.from(document.querySelectorAll(`#tab_attributes tr`)).forEach(c => {
    	const attrLabelEl = c.querySelector("td.form-label");
    	const attrValEl = c.querySelector("td.form-input");
        const attrVal = attrValEl?.innerText;
    	if (attrVal?.startsWith("{")) {
    		try {
    			attrValEl.innerHTML = "<textarea rows='4' cols='50'>" + JSON.stringify(JSON.parse(attrVal), undefined, 2) + "</textarea>";
    		} catch (e) {
    			console.info("skipped invalid JSON", attrLabelEl.innerText);
    		}
    	}

        if (attrVal?.length > 5) {
            const copyBtn = document.createElement("button");
            copyBtn.innerText = "Copy to clipboard";
            copyBtn.classList.add("copy-btn");
            copyBtn.addEventListener('click', e => {
                e.preventDefault();
                navigator.clipboard.writeText(attrVal);
            });
            attrLabelEl?.append(copyBtn);
        }
});

    const headerTitles = ["Attribute Name", "Attribute Value", "Input to Rules"];
    headerTitles.forEach((headerTitle, i) => {
        const attrNameHead = $(`#tab_attributes td.view-header:contains("${headerTitle}")`);
        const searchAttrEl = $(`<input type='text' class='pipeline-search' placeholder='${headerTitle}' ${i==0 ? 'autofocus' : ''} />`);
        searchAttrEl.keyup(se => {
            const currentEl = $(se.target);
            $(".pipeline-search").not(currentEl).val('');
            $(`#tab_attributes tr.bgcolor-form`).addClass('hidden-row');
            if (i == 0) {
                // Search labels colum
                $(`tr td.form-label:containsi("${searchAttrEl.val()}")`).each((i, e) => $(e).closest('tr').removeClass("hidden-row"));
            } else {
                // Search other columns
                $(`tr td.form-input:nth-child(${i+1}):containsi("${searchAttrEl.val()}")`).each((i, e) => $(e).closest('tr').removeClass("hidden-row"));
            }
        });
        attrNameHead.html(searchAttrEl);
    });

}



// Styling Stuff
var bm_css_src = `
/* Stupid Field: fix to text box that is completely covered by scroll bar in firefox */
.x-form-field-wrap.x-component,
.x-small-editor, .x-grid-editor
{
	overflow: hidden !important;
}
/* Force enable all homepage tabs */
#mainnav li {
	display: inline !important;
}
button.auto-refresh {
	position: fixed;
	bottom: 0;
	left: 5em;
	color: #fff;
	background-color: #d9534f;
	border-color: #d43f3a;
	display: inline-block;
	padding: 3px 6px;
	margin-bottom: 0;
	font-size: 11px;
	text-align: center;
	white-space: nowrap;
	vertical-align: middle;
	-ms-touch-action: manipulation;
	cursor: pointer;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
	background-image: none;
	border: 1px solid transparent;
	border-radius: 5px 5px 0 0;
}
ul.features-menu {
	position: fixed;
	left: -71%;
	bottom: 0;
	width: 70%;
	transition: left .3s ease-in-out;
	margin: 0 0 10px 25px;
}
ul.features-menu.expanded {
	left: 100px;
}
ul.features-menu li {
	display: inline-block;
	border: 2px solid red;
	border-radius: 3px;
	cursor: pointer;
	padding: 3px;
	margin: 3px;
	color: #777;
	background-color: #fff;
	box-shadow: 0 0 3px 1px #888;
}
ul.features-menu li.enabled {
	border: 2px solid green;
	color: #000;
}
ul.features-menu li.btn-expand {
	float: right;
	position: absolute;
	bottom: 0;
	right: -35px;
	width: 16px;
	height: 16px;
	font-size: 3em;
	line-height: .3;
}
.btn-show-hidden-grid {
    position: fixed;
    bottom: 0;
    right: 10%;
    translate: 50%;
    background: rgba(90, 90, 90, 0.3);
    border: 1px solid #ccc;
    font-size: 12px;
    padding: 5px 10px;
    cursor: pointer;
    z-index: 999;
}
iframe.ext-shim,
tr.hidden-row {
    display: none !important;
}
.hwsw-util .x-tree3-node-text,
.cc-util .x-tree3-node-text,
.c-util .x-tree3-node-text {
    font-weight: bold;
}
.hwsw-util {
    background-color: rgba(50, 50, 250, 0.5);
}
.cc-util {
    background-color: rgba(250, 50, 50, 0.5)
}
.c-util {
    background-color: rgba(50, 250, 50, 0.5);
}
.other-util, .in-sync-util {
    opacity: 0.5;
}
td.view-header {
    text-align: center;
}
td.form-input {
    max-width: 700px;
    overflow-wrap: break-word;
}
button.copy-btn {
    margin: 1em 0 1em 1em;
    background: none;
    border: 1px solid #ccc;
    cursor: pointer;
}
`;

// eslint-disable-next-line no-undef
GM_addStyle(bm_css_src);