text: /*
   * This file is part of yoob.js version 0.13
   * Available from https://github.com/catseye/yoob.js/
   * This file is in the public domain.  See http://unlicense.org/ for details.
   */
  if (window.yoob === undefined) yoob = {};
  
  /*
   * An object for managing a set of "presets" -- which, for an esolang,
   * might be example programs; for an emulator, might be ROM images;
   * for a control panel, may be pre-selected combinations of settings;
   * and so forth.
   *
   * Mostly intended to be connected to a yoob.Controller -- but need not be.
   */
  yoob.PresetManager = function() {
      /*
       * The single argument is a dictionary (object) where the keys are:
       *
       *    selectElem: (required) the <select> DOM element that will be
       *        populated with the available example programs.  Selecting one
       *        will cause the .select() method of this manager to be called.
       *        it will also call .onselect if that method is present.
       *
       *    setPreset: (optional) a callback which will be called whenever
       *        a new preset is selected.  If this is not given, an individual
       *        callback must be supplied with each preset as it is added.
       */
      this.init = function(cfg) {
          this.selectElem = cfg.selectElem;
          if (cfg.setPreset) {
              this.setPreset = cfg.setPreset;
          }
          this.clear();
          var $this = this;
          this.selectElem.onchange = function() {
              $this._select(this.options[this.selectedIndex].value);
          }
          return this;
      };
  
      /*
       * Removes all options from the selectElem, and their associated data.
       */
      this.clear = function() {
          this.reactTo = {};
          while (this.selectElem.firstChild) {
              this.selectElem.removeChild(this.selectElem.firstChild);
          }
          this.add('(select one...)', function() {});
          return this;
      };
  
      /*
       * Adds a preset to this PresetManager.  When it is selected,
       * the given callback will be called, being passed the id as the
       * first argument.  If no callback is provided, the default callback,
       * configured with setPreset in the init() configuration, will be used.
       */
      this.add = function(id, callback) {
          var opt = document.createElement("option");
          opt.text = id;
          opt.value = id;
          this.selectElem.options.add(opt);
          var $this = this;
          this.reactTo[id] = callback || this.setPreset;
          return this;
      };
  
      this.setPreset = function(id) {
          throw new Error("No default setPreset callback configured");
      };
  
      /*
       * Called by the selectElem's onchange event.  For sanity, you should
       * probably not call this yourself.
       */
      this._select = function(id) {
          this.reactTo[id](id);
          if (this.onselect) {
              this.onselect(id);
          }
      };
  
      /*
       * Call this to programmatically select an item.  This will change
       * the selected option in the selectElem and trigger the appropriate
       * callback in this PresetManager.
       */
      this.select = function(id) {
          var i = 0;
          var opt = this.selectElem.options[i];
          while (opt) {
              if (opt.value === id) {
                  this.selectElem.selectedIndex = i;
                  this._select(id);
                  return this;
              }
              i++;
              opt = this.selectElem.options[i];
          }
          // if not found, select the "(select one...)" option
          this.selectElem.selectedIndex = 0;
          return this;
      };
  
      /*
       * When called, every DOM element in the document with the given
       * class will be considered a preset, and the manager
       * will be populated with these.  Generally the CSS for the class
       * will have `display: none` and the elements will be <div>s.
       *
       * callback is as described for the .add() method.
       */
      this.populateFromClass = function(className, callback) {
          var elements = document.getElementsByClassName(className);
          for (var i = 0; i < elements.length; i++) {
              var e = elements[i];
              this.add(e.id, callback);
          }
          return this;
      };
  
      /*
       * When called with a yoob.SourceManager and an array of
       * 2-element arrays of a name and a source text, this preset
       * manager will be populated with each source text as a
       * named preset.  A callback to load the source text with
       * the SourceManager will be automatically supplied.
       */
      this.populateFromPairs = function(sourceManager, pairs) {
          function makeCallback(sourceText) {
              return function(id) {
                  sourceManager.loadSource(sourceText);
              }
          }
  
          for (var i = 0; i < pairs.length; i++) {
              this.add(pairs[i][0], makeCallback(pairs[i][1]));
          }
          this.select(pairs[0][0]);
          return this;
      };
  };
  