Online IDE Support JS

Injects elements into a codepen IDE for demonstration purposes

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/438329/1218573/Online%20IDE%20Support%20JS.js

// Online IDE Support JS

// GLOBAL EVENT LISTENER
const addEventListenerAll = (target, listener, ...otherArguments) => {
    // install listeners for all natively triggered events
    for (const key in target) {
      if (/^on/.test(key)) {
        const eventType = key.substr(2);
        target.addEventListener(eventType, listener, ...otherArguments);
      }
    }
  
    // dynamically install listeners for all manually triggered events, just-in-time before they're dispatched ;D
    const dispatchEvent_original = EventTarget.prototype.dispatchEvent;
    function dispatchEvent(event) {
      target.addEventListener(event.type, listener, ...otherArguments);  // multiple identical listeners are automatically discarded
      dispatchEvent_original.apply(this, arguments);
    }
    EventTarget.prototype.dispatchEvent = dispatchEvent;
    if (EventTarget.prototype.dispatchEvent !== dispatchEvent) throw new Error(`Browser is smarter than you think!`);
  };
  
  
  let demo;
  let sources;
  let initCounter = 10;
  let defaultVersion = `2.29.0`;
  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);
      },
      arraysEqual: (first, second) => {
        if(first.length !== second.length){
          return false;
        };
        for(let i = 0; i < first.length; i++){
          if(!second.includes(first[i])){
            return false;
          };
        };
        return true;
      },
      arrayRemove: (arr, value) => { 
          return arr.filter(function(ele){ 
              return ele != value; 
          });
      },
      /**
      * 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, ""));
      },
      debounce: (func, timeout = 780) => {
          let timer;
          return (...args) => {
              clearTimeout(timer);
              timer = setTimeout(() => {
                  func.apply(this, args);
              }, timeout);
          };
      },
      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) {
          const penLay = document.getElementById(`penlay`);
          if (penLay) penLay.remove();
          demo = {
            version: defaultVersion
          };
          sources = pen.addLinks();
        }
        return;
      }
      if (demo.version) {
        sources = pen.addLinks();
      }
      setTimeout(() => {
        const penLay = document.getElementById(`penlay`);
        if (penLay) penLay.remove();
        if (!demo.components) {
            demo.components = [];
            demo.components.push({ // for backward compatibility
              selector: demo.selector,
              options: demo.options,
            });
        }
        demo.components.forEach(demoComp => {
          const method = pen.utils.capitalize(pen.utils.dashCamel(demoComp.selector));
          document.querySelectorAll(`[data-dds="${demoComp.selector}"]`).forEach((element) => {
            if (!element[method]) {
                demoComp.api = DDS[method](element, demoComp.options);
            } else {
                demoComp.api = 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`);
            }
            if (elString === 'api' && (demo.api === `off` || demo.api === false)) {
              pen.elements[elString].classList.add(`pen__none`);
            }
            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;
        demo.components.forEach(demoComp => {
            const comp = demoComp.api;
            if (comp) {
                const method = pen.utils.capitalize(pen.utils.dashCamel(demoComp.selector));
                Object.keys(comp).forEach((key, index) => {
                  const selectorScript = `document.querySelector('[data-dds="${demoComp.selector}"]').${method}`;
                  if (typeof comp[key] === `function`) {
                    if (key !== `dispose`) {
                      if (!deprecatedEvents[demoComp.selector] || !deprecatedEvents[demoComp.selector].includes(key)) {
                        const parameterCount = comp[key].length;
                        let comment = parameterCount > 0 ? ` // takes ${parameterCount} parameters` : ``;
                        pen.actions.api.addButton(key, comp[key]);
                        const logComment = `${selectorScript}.${key}();${comment}`;
                        pen.actions.log(logComment);
                      }
                    } else {
                      hasDispose = true;
                      pen.actions.log(`${selectorScript}.dispose()`);
                    }
                  } else {
                    pen.actions.log(`${selectorScript}.${key} = ${comp[key]}`);
                  }
              });
              pen.actions.log(`:::::::::::::::::::::::::::::::::::::::::::::::::::`);
              pen.actions.log(`\n\n${demoComp.selector} properties / methods:::::::::::::::::::::::`);
         
              hasDispose && (pen.actions.api.addButton(`dispose`, () => {
                comp[`dispose`]();
                pen.elements.api.querySelectorAll(`button`).forEach(b => b.disabled = `true`);
              }));
         
         
              addEventListenerAll(window, (evt) => {
                  if (!evt.type.match(/dds|ddv/)) {
                    return;
                  }
                  let detail = JSON.stringify(evt.detail) || JSON.stringify(evt) || ``;
                  pen.actions.log(`${evt.type} was fired with {event}.detail = ${detail}`);
              });
  
              pen.actions.log(`::::::::::::::::::::::::::::::::::::::`);
              pen.actions.log(`\n${demoComp.selector} events:::::::::::::::::::::::`);
              
              // BEGIN LOGGING INITIALIZATION
              pen.actions.log(`
    let components = [];
    document.querySelectorAll('[data-dds="${demoComp.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 {
          setTimeout(() => {
              callback();
          }, 500);
      }
    },
    reset: () => {
      demo.components.forEach(demoComp => {
          const method = pen.utils.capitalize(pen.utils.dashCamel(demoComp.selector));
       
          // remove element properties
          document.querySelectorAll(`[data-dds="${demoComp.selector}"]`).forEach((element) => {
              element[method] = undefined;
          });
       
          // dispose of components
          try {
          demoComp.api.dispose();
          } catch (err) {
             console.log(err);
          }
          demoComp.api = undefined;
      });
   
      // 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)
  })();