brobada / CPQ Model Parts

// ==UserScript==
// @name        CPQ Model Parts
// @namespace   https://github.com/okadri/bmTweaks
// @version     0.63
// @description CPQ Model Parts
// @author      Obada Kadri
// @match       *://*.bigmachines.com/commerce/new_equipment/products/model_configs.jsp*
// @require     https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js
// @require     https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js
// @grant       GM_addStyle
// ==/UserScript==

function cssElement(url) {
  var link = document.createElement("link");
  link.href = url;
  link.rel="stylesheet";
  link.type="text/css";
  return link;
}

function getStats () {
    // Grab the assetString (might have different names depending on model)
    // var configAssetString = document.querySelector("[name='configAssetString_ERP']");
    var configAssetString = document.querySelector("[name='lolwut']");
    var minimumQuantityString = document.querySelector("[name='minimumQuantityString_PF']");
    var assetString = (configAssetString ? configAssetString.value : "") + (minimumQuantityString ? minimumQuantityString.value : "");
    var assetStringArr = assetString.split("@@")
    	.filter(function (p) { return p.length; })
    	.map(function (p) { return p.split('##'); });

    // Query all parts in the config model
    var modelAttributes = document.querySelectorAll("[id^='attribute-'][type='hidden'], .attribute-field");

    // Create an array of all the common elements in assetStringArr and modelAttributes
    var modelParts = assetStringArr.map( function (p) {
    	var partInAssetString = Array.from(modelAttributes).some(function (a) {
            return p[1] == a.name;
        });
    	return partInAssetString ?
    	{
    	    partNumber: p[0],
    	    attributeName: p[1]
    	} : null;
    }).filter(function(p) {
        return p;
    }).sort(function(p1, p2) {
        return p1.partNumber>p2.partNumber?1:-1;
    });

    return {
        assetStringArr: assetStringArr,
        modelAttributes: Array.from(modelAttributes),
        modelParts: modelParts
    };
}

function setActiveStat(elementId) {
    var btnStat = document.getElementById(elementId);
    var statData = document.getElementById(elementId + "-data");

    // Reset All elements
    Array.from(document.querySelectorAll('span.stat')).forEach(function(e){ e.classList.remove("stat-active"); });
    Array.from(document.querySelectorAll('.part-data')).forEach(function(e){ e.classList.remove("part-data-active"); });

    // Highlight/Show the new active one
    btnStat.classList.add("stat-active");
    statData.classList.add("part-data-active");
}

function showStats () {
    'use strict';
    var stats = getStats();
    var totals = `
        <span class="stat" id="asset-string">Total Asset String entries: ${stats.assetStringArr.length}</span>
        <span class="stat" id="model-attributes">Total Attributes in model: ${stats.modelAttributes.length}</span>
        <span class="stat stat-active" id="config-model-parts">Total config model parts: ${stats.modelParts.length}</span>
    `;
    var assetStringParts = stats.assetStringArr.map(function(p) {
        var partClass = stats.modelParts.some(function(mp) { return mp.partNumber == p[0]; }) ? "text-success" : "text-danger";
        return "<li class='" + partClass + "'>" + p[0] + " <small>(" + p[1] + ")</small></li>";
    }).join('');
    var modelAttributes = stats.modelAttributes.map(function(ma) {
        var partClass = stats.modelParts.some(function(mp) { return mp.attributeName == ma.name; }) ? "text-success" : "text-danger";
        return "<li class='" + partClass + "'>" + ma.name + "</li>";
    }).join('');
    var configModelParts = stats.modelParts.map(function(p) {
        return "<li>" + p.partNumber + " <small class='text-muted'>(" + p.attributeName + ")</small></li>";
    }).join('');
    var deleteButtonHtml = `
    <button type="button" class="show-stats" data-toggle="modal" data-target="#myModal">
        Show Model Stats
    </button>
</div>
`;
    var modalHtml = `
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  <div class="modal-dialog modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel">Config Model Stats</h4>
      </div>
      <div class="modal-body">
        <h3 class="header">Totals</h3>
        <div class="totals">${totals}</div>
        <div class="part-data" id="asset-string-data">
            <h3 class="header">Asset String Entries</h3>
            <ul>${assetStringParts}</ul>
        </div>
        <div class="part-data" id="model-attributes-data">
            <h3 class="header">Model Attributes</h3>
            <ul>${modelAttributes}</ul>
        </div>
        <div class="part-data part-data-active" id="config-model-parts-data">
            <h3 class="header">Config Model Parts</h3>
            <ul>${configModelParts}</ul>
        </div>
      </div>
    </div>
  </div>
</div>
`;

    //--- Add nodes to page
    $("body").append(deleteButtonHtml);
    $("body").append(modalHtml);

    Array.from(document.querySelectorAll('span.stat')).forEach(function(e) {
       e.addEventListener('click', function(event) {
           setActiveStat(event.target.id);
       });
    });

}

showStats();

// Styling Stuff
var bm_css_src = `
button.show-stats {
    position: fixed;
    bottom: 0;
    right: 5em;
    color: #fff;
    background-color: #08c;
    border-color: #08c;
    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;
}

h3.header {
    font-size: 1.5em;
    margin: .5em 0 1em 0;
}

.totals {
    font-size: 12px;
}

.text-muted {
    color: #777;
}

span.stat {
cursor: pointer;
    display: inline-block;
    padding: 3px 10px;
    margin: 5px;
    border: 1px solid #333;
    border-radius: 3px;
    width: 25%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

span.stat-active {
    font-weight: bold;
}

.part-data {
    display: none;
}

.part-data-active {
    display: block;
}

.part-data ul {
    max-height: 65vh;
    overflow-y: auto;
}

.text-success {
    color: #3c763d;
}

.text-danger {
    color: #a94442;
    font-weight: bold;
}

/* Bootstrap Modal CSS */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
  display: block;
}
.close {
  float: right;
  font-size: 21px;
  font-weight: bold;
  line-height: 1;
  color: #000000;
  text-shadow: 0 1px 0 #ffffff;
  opacity: 0.2;
  filter: alpha(opacity=20);
}
.close:hover,
.close:focus {
  color: #000000;
  text-decoration: none;
  cursor: pointer;
  opacity: 0.5;
  filter: alpha(opacity=50);
}
button.close {
  padding: 0;
  cursor: pointer;
  background: transparent;
  border: 0;
  -webkit-appearance: none;
}
.modal-open {
  overflow: hidden;
}
.modal {
  display: none;
  overflow: hidden;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1050;
  -webkit-overflow-scrolling: touch;
  outline: 0;
}
.modal.fade .modal-dialog {
  -webkit-transform: translate(0, -25%);
  -ms-transform: translate(0, -25%);
  -o-transform: translate(0, -25%);
  transform: translate(0, -25%);
  -webkit-transition: -webkit-transform 0.3s ease-out;
  -o-transition: -o-transform 0.3s ease-out;
  transition: transform 0.3s ease-out;
}
.modal.in .modal-dialog {
  -webkit-transform: translate(0, 0);
  -ms-transform: translate(0, 0);
  -o-transform: translate(0, 0);
  transform: translate(0, 0);
}
.modal-open .modal {
  overflow-x: hidden;
  overflow-y: auto;
}
.modal-dialog {
  position: relative;
  width: auto;
  margin: 10px;
}
.modal-content {
  position: relative;
  background-color: #ffffff;
  border: 1px solid #999999;
  border: 1px solid rgba(0, 0, 0, 0.2);
  border-radius: 6px;
  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
  -webkit-background-clip: padding-box;
          background-clip: padding-box;
  outline: 0;
}
.modal-backdrop {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1040;
  background-color: #000000;
}
.modal-backdrop.fade {
  opacity: 0;
  filter: alpha(opacity=0);
}
.modal-backdrop.in {
  opacity: 0.5;
  filter: alpha(opacity=50);
}
.modal-header {
  padding: 15px;
  border-bottom: 1px solid #e5e5e5;
  min-height: 16.42857143px;
}
.modal-header .close {
  margin-top: -2px;
}
.modal-title {
  margin: 0;
  line-height: 1.42857143;
}
.modal-body {
  position: relative;
  padding: 15px;
}
.modal-footer {
  padding: 15px;
  text-align: right;
  border-top: 1px solid #e5e5e5;
}
.modal-footer .btn + .btn {
  margin-left: 5px;
  margin-bottom: 0;
}
.modal-footer .btn-group .btn + .btn {
  margin-left: -1px;
}
.modal-footer .btn-block + .btn-block {
  margin-left: 0;
}
.modal-scrollbar-measure {
  position: absolute;
  top: -9999px;
  width: 50px;
  height: 50px;
  overflow: scroll;
}
.clickable {
  cursor:pointer;
}
@media (min-width: 768px) {
  .modal-dialog {
    width: 600px;
    margin: 30px auto;
  }
  .modal-content {
    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
  }
  .modal-sm {
    width: 300px;
  }
}
@media (min-width: 992px) {
  .modal-lg {
    width: 900px;
  }
}
.clearfix:before,
.clearfix:after,
.modal-footer:before,
.modal-footer:after {
  content: " ";
  display: table;
}
.clearfix:after,
.modal-footer:after {
  clear: both;
}
.center-block {
  display: block;
  margin-left: auto;
  margin-right: auto;
}
.pull-right {
  float: right !important;
}
.pull-left {
  float: left !important;
}
.hide {
  display: none !important;
}
.show {
  display: block !important;
}
.invisible {
  visibility: hidden;
}
.text-hide {
  font: 0/0 a;
  color: transparent;
  text-shadow: none;
  background-color: transparent;
  border: 0;
}
.hidden {
  display: none !important;
}
.affix {
  position: fixed;
}
`;
GM_addStyle(bm_css_src);