<commit_before>/**
 * A basic unofficial compatibility object that can read {@link http://api.ai|Api.ai} exported packages and simulates its output.
 * @constructor
 * @param {string} packagePath - The path to the exported Api.ai package root.
 * @param {string} extensions.language - The language to be used. Possible values are namespace names of {@link Bravey.Language}.
 * @param {string} extensions.nlp - The NLP processor to be used. Possible values are namespace names of {@link Bravey.Nlp}.
 */
Bravey.ApiAiAdapter = function(packagePath, extensions) {
  extensions = extensions || {};

  var files = [];
  var loadedData = {};
  var intents = [];
  var entities = [];

  var nlp = new Bravey.Nlp[extensions.nlp || "Fuzzy"]("apiai", {
    stemmer: Bravey.Language[extensions.language].Stemmer
  });
  nlp.addEntity(new Bravey.Language[extensions.language].NumberEntityRecognizer("sys_number"));
  nlp.addEntity(new Bravey.Language[extensions.language].TimeEntityRecognizer("sys_time"));
  nlp.addEntity(new Bravey.Language[extensions.language].DateEntityRecognizer("sys_date"));
  nlp.addEntity(new Bravey.Language[extensions.language].TimePeriodEntityRecognizer("sys_time_period"));

  var pos = 0;
  var onready;

  function sanitizeApiAiId(id) {
    return id.replace(/[^a-z0-9:]/g, "_");
  }

  function loadNext() {
    Bravey.File.load(files[pos], function(text) {
      loadedData[files[pos]] = text;
      pos++;
      if (!files[pos]) dataLoaded();
      else loadNext();
    })
  }

  function dataLoaded() {
    var entity, missingEntity = {};

    for (var e = 0; e < entities.length; e++) {
      var data = JSON.parse(loadedData[entities[e].file]);
      var newEntity = new Bravey.StringEntityRecognizer(entities[e].name);
      for (var i = 0; i < data.entries.length; i++)
        for (var j = 0; j < data.entries[i].synonyms.length; j++)
          newEntity.addMatch(data.entries[i].value, data.entries[i].synonyms[j]);
      nlp.addEntity(newEntity);
    }

    for (var e = 0; e < intents.length; e++) {
      var data = JSON.parse(loadedData[intents[e].file]);
      for (var i = 0; i < data.userSays.length; i++) {
        var text = "",
          skip = false;
        for (var j = 0; j < data.userSays[i].data.length; j++) {
          if (data.userSays[i].data[j].meta) {
            entity = data.userSays[i].data[j].meta.substr(1);
            text += "@" + entity + ":" + data.userSays[i].data[j].alias;
          } else text += data.userSays[i].data[j].text;
        }
        var names = [];
        text = text.replace(/\@([.a-z0-9_-]+):([.a-z0-9_-]+)/g, function(a, b, c) {
          b = sanitizeApiAiId(b);
          c = sanitizeApiAiId(c);
          if (!nlp.hasEntity(b)) {
            skip = true;
            if (!missingEntity[b]) {
              console.warn("Missing entity", b, data.userSays[i].data);
              missingEntity[b] = 1;
            }
          }
          names.push(c);
          return "{" + b + "}";
        });
        if (!skip)
          nlp.addDocument(text.trim(), intents[e].name, {
            fromTaggedSentence: true,
            expandIntent: true,
            withNames: names
          });
      }
    }

    onready();
  }

  /**
   * Prepare for loading an intent from the specified package.
   * @param {string} name - The intent name.
   */
  this.loadIntent = function(name) {
    var filename = packagePath + "/intents/" + name + ".json";
    files.push(filename);
    intents.push({
      file: filename,
      name: name
    });
  }

  /**
   * Prepare for loading an entity from the specified package.
   * @param {string} name - The intent name.
   */
  this.loadEntity = function(name) {
    var filename = packagePath + "/entities/" + name + ".json";
    files.push(filename);
    entities.push({
      file: filename,
      name: name
    });
  }

  /**
   * Load the needed files and prepares the NLP.
   * @param {function} cb - The callback called when everything is ready.
   */
  this.prepare = function(cb) {
    onready = cb;
    loadNext();
  }

  /**
   * Check if a given sentence matches an intent and extracts its entities. Output simulates Api.ai structure. For arguments, check {@link Bravey.Nlp.test}.
   */
  this.test = function(text, method) {
    var out = this.nlp.test(text, method);
    if (out) {
      var ret = {
        result: {
          source: "agent",
          resolvedQuery: text,
          action: "",
          actionIncomplete: false,
          parameters: {

          },
          contexts: [],
          metadata: {
            intentName: ""
          },
          fulfillment: {
            speech: ""
          },
          score: 0
        },
        status: {
          code: 200,
          errorType: "success"
        }
      };
      ret.result.metadata.intentName = out.intent;
      ret.result.score = out.score;
      for (var a in out.entitiesIndex) ret.result.parameters[a] = out.entitiesIndex[a].value;
      return ret;
    } else
      return {
        result: {
          resolvedQuery: text,
          contexts: [],
          metadata: {},
          fulfillment: {
            speech: ""
          },
          score: 0
        },
        status: {
          code: 200,
          errorType: "success"
        }
      };
  }

  /** @property {Bravey.Nlp} nlp The raw Nlp instance. */
  this.nlp = nlp;
}<commit_msg>Added token filters to adapter.<commit_after>/**
 * A basic unofficial compatibility object that can read {@link http://api.ai|Api.ai} exported packages and simulates its output.
 * @constructor
 * @param {string} packagePath - The path to the exported Api.ai package root.
 * @param {string} extensions.language - The language to be used. Possible values are namespace names of {@link Bravey.Language}.
 * @param {string} extensions.nlp - The NLP processor to be used. Possible values are namespace names of {@link Bravey.Nlp}.
 */
Bravey.ApiAiAdapter = function(packagePath, extensions) {
  extensions = extensions || {};

  var files = [];
  var loadedData = {};
  var intents = [];
  var entities = [];

  var nlp = new Bravey.Nlp[extensions.nlp || "Fuzzy"]("apiai", {
    stemmer: Bravey.Language[extensions.language].Stemmer,
    filter: extensions.filter
  });
  nlp.addEntity(new Bravey.Language[extensions.language].NumberEntityRecognizer("sys_number"));
  nlp.addEntity(new Bravey.Language[extensions.language].TimeEntityRecognizer("sys_time"));
  nlp.addEntity(new Bravey.Language[extensions.language].DateEntityRecognizer("sys_date"));
  nlp.addEntity(new Bravey.Language[extensions.language].TimePeriodEntityRecognizer("sys_time_period"));

  var pos = 0;
  var onready;

  function sanitizeApiAiId(id) {
    return id.replace(/[^a-z0-9:]/g, "_");
  }

  function loadNext() {
    Bravey.File.load(files[pos], function(text) {
      loadedData[files[pos]] = text;
      pos++;
      if (!files[pos]) dataLoaded();
      else loadNext();
    })
  }

  function dataLoaded() {
    var entity, missingEntity = {};

    for (var e = 0; e < entities.length; e++) {
      var data = JSON.parse(loadedData[entities[e].file]);
      var newEntity = new Bravey.StringEntityRecognizer(entities[e].name);
      for (var i = 0; i < data.entries.length; i++)
        for (var j = 0; j < data.entries[i].synonyms.length; j++)
          newEntity.addMatch(data.entries[i].value, data.entries[i].synonyms[j]);
      nlp.addEntity(newEntity);
    }

    for (var e = 0; e < intents.length; e++) {
      var data = JSON.parse(loadedData[intents[e].file]);
      for (var i = 0; i < data.userSays.length; i++) {
        var text = "",
          skip = false;
        for (var j = 0; j < data.userSays[i].data.length; j++) {
          if (data.userSays[i].data[j].meta) {
            entity = data.userSays[i].data[j].meta.substr(1);
            text += "@" + entity + ":" + data.userSays[i].data[j].alias;
          } else text += data.userSays[i].data[j].text;
        }
        var names = [];
        text = text.replace(/\@([.a-z0-9_-]+):([.a-z0-9_-]+)/g, function(a, b, c) {
          b = sanitizeApiAiId(b);
          c = sanitizeApiAiId(c);
          if (!nlp.hasEntity(b)) {
            skip = true;
            if (!missingEntity[b]) {
              console.warn("Missing entity", b, data.userSays[i].data);
              missingEntity[b] = 1;
            }
          }
          names.push(c);
          return "{" + b + "}";
        });
        if (!skip)
          nlp.addDocument(text.trim(), intents[e].name, {
            fromTaggedSentence: true,
            expandIntent: true,
            withNames: names
          });
      }
    }

    onready();
  }

  /**
   * Prepare for loading an intent from the specified package.
   * @param {string} name - The intent name.
   */
  this.loadIntent = function(name) {
    var filename = packagePath + "/intents/" + name + ".json";
    files.push(filename);
    intents.push({
      file: filename,
      name: name
    });
  }

  /**
   * Prepare for loading an entity from the specified package.
   * @param {string} name - The intent name.
   */
  this.loadEntity = function(name) {
    var filename = packagePath + "/entities/" + name + ".json";
    files.push(filename);
    entities.push({
      file: filename,
      name: name
    });
  }

  /**
   * Load the needed files and prepares the NLP.
   * @param {function} cb - The callback called when everything is ready.
   */
  this.prepare = function(cb) {
    onready = cb;
    loadNext();
  }

  /**
   * Check if a given sentence matches an intent and extracts its entities. Output simulates Api.ai structure. For arguments, check {@link Bravey.Nlp.test}.
   */
  this.test = function(text, method) {
    var out = this.nlp.test(text, method);
    if (out) {
      var ret = {
        result: {
          source: "agent",
          resolvedQuery: text,
          action: "",
          actionIncomplete: false,
          parameters: {

          },
          contexts: [],
          metadata: {
            intentName: ""
          },
          fulfillment: {
            speech: ""
          },
          score: 0
        },
        status: {
          code: 200,
          errorType: "success"
        }
      };
      ret.result.metadata.intentName = out.intent;
      ret.result.score = out.score;
      for (var a in out.entitiesIndex) ret.result.parameters[a] = out.entitiesIndex[a].value;
      return ret;
    } else
      return {
        result: {
          resolvedQuery: text,
          contexts: [],
          metadata: {},
          fulfillment: {
            speech: ""
          },
          score: 0
        },
        status: {
          code: 200,
          errorType: "success"
        }
      };
  }

  /** @property {Bravey.Nlp} nlp The raw Nlp instance. */
  this.nlp = nlp;
}<|endoftext|>


<commit_before>
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext || window.oAudioContext;

/**
 * @scope enchant.WebAudioSound.prototype
 * @type {*}
 */
enchant.WebAudioSound = enchant.Class.create(enchant.EventTarget, {
    /**
     * @name enchant.WebAudioSound
     * @class
     * Sound wrapper class for Web Audio API (supported on some webkit-based browsers)
     *
     * @constructs
     */
    initialize: function() {
        if(!window.AudioContext){
            throw new Error("This browser does not support WebAudio API.");
        }
        enchant.EventTarget.call(this);
        this.context = enchant.WebAudioSound.audioContext;
        this.src = this.context.createBufferSource();
        this.buffer = null;
        this._volume = 1;
        this._currentTime = 0;
        this._state = 0;
        this.connectTarget = enchant.WebAudioSound.destination;
    },
    play: function(dup) {
        if (this._state === 1 && !dup) {
            this.src.disconnect(this.connectTarget);
        }
        if (this._state !== 2) {
            this._currentTime = 0;
        }
        var offset = this._currentTime;
        var actx = this.context;
        this.src = actx.createBufferSource();
        if (actx.createGain != null) {
            this._gain = actx.createGain();
        } else {
            this._gain = actx.createGainNode();
	}
        this.src.buffer = this.buffer;
        this._gain.gain.value = this._volume;

        this.src.connect(this._gain);
        this._gain.connect(this.connectTarget);
	if (this.src.start != null) {
            this.src.start(0, offset, this.buffer.duration - offset - 1.192e-7);
	} else {
            this.src.noteGrainOn(0, offset, this.buffer.duration - offset - 1.192e-7);
	}
        this._startTime = actx.currentTime - this._currentTime;
        this._state = 1;
    },
    pause: function() {
        var currentTime = this.currentTime;
        if (currentTime === this.duration) {
            return;
        }
	if (this.src.stop != null) {
            this.src.stop(0);
	} else {
            this.src.noteOff(0);
	}
        this._currentTime = currentTime;
        this._state = 2;
    },
    stop: function() {
	if (this.src.stop != null) {
            this.src.stop(0);
	} else {
            this.src.noteOff(0);
	}
        this._state = 0;
    },
    clone: function() {
        var sound = new enchant.WebAudioSound();
        sound.buffer = this.buffer;
        return sound;
    },
    duration: {
        get: function() {
            if (this.buffer) {
                return this.buffer.duration;
            } else {
                return 0;
            }
        }
    },
    volume: {
        get: function() {
            return this._volume;
        },
        set: function(volume) {
            volume = Math.max(0, Math.min(1, volume));
            this._volume = volume;
            if (this.src) {
		this._gain.gain.value = volume;
            }
        }
    },
    currentTime: {
        get: function() {
            return Math.max(0, Math.min(this.duration, this.src.context.currentTime - this._startTime));
        },
        set: function(time) {
            this._currentTime = time;
            if (this._state !== 2) {
                this.play(false);
            }
        }
    }
});

enchant.WebAudioSound.load = function(src, type, callback, onerror) {
    var canPlay = (new Audio()).canPlayType(type);
    var sound = new enchant.WebAudioSound();
    onerror = onerror || function() {};
    sound.addEventListener(enchant.Event.LOAD, callback);
    sound.addEventListener(enchant.Event.ERROR, onerror);
    function dispatchErrorEvent() {
        var e = new enchant.Event(enchant.Event.ERROR);
        e.message = 'Cannot load an asset: ' + src;
        enchant.Core.instance.dispatchEvent(e);
        sound.dispatchEvent(e);
    }
    var actx, xhr;
    if (canPlay === 'maybe' || canPlay === 'probably') {
        actx = enchant.WebAudioSound.audioContext;
        xhr = new XMLHttpRequest();
        xhr.open('GET', src, true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function() {
            actx.decodeAudioData(xhr.response, function(buffer) {
                sound.buffer = buffer;
                sound.dispatchEvent(new enchant.Event(enchant.Event.LOAD));
            }, dispatchErrorEvent);
        };
        xhr.onerror = dispatchErrorEvent;
        xhr.send(null);
    } else {
        setTimeout(dispatchErrorEvent,  50);
    }
    return sound;
};

if(window.AudioContext){
    enchant.WebAudioSound.audioContext = new window.AudioContext();
    enchant.WebAudioSound.destination = enchant.WebAudioSound.audioContext.destination;
}
<commit_msg>retab<commit_after>
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext || window.oAudioContext;

/**
 * @scope enchant.WebAudioSound.prototype
 * @type {*}
 */
enchant.WebAudioSound = enchant.Class.create(enchant.EventTarget, {
    /**
     * @name enchant.WebAudioSound
     * @class
     * Sound wrapper class for Web Audio API (supported on some webkit-based browsers)
     *
     * @constructs
     */
    initialize: function() {
        if (!window.AudioContext) {
            throw new Error("This browser does not support WebAudio API.");
        }
        enchant.EventTarget.call(this);
        this.context = enchant.WebAudioSound.audioContext;
        this.src = this.context.createBufferSource();
        this.buffer = null;
        this._volume = 1;
        this._currentTime = 0;
        this._state = 0;
        this.connectTarget = enchant.WebAudioSound.destination;
    },
    play: function(dup) {
        if (this._state === 1 && !dup) {
            this.src.disconnect(this.connectTarget);
        }
        if (this._state !== 2) {
            this._currentTime = 0;
        }
        var offset = this._currentTime;
        var actx = this.context;
        this.src = actx.createBufferSource();
        if (actx.createGain != null) {
            this._gain = actx.createGain();
        } else {
            this._gain = actx.createGainNode();
        }
        this.src.buffer = this.buffer;
        this._gain.gain.value = this._volume;

        this.src.connect(this._gain);
        this._gain.connect(this.connectTarget);
        if (this.src.start != null) {
            this.src.start(0, offset, this.buffer.duration - offset - 1.192e-7);
        } else {
            this.src.noteGrainOn(0, offset, this.buffer.duration - offset - 1.192e-7);
        }
        this._startTime = actx.currentTime - this._currentTime;
        this._state = 1;
    },
    pause: function() {
        var currentTime = this.currentTime;
        if (currentTime === this.duration) {
            return;
        }
        if (this.src.stop != null) {
            this.src.stop(0);
        } else {
            this.src.noteOff(0);
        }
        this._currentTime = currentTime;
        this._state = 2;
    },
    stop: function() {
        if (this.src.stop != null) {
            this.src.stop(0);
        } else {
            this.src.noteOff(0);
        }
        this._state = 0;
    },
    clone: function() {
        var sound = new enchant.WebAudioSound();
        sound.buffer = this.buffer;
        return sound;
    },
    duration: {
        get: function() {
            if (this.buffer) {
                return this.buffer.duration;
            } else {
                return 0;
            }
        }
    },
    volume: {
        get: function() {
            return this._volume;
        },
        set: function(volume) {
            volume = Math.max(0, Math.min(1, volume));
            this._volume = volume;
            if (this.src) {
                this._gain.gain.value = volume;
            }
        }
    },
    currentTime: {
        get: function() {
            return Math.max(0, Math.min(this.duration, this.src.context.currentTime - this._startTime));
        },
        set: function(time) {
            this._currentTime = time;
            if (this._state !== 2) {
                this.play(false);
            }
        }
    }
});

enchant.WebAudioSound.load = function(src, type, callback, onerror) {
    var canPlay = (new Audio()).canPlayType(type);
    var sound = new enchant.WebAudioSound();
    onerror = onerror || function() {};
    sound.addEventListener(enchant.Event.LOAD, callback);
    sound.addEventListener(enchant.Event.ERROR, onerror);
    function dispatchErrorEvent() {
        var e = new enchant.Event(enchant.Event.ERROR);
        e.message = 'Cannot load an asset: ' + src;
        enchant.Core.instance.dispatchEvent(e);
        sound.dispatchEvent(e);
    }
    var actx, xhr;
    if (canPlay === 'maybe' || canPlay === 'probably') {
        actx = enchant.WebAudioSound.audioContext;
        xhr = new XMLHttpRequest();
        xhr.open('GET', src, true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function() {
            actx.decodeAudioData(xhr.response, function(buffer) {
                sound.buffer = buffer;
                sound.dispatchEvent(new enchant.Event(enchant.Event.LOAD));
            }, dispatchErrorEvent);
        };
        xhr.onerror = dispatchErrorEvent;
        xhr.send(null);
    } else {
        setTimeout(dispatchErrorEvent,  50);
    }
    return sound;
};

if(window.AudioContext){
    enchant.WebAudioSound.audioContext = new window.AudioContext();
    enchant.WebAudioSound.destination = enchant.WebAudioSound.audioContext.destination;
}
<|endoftext|>


<commit_before>/*jslint browser: true, node: true */
/*global window */

/*!

   Thumbnail image plugin for Flowplayer HTML5

   Copyright (c) 2015-2016, Flowplayer Oy

   Released under the MIT License:
   http://www.opensource.org/licenses/mit-license.php

   requires:
   - Flowplayer HTML5 version 6.x or greater
   revision: $GIT_ID$

*/

(function (flowplayer) {
    "use strict";
    flowplayer(function (api, root) {
        var common = flowplayer.common,
            bean = flowplayer.bean,
            support = flowplayer.support,
            timeline = common.find('.fp-timeline', root)[0],
            timelineTooltip = common.find('.fp-timeline-tooltip', root)[0];

        if (support.touch || !support.inlineVideo) {
            return;
        }

        api.on('ready', function (ev, a, video) {
            // cleanup
            bean.off(root, '.thumbnails');
            common.css(timelineTooltip, {
                width: '',
                height: '',
                'background-image': '',
                'background-repeat': '',
                'background-size': '',
                'background-position': '',
                'border': '',
                'text-shadow': ''
            });

            var c = video.thumbnails || api.conf.thumbnails,
                preloadImages = function (tmpl, max, start) {
                    if (start === undefined) {
                        start = 1;
                    }
                    function load() {
                        if (start > max) {
                            return;
                        }
                        var img = new Image();
                        img.src = tmpl.replace('{time}', start);
                        img.onload = function () {
                            start += 1;
                            load();
                        };
                    }
                    load();
                };

            if (!c) {
                return;
            }

            var ratio = video.height / video.width;
            if (c.preload !== false) {
                preloadImages(c.template, video.duration);
            }

            bean.on(root, 'mousemove.thumbnails', '.fp-timeline', function (ev) {
                var x = ev.pageX || ev.clientX,
                    delta = x - common.offset(timeline).left,
                    percentage = delta / common.width(timeline),
                    seconds = Math.round(percentage * api.video.duration);

                // 2nd condition safeguards at out of range retrieval attempts
                if (seconds < 0 || seconds > Math.round(api.video.duration)) {
                    return;
                }
                /* enables greater interval than one second between thumbnails */
                if (c.interval !== undefined && c.interval > 1 && seconds > 0) {
                    seconds = Math.ceil(seconds / c.interval) - 1;
                }

                var height = c.height || 80;

                common.css(timelineTooltip, {
                    width: (height / ratio) + 'px',
                    height: height + 'px',
                    // {time} template expected to start at 1, video time/first frame starts at 0
                    'background-image': "url('" + c.template.replace('{time}', seconds + 1) + "')",
                    'background-repeat': 'no-repeat',
                    'background-size': 'cover',
                    'background-position': 'center',
                    'border': '1px solid #333',
                    'text-shadow': '1px 1px #000'
                });
            });
        });

    });

}((typeof module === "object" && module.exports)
    ? require('flowplayer')
    : window.flowplayer));
<commit_msg>Set variables when they are needed, and on ready if possible<commit_after>/*jslint browser: true, node: true */
/*global window */

/*!

   Thumbnail image plugin for Flowplayer HTML5

   Copyright (c) 2015-2016, Flowplayer Oy

   Released under the MIT License:
   http://www.opensource.org/licenses/mit-license.php

   requires:
   - Flowplayer HTML5 version 6.x or greater
   revision: $GIT_ID$

*/

(function (flowplayer) {
    "use strict";
    flowplayer(function (api, root) {
        var common = flowplayer.common,
            bean = flowplayer.bean,
            support = flowplayer.support,
            timeline = common.find('.fp-timeline', root)[0],
            timelineTooltip = common.find('.fp-timeline-tooltip', root)[0];

        if (support.touch || !support.inlineVideo) {
            return;
        }

        api.on('ready', function (ev, a, video) {
            // cleanup
            bean.off(root, '.thumbnails');
            common.css(timelineTooltip, {
                width: '',
                height: '',
                'background-image': '',
                'background-repeat': '',
                'background-size': '',
                'background-position': '',
                'border': '',
                'text-shadow': ''
            });

            var c = video.thumbnails || api.conf.thumbnails;

            if (!c) {
                return;
            }

            var height = c.height || 80,
                ratio = video.height / video.width,
                preloadImages = function (tmpl, max, start) {
                    if (start === undefined) {
                        start = 1;
                    }
                    function load() {
                        if (start > max) {
                            return;
                        }
                        var img = new Image();
                        img.src = tmpl.replace('{time}', start);
                        img.onload = function () {
                            start += 1;
                            load();
                        };
                    }
                    load();
                };

            if (c.preload !== false) {
                preloadImages(c.template, video.duration);
            }

            bean.on(root, 'mousemove.thumbnails', '.fp-timeline', function (ev) {
                var x = ev.pageX || ev.clientX,
                    delta = x - common.offset(timeline).left,
                    percentage = delta / common.width(timeline),
                    seconds = Math.round(percentage * api.video.duration);

                // 2nd condition safeguards at out of range retrieval attempts
                if (seconds < 0 || seconds > Math.round(api.video.duration)) {
                    return;
                }
                /* enables greater interval than one second between thumbnails */
                if (c.interval !== undefined && c.interval > 1 && seconds > 0) {
                    seconds = Math.ceil(seconds / c.interval) - 1;
                }

                common.css(timelineTooltip, {
                    width: (height / ratio) + 'px',
                    height: height + 'px',
                    // {time} template expected to start at 1, video time/first frame starts at 0
                    'background-image': "url('" + c.template.replace('{time}', seconds + 1) + "')",
                    'background-repeat': 'no-repeat',
                    'background-size': 'cover',
                    'background-position': 'center',
                    'border': '1px solid #333',
                    'text-shadow': '1px 1px #000'
                });
            });
        });

    });

}((typeof module === "object" && module.exports)
    ? require('flowplayer')
    : window.flowplayer));
<|endoftext|>


<commit_before>import { dragElement, setURL, buildCheckBox } from '../util.js';
import { controllerEvents, controllers } from './Controller.js';
import { OpacitySlider } from './opacitySlider.js';
import { simVars } from '../simVars.js';
import { map } from '../map.js';
import { SimulationLayer } from './simulationLayer.js';
import { ThreadManager } from '../../threadManager.js';

/**
 * Component that handles adding and removing layers to the map. Provides user with a window
 * to choose different layers available to add.
 */
export class LayerController extends HTMLElement {
    constructor() {
        super();
        this.innerHTML = `

                    layers

Base Maps

Rasters

Overlays

Top Layer Opacity

        `;
        this.mapType = 'OSM';
        this.overlayDict = {};
        this.rasterDict = {};
        this.activeLayers = {};
        this.threadManager;
    }

    /** Disable map events from within the layer selection window to prevent unwanted zooming
     * and panning. Set up callbacks to trigger when currentdomain updates and currentTimestamp
     * updates. */
    connectedCallback() {
        const layerController = this.querySelector('#layer-controller-container');
        dragElement(layerController, '');
        L.DomEvent.disableClickPropagation(layerController);
        L.DomEvent.disableScrollPropagation(layerController);
        this.setLayerButton();
        // simplify this
        const domainSubscription = () => {
            this.resetLayers();
            this.domainSwitch();
            this.updateTime();
            var startDate = controllers.startDate.value;
            var endDate = controllers.endDate.value;
            this.loadWithPriority(startDate, endDate, simVars.overlayOrder);
        }
        const domainResetSubscription = () => {
            this.resetLayers();
            this.resetLayerController();
            this.domainSwitch();
            this.updateTime();
        }
        controllers.currentDomain.subscribe(domainSubscription);
        controllers.currentDomain.subscribe(domainResetSubscription, controllerEvents.simReset);

        controllers.currentTimestamp.subscribe(() => this.updateTime());
        controllers.opacity.subscribe(() => {
            var newOpacity = controllers.opacity.getValue();
            if (simVars.overlayOrder.length > 0) {
                var currentDomain = controllers.currentDomain.getValue();
                var topLayerName = simVars.overlayOrder[simVars.overlayOrder.length - 1];
                var topLayer = this.getLayer(currentDomain, topLayerName);
                topLayer.setOpacity(newOpacity);
            }
        });

        const reload = () => {
            var startDate = controllers.startDate.getValue();
            var endDate = controllers.endDate.getValue();
            this.loadWithPriority(startDate, endDate, simVars.overlayOrder);
        }
        controllers.startDate.subscribe(reload);
        controllers.endDate.subscribe(reload);

        const opacitySlider = new OpacitySlider();
        const opacitySliderContainer = this.querySelector('#opacity-slider-container');
        opacitySliderContainer.appendChild(opacitySlider);
        this.buildMapBase();
        this.startThreadManager();
    }

    startThreadManager() {
        const callback = (loadInfo) => {
        // const callback = (batch) => {
        //     for (var loadInfo of batch) {
                const blob = loadInfo.blob;
                const layerDomain = loadInfo.layerDomain;
                const layerName = loadInfo.layerName;
                const timestamp = loadInfo.timeStamp;
                const colorbar = loadInfo.colorbar;

                var objectURL = null;
                if (blob.size > 0) {
                    objectURL = URL.createObjectURL(blob);
                }
                var layer = this.getLayer(layerDomain, layerName);
                layer.setImageLoaded(timestamp, objectURL, colorbar);
            // }

            // controllers.loadingProgress.frameLoaded(batch.length);
            controllers.loadingProgress.frameLoaded();
        };

        this.threadManager = new ThreadManager(callback);
    }

    setLayerButton() {
        const layersButton = this.querySelector('#layers-button');

        L.DomEvent.disableClickPropagation(layersButton);
        layersButton.onpointerdown = (e) => {
            const layersSelector = document.querySelector('#layer-controller-container');
            if (layersSelector.classList.contains('hidden')) {
                document.querySelector('.catalog-menu').classList.add('hidden');
                document.querySelector('#domain-selector').classList.add('hidden');
                layersSelector.classList.remove('hidden');
            } else {
                layersSelector.classList.add('hidden');
            }
        }
    }

    /** Triggered whenever currentTimestamp is changed. For every layer currently selected
     * need to set its image url to the point to the image associated with the current time.
     * Need to update the colorbar on top to the current time as well.
     */
    updateTime() {
        var currentDomain = controllers.currentDomain.getValue();
        var currentTimestamp = controllers.currentTimestamp.getValue();
        var load = false;
        for (var layerName of simVars.overlayOrder) {
            var layer = this.getLayer(currentDomain, layerName);
            if (!load && !layer.isPreloaded(currentTimestamp)) {
                load = true;
                this.threadManager.cancelLoad();
            }
            layer.setTimestamp(currentTimestamp);
        }
        if (load) {
            var endTime = simVars.sortedTimestamps[simVars.sortedTimestamps.length - 1];
            this.loadWithPriority(currentTimestamp, endTime, simVars.overlayOrder);
        }
    }

    async loadWithPriority(startTime, endTime, layerNames) {
        var currentDomain = controllers.currentDomain.getValue();
        var startDate = controllers.startDate.getValue();
        var endDate = controllers.endDate.getValue();
        var timestampsToLoad = simVars.sortedTimestamps.filter((timestamp) => {
            return (timestamp >= startDate && timestamp <= endDate);
        });

        var nFrames = 0;
        var layers = [];
        controllers.loadingProgress.setValue(0);
        for (var layerName of layerNames) {
            var layer = this.getLayer(currentDomain, layerName);
            var layerFrames = layer.hasColorbar ? 2 : 1;
            nFrames += layerFrames * timestampsToLoad.length;
            layers.push(layer);
        }
        controllers.loadingProgress.setFrames(nFrames);

        var loadNow = [];
        var loadLater = [];
        var preloaded = 0;
        for (var timestamp of timestampsToLoad) {
            for (layer of layers) {
                var toLoad = layer.toLoadTimestamp(timestamp);
                if (toLoad) {
                    if (timestamp >= startTime && timestamp <= endTime) {
                        loadNow = loadNow.concat(toLoad);
                    } else {
                        loadLater = loadLater.concat(toLoad);
                    }
                } else {
                    var layerFrames = layer.hasColorbar ? 2 : 1;
                    preloaded += layerFrames;
                }
            }
        }

        controllers.loadingProgress.frameLoaded(preloaded);

        this.threadManager.loadImages(loadNow, loadLater);
    }

    resetLayers() {
        this.threadManager.cancelLoad();
        for (var layerName of simVars.overlayOrder) {
            var layer = this.activeLayers[layerName];
            if (layer != null) {
                layer.imageOverlay.remove(map);
            }
            delete this.activeLayers[layerName];
        }
        simVars.displayedColorbar = null;
        const rasterColorbar = document.querySelector('#raster-colorbar');
        rasterColorbar.src = '';
        rasterColorbar.style.display = 'none';
    }

    resetLayerController() {
        simVars.overlayOrder = [];
        var presetRasters = simVars.presets.rasters;
        if (presetRasters) {
            simVars.overlayOrder = presetRasters;
            simVars.presets.rasters = null;
        }

        // clear cache
        this.rasterDict = this.clearCache(this.rasterDict);
        this.overlayDict = this.clearCache(this.overlayDict);

        this.querySelector('#layer-controller-container').classList.remove('hidden');
        document.querySelector('#copyLink').classList.remove('hidden');
    }

    clearCache(domainDict) {
        for (var domain in domainDict) {
            var layerDict = domainDict[domain];
            for (var timestamp in layerDict) {
                var layer = layerDict[timestamp];
                layer.clearCache();
            }
        }
        return {};
    }

    /** Called when a new domain is selected or a new simulation is selected. */
    domainSwitch() {
        // build the layer groups of the current domain
        var currentDomain = controllers.currentDomain.getValue();
        var timestamp = simVars.sortedTimestamps[0];
        if (this.rasterDict[currentDomain] == null) {
            this.rasterDict[currentDomain] = {};
        }
        if (this.overlayDict[currentDomain] == null) {
            this.overlayDict[currentDomain] = {};
        }

        var firstRasters = simVars.rasters[currentDomain][timestamp];
        var vars = Object.keys(firstRasters);
        var cs = firstRasters[vars[0]].coords;

        for (var layerName in firstRasters) {
            var rasterInfo = firstRasters[layerName];
            var layer = this.getLayer(currentDomain, layerName);
            if (layer == null) {
                layer = new SimulationLayer(layerName, currentDomain, rasterInfo);
                if(simVars.overlayList.indexOf(layerName) >= 0) {
                    this.overlayDict[currentDomain][layerName] = layer;
                } else {
                    this.rasterDict[currentDomain][layerName] = layer;
                }
            }
        };

        var filteredOverlays = simVars.overlayOrder.filter(overlay => {
            return (overlay in this.overlayDict[currentDomain]) || (overlay in this.rasterDict[currentDomain]);
        })
        simVars.overlayOrder = filteredOverlays;

        if (simVars.presets.pan || simVars.presets.zoom) {
            this.setPresetView();
        } else {
            map.fitBounds([ [cs[0][1], cs[0][0]], [cs[2][1], cs[2][0]] ]);
        }

        this.buildLayerBoxes();
    }

    setPresetView() {
        var pan = simVars.presets.pan;
        if (!pan || pan.length != 2 || isNaN(pan[0]) || isNaN(pan[1])) {
            var mapCenter = map.getCenter();
            pan = [mapCenter.lat.toFixed(2), mapCenter.lng.toFixed(2)];
        }
        simVars.presets.pan = null;

        var zoom = simVars.presets.zoom;
        if (!zoom || isNaN(zoom)) {
            zoom = map.getZoom();
        }
        simVars.presets.zoom = null;

        map.setView(pan, zoom);
    }

    /** Called when a layer is selected. */
    handleOverlayadd(layerName) {
        // register in currently displayed layers and bring to front if it's an overlay
        var currentDomain = controllers.currentDomain.getValue();
        var layer = this.getLayer(currentDomain, layerName);
        console.log('name ' + layerName + ' layer ' + layer.imageOverlay);
        layer.addLayerToMap();
        this.activeLayers[layerName] = layer;
        // Make sure overlays are still on top
        for (var overlay of simVars.overlayOrder) {
            if (simVars.overlayList.includes(overlay)) {
                var overlayLayer = this.getLayer(currentDomain, overlay);
                overlayLayer.bringToFront();
            }
        }
        if (simVars.overlayOrder.length > 1) {
            var lastLayerName = simVars.overlayOrder[simVars.overlayOrder.length - 2];
            var lastLayer = this.getLayer(currentDomain, lastLayerName);
            lastLayer.setOpacity(.5);
        }
        setURL();
    }

    /** Called when a layer is de-selected. */
    handleOverlayRemove(layerName) {
        var currentDomain = controllers.currentDomain.getValue();
        var currentTimestamp = controllers.currentTimestamp.getValue();
        var removedLayer = this.getLayer(currentDomain, layerName);
        removedLayer.removeLayer();
        delete this.activeLayers[layerName];

        if (simVars.overlayOrder.length > 0) {
            var topOpacity = controllers.opacity.value;
            var lastLayerName = simVars.overlayOrder[simVars.overlayOrder.length - 1];
            var lastLayer = this.getLayer(currentDomain, lastLayerName);
            lastLayer.setOpacity(topOpacity);
        }
        const rasterColorbar = document.querySelector('#raster-colorbar');
        var rasters_now = simVars.rasters[currentDomain][currentTimestamp];
        var mostRecentColorbar = null;
        var colorbarSrc = '';
        var colorbarDisplay = 'none';
        for (var i = simVars.overlayOrder.length - 1; i >= 0; i--) {
            if ('colorbar' in rasters_now[simVars.overlayOrder[i]]) {
                mostRecentColorbar = simVars.overlayOrder[i];
                colorbarSrc = simVars.rasterBase + rasters_now[simVars.overlayOrder[i]].colorbar;
                colorbarDisplay = 'block';
                break;
            }
        }

        simVars.displayedColorbar = mostRecentColorbar;
        rasterColorbar.src = colorbarSrc;
        rasterColorbar.style.display = colorbarDisplay;
        var startDate = controllers.currentTimestamp.getValue();
        var endDate = simVars.sortedTimestamps[simVars.sortedTimestamps.length - 1];
        this.loadWithPriority(startDate, endDate, simVars.overlayOrder);
        setURL();
    }

    /** Returns the layer associated with a given name */
    getLayer(domain, name) {
        if (simVars.overlayList.includes(name)) {
            if (this.overlayDict[domain] == null) {
                return;
            }
            return this.overlayDict[domain][name];
        }
        if (this.rasterDict[domain] == null) {
            return;
        }
        return this.rasterDict[domain][name];
    }

    /** Adds checkboxes for the different available map types. Should only be called once after
     * the map has been initialized. */
    buildMapBase() {
        const baseMapDiv = this.querySelector('#map-checkboxes');
        const mapCheckCallback = ([layerName, layer]) => {
            if (layerName != this.mapType) {
                layer.addTo(map);
                this.mapType = layerName;
                layer.bringToFront();
            } else {
                layer.remove(map);
            }
        }
        for (const [layerName, layer] of Object.entries(simVars.baseLayerDict)) {
            var checked = layerName == this.mapType;
            let mapCheckBox = buildCheckBox(layerName, 'radio', 'base',
                                             checked, mapCheckCallback, [layerName, layer]);
            baseMapDiv.appendChild(mapCheckBox);
        }
    }

    /** Builds a checkbox for each raster layer and overlay layer */
    buildLayerBoxes() {
        const rasterRegion = this.querySelector('#raster-layers');
        rasterRegion.classList.remove('hidden');
        if (Object.keys(this.rasterDict).length == 0) {
            rasterRegion.classList.add('hidden');
        }
        const overlayRegion = this.querySelector('#overlay-layers');
        overlayRegion.classList.remove('hidden');
        if (Object.keys(this.overlayDict).length == 0) {
            overlayRegion.classList.add('hidden');
        }

        const rasterDiv = this.querySelector('#raster-checkboxes');
        rasterDiv.innerHTML = '';
        const overlayDiv = this.querySelector('#overlay-checkboxes');
        overlayDiv.innerHTML = '';

        var currentDomain = controllers.currentDomain.getValue();
        var rasterDict = this.rasterDict[currentDomain];
        var overlayDict = this.overlayDict[currentDomain];
        const layerClick = (layerName) => {
            if (!simVars.overlayOrder.includes(layerName)) {
                var startDate = controllers.currentTimestamp.getValue();
                var endDate = controllers.endDate.getValue();
                this.handleOverlayadd(layerName);
                this.loadWithPriority(startDate, endDate, simVars.overlayOrder);
            } else {
                this.handleOverlayRemove(layerName);
            }
        };
        for (const [layerDiv, layerDict] of [[rasterDiv, rasterDict], [overlayDiv, overlayDict]]) {
            for (var layerName in layerDict) {
                var checked = simVars.overlayOrder.includes(layerName);
                var layerBox = buildCheckBox(layerName, 'checkbox', 'layers',
                                              checked, layerClick, layerName);
                layerDiv.appendChild(layerBox);
            }
        }
        for (var layerName of simVars.overlayOrder) {
            this.handleOverlayadd(layerName);
        }
    }
}

window.customElements.define('layer-controller', LayerController);<commit_msg>BUG FIX: Reorders chain of events for switching domains so the loading happens before the timestamp is updated.<commit_after>import { dragElement, setURL, buildCheckBox } from '../util.js';
import { controllerEvents, controllers } from './Controller.js';
import { OpacitySlider } from './opacitySlider.js';
import { simVars } from '../simVars.js';
import { map } from '../map.js';
import { SimulationLayer } from './simulationLayer.js';
import { ThreadManager } from '../../threadManager.js';

/**
 * Component that handles adding and removing layers to the map. Provides user with a window
 * to choose different layers available to add.
 */
export class LayerController extends HTMLElement {
    constructor() {
        super();
        this.innerHTML = `

                    layers

Base Maps

Rasters

Overlays

Top Layer Opacity

        `;
        this.mapType = 'OSM';
        this.overlayDict = {};
        this.rasterDict = {};
        this.activeLayers = {};
        this.threadManager;
    }

    /** Disable map events from within the layer selection window to prevent unwanted zooming
     * and panning. Set up callbacks to trigger when currentdomain updates and currentTimestamp
     * updates. */
    connectedCallback() {
        const layerController = this.querySelector('#layer-controller-container');
        dragElement(layerController, '');
        L.DomEvent.disableClickPropagation(layerController);
        L.DomEvent.disableScrollPropagation(layerController);
        this.setLayerButton();
        // simplify this
        const domainSubscription = () => {
            this.resetLayers();
            this.domainSwitch();
            var startDate = controllers.startDate.value;
            var endDate = controllers.endDate.value;
            this.loadWithPriority(startDate, endDate, simVars.overlayOrder);
            this.updateTime();
        }
        const domainResetSubscription = () => {
            this.resetLayers();
            this.resetLayerController();
            this.domainSwitch();
            this.updateTime();
        }
        controllers.currentDomain.subscribe(domainSubscription);
        controllers.currentDomain.subscribe(domainResetSubscription, controllerEvents.simReset);

        controllers.currentTimestamp.subscribe(() => this.updateTime());
        controllers.opacity.subscribe(() => {
            var newOpacity = controllers.opacity.getValue();
            if (simVars.overlayOrder.length > 0) {
                var currentDomain = controllers.currentDomain.getValue();
                var topLayerName = simVars.overlayOrder[simVars.overlayOrder.length - 1];
                var topLayer = this.getLayer(currentDomain, topLayerName);
                topLayer.setOpacity(newOpacity);
            }
        });

        const reload = () => {
            var startDate = controllers.startDate.getValue();
            var endDate = controllers.endDate.getValue();
            this.loadWithPriority(startDate, endDate, simVars.overlayOrder);
        }
        controllers.startDate.subscribe(reload);
        controllers.endDate.subscribe(reload);

        const opacitySlider = new OpacitySlider();
        const opacitySliderContainer = this.querySelector('#opacity-slider-container');
        opacitySliderContainer.appendChild(opacitySlider);
        this.buildMapBase();
        this.startThreadManager();
    }

    startThreadManager() {
        const callback = (loadInfo) => {
            const blob = loadInfo.blob;
            const layerDomain = loadInfo.layerDomain;
            const layerName = loadInfo.layerName;
            const timestamp = loadInfo.timeStamp;
            const colorbar = loadInfo.colorbar;

            var objectURL = null;
            if (blob.size > 0) {
                objectURL = URL.createObjectURL(blob);
            }
            var layer = this.getLayer(layerDomain, layerName);
            layer.setImageLoaded(timestamp, objectURL, colorbar);

            controllers.loadingProgress.frameLoaded();
        };

        this.threadManager = new ThreadManager(callback);
    }

    setLayerButton() {
        const layersButton = this.querySelector('#layers-button');

        L.DomEvent.disableClickPropagation(layersButton);
        layersButton.onpointerdown = (e) => {
            const layersSelector = document.querySelector('#layer-controller-container');
            if (layersSelector.classList.contains('hidden')) {
                document.querySelector('.catalog-menu').classList.add('hidden');
                document.querySelector('#domain-selector').classList.add('hidden');
                layersSelector.classList.remove('hidden');
            } else {
                layersSelector.classList.add('hidden');
            }
        }
    }

    /** Triggered whenever currentTimestamp is changed. For every layer currently selected
     * need to set its image url to the point to the image associated with the current time.
     * Need to update the colorbar on top to the current time as well.
     */
    updateTime() {
        var currentDomain = controllers.currentDomain.getValue();
        var currentTimestamp = controllers.currentTimestamp.getValue();
        var load = false;
        for (var layerName of simVars.overlayOrder) {
            var layer = this.getLayer(currentDomain, layerName);
            if (!load && !layer.isPreloaded(currentTimestamp)) {
                load = true;
                this.threadManager.cancelLoad();
            }
            layer.setTimestamp(currentTimestamp);
        }
        if (load) {
            var endTime = simVars.sortedTimestamps[simVars.sortedTimestamps.length - 1];
            this.loadWithPriority(currentTimestamp, endTime, simVars.overlayOrder);
        }
    }

    async loadWithPriority(startTime, endTime, layerNames) {
        var currentDomain = controllers.currentDomain.getValue();
        var startDate = controllers.startDate.getValue();
        var endDate = controllers.endDate.getValue();
        var timestampsToLoad = simVars.sortedTimestamps.filter((timestamp) => {
            return (timestamp >= startDate && timestamp <= endDate);
        });

        var nFrames = 0;
        var layers = [];
        controllers.loadingProgress.setValue(0);
        for (var layerName of layerNames) {
            var layer = this.getLayer(currentDomain, layerName);
            var layerFrames = layer.hasColorbar ? 2 : 1;
            nFrames += layerFrames * timestampsToLoad.length;
            layers.push(layer);
        }
        controllers.loadingProgress.setFrames(nFrames);

        var loadNow = [];
        var loadLater = [];
        var preloaded = 0;
        for (var timestamp of timestampsToLoad) {
            for (layer of layers) {
                var toLoad = layer.toLoadTimestamp(timestamp);
                if (toLoad) {
                    if (timestamp >= startTime && timestamp <= endTime) {
                        loadNow = loadNow.concat(toLoad);
                    } else {
                        loadLater = loadLater.concat(toLoad);
                    }
                } else {
                    var layerFrames = layer.hasColorbar ? 2 : 1;
                    preloaded += layerFrames;
                }
            }
        }

        controllers.loadingProgress.frameLoaded(preloaded);

        this.threadManager.loadImages(loadNow, loadLater);
    }

    resetLayers() {
        this.threadManager.cancelLoad();
        for (var layerName of simVars.overlayOrder) {
            var layer = this.activeLayers[layerName];
            if (layer != null) {
                layer.imageOverlay.remove(map);
            }
            delete this.activeLayers[layerName];
        }
        simVars.displayedColorbar = null;
        const rasterColorbar = document.querySelector('#raster-colorbar');
        rasterColorbar.src = '';
        rasterColorbar.style.display = 'none';
    }

    resetLayerController() {
        simVars.overlayOrder = [];
        var presetRasters = simVars.presets.rasters;
        if (presetRasters) {
            simVars.overlayOrder = presetRasters;
            simVars.presets.rasters = null;
        }

        // clear cache
        this.rasterDict = this.clearCache(this.rasterDict);
        this.overlayDict = this.clearCache(this.overlayDict);

        this.querySelector('#layer-controller-container').classList.remove('hidden');
        document.querySelector('#copyLink').classList.remove('hidden');
    }

    clearCache(domainDict) {
        for (var domain in domainDict) {
            var layerDict = domainDict[domain];
            for (var timestamp in layerDict) {
                var layer = layerDict[timestamp];
                layer.clearCache();
            }
        }
        return {};
    }

    /** Called when a new domain is selected or a new simulation is selected. */
    domainSwitch() {
        // build the layer groups of the current domain
        var currentDomain = controllers.currentDomain.getValue();
        var timestamp = simVars.sortedTimestamps[0];
        if (this.rasterDict[currentDomain] == null) {
            this.rasterDict[currentDomain] = {};
        }
        if (this.overlayDict[currentDomain] == null) {
            this.overlayDict[currentDomain] = {};
        }

        var firstRasters = simVars.rasters[currentDomain][timestamp];
        var vars = Object.keys(firstRasters);
        var cs = firstRasters[vars[0]].coords;

        for (var layerName in firstRasters) {
            var rasterInfo = firstRasters[layerName];
            var layer = this.getLayer(currentDomain, layerName);
            if (layer == null) {
                layer = new SimulationLayer(layerName, currentDomain, rasterInfo);
                if(simVars.overlayList.indexOf(layerName) >= 0) {
                    this.overlayDict[currentDomain][layerName] = layer;
                } else {
                    this.rasterDict[currentDomain][layerName] = layer;
                }
            }
        };

        var filteredOverlays = simVars.overlayOrder.filter(overlay => {
            return (overlay in this.overlayDict[currentDomain]) || (overlay in this.rasterDict[currentDomain]);
        })
        simVars.overlayOrder = filteredOverlays;

        if (simVars.presets.pan || simVars.presets.zoom) {
            this.setPresetView();
        } else {
            map.fitBounds([ [cs[0][1], cs[0][0]], [cs[2][1], cs[2][0]] ]);
        }

        this.buildLayerBoxes();
    }

    setPresetView() {
        var pan = simVars.presets.pan;
        if (!pan || pan.length != 2 || isNaN(pan[0]) || isNaN(pan[1])) {
            var mapCenter = map.getCenter();
            pan = [mapCenter.lat.toFixed(2), mapCenter.lng.toFixed(2)];
        }
        simVars.presets.pan = null;

        var zoom = simVars.presets.zoom;
        if (!zoom || isNaN(zoom)) {
            zoom = map.getZoom();
        }
        simVars.presets.zoom = null;

        map.setView(pan, zoom);
    }

    /** Called when a layer is selected. */
    handleOverlayadd(layerName) {
        // register in currently displayed layers and bring to front if it's an overlay
        var currentDomain = controllers.currentDomain.getValue();
        var layer = this.getLayer(currentDomain, layerName);
        console.log('name ' + layerName + ' layer ' + layer.imageOverlay);
        layer.addLayerToMap();
        this.activeLayers[layerName] = layer;
        // Make sure overlays are still on top
        for (var overlay of simVars.overlayOrder) {
            if (simVars.overlayList.includes(overlay)) {
                var overlayLayer = this.getLayer(currentDomain, overlay);
                overlayLayer.bringToFront();
            }
        }
        if (simVars.overlayOrder.length > 1) {
            var lastLayerName = simVars.overlayOrder[simVars.overlayOrder.length - 2];
            var lastLayer = this.getLayer(currentDomain, lastLayerName);
            lastLayer.setOpacity(.5);
        }
        setURL();
    }

    /** Called when a layer is de-selected. */
    handleOverlayRemove(layerName) {
        var currentDomain = controllers.currentDomain.getValue();
        var currentTimestamp = controllers.currentTimestamp.getValue();
        var removedLayer = this.getLayer(currentDomain, layerName);
        removedLayer.removeLayer();
        delete this.activeLayers[layerName];

        if (simVars.overlayOrder.length > 0) {
            var topOpacity = controllers.opacity.value;
            var lastLayerName = simVars.overlayOrder[simVars.overlayOrder.length - 1];
            var lastLayer = this.getLayer(currentDomain, lastLayerName);
            lastLayer.setOpacity(topOpacity);
        }
        const rasterColorbar = document.querySelector('#raster-colorbar');
        var rasters_now = simVars.rasters[currentDomain][currentTimestamp];
        var mostRecentColorbar = null;
        var colorbarSrc = '';
        var colorbarDisplay = 'none';
        for (var i = simVars.overlayOrder.length - 1; i >= 0; i--) {
            if ('colorbar' in rasters_now[simVars.overlayOrder[i]]) {
                mostRecentColorbar = simVars.overlayOrder[i];
                colorbarSrc = simVars.rasterBase + rasters_now[simVars.overlayOrder[i]].colorbar;
                colorbarDisplay = 'block';
                break;
            }
        }

        simVars.displayedColorbar = mostRecentColorbar;
        rasterColorbar.src = colorbarSrc;
        rasterColorbar.style.display = colorbarDisplay;
        var startDate = controllers.currentTimestamp.getValue();
        var endDate = simVars.sortedTimestamps[simVars.sortedTimestamps.length - 1];
        this.loadWithPriority(startDate, endDate, simVars.overlayOrder);
        setURL();
    }

    /** Returns the layer associated with a given name */
    getLayer(domain, name) {
        if (simVars.overlayList.includes(name)) {
            if (this.overlayDict[domain] == null) {
                return;
            }
            return this.overlayDict[domain][name];
        }
        if (this.rasterDict[domain] == null) {
            return;
        }
        return this.rasterDict[domain][name];
    }

    /** Adds checkboxes for the different available map types. Should only be called once after
     * the map has been initialized. */
    buildMapBase() {
        const baseMapDiv = this.querySelector('#map-checkboxes');
        const mapCheckCallback = ([layerName, layer]) => {
            if (layerName != this.mapType) {
                layer.addTo(map);
                this.mapType = layerName;
                layer.bringToFront();
            } else {
                layer.remove(map);
            }
        }
        for (const [layerName, layer] of Object.entries(simVars.baseLayerDict)) {
            var checked = layerName == this.mapType;
            let mapCheckBox = buildCheckBox(layerName, 'radio', 'base',
                                             checked, mapCheckCallback, [layerName, layer]);
            baseMapDiv.appendChild(mapCheckBox);
        }
    }

    /** Builds a checkbox for each raster layer and overlay layer */
    buildLayerBoxes() {
        const rasterRegion = this.querySelector('#raster-layers');
        rasterRegion.classList.remove('hidden');
        if (Object.keys(this.rasterDict).length == 0) {
            rasterRegion.classList.add('hidden');
        }
        const overlayRegion = this.querySelector('#overlay-layers');
        overlayRegion.classList.remove('hidden');
        if (Object.keys(this.overlayDict).length == 0) {
            overlayRegion.classList.add('hidden');
        }

        const rasterDiv = this.querySelector('#raster-checkboxes');
        rasterDiv.innerHTML = '';
        const overlayDiv = this.querySelector('#overlay-checkboxes');
        overlayDiv.innerHTML = '';

        var currentDomain = controllers.currentDomain.getValue();
        var rasterDict = this.rasterDict[currentDomain];
        var overlayDict = this.overlayDict[currentDomain];
        const layerClick = (layerName) => {
            if (!simVars.overlayOrder.includes(layerName)) {
                var startDate = controllers.currentTimestamp.getValue();
                var endDate = controllers.endDate.getValue();
                this.handleOverlayadd(layerName);
                this.loadWithPriority(startDate, endDate, simVars.overlayOrder);
            } else {
                this.handleOverlayRemove(layerName);
            }
        };
        for (const [layerDiv, layerDict] of [[rasterDiv, rasterDict], [overlayDiv, overlayDict]]) {
            for (var layerName in layerDict) {
                var checked = simVars.overlayOrder.includes(layerName);
                var layerBox = buildCheckBox(layerName, 'checkbox', 'layers',
                                              checked, layerClick, layerName);
                layerDiv.appendChild(layerBox);
            }
        }
        for (var layerName of simVars.overlayOrder) {
            this.handleOverlayadd(layerName);
        }
    }
}

window.customElements.define('layer-controller', LayerController);<|endoftext|>


<commit_before>// m31_001 #1021 JSTOR DDA not eligible for CLS
//
//
casper.test.begin('m31_001 #1021 JSTOR DDA not eligible for CLS request', 3, function suite(test) {
casper.echo("Test environment: "+SERVER);
var USECASE='/item/15090040';
var ENTITY=SERVER+USECASE;
	casper.start(ENTITY, function() {
	test.assertTextExists('0691115311' , 'verified page');
	test.assertTextExists('JSTOR DDA' , 'verified DDA holding');
	test.assertTextDoesntExist('Request' , 'verified no request link on page');
    }).run(function() {
        test.done();
    });
});
<commit_msg>update use case<commit_after>// m31_001 #1021 JSTOR DDA not eligible for CLS
//
//
casper.test.begin('m31_001 #1021 JSTOR DDA not eligible for CLS request', 3, function suite(test) {
casper.echo("Test environment: "+SERVER);
var USECASE='/item/15663185';
var ENTITY=SERVER+USECASE;
	casper.start(ENTITY, function() {
	test.assertTextExists('Instrumental community' , 'verified title');
	test.assertTextExists('JSTOR DDA' , 'verified DDA holding');
	test.assertTextDoesntExist('Request' , 'verified no request link on page');
    }).run(function() {
        test.done();
    });
});
<|endoftext|>


<commit_before>function drawIssuesActivity() {
    $.getJSON(API_BASE + '/issues_activity')
        .done(function (json) {
            data = json.data;
            var opened = data.opened;
            var closed = data.closed;
            $('#issues-activity').highcharts({
                chart: {
                    type: 'spline'
                },
                title: {
                    text: 'Issues Burndown'
                },
                subtitle: {
                    text: '# of issues opened vs closed, monthly'
                },
                xAxis: {
                    categories: opened.map(function(e) { return e.month; })
                },
                yAxis: {
                    min: 0,
                    title: {
                        text: '# Issues'
                    }
                },
                legend: {
                    enabled: false
                },
                series: [
                    {
                        name: 'Opened',
                        data: opened.map(function(e) { return e.value; }),
                        lineColor: '#FF4E50',
                        color: '#FF4E50'
                    },
                    {
                        name: 'Closed',
                        data: closed.map(function(e) { return e.value; }),
                        lineColor: '#88C425',
                        color: '#88C425'
                    }
                ]
            });
        })
        .fail(displayFailMessage);
}

var issuesListTemplate = Handlebars.compile($('#issues-list-template').html());

function drawUntouchedIssues() {
    $.getJSON(API_BASE + '/untouched_issues')
        .done(function(json) {
            var data = json.data;
            var context = {
                issues: data,
                title: "Untouched Issues",
                subtitle: "(max. 20 results)"
            }
            var $list = $(issuesListTemplate(context));
            $('#untouched-issues').empty().append($list);
        })
        .fail(displayFailMessage);
}

function drawInactiveIssues() {
    $.getJSON(API_BASE + '/inactive_issues')
        .done(function(json) {
            var data = json.data;
            var context = {
                issues: data,
                title: "Inactive Issues (2 weeks)",
                subtitle: "(max. 20 results)"
            }
            var $list = $(issuesListTemplate(context));
            $('#inactive-issues').empty().append($list);
        })
        .fail(displayFailMessage);
}

function drawAvgIssueTime() {
    makeXYGraph('#avg-issue-time', {
        endpoint: '/avg_issue_time',
        type: 'spline',
        title: "Average Issue Time",
        subtitle: "From the time it's opened until it's closed",
        keyName: 'month',
        valueName: function(e) {
            var m = moment.duration(e.value, 'seconds');
            return {
                name: m.humanize(),
                y: Math.ceil(e.value / (3600 * 24))
            };
        },
        yTitle: 'Days',
        label: 'days'
    });
}

function makeD3Graph(issues_data) {
    var nodes = [];
    var links = [];
    for (number in issues_data) {
        var user_nodes = [];
        var issue = issues_data[number];
        issue.users.forEach(function(user) {
            var node = {
                type: 'user',
                data: user
            }
            nodes.push(node);
            user_nodes.push(node);
        });
        var issue_node = {
            type: 'issue',
            data: issue.issue
        }
        nodes.push(issue_node);

        // make links
        user_nodes.forEach(function(node) {
            var link = {
                source: node,
                target: issue_node
            };
            links.push(link);
        });
    }
    return {
        nodes: nodes,
        links: links
    }
}

var tooltipUserTemplate = Handlebars.compile($('#tooltip-user-template').html());
var tooltipIssueTemplate = Handlebars.compile($('#tooltip-issue-template').html());
function drawIssuesInvolvement() {
    $.getJSON(API_BASE + '/issues_involvement')
        .done(function(json) {
            var $container = $('#issues-involvement-graph-container');
            var width = $container.width();
            var height = $container.height();
            var radius = 25;
            var ratio = 1.5;

            var graph_data = makeD3Graph(json.data);
            var nodes = graph_data.nodes;
            var links = graph_data.links;

            var force = d3.layout.force()
                .charge(-350)
                .linkDistance(80)
                .gravity(0.05)
                .size([width, height]);

            var tip = d3.tip().attr('class', 'd3-tip').html(function(d) {
                var context;
                var template;
                if (d.type == 'issue') {
                    context = {
                        number: d.data.number,
                        title: d.data.title,
                        url: d.data.html_url,
                        comments: d.data.comments,
                        ago: moment().from(d.data.created_at, true)
                    };
                    template = tooltipIssueTemplate;
                } else {
                    context = {
                        login: d.data.login,
                        imgURL: d.data.avatar_url
                    };
                    template = tooltipUserTemplate;
                }
                return template(context);
            });

            var svg = d3.select('#issues-involvement-graph-container').append('svg')
                .attr('width', width)
                .attr('height', height)
                .on('mousedown', tip.hide);

            svg.call(tip);

            force.nodes(nodes)
                .links(links)
                .start();

            var link = svg.selectAll(".link")
                .data(links)
                .enter().append("line")
                .attr("class", "link")
                .style("stroke-width", 2);

            var gnodes = svg.selectAll('g.gnode')
                .data(nodes)
                .enter()
                .append('g')
                .classed('gnode', true)
                .call(force.drag);

            var nodes = gnodes.append('circle')
                .attr('class', 'node')
                .attr("r", function(d) {
                    if (d.type == 'issue') {
                        return radius;
                    }
                    return radius / ratio;
                })
                .style("fill", function(d) {
                    if (d.type == 'issue') {
                        return '#FF4E50';
                    }
                    return '#88C425';
                });

            var labels = gnodes.append('text')
                .attr('class', 'textnode')
                .attr('dx', function (d) {
                    if (d.type == 'issue') {
                        return radius + 3;
                    }
                    return radius / ratio + 3;
                })
                .attr('dy', 6)
                .style('fill', 'white')
                .text(function(d) {
                    if (d.type == 'issue') {
                        return '#' + d.data.number;
                    }
                    return d.data.login;
                })
                .on('mousedown', function(e) {
                    d3.event.stopPropagation();
                    tip.show(e);
                });

            force.on("tick", function() {
                link.attr("x1", function(d) { return d.source.x; })
                    .attr("y1", function(d) { return d.source.y; })
                    .attr("x2", function(d) { return d.target.x; })
                    .attr("y2", function(d) { return d.target.y; });

                gnodes.attr("transform", function(d) {
                    return 'translate(' + [d.x, d.y] + ')';
                });

                // http://mbostock.github.io/d3/talk/20110921/bounding.html
                var rl = radius + 5;
                gnodes.attr("cx", function(d) { return d.x = Math.max(rl, Math.min(width - rl, d.x)); })
                    .attr("cy", function(d) { return d.y = Math.max(rl, Math.min(height - rl, d.y)); });
            });
        })
        .fail(displayFailMessage);
}

function addMilestoneStatus() {

    var endpoint = API_HOST + REPO + '/milestones';
    var $milestones = $('#milestones');

    $.get(endpoint)
        .success(displayData)
        .fail(displayFailMessage);

    function displayData(data) {

        if (data.data.length) {

            var template = Handlebars.compile($('#insights-milestone').html());

            $.each(data.data, function(idx, milestone) {

                var due_date = 0;
                var row;
                var delay = false;

                if (idx % 2 == 0) { // two milestones per row
                    row = $('<div/>').addClass('row');
                    $milestones.append(row);
                }

                if (milestone.due_on) {
                    var start = moment(new Date());
                    var end = moment((new Date(milestone.due_on)).getTime());
                    due_date = start.from(end, true);
                    if ((new Date(milestone.due_on)).getTime() > (new Date()).getTime()) {
                        due_date = 'in ' + due_date;
                    } else {
                        due_date += ' ago';
                        delay = true;
                    }
                }

                var context = {
                    title: milestone.title,
                    due: due_date,
                    progress: parseInt(milestone.closed_issues / milestone.open_issues * 100, 10),
                    delay: delay
                }

                $('.row:last-child', $milestones).append(template(context));

            });
        }

    }

}

function drawInsights () {
    drawIssuesActivity();
    drawUntouchedIssues();
    drawInactiveIssues();
    drawAvgIssueTime();
    drawIssuesInvolvement();
    addMilestoneStatus();
}
<commit_msg>Fix JSHint errors<commit_after>/* globals API_BASE, REPO, API_HOST, $, Handlebars, d3, moment */

'use strict';

function drawIssuesActivity() {
    $.getJSON(API_BASE + '/issues_activity')
        .done(function (json) {
            var data = json.data;
            var opened = data.opened;
            var closed = data.closed;
            $('#issues-activity').highcharts({
                chart: {
                    type: 'spline'
                },
                title: {
                    text: 'Issues Burndown'
                },
                subtitle: {
                    text: '# of issues opened vs closed, monthly'
                },
                xAxis: {
                    categories: opened.map(function(e) { return e.month; })
                },
                yAxis: {
                    min: 0,
                    title: {
                        text: '# Issues'
                    }
                },
                legend: {
                    enabled: false
                },
                series: [
                    {
                        name: 'Opened',
                        data: opened.map(function(e) { return e.value; }),
                        lineColor: '#FF4E50',
                        color: '#FF4E50'
                    },
                    {
                        name: 'Closed',
                        data: closed.map(function(e) { return e.value; }),
                        lineColor: '#88C425',
                        color: '#88C425'
                    }
                ]
            });
        })
        .fail(displayFailMessage);
}

var issuesListTemplate = Handlebars.compile($('#issues-list-template').html());

function drawUntouchedIssues() {
    $.getJSON(API_BASE + '/untouched_issues')
        .done(function(json) {
            var data = json.data;
            var context = {
                issues: data,
                title: "Untouched Issues",
                subtitle: "(max. 20 results)"
            };
            var $list = $(issuesListTemplate(context));
            $('#untouched-issues').empty().append($list);
        })
        .fail(displayFailMessage);
}

function drawInactiveIssues() {
    $.getJSON(API_BASE + '/inactive_issues')
        .done(function(json) {
            var data = json.data;
            var context = {
                issues: data,
                title: "Inactive Issues (2 weeks)",
                subtitle: "(max. 20 results)"
            }
            var $list = $(issuesListTemplate(context));
            $('#inactive-issues').empty().append($list);
        })
        .fail(displayFailMessage);
}

function drawAvgIssueTime() {
    makeXYGraph('#avg-issue-time', {
        endpoint: '/avg_issue_time',
        type: 'spline',
        title: "Average Issue Time",
        subtitle: "From the time it's opened until it's closed",
        keyName: 'month',
        valueName: function(e) {
            var m = moment.duration(e.value, 'seconds');
            return {
                name: m.humanize(),
                y: Math.ceil(e.value / (3600 * 24))
            };
        },
        yTitle: 'Days',
        label: 'days'
    });
}

function makeD3Graph(issues_data) {
    var nodes = [];
    var links = [];
    for (var number in issues_data) {
        var user_nodes = [];
        var issue = issues_data[number];
        issue.users.forEach(function(user) {
            var node = {
                type: 'user',
                data: user
            }
            nodes.push(node);
            user_nodes.push(node);
        });
        var issue_node = {
            type: 'issue',
            data: issue.issue
        };
        nodes.push(issue_node);

        // make links
        user_nodes.forEach(function(node) {
            var link = {
                source: node,
                target: issue_node
            };
            links.push(link);
        });
    }
    return {
        nodes: nodes,
        links: links
    }
}

var tooltipUserTemplate = Handlebars.compile($('#tooltip-user-template').html());
var tooltipIssueTemplate = Handlebars.compile($('#tooltip-issue-template').html());
function drawIssuesInvolvement() {
    $.getJSON(API_BASE + '/issues_involvement')
        .done(function(json) {
            var $container = $('#issues-involvement-graph-container');
            var width = $container.width();
            var height = $container.height();
            var radius = 25;
            var ratio = 1.5;

            var graph_data = makeD3Graph(json.data);
            var nodes = graph_data.nodes;
            var links = graph_data.links;

            var force = d3.layout.force()
                .charge(-350)
                .linkDistance(80)
                .gravity(0.05)
                .size([width, height]);

            var tip = d3.tip().attr('class', 'd3-tip').html(function(d) {
                var context;
                var template;
                if (d.type == 'issue') {
                    context = {
                        number: d.data.number,
                        title: d.data.title,
                        url: d.data.html_url,
                        comments: d.data.comments,
                        ago: moment().from(d.data.created_at, true)
                    };
                    template = tooltipIssueTemplate;
                } else {
                    context = {
                        login: d.data.login,
                        imgURL: d.data.avatar_url
                    };
                    template = tooltipUserTemplate;
                }
                return template(context);
            });

            var svg = d3.select('#issues-involvement-graph-container').append('svg')
                .attr('width', width)
                .attr('height', height)
                .on('mousedown', tip.hide);

            svg.call(tip);

            force.nodes(nodes)
                .links(links)
                .start();

            var link = svg.selectAll(".link")
                .data(links)
                .enter().append("line")
                .attr("class", "link")
                .style("stroke-width", 2);

            var gnodes = svg.selectAll('g.gnode')
                .data(nodes)
                .enter()
                .append('g')
                .classed('gnode', true)
                .call(force.drag);

            var nodes = gnodes.append('circle')
                .attr('class', 'node')
                .attr("r", function(d) {
                    if (d.type == 'issue') {
                        return radius;
                    }
                    return radius / ratio;
                })
                .style("fill", function(d) {
                    if (d.type == 'issue') {
                        return '#FF4E50';
                    }
                    return '#88C425';
                });

            var labels = gnodes.append('text')
                .attr('class', 'textnode')
                .attr('dx', function (d) {
                    if (d.type == 'issue') {
                        return radius + 3;
                    }
                    return radius / ratio + 3;
                })
                .attr('dy', 6)
                .style('fill', 'white')
                .text(function(d) {
                    if (d.type == 'issue') {
                        return '#' + d.data.number;
                    }
                    return d.data.login;
                })
                .on('mousedown', function(e) {
                    d3.event.stopPropagation();
                    tip.show(e);
                });

            force.on("tick", function() {
                link.attr("x1", function(d) { return d.source.x; })
                    .attr("y1", function(d) { return d.source.y; })
                    .attr("x2", function(d) { return d.target.x; })
                    .attr("y2", function(d) { return d.target.y; });

                gnodes.attr("transform", function(d) {
                    return 'translate(' + [d.x, d.y] + ')';
                });

                // http://mbostock.github.io/d3/talk/20110921/bounding.html
                var rl = radius + 5;
                gnodes.attr("cx", function(d) { return d.x = Math.max(rl, Math.min(width - rl, d.x)); })
                    .attr("cy", function(d) { return d.y = Math.max(rl, Math.min(height - rl, d.y)); });
            });
        })
        .fail(displayFailMessage);
}

function addMilestoneStatus() {

    var endpoint = API_HOST + REPO + '/milestones';
    var $milestones = $('#milestones');

    $.get(endpoint)
        .success(displayData)
        .fail(displayFailMessage);

    function displayData(data) {

        if (data.data.length) {

            var template = Handlebars.compile($('#insights-milestone').html());

            $.each(data.data, function(idx, milestone) {

                var due_date = 0;
                var row;
                var delay = false;

                if (idx % 2 === 0) { // two milestones per row
                    row = $('<div/>').addClass('row');
                    $milestones.append(row);
                }

                if (milestone.due_on) {
                    var start = moment(new Date());
                    var end = moment((new Date(milestone.due_on)).getTime());
                    due_date = start.from(end, true);
                    if ((new Date(milestone.due_on)).getTime() > (new Date()).getTime()) {
                        due_date = 'in ' + due_date;
                    } else {
                        due_date += ' ago';
                        delay = true;
                    }
                }

                var context = {
                    title: milestone.title,
                    due: due_date,
                    progress: parseInt(milestone.closed_issues / milestone.open_issues * 100, 10),
                    delay: delay
                };

                $('.row:last-child', $milestones).append(template(context));

            });
        }

    }

}

function drawInsights () {
    drawIssuesActivity();
    drawUntouchedIssues();
    drawInactiveIssues();
    drawAvgIssueTime();
    drawIssuesInvolvement();
    addMilestoneStatus();
}
<|endoftext|>