text: /*
   * Copyright 2016, Gregg Tavares.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met:
   *
   *     * Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above
   * copyright notice, this list of conditions and the following disclaimer
   * in the documentation and/or other materials provided with the
   * distribution.
   *     * Neither the name of Gregg Tavares. nor the names of his
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  
  (function(root, factory) {  // eslint-disable-line
      if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], function() {
          return factory.call(root);
        });
      } else {
        // Browser globals
        root.webglLessonsUI = factory.call(root);
      }
    }(this, function() {
      'use strict';
      const gopt = getQueryParams();
    
      function setupSlider(selector, options) {
        var parent = document.querySelector(selector);
        if (!parent) {
          // like jquery don't fail on a bad selector
          return;
        }
        if (!options.name) {
          options.name = selector.substring(1);
        }
        return createSlider(parent, options); // eslint-disable-line
      }
    
      function createSlider(parent, options) {
        var precision = options.precision || 0;
        var min = options.min || 0;
        var step = options.step || 1;
        var value = options.value || 0;
        var max = options.max || 1;
        var fn = options.slide;
        var name = gopt["ui-" + options.name] || options.name;
        var uiPrecision = options.uiPrecision === undefined ? precision : options.uiPrecision;
        var uiMult = options.uiMult || 1;
    
        min /= step;
        max /= step;
        value /= step;
    
        parent.innerHTML = `
          <div class="gman-widget-outer">
            <div class="gman-widget-label">${name}</div>
            <div class="gman-widget-value"></div>
            <input class="gman-widget-slider" type="range" min="${min}" max="${max}" value="${value}" />
          </div>
        `;
        var valueElem = parent.querySelector(".gman-widget-value");
        var sliderElem = parent.querySelector(".gman-widget-slider");
    
        function updateValue(value) {
          valueElem.textContent = (value * step * uiMult).toFixed(uiPrecision);
        }
    
        updateValue(value);
    
        function handleChange(event) {
          var value = parseInt(event.target.value);
          updateValue(value);
          fn(event, { value: value * step });
        }
    
        sliderElem.addEventListener('input', handleChange);
        sliderElem.addEventListener('change', handleChange);
    
        return {
          elem: parent,
          updateValue: (v) => {
            v /= step;
            sliderElem.value = v;
            updateValue(v);
          },
        };
      }
    
      function makeSlider(options) {
        const div = document.createElement("div");
        return createSlider(div, options);
      }
    
      var widgetId = 0;
      function getWidgetId() {
        return "__widget_" + widgetId++;
      }
    
      function makeCheckbox(options) {
        const div = document.createElement("div");
        div.className = "gman-widget-outer";
        const label = document.createElement("label");
        const id = getWidgetId();
        label.setAttribute('for', id);
        label.textContent = gopt["ui-" + options.name] || options.name;
        label.className = "gman-checkbox-label";
        const input = document.createElement("input");
        input.type = "checkbox";
        input.checked = options.value;
        input.id = id;
        input.className = "gman-widget-checkbox";
        div.appendChild(label);
        div.appendChild(input);
        input.addEventListener('change', function(e) {
           options.change(e, {
             value: e.target.checked,
           });
        });
    
        return {
          elem: div,
          updateValue: function(v) {
            input.checked = !!v;
          },
        };
      }
    
      function makeOption(options) {
        const div = document.createElement("div");
        div.className = "gman-widget-outer";
        const label = document.createElement("label");
        const id = getWidgetId();
        label.setAttribute('for', id);
        label.textContent = gopt["ui-" + options.name] || options.name;
        label.className = "gman-widget-label";
        const selectElem = document.createElement("select");
        options.options.forEach((name, ndx) => {
          const opt = document.createElement("option");
          opt.textContent = gopt["ui-" + name] || name;
          opt.value = ndx;
          opt.selected = ndx === options.value;
          selectElem.appendChild(opt);
        });
        selectElem.className = "gman-widget-select";
        div.appendChild(label);
        div.appendChild(selectElem);
        selectElem.addEventListener('change', function(e) {
           options.change(e, {
             value: selectElem.selectedIndex,
           });
        });
    
        return {
          elem: div,
          updateValue: function(v) {
            selectElem.selectedIndex = v;
          },
        };
      }
    
      function noop() {
      }
    
      function genSlider(object, ui) {
        const changeFn = ui.change || noop;
        ui.name = ui.name || ui.key;
        ui.value = object[ui.key];
        ui.slide = ui.slide || function(event, uiInfo) {
          object[ui.key] = uiInfo.value;
          changeFn();
        };
        return makeSlider(ui);
      }
    
      function genCheckbox(object, ui) {
        const changeFn = ui.change || noop;
        ui.value = object[ui.key];
        ui.name = ui.name || ui.key;
        ui.change = function(event, uiInfo) {
          object[ui.key] = uiInfo.value;
          changeFn();
        };
        return makeCheckbox(ui);
      }
    
      function genOption(object, ui) {
        const changeFn = ui.change || noop;
        ui.value = object[ui.key];
        ui.name = ui.name || ui.key;
        ui.change = function(event, uiInfo) {
          object[ui.key] = uiInfo.value;
          changeFn();
        };
        return makeOption(ui);
      }
    
      const uiFuncs = {
        slider: genSlider,
        checkbox: genCheckbox,
        option: genOption,
      };
    
      function setupUI(parent, object, uiInfos) {
        const widgets = {};
        uiInfos.forEach(function(ui) {
          const widget = uiFuncs[ui.type](object, ui);
          parent.appendChild(widget.elem);
          widgets[ui.key] = widget;
        });
        return widgets;
      }
    
      function updateUI(widgets, data) {
        Object.keys(widgets).forEach(key => {
          const widget = widgets[key];
          widget.updateValue(data[key]);
        });
      }
    
      function getQueryParams() {
        var params = {};
        if (window.hackedParams) {
          Object.keys(window.hackedParams).forEach(function(key) {
            params[key] = window.hackedParams[key];
          });
        }
        if (window.location.search) {
          window.location.search.substring(1).split("&").forEach(function(pair) {
            var keyValue = pair.split("=").map(function(kv) {
              return decodeURIComponent(kv);
            });
            params[keyValue[0]] = keyValue[1];
          });
        }
        return params;
      }
    
      return {
        setupUI: setupUI,
        updateUI: updateUI,
        setupSlider: setupSlider,
        makeSlider: makeSlider,
        makeCheckbox: makeCheckbox,
      };
    
    }));