<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import HTML5VideoPlayback from 'playbacks/html5_video'
import HLSJS from 'hls.js'
import Events from 'base/events'
import Playback from 'base/playback'
import Browser from 'components/browser'

export default class HLS extends HTML5VideoPlayback {
  get name() { return 'hls' }

  getPlayableStartTime() {
    if (this.hls && this.hls.levels[this.hls.currentLevel] && this.hls.levels[this.hls.currentLevel].details) {
      return super.getDuration() - this.hls.levels[this.hls.currentLevel].details.totalduration
    }
    return 0
  }

  constructor(options) {
    super(options)
    this.minDvrSize = options.hlsMinimumDvrSize ? options.hlsMinimumDvrSize : 60
    this.playbackType = Playback.VOD
    this.dvrInUse = false
  }

  setupHls() {
    this.hls = new HLSJS(this.options.hlsjsConfig || {})
    this.hls.on(HLSJS.Events.MSE_ATTACHED, () => this.hls.loadSource(this.options.src))
    this.hls.on(HLSJS.Events.MANIFEST_PARSED, () => { this.options.autoPlay && this.play() })
    this.hls.on(HLSJS.Events.LEVEL_LOADED, (evt, data) => this.updatePlaybackType(evt, data))
    this.hls.attachVideo(this.el)
  }

  getCurrentTime() {
    return this.el.currentTime - this.getPlayableStartTime()
  }

  getDuration() {
    if (this.hls && this.hls.levels[this.hls.currentLevel] && this.hls.levels[this.hls.currentLevel].details) {
      return this.hls.levels[this.hls.currentLevel].details.totalduration
    }
    return 0
  }

  seek(seekBarValue) {
    var seekTo = 0
    if (seekBarValue > 0) {
      seekTo = this.getDuration() * (seekBarValue / 100)
    }
    var onDvr = this.dvrEnabled && seekBarValue > 0 && seekBarValue < 100
    seekTo += this.getPlayableStartTime()
    super.seekSeconds(seekTo)
    this.updateDvr(onDvr)
  }

  updateDvr(status) {
    this.dvrInUse = status
    this.trigger(Events.PLAYBACK_DVR, this.dvrInUse)
    this.trigger(Events.PLAYBACK_STATS_ADD, {'dvr': this.dvrInUse})
  }

  durationChange() {
    if (this.playbackType === Playback.VOD) {
      this.settings.left = ["playpause", "position", "duration"]
    } else if (this.dvrEnabled) {
      this.settings.left = ["playpause"]
    } else {
      this.settings.left = ["playstop"]
    }
    this.settings.seekEnabled = this.isSeekEnabled()
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
  }

  timeUpdated() {
    if (this.dvrEnabled) {
      this.trigger(Events.PLAYBACK_TIMEUPDATE, this.dvrInUse ? this.getCurrentTime() : this.getDuration(), this.getDuration(), this.name)
    } else {
      super.timeUpdated()
    }
  }

  play() {
    if (!this.hls) {
      this.setupHls()
    }
    super.play()
  }

  pause() {
    super.pause()
    if (this.dvrEnabled) {
      this.updateDvr(true)
    }
  }

  stop() {
    if (this.hls) {
      this.hls.detachVideo()
      delete this.hls
    }
  }

  updatePlaybackType(evt, data) {
    this.playbackType = data.details.live ? Playback.LIVE : Playback.VOD
  }

  get dvrEnabled() {
    return (this.getDuration() >= this.minDvrSize && this.getPlaybackType() === Playback.LIVE)
  }

  getPlaybackType() {
    return this.playbackType
  }

  isSeekEnabled() {
    return (this.playbackType === Playback.VOD || this.dvrEnabled)
  }
}

HLS.canPlay = function(resource, mimeType) {
  var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
  var isHls = ((resourceParts.length > 1 && resourceParts[1] === "m3u8") ||
        mimeType === 'application/x-mpegURL' || mimeType === 'application/vnd.apple.mpegurl')
  var ignoredBrowser = Browser.isSafari || Browser.isFirefox

  return !!(HLSJS.isSupported() && isHls && !ignoredBrowser)
}
<commit_msg>hls: expose level select interface<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import HTML5VideoPlayback from 'playbacks/html5_video'
import HLSJS from 'hls.js'
import Events from 'base/events'
import Playback from 'base/playback'
import Browser from 'components/browser'

export default class HLS extends HTML5VideoPlayback {
  get name() { return 'hls' }

  get levels() { return (this.hls && this.hls.levels) || [] }
  get currentLevel() { return (this.hls && this.hls.currentLevel) || -1 }
  set currentLevel(level) { this.hls && (this.hls.currentLevel = level) }

  getPlayableStartTime() {
    if (this.hls && this.hls.levels[this.hls.currentLevel] && this.hls.levels[this.hls.currentLevel].details) {
      return super.getDuration() - this.hls.levels[this.hls.currentLevel].details.totalduration
    }
    return 0
  }

  constructor(options) {
    super(options)
    this.minDvrSize = options.hlsMinimumDvrSize ? options.hlsMinimumDvrSize : 60
    this.playbackType = Playback.VOD
    this.dvrInUse = false
  }

  setupHls() {
    this.hls = new HLSJS(this.options.hlsjsConfig || {})
    this.hls.on(HLSJS.Events.MSE_ATTACHED, () => this.hls.loadSource(this.options.src))
    this.hls.on(HLSJS.Events.MANIFEST_PARSED, () => { this.options.autoPlay && this.play() })
    this.hls.on(HLSJS.Events.LEVEL_LOADED, (evt, data) => this.updatePlaybackType(evt, data))
    this.hls.attachVideo(this.el)
  }

  getCurrentTime() {
    return this.el.currentTime - this.getPlayableStartTime()
  }

  getDuration() {
    if (this.hls && this.hls.levels[this.hls.currentLevel] && this.hls.levels[this.hls.currentLevel].details) {
      return this.hls.levels[this.hls.currentLevel].details.totalduration
    }
    return 0
  }

  seek(seekBarValue) {
    var seekTo = 0
    if (seekBarValue > 0) {
      seekTo = this.getDuration() * (seekBarValue / 100)
    }
    var onDvr = this.dvrEnabled && seekBarValue > 0 && seekBarValue < 100
    seekTo += this.getPlayableStartTime()
    super.seekSeconds(seekTo)
    this.updateDvr(onDvr)
  }

  updateDvr(status) {
    this.dvrInUse = status
    this.trigger(Events.PLAYBACK_DVR, this.dvrInUse)
    this.trigger(Events.PLAYBACK_STATS_ADD, {'dvr': this.dvrInUse})
  }

  durationChange() {
    if (this.playbackType === Playback.VOD) {
      this.settings.left = ["playpause", "position", "duration"]
    } else if (this.dvrEnabled) {
      this.settings.left = ["playpause"]
    } else {
      this.settings.left = ["playstop"]
    }
    this.settings.seekEnabled = this.isSeekEnabled()
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
  }

  timeUpdated() {
    if (this.dvrEnabled) {
      this.trigger(Events.PLAYBACK_TIMEUPDATE, this.dvrInUse ? this.getCurrentTime() : this.getDuration(), this.getDuration(), this.name)
    } else {
      super.timeUpdated()
    }
  }

  play() {
    if (!this.hls) {
      this.setupHls()
    }
    super.play()
  }

  pause() {
    super.pause()
    if (this.dvrEnabled) {
      this.updateDvr(true)
    }
  }

  stop() {
    if (this.hls) {
      this.hls.detachVideo()
      delete this.hls
    }
  }

  updatePlaybackType(evt, data) {
    this.playbackType = data.details.live ? Playback.LIVE : Playback.VOD
  }

  get dvrEnabled() {
    return (this.getDuration() >= this.minDvrSize && this.getPlaybackType() === Playback.LIVE)
  }

  getPlaybackType() {
    return this.playbackType
  }

  isSeekEnabled() {
    return (this.playbackType === Playback.VOD || this.dvrEnabled)
  }
}

HLS.canPlay = function(resource, mimeType) {
  var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
  var isHls = ((resourceParts.length > 1 && resourceParts[1] === "m3u8") ||
        mimeType === 'application/x-mpegURL' || mimeType === 'application/vnd.apple.mpegurl')
  var ignoredBrowser = Browser.isSafari || Browser.isFirefox

  return !!(HLSJS.isSupported() && isHls && !ignoredBrowser)
}
<|endoftext|>


<commit_before>/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var mouseChange = require('mouse-change');
var mouseWheel = require('mouse-wheel');
var cartesianConstants = require('../cartesian/constants');

module.exports = createCamera;

function Camera2D(element, plot) {
    this.element = element;
    this.plot = plot;
    this.mouseListener = null;
    this.wheelListener = null;
    this.lastInputTime = Date.now();
    this.lastPos = [0, 0];
    this.boxEnabled = false;
    this.boxInited = false;
    this.boxStart = [0, 0];
    this.boxEnd = [0, 0];
    this.dragStart = [0, 0];
}

function createCamera(scene) {
    var element = scene.mouseContainer,
        plot = scene.glplot,
        result = new Camera2D(element, plot);

    function unSetAutoRange() {
        scene.xaxis.autorange = false;
        scene.yaxis.autorange = false;
    }

    function getSubplotConstraint() {
        // note: this assumes we only have one x and one y axis on this subplot
        // when this constraint is lifted this block won't make sense
        var constraints = scene.graphDiv._fullLayout._axisConstraintGroups;
        var xaId = scene.xaxis._id;
        var yaId = scene.yaxis._id;
        for(var i = 0; i < constraints.length; i++) {
            if(constraints[i][xaId] !== -1) {
                if(constraints[i][yaId] !== -1) return true;
                break;
            }
        }
        return false;
    }

    result.mouseListener = mouseChange(element, function(buttons, x, y) {
        var dataBox = scene.calcDataBox(),
            viewBox = plot.viewBox;

        var lastX = result.lastPos[0],
            lastY = result.lastPos[1];

        var MINDRAG = cartesianConstants.MINDRAG * plot.pixelRatio;
        var MINZOOM = cartesianConstants.MINZOOM * plot.pixelRatio;

        var dx, dy;

        x *= plot.pixelRatio;
        y *= plot.pixelRatio;

        // mouseChange gives y about top; convert to about bottom
        y = (viewBox[3] - viewBox[1]) - y;

        function updateRange(i0, start, end) {
            var range0 = Math.min(start, end),
                range1 = Math.max(start, end);

            if(range0 !== range1) {
                dataBox[i0] = range0;
                dataBox[i0 + 2] = range1;
                result.dataBox = dataBox;
                scene.setRanges(dataBox);
            }
            else {
                scene.selectBox.selectBox = [0, 0, 1, 1];
                scene.glplot.setDirty();
            }
        }

        switch(scene.fullLayout.dragmode) {
            case 'zoom':
                if(buttons) {
                    var dataX = x /
                            (viewBox[2] - viewBox[0]) * (dataBox[2] - dataBox[0]) +
                        dataBox[0];
                    var dataY = y /
                            (viewBox[3] - viewBox[1]) * (dataBox[3] - dataBox[1]) +
                        dataBox[1];

                    if(!result.boxInited) {
                        result.boxStart[0] = dataX;
                        result.boxStart[1] = dataY;
                        result.dragStart[0] = x;
                        result.dragStart[1] = y;
                    }

                    result.boxEnd[0] = dataX;
                    result.boxEnd[1] = dataY;

                    // we need to mark the box as initialized right away
                    // so that we can tell the start and end points apart
                    result.boxInited = true;

                    // but don't actually enable the box until the cursor moves
                    if(!result.boxEnabled && (
                        result.boxStart[0] !== result.boxEnd[0] ||
                        result.boxStart[1] !== result.boxEnd[1])
                    ) {
                        result.boxEnabled = true;
                    }

                    // constrain aspect ratio if the axes require it
                    var smallDx = Math.abs(result.dragStart[0] - x) < MINZOOM;
                    var smallDy = Math.abs(result.dragStart[1] - y) < MINZOOM;
                    if(getSubplotConstraint() && !(smallDx && smallDy)) {
                        dx = result.boxEnd[0] - result.boxStart[0];
                        dy = result.boxEnd[1] - result.boxStart[1];
                        var dydx = (dataBox[3] - dataBox[1]) / (dataBox[2] - dataBox[0]);

                        if(Math.abs(dx * dydx) > Math.abs(dy)) {
                            result.boxEnd[1] = result.boxStart[1] +
                                Math.abs(dx) * dydx * (Math.sign(dy) || 1);

                            // gl-select-box clips to the plot area bounds,
                            // which breaks the axis constraint, so don't allow
                            // this box to go out of bounds
                            if(result.boxEnd[1] < dataBox[1]) {
                                result.boxEnd[1] = dataBox[1];
                                result.boxEnd[0] = result.boxStart[0] +
                                    (dataBox[1] - result.boxStart[1]) / Math.abs(dydx);
                            }
                            else if(result.boxEnd[1] > dataBox[3]) {
                                result.boxEnd[1] = dataBox[3];
                                result.boxEnd[0] = result.boxStart[0] +
                                    (dataBox[3] - result.boxStart[1]) / Math.abs(dydx);
                            }
                        }
                        else {
                            result.boxEnd[0] = result.boxStart[0] +
                                Math.abs(dy) / dydx * (Math.sign(dx) || 1);

                            if(result.boxEnd[0] < dataBox[0]) {
                                result.boxEnd[0] = dataBox[0];
                                result.boxEnd[1] = result.boxStart[1] +
                                    (dataBox[0] - result.boxStart[0]) * Math.abs(dydx);
                            }
                            else if(result.boxEnd[0] > dataBox[2]) {
                                result.boxEnd[0] = dataBox[2];
                                result.boxEnd[1] = result.boxStart[1] +
                                    (dataBox[2] - result.boxStart[0]) * Math.abs(dydx);
                            }
                        }
                    }
                    // otherwise clamp small changes to the origin so we get 1D zoom
                    else {
                        if(smallDx) result.boxEnd[0] = result.boxStart[0];
                        if(smallDy) result.boxEnd[1] = result.boxStart[1];
                    }
                }
                else if(result.boxEnabled) {
                    dx = result.boxStart[0] !== result.boxEnd[0];
                    dy = result.boxStart[1] !== result.boxEnd[1];
                    if(dx || dy) {
                        if(dx) {
                            updateRange(0, result.boxStart[0], result.boxEnd[0]);
                            scene.xaxis.autorange = false;
                        }
                        if(dy) {
                            updateRange(1, result.boxStart[1], result.boxEnd[1]);
                            scene.yaxis.autorange = false;
                        }
                        scene.relayoutCallback();
                    }
                    else {
                        scene.glplot.setDirty();
                    }
                    result.boxEnabled = false;
                    result.boxInited = false;
                }
                // if box was inited but button released then - reset the box
                else if(result.boxInited) {
                    result.boxInited = false;
                }
                break;

            case 'pan':
                result.boxEnabled = false;
                result.boxInited = false;

                if(buttons) {
                    if(!result.panning) {
                        result.dragStart[0] = x;
                        result.dragStart[1] = y;
                    }

                    if(Math.abs(result.dragStart[0] - x) < MINDRAG) x = result.dragStart[0];
                    if(Math.abs(result.dragStart[1] - y) < MINDRAG) y = result.dragStart[1];

                    dx = (lastX - x) * (dataBox[2] - dataBox[0]) /
                        (plot.viewBox[2] - plot.viewBox[0]);
                    dy = (lastY - y) * (dataBox[3] - dataBox[1]) /
                        (plot.viewBox[3] - plot.viewBox[1]);

                    dataBox[0] += dx;
                    dataBox[2] += dx;
                    dataBox[1] += dy;
                    dataBox[3] += dy;

                    scene.setRanges(dataBox);

                    result.panning = true;
                    result.lastInputTime = Date.now();
                    unSetAutoRange();
                    scene.cameraChanged();
                    scene.handleAnnotations();
                }
                else if(result.panning) {
                    result.panning = false;
                    scene.relayoutCallback();
                }
                break;
        }

        result.lastPos[0] = x;
        result.lastPos[1] = y;
    });

    result.wheelListener = mouseWheel(element, function(dx, dy) {
        var dataBox = scene.calcDataBox(),
            viewBox = plot.viewBox;

        var lastX = result.lastPos[0],
            lastY = result.lastPos[1];

        switch(scene.fullLayout.dragmode) {
            case 'zoom':
                break;

            case 'pan':
                var scale = Math.exp(0.1 * dy / (viewBox[3] - viewBox[1]));

                var cx = lastX /
                        (viewBox[2] - viewBox[0]) * (dataBox[2] - dataBox[0]) +
                    dataBox[0];
                var cy = lastY /
                        (viewBox[3] - viewBox[1]) * (dataBox[3] - dataBox[1]) +
                    dataBox[1];

                dataBox[0] = (dataBox[0] - cx) * scale + cx;
                dataBox[2] = (dataBox[2] - cx) * scale + cx;
                dataBox[1] = (dataBox[1] - cy) * scale + cy;
                dataBox[3] = (dataBox[3] - cy) * scale + cy;

                scene.setRanges(dataBox);

                result.lastInputTime = Date.now();
                unSetAutoRange();
                scene.cameraChanged();
                scene.handleAnnotations();
                scene.relayoutCallback();
                break;
        }

        return true;
    });

    return result;
}
<commit_msg>remove Math.sign<commit_after>/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var mouseChange = require('mouse-change');
var mouseWheel = require('mouse-wheel');
var cartesianConstants = require('../cartesian/constants');

module.exports = createCamera;

function Camera2D(element, plot) {
    this.element = element;
    this.plot = plot;
    this.mouseListener = null;
    this.wheelListener = null;
    this.lastInputTime = Date.now();
    this.lastPos = [0, 0];
    this.boxEnabled = false;
    this.boxInited = false;
    this.boxStart = [0, 0];
    this.boxEnd = [0, 0];
    this.dragStart = [0, 0];
}

function createCamera(scene) {
    var element = scene.mouseContainer,
        plot = scene.glplot,
        result = new Camera2D(element, plot);

    function unSetAutoRange() {
        scene.xaxis.autorange = false;
        scene.yaxis.autorange = false;
    }

    function getSubplotConstraint() {
        // note: this assumes we only have one x and one y axis on this subplot
        // when this constraint is lifted this block won't make sense
        var constraints = scene.graphDiv._fullLayout._axisConstraintGroups;
        var xaId = scene.xaxis._id;
        var yaId = scene.yaxis._id;
        for(var i = 0; i < constraints.length; i++) {
            if(constraints[i][xaId] !== -1) {
                if(constraints[i][yaId] !== -1) return true;
                break;
            }
        }
        return false;
    }

    result.mouseListener = mouseChange(element, function(buttons, x, y) {
        var dataBox = scene.calcDataBox(),
            viewBox = plot.viewBox;

        var lastX = result.lastPos[0],
            lastY = result.lastPos[1];

        var MINDRAG = cartesianConstants.MINDRAG * plot.pixelRatio;
        var MINZOOM = cartesianConstants.MINZOOM * plot.pixelRatio;

        var dx, dy;

        x *= plot.pixelRatio;
        y *= plot.pixelRatio;

        // mouseChange gives y about top; convert to about bottom
        y = (viewBox[3] - viewBox[1]) - y;

        function updateRange(i0, start, end) {
            var range0 = Math.min(start, end),
                range1 = Math.max(start, end);

            if(range0 !== range1) {
                dataBox[i0] = range0;
                dataBox[i0 + 2] = range1;
                result.dataBox = dataBox;
                scene.setRanges(dataBox);
            }
            else {
                scene.selectBox.selectBox = [0, 0, 1, 1];
                scene.glplot.setDirty();
            }
        }

        switch(scene.fullLayout.dragmode) {
            case 'zoom':
                if(buttons) {
                    var dataX = x /
                            (viewBox[2] - viewBox[0]) * (dataBox[2] - dataBox[0]) +
                        dataBox[0];
                    var dataY = y /
                            (viewBox[3] - viewBox[1]) * (dataBox[3] - dataBox[1]) +
                        dataBox[1];

                    if(!result.boxInited) {
                        result.boxStart[0] = dataX;
                        result.boxStart[1] = dataY;
                        result.dragStart[0] = x;
                        result.dragStart[1] = y;
                    }

                    result.boxEnd[0] = dataX;
                    result.boxEnd[1] = dataY;

                    // we need to mark the box as initialized right away
                    // so that we can tell the start and end points apart
                    result.boxInited = true;

                    // but don't actually enable the box until the cursor moves
                    if(!result.boxEnabled && (
                        result.boxStart[0] !== result.boxEnd[0] ||
                        result.boxStart[1] !== result.boxEnd[1])
                    ) {
                        result.boxEnabled = true;
                    }

                    // constrain aspect ratio if the axes require it
                    var smallDx = Math.abs(result.dragStart[0] - x) < MINZOOM;
                    var smallDy = Math.abs(result.dragStart[1] - y) < MINZOOM;
                    if(getSubplotConstraint() && !(smallDx && smallDy)) {
                        dx = result.boxEnd[0] - result.boxStart[0];
                        dy = result.boxEnd[1] - result.boxStart[1];
                        var dydx = (dataBox[3] - dataBox[1]) / (dataBox[2] - dataBox[0]);

                        if(Math.abs(dx * dydx) > Math.abs(dy)) {
                            result.boxEnd[1] = result.boxStart[1] +
                                Math.abs(dx) * dydx * (dy >= 0 ? 1 : -1);

                            // gl-select-box clips to the plot area bounds,
                            // which breaks the axis constraint, so don't allow
                            // this box to go out of bounds
                            if(result.boxEnd[1] < dataBox[1]) {
                                result.boxEnd[1] = dataBox[1];
                                result.boxEnd[0] = result.boxStart[0] +
                                    (dataBox[1] - result.boxStart[1]) / Math.abs(dydx);
                            }
                            else if(result.boxEnd[1] > dataBox[3]) {
                                result.boxEnd[1] = dataBox[3];
                                result.boxEnd[0] = result.boxStart[0] +
                                    (dataBox[3] - result.boxStart[1]) / Math.abs(dydx);
                            }
                        }
                        else {
                            result.boxEnd[0] = result.boxStart[0] +
                                Math.abs(dy) / dydx * (dx >= 0 ? 1 : -1);

                            if(result.boxEnd[0] < dataBox[0]) {
                                result.boxEnd[0] = dataBox[0];
                                result.boxEnd[1] = result.boxStart[1] +
                                    (dataBox[0] - result.boxStart[0]) * Math.abs(dydx);
                            }
                            else if(result.boxEnd[0] > dataBox[2]) {
                                result.boxEnd[0] = dataBox[2];
                                result.boxEnd[1] = result.boxStart[1] +
                                    (dataBox[2] - result.boxStart[0]) * Math.abs(dydx);
                            }
                        }
                    }
                    // otherwise clamp small changes to the origin so we get 1D zoom
                    else {
                        if(smallDx) result.boxEnd[0] = result.boxStart[0];
                        if(smallDy) result.boxEnd[1] = result.boxStart[1];
                    }
                }
                else if(result.boxEnabled) {
                    dx = result.boxStart[0] !== result.boxEnd[0];
                    dy = result.boxStart[1] !== result.boxEnd[1];
                    if(dx || dy) {
                        if(dx) {
                            updateRange(0, result.boxStart[0], result.boxEnd[0]);
                            scene.xaxis.autorange = false;
                        }
                        if(dy) {
                            updateRange(1, result.boxStart[1], result.boxEnd[1]);
                            scene.yaxis.autorange = false;
                        }
                        scene.relayoutCallback();
                    }
                    else {
                        scene.glplot.setDirty();
                    }
                    result.boxEnabled = false;
                    result.boxInited = false;
                }
                // if box was inited but button released then - reset the box
                else if(result.boxInited) {
                    result.boxInited = false;
                }
                break;

            case 'pan':
                result.boxEnabled = false;
                result.boxInited = false;

                if(buttons) {
                    if(!result.panning) {
                        result.dragStart[0] = x;
                        result.dragStart[1] = y;
                    }

                    if(Math.abs(result.dragStart[0] - x) < MINDRAG) x = result.dragStart[0];
                    if(Math.abs(result.dragStart[1] - y) < MINDRAG) y = result.dragStart[1];

                    dx = (lastX - x) * (dataBox[2] - dataBox[0]) /
                        (plot.viewBox[2] - plot.viewBox[0]);
                    dy = (lastY - y) * (dataBox[3] - dataBox[1]) /
                        (plot.viewBox[3] - plot.viewBox[1]);

                    dataBox[0] += dx;
                    dataBox[2] += dx;
                    dataBox[1] += dy;
                    dataBox[3] += dy;

                    scene.setRanges(dataBox);

                    result.panning = true;
                    result.lastInputTime = Date.now();
                    unSetAutoRange();
                    scene.cameraChanged();
                    scene.handleAnnotations();
                }
                else if(result.panning) {
                    result.panning = false;
                    scene.relayoutCallback();
                }
                break;
        }

        result.lastPos[0] = x;
        result.lastPos[1] = y;
    });

    result.wheelListener = mouseWheel(element, function(dx, dy) {
        var dataBox = scene.calcDataBox(),
            viewBox = plot.viewBox;

        var lastX = result.lastPos[0],
            lastY = result.lastPos[1];

        switch(scene.fullLayout.dragmode) {
            case 'zoom':
                break;

            case 'pan':
                var scale = Math.exp(0.1 * dy / (viewBox[3] - viewBox[1]));

                var cx = lastX /
                        (viewBox[2] - viewBox[0]) * (dataBox[2] - dataBox[0]) +
                    dataBox[0];
                var cy = lastY /
                        (viewBox[3] - viewBox[1]) * (dataBox[3] - dataBox[1]) +
                    dataBox[1];

                dataBox[0] = (dataBox[0] - cx) * scale + cx;
                dataBox[2] = (dataBox[2] - cx) * scale + cx;
                dataBox[1] = (dataBox[1] - cy) * scale + cy;
                dataBox[3] = (dataBox[3] - cy) * scale + cy;

                scene.setRanges(dataBox);

                result.lastInputTime = Date.now();
                unSetAutoRange();
                scene.cameraChanged();
                scene.handleAnnotations();
                scene.relayoutCallback();
                break;
        }

        return true;
    });

    return result;
}
<|endoftext|>


<commit_before>import Popper from '../popper';
import utils from '../../utils/utils';

const DEFAULT_OPTIONS = {
  container: false,
  delay: 0,
  html: false,
  placement: 'top',
  triggerOnce: false,
  content: '',
  disabled: false,
  trigger: 'hover focus',
  offset: 0,
  equalWidth: false,
  type: 'dropdown',
  preventOverflow: false
};

/**
 * Create a new Pop.js instance
 * @class Pop
 * @param {HTMLElement} reference - The reference element used to position the pop
 * @param {Object} options
 * @param {String} options.placement=bottom
 *      Placement of the popper accepted values: `top(-start, -end), right(-start, -end), bottom(-start, -end),
 *      left(-start, -end)`
 *
 * @param {HTMLElement} reference - The DOM node used as reference of the pop (it can be a jQuery element).
 * @param {Object} options - Configuration of the pop
 * @param {HTMLElement|String|false} options.container=false - Append the pop to a specific element.
 * @param {Number|Object} options.delay=0
 *      Delay showing and hiding the pop (ms) - does not apply to manual trigger type.
 *      If a number is supplied, delay is applied to both hide/show.
 *      Object structure is: `{ show: 500, hide: 100 }`
 * @param {Boolean} options.html=false - Insert HTML into the pop. If false, the content will inserted with `innerText`.
 * @param {String|PlacementFunction} options.placement='top' - One of the allowed placements, or a function returning one of them.
 * @param {String} options.template='

'
 *      Base HTML to used when creating the pop.
 *      The pop's `content` will be injected into the `.pop-inner` or `.pop__inner`.
 *      `.pop-arrow` or `.pop__arrow` will become the pop's arrow.
 *      The outermost wrapper element should have the `.pop` class.
 * @param {String|HTMLElement|ContentFunction} options.content='' - Default content value if `content` attribute isn't present.
 * @param {String} options.trigger='hover focus manual'
 *      How pop is triggered - click | hover | focus | manual.
 *      You may pass multiple triggers; separate them with a space. `manual` cannot be combined with any other trigger.
 * @param {HTMLElement} options.boundariesElement
 *      The element used as boundaries for the pop. For more information refer to Popper.js'
 *      [boundariesElement docs](https://popper.js.org/popper-documentation.html)
 * @param {Number|String} options.offset=0 - Offset of the pop relative to its reference. For more information refer to Popper.js'
 *      [offset docs](https://popper.js.org/popper-documentation.html)
 * @return {Object} instance - The generated pop instance
 */
class Pop {

  constructor(reference, options) {
    options = utils.extend({}, DEFAULT_OPTIONS, options);
    this.reference = reference;

    this.options = options;

    const triggerEvents = typeof options.trigger === 'string' ? options.trigger.split(' ').filter((trigger) => {
      return ['click', 'hover', 'focus'].indexOf(trigger) !== -1;
    }) : [];

    this.isOpen = false;

    this.arrowSelector = options.arrowSelector;
    this.innerSelector = options.innerSelector;
    this.triggerEvents = [];
    if (options.content.nodeType === 1) {
      options.content.style.display = "none";
    }
    this.setEventListeners(triggerEvents, options);
  }

  toggle() {
    if (this.isOpen) {
      return this.hide();
    } else {
      return this.show();
    }
  }

  create(reference, template, content) {
    const popGenerator = window.document.createElement('div');
    popGenerator.innerHTML = template;
    const popNode = popGenerator.childNodes[0];
    const allowHtml = this.options.html;

    popNode.id = `pop_${Math.random().toString(36).substr(2, 10)}`;
    const contentNode = popGenerator.querySelector(this.innerSelector);
    if (content.nodeType === 1) {
      if (allowHtml) contentNode.appendChild(content);
      content.style.display = "block";
    } else if (utils.isFunction(content)) {
      const contentText = content.call(reference);
      if (allowHtml) {
        contentNode.innerHTML = contentText;
      } else {
        contentNode.innerText = contentText;
      }
    } else if (allowHtml) {
      contentNode.innerHTML = content;
    } else {
      contentNode.innerText = content;
    }

    return popNode;
  }

  updateContent(content) {
    this.options.content = content;
    if (!this.popNode || content == undefined) {
      return;
    }
    const contentNode = this.popNode.querySelector(this.innerSelector);
    const allowHtml = this.options.html;
    if (content.nodeType === 1) {
      if (allowHtml) contentNode.replaceChild(content, contentNode.firstChild);
      content.style.display = "block";
    } else if (allowHtml) {
      contentNode.innerHTML = content;
    } else {
      contentNode.innerText = content;
    }
    this.update();
  }

  initPopNode() {
    let reference = this.reference;
    let options = this.options
    const content = options.content || reference.getAttribute('content');

    if (!content) { return this; }

    const popNode = this.create(reference, options.template, content, options.html);

    popNode.setAttribute('aria-describedby', popNode.id);
    const container = this.findContainer(options.container, reference);

    this.append(popNode, container);
    if (options.class) {
      utils.addClass(popNode, options.class);
    }

    this.popNode = popNode;
    this.popNode.setAttribute('aria-hidden', 'true');
  }

  initPopper() {
    let reference = this.reference;
    let options = this.options;
    let popNode = this.popNode;
    const container = this.findContainer(options.container, reference);

    let modifiers = {
      computeStyle: { gpuAcceleration: false },
      arrow: {
        enabled: false
      },
      inner: {
        enabled: false
      }
    }

    // if (this.options.arrowSelector) {
    //   modifiers.arrow = {
    //     enabled: true,
    //     element: this.options.arrowSelector
    //   }
    // }
    // if (this.options.innerSelector) {
    //   modifiers.inner = {
    //     enabled: true,
    //     element: this.options.innerSelector
    //   }
    // }

    if (this.options.offset) {
      modifiers.offset = {
        enabled: true,
        offset: this.options.offset
      }
    }

    if (this.options.preventOverflow && container.tagName != 'BODY') {
      modifiers.hide = {
        enabled: false,
      };
      modifiers.flip = {
        boundariesElement: container,
        enabled: true
      }

      modifiers.preventOverflow = {
        enabled: false,
      }
    }

    let popperOptions = {
      placement: options.placement,
      modifiers
    };
    this.popperInstance = new Popper(reference, popNode, popperOptions);
  }

  disabled() {
    this.options.disabled = true;
  }

  enabled() {
    this.options.disabled = false;
  }

  _doshow() {
    if (!this.popNode) {
      this.initPopNode();
    }
    if (!this.popperInstance) {
      this.initPopper();
    }
    this.popperInstance.enableEventListeners();
    if (!this.popNode) {
      return;
    }
    if (this.options.equalWidth) {
      this.popNode.style.width = `${this.reference.clientWidth}px`;
    }

    this.popNode.style.display = '';
    utils.addClass(this.reference, 'h-pop-trigger');
    if (this.timeout) clearTimeout(this.timeout);
    this.timeout = setTimeout(() => {
      this.popNode.setAttribute('aria-hidden', 'false');
      this.popperInstance.update();
    }, 0);
  }

  show() {
    if (this.isOpen || this.options.disabled) { return this; }
    // if (this.type == 'tooltip' && )
    this.isOpen = true;
    if (this.options.events && utils.isFunction(this.options.events.show)) {
      // this.options.events.show(() => {
      //   this._doshow();
      // });
      this.options.events.show();
      // return;
    }
    this._doshow();
    return this;
  }

  update() {
    if (this.popperInstance) {
      this.popperInstance.update();
    }
  }

  hide() {
    if (!this.popperInstance) return this;
    if (!this.isOpen) { return this; }
    if (this.options.events && utils.isFunction(this.options.events.hide)) {
      this.options.events.hide.call(null);
    }
    this.isOpen = false;
    this.popNode.setAttribute('aria-hidden', 'true');
    utils.removeClass(this.reference, 'h-pop-trigger');
    if (this.timeout) clearTimeout(this.timeout);
    this.timeout = setTimeout(() => {
      if (this.popNode) {
        this.popNode.style.display = 'none';

        if (this.popperInstance) {
          this.popperInstance.disableEventListeners();
        }
      }
    }, this.options.delay);
    return this;
  }

  destory() {
    if (this.documentHandler) {
      document.removeEventListener('click', this.documentHandler);
    }
    if (this.popperInstance) {
      this.popperInstance.destroy();
    }

    this.triggerEvents.forEach(({ event, func }) => {
      this.reference.removeEventListener(event, func, event == 'focus' || event == 'blur');
    });
    this.triggerEvents = [];

    if (this.popNode) {
      this.hide();
      this.popNode.parentNode.removeChild(this.popNode);
      this.popNode = null;
    }
    return this;
  }

  findContainer(container, reference) {
    if (typeof container === 'string') {
      container = window.document.querySelector(container);
    } else if (container === false) {
      container = reference.parentNode;
    }
    return container;
  }

  append(popNode, container) {
    container.appendChild(popNode);
  }

  setEventListeners(triggerEvents, options) {
    let reference = this.reference;
    const directtriggerEvents = [];
    const oppositetriggerEvents = [];

    triggerEvents.forEach((event) => {
      switch (event) {
        case 'hover':
          directtriggerEvents.push('mouseenter');
          oppositetriggerEvents.push('mouseleave');
          break;
        case 'focus':
          directtriggerEvents.push('focus');
          oppositetriggerEvents.push('blur');
          break;
        case 'click':
          directtriggerEvents.push('click');
          if (!this.options.triggerOnce) oppositetriggerEvents.push('click');
          break;
        default:
          break;
      }
    });

    directtriggerEvents.forEach((event) => {
      const func = (evt) => {
        if (this.isOpen === true) { return; }
        evt.usedByPop = true;
        this.scheduleShow(reference, options, evt);
      };
      this.triggerEvents.push({ event, func });
      reference.addEventListener(event, func, event == 'focus');
    });

    oppositetriggerEvents.forEach((event) => {
      const func = (evt) => {
        if (evt.usedByPop === true) { return; }
        this.scheduleHide(reference, options, evt);
      };
      this.triggerEvents.push({ event, func });
      reference.addEventListener(event, func, event == 'blur');
    });

    if (options.closeOnClickBody) {
      this.documentHandler = (e) => {
        if (!this.popNode || e.target.parentNode == null) return;
        if (!this.isOpen || reference.contains(e.target) || this.popNode.contains(e.target)) {
          return false;
        }

        let targetReference = e.reference;
        if (reference && this.popNode.contains(targetReference)) {
          return false;
        }
        this.hide();
      };
      document.addEventListener('click', this.documentHandler);
    }
  }

  scheduleShow() {
    this.show();
  }

  scheduleHide(reference, options, evt) {
    if (this.isOpen === false) { return; }
    if (!document.body.contains(this.popNode)) { return; }
    if (evt.type === 'mouseleave') {
      const isSet = this.setPopNodeEvent(evt, reference, options);
      if (isSet) { return; }
    }

    this.hide(reference, options);
  }

  setPopNodeEvent(evt, reference, options) {
    const relatedreference = evt.relatedreference || evt.relatedTarget || evt.toElement;

    const callback = (evt2) => {
      const relatedreference2 = evt2.relatedreference || evt2.toElement;

      this.popNode.removeEventListener(evt.type, callback);

      if (!reference.contains(relatedreference2)) {
        this.scheduleHide(reference, options, evt2);
      }
    };
    if (this.popNode.contains(relatedreference)) {
      this.popNode.addEventListener(evt.type, callback);
      return true;
    }

    return false;
  }
}

export default Pop;
<commit_msg>fix: pop default preventOverflow window<commit_after>import Popper from '../popper';
import utils from '../../utils/utils';

const DEFAULT_OPTIONS = {
  container: false,
  delay: 0,
  html: false,
  placement: 'top',
  triggerOnce: false,
  content: '',
  disabled: false,
  trigger: 'hover focus',
  offset: 0,
  equalWidth: false,
  type: 'dropdown',
  preventOverflow: false
};

/**
 * Create a new Pop.js instance
 * @class Pop
 * @param {HTMLElement} reference - The reference element used to position the pop
 * @param {Object} options
 * @param {String} options.placement=bottom
 *      Placement of the popper accepted values: `top(-start, -end), right(-start, -end), bottom(-start, -end),
 *      left(-start, -end)`
 *
 * @param {HTMLElement} reference - The DOM node used as reference of the pop (it can be a jQuery element).
 * @param {Object} options - Configuration of the pop
 * @param {HTMLElement|String|false} options.container=false - Append the pop to a specific element.
 * @param {Number|Object} options.delay=0
 *      Delay showing and hiding the pop (ms) - does not apply to manual trigger type.
 *      If a number is supplied, delay is applied to both hide/show.
 *      Object structure is: `{ show: 500, hide: 100 }`
 * @param {Boolean} options.html=false - Insert HTML into the pop. If false, the content will inserted with `innerText`.
 * @param {String|PlacementFunction} options.placement='top' - One of the allowed placements, or a function returning one of them.
 * @param {String} options.template='

'
 *      Base HTML to used when creating the pop.
 *      The pop's `content` will be injected into the `.pop-inner` or `.pop__inner`.
 *      `.pop-arrow` or `.pop__arrow` will become the pop's arrow.
 *      The outermost wrapper element should have the `.pop` class.
 * @param {String|HTMLElement|ContentFunction} options.content='' - Default content value if `content` attribute isn't present.
 * @param {String} options.trigger='hover focus manual'
 *      How pop is triggered - click | hover | focus | manual.
 *      You may pass multiple triggers; separate them with a space. `manual` cannot be combined with any other trigger.
 * @param {HTMLElement} options.boundariesElement
 *      The element used as boundaries for the pop. For more information refer to Popper.js'
 *      [boundariesElement docs](https://popper.js.org/popper-documentation.html)
 * @param {Number|String} options.offset=0 - Offset of the pop relative to its reference. For more information refer to Popper.js'
 *      [offset docs](https://popper.js.org/popper-documentation.html)
 * @return {Object} instance - The generated pop instance
 */
class Pop {

  constructor(reference, options) {
    options = utils.extend({}, DEFAULT_OPTIONS, options);
    this.reference = reference;

    this.options = options;

    const triggerEvents = typeof options.trigger === 'string' ? options.trigger.split(' ').filter((trigger) => {
      return ['click', 'hover', 'focus'].indexOf(trigger) !== -1;
    }) : [];

    this.isOpen = false;

    this.arrowSelector = options.arrowSelector;
    this.innerSelector = options.innerSelector;
    this.triggerEvents = [];
    if (options.content.nodeType === 1) {
      options.content.style.display = "none";
    }
    this.setEventListeners(triggerEvents, options);
  }

  toggle() {
    if (this.isOpen) {
      return this.hide();
    } else {
      return this.show();
    }
  }

  create(reference, template, content) {
    const popGenerator = window.document.createElement('div');
    popGenerator.innerHTML = template;
    const popNode = popGenerator.childNodes[0];
    const allowHtml = this.options.html;

    popNode.id = `pop_${Math.random().toString(36).substr(2, 10)}`;
    const contentNode = popGenerator.querySelector(this.innerSelector);
    if (content.nodeType === 1) {
      if (allowHtml) contentNode.appendChild(content);
      content.style.display = "block";
    } else if (utils.isFunction(content)) {
      const contentText = content.call(reference);
      if (allowHtml) {
        contentNode.innerHTML = contentText;
      } else {
        contentNode.innerText = contentText;
      }
    } else if (allowHtml) {
      contentNode.innerHTML = content;
    } else {
      contentNode.innerText = content;
    }

    return popNode;
  }

  updateContent(content) {
    this.options.content = content;
    if (!this.popNode || content == undefined) {
      return;
    }
    const contentNode = this.popNode.querySelector(this.innerSelector);
    const allowHtml = this.options.html;
    if (content.nodeType === 1) {
      if (allowHtml) contentNode.replaceChild(content, contentNode.firstChild);
      content.style.display = "block";
    } else if (allowHtml) {
      contentNode.innerHTML = content;
    } else {
      contentNode.innerText = content;
    }
    this.update();
  }

  initPopNode() {
    let reference = this.reference;
    let options = this.options
    const content = options.content || reference.getAttribute('content');

    if (!content) { return this; }

    const popNode = this.create(reference, options.template, content, options.html);

    popNode.setAttribute('aria-describedby', popNode.id);
    const container = this.findContainer(options.container, reference);

    this.append(popNode, container);
    if (options.class) {
      utils.addClass(popNode, options.class);
    }

    this.popNode = popNode;
    this.popNode.setAttribute('aria-hidden', 'true');
  }

  initPopper() {
    let reference = this.reference;
    let options = this.options;
    let popNode = this.popNode;
    const container = this.findContainer(options.container, reference);

    let modifiers = {
      computeStyle: { gpuAcceleration: false },
      arrow: {
        enabled: false
      },
      inner: {
        enabled: false
      },
      preventOverflow: {
        boundariesElement: 'window',
        enabled: true
      }
    }

    // if (this.options.arrowSelector) {
    //   modifiers.arrow = {
    //     enabled: true,
    //     element: this.options.arrowSelector
    //   }
    // }
    // if (this.options.innerSelector) {
    //   modifiers.inner = {
    //     enabled: true,
    //     element: this.options.innerSelector
    //   }
    // }

    if (this.options.offset) {
      modifiers.offset = {
        enabled: true,
        offset: this.options.offset
      }
    }

    if (this.options.preventOverflow && container.tagName != 'BODY') {
      modifiers.hide = {
        enabled: false,
      };
      modifiers.flip = {
        boundariesElement: container,
        enabled: true
      }

      modifiers.preventOverflow = {
        enabled: false,
      }
    }

    let popperOptions = {
      placement: options.placement,
      modifiers
    };
    this.popperInstance = new Popper(reference, popNode, popperOptions);
  }

  disabled() {
    this.options.disabled = true;
  }

  enabled() {
    this.options.disabled = false;
  }

  _doshow() {
    if (!this.popNode) {
      this.initPopNode();
    }
    if (!this.popperInstance) {
      this.initPopper();
    }
    this.popperInstance.enableEventListeners();
    if (!this.popNode) {
      return;
    }
    if (this.options.equalWidth) {
      this.popNode.style.width = `${this.reference.clientWidth}px`;
    }

    this.popNode.style.display = '';
    utils.addClass(this.reference, 'h-pop-trigger');
    if (this.timeout) clearTimeout(this.timeout);
    this.timeout = setTimeout(() => {
      this.popNode.setAttribute('aria-hidden', 'false');
      this.popperInstance.update();
    }, 0);
  }

  show() {
    if (this.isOpen || this.options.disabled) { return this; }
    // if (this.type == 'tooltip' && )
    this.isOpen = true;
    if (this.options.events && utils.isFunction(this.options.events.show)) {
      // this.options.events.show(() => {
      //   this._doshow();
      // });
      this.options.events.show();
      // return;
    }
    this._doshow();
    return this;
  }

  update() {
    if (this.popperInstance) {
      this.popperInstance.update();
    }
  }

  hide() {
    if (!this.popperInstance) return this;
    if (!this.isOpen) { return this; }
    if (this.options.events && utils.isFunction(this.options.events.hide)) {
      this.options.events.hide.call(null);
    }
    this.isOpen = false;
    this.popNode.setAttribute('aria-hidden', 'true');
    utils.removeClass(this.reference, 'h-pop-trigger');
    if (this.timeout) clearTimeout(this.timeout);
    this.timeout = setTimeout(() => {
      if (this.popNode) {
        this.popNode.style.display = 'none';

        if (this.popperInstance) {
          this.popperInstance.disableEventListeners();
        }
      }
    }, this.options.delay);
    return this;
  }

  destory() {
    if (this.documentHandler) {
      document.removeEventListener('click', this.documentHandler);
    }
    if (this.popperInstance) {
      this.popperInstance.destroy();
    }

    this.triggerEvents.forEach(({ event, func }) => {
      this.reference.removeEventListener(event, func, event == 'focus' || event == 'blur');
    });
    this.triggerEvents = [];

    if (this.popNode) {
      this.hide();
      this.popNode.parentNode.removeChild(this.popNode);
      this.popNode = null;
    }
    return this;
  }

  findContainer(container, reference) {
    if (typeof container === 'string') {
      container = window.document.querySelector(container);
    } else if (container === false) {
      container = reference.parentNode;
    }
    return container;
  }

  append(popNode, container) {
    container.appendChild(popNode);
  }

  setEventListeners(triggerEvents, options) {
    let reference = this.reference;
    const directtriggerEvents = [];
    const oppositetriggerEvents = [];

    triggerEvents.forEach((event) => {
      switch (event) {
        case 'hover':
          directtriggerEvents.push('mouseenter');
          oppositetriggerEvents.push('mouseleave');
          break;
        case 'focus':
          directtriggerEvents.push('focus');
          oppositetriggerEvents.push('blur');
          break;
        case 'click':
          directtriggerEvents.push('click');
          if (!this.options.triggerOnce) oppositetriggerEvents.push('click');
          break;
        default:
          break;
      }
    });

    directtriggerEvents.forEach((event) => {
      const func = (evt) => {
        if (this.isOpen === true) { return; }
        evt.usedByPop = true;
        this.scheduleShow(reference, options, evt);
      };
      this.triggerEvents.push({ event, func });
      reference.addEventListener(event, func, event == 'focus');
    });

    oppositetriggerEvents.forEach((event) => {
      const func = (evt) => {
        if (evt.usedByPop === true) { return; }
        this.scheduleHide(reference, options, evt);
      };
      this.triggerEvents.push({ event, func });
      reference.addEventListener(event, func, event == 'blur');
    });

    if (options.closeOnClickBody) {
      this.documentHandler = (e) => {
        if (!this.popNode || e.target.parentNode == null) return;
        if (!this.isOpen || reference.contains(e.target) || this.popNode.contains(e.target)) {
          return false;
        }

        let targetReference = e.reference;
        if (reference && this.popNode.contains(targetReference)) {
          return false;
        }
        this.hide();
      };
      document.addEventListener('click', this.documentHandler);
    }
  }

  scheduleShow() {
    this.show();
  }

  scheduleHide(reference, options, evt) {
    if (this.isOpen === false) { return; }
    if (!document.body.contains(this.popNode)) { return; }
    if (evt.type === 'mouseleave') {
      const isSet = this.setPopNodeEvent(evt, reference, options);
      if (isSet) { return; }
    }

    this.hide(reference, options);
  }

  setPopNodeEvent(evt, reference, options) {
    const relatedreference = evt.relatedreference || evt.relatedTarget || evt.toElement;

    const callback = (evt2) => {
      const relatedreference2 = evt2.relatedreference || evt2.toElement;

      this.popNode.removeEventListener(evt.type, callback);

      if (!reference.contains(relatedreference2)) {
        this.scheduleHide(reference, options, evt2);
      }
    };
    if (this.popNode.contains(relatedreference)) {
      this.popNode.addEventListener(evt.type, callback);
      return true;
    }

    return false;
  }
}

export default Pop;
<|endoftext|>


<commit_before>
const args = require('args-parser')(process.argv);
const settings = require('./settings.json');

module.exports = function () {
  const fields = [
    // Webapp settings
    {
      local: 'webroot',
      env: 'WEB_ROOT',
      default: ''
    },
    {
      local: 'webapp_port',
      env: 'WEB_PORT',
      default: '8088'
    },
    {
      local: 'accessUrl',
      env: 'WEB_ACCESSURL',
      default: ''
    },
    {
      local: 'autoJoin',
      env: 'AUTOJOIN_ENABLED',
      default: 'false'
    },
    {
      local: 'autoJoinServer',
      env: 'AUTOJOIN_SERVERURL',
      default: ''
    },
    {
      local: 'autoJoinRoom',
      env: 'AUTOJOIN_ROOM',
      default: ''
    },
    {
      local: 'autoJoinPassword',
      env: 'AUTOJOIN_PASSWORD',
      default: ''
    },
    {
      local: 'authentication',
      env: 'AUTHENTICATION',
      default: JSON.stringify({
        mechanism: 'none'
      })
    },
    {
      local: 'customServer',
      env: 'CUSTOM_SERVER',
      default: ''
    },
    {
      local: 'servers',
      env: 'SERVERS',
      default: ''
    },
    // Server settings
    {
      local: 'serverroot',
      env: 'SERVER_ROOT',
      default: '/slserver'
    },
    {
      local: 'server_port',
      env: 'SERVER_PORT',
      default: '8089'
    }
  ];
  // Load and export our settings in preference of Args -> ENV -> Settings file -> Default
  const output = {};
  for (let i = 0; i < fields.length; i++) {
    const setting = fields[i];
    // console.log('Processing setting', setting);
    // console.log(`Args: '${args[setting.env]}'; '${args[setting.local]}'`);
    // console.log(`ENV: '${process.env[setting.env]}'; '${process.env[setting.local]}'`);
    // console.log(`Settings: '${settings[setting.local]}'; '${setting.default}'`);
    output[setting.local] = args[setting.env] || args[setting.local] || process.env[setting.env] || process.env[setting.local] || settings[setting.env] || settings[setting.local] || setting.default;

    // Make sure these settings are properly formatted
    if(output[setting.local]) {
      // Make sure these settings are properly formatted to JSON
      let jsonSettings = ['authentication', 'customServer'];
      if(jsonSettings.includes(setting.local) && output[setting.local]) {
        if(typeof output[setting.local] !== "object") {
          console.log(`${setting.local}/${setting.env} must be a JSON object. Attempting to convert it for you.`);
          try {
            let parsed = JSON.parse(output[setting.local]);
            output[setting.local] = parsed;
          }
          catch(e) {
            console.log(`- Unable to parse '${output[setting.local]}'`);
            console.log(`- Please check your syntax. Reverting to default.`);
          }
          console.log(`- Done.`);
        }
      }
      // Make sure these settings are properly formatted to an Array
      // This currently is only coded to handle the servers setting which is an Array of JSON objects
      let arraySettings = ['servers'];
      if(arraySettings.includes(setting.local)) {
        if(!Array.isArray(output[setting.local])) {
          console.log(`${setting.local}/${setting.env} must be an Array of JSON objects. Attempting to convert it for you.`);

          let temp = output[setting.local].trim();

          temp = temp.replace("[", "");
          temp = temp.replace("]", "");
          let tempArr = temp.split("},");

          if(tempArr.length > 0) {
            let tempOutArr = [];
            tempArr.forEach(element => {
              try {
                if(!element.endsWith('}')) {
                  element = element + '}';
                }
                let parsed = JSON.parse(element);

                tempOutArr.push(parsed);
              }
              catch(e) {
                console.log(`- Unable to parse '${element}'`);
                console.log(`- Please check your syntax. Skipping...`);
              }
            });

            if(tempOutArr.length == 0) {
              console.log(`- No elements to use. Reverting to default.`);
            }
            output[setting.local] = tempOutArr;
          }
          else {
            console.log(`No array elements found =(. Reverting to default.`);
            output[setting.local] = setting.default;
          }
          console.log(`- Done.`);
        }
      }
    }
    // Backwards compatibilty for PORT ENV setting
    if(setting.local == 'webapp_port' && output[setting.local] == 8088) {
      let port = args['PORT'] || process.env['PORT'] || settings['PORT'];
      if(port && port !== 8088) {
        console.log(`Please change 'PORT' to 'WEB_PORT'. Setting WEB_PORT to '${port}'`)
        output[setting.local] = port;
      }
    }

    // Remove trailing slashes, if they exist
    if ((setting.local == 'webroot' || setting.local == 'accessUrl') && output[setting.local].endsWith("/")) {
      console.log(`${setting.local}/${setting.env} should not end in '/'. Removing trailing slash(es) for you.`);
      output[setting.local] = output[setting.local].replace(/\/+$/, "");
      console.log(`- Done.`);
    }
    // Add leading slash, if not provided
    if (setting.local == 'webroot' && output[setting.local].length > 1 && !output[setting.local].startsWith("/")) {
      console.log(`${setting.local}/${setting.env} should always start with '/'. Adding the leading slash for you.`);
      // Make sure it starts with one leading slash
      output[setting.local] = `/${output[setting.local]}`;
      console.log(`- Done.`);
    }
    // Make sure 'webroot' and 'serverroot' aren't set to '/'. Revert to default if they do.
    if ((setting.local == 'webroot' || setting.local == 'serverroot') && output[setting.local] == '/') {
      console.log(`${setting.local}/${setting.env} cannot be set to '/'. Reverting to default: '${setting.default}'`);
      output[setting.local] = setting.default;
      console.log(`- Done.`);
    }
    process.env[setting.env] = output[setting.local];
  }
  //console.log('Our settings are', output)
  return output;
};
<commit_msg>Make sure process.env variables are stringified<commit_after>
const args = require('args-parser')(process.argv);
const settings = require('./settings.json');

module.exports = function () {
  const fields = [
    // Webapp settings
    {
      local: 'webroot',
      env: 'WEB_ROOT',
      default: ''
    },
    {
      local: 'webapp_port',
      env: 'WEB_PORT',
      default: '8088'
    },
    {
      local: 'accessUrl',
      env: 'WEB_ACCESSURL',
      default: ''
    },
    {
      local: 'autoJoin',
      env: 'AUTOJOIN_ENABLED',
      default: 'false'
    },
    {
      local: 'autoJoinServer',
      env: 'AUTOJOIN_SERVERURL',
      default: ''
    },
    {
      local: 'autoJoinRoom',
      env: 'AUTOJOIN_ROOM',
      default: ''
    },
    {
      local: 'autoJoinPassword',
      env: 'AUTOJOIN_PASSWORD',
      default: ''
    },
    {
      local: 'authentication',
      env: 'AUTHENTICATION',
      default: JSON.stringify({
        mechanism: 'none'
      })
    },
    {
      local: 'customServer',
      env: 'CUSTOM_SERVER',
      default: ''
    },
    {
      local: 'servers',
      env: 'SERVERS',
      default: ''
    },
    // Server settings
    {
      local: 'serverroot',
      env: 'SERVER_ROOT',
      default: '/slserver'
    },
    {
      local: 'server_port',
      env: 'SERVER_PORT',
      default: '8089'
    }
  ];
  // Load and export our settings in preference of Args -> ENV -> Settings file -> Default
  const output = {};
  for (let i = 0; i < fields.length; i++) {
    const setting = fields[i];
    // console.log('Processing setting', setting);
    // console.log(`Args: '${args[setting.env]}'; '${args[setting.local]}'`);
    // console.log(`ENV: '${process.env[setting.env]}'; '${process.env[setting.local]}'`);
    // console.log(`Settings: '${settings[setting.local]}'; '${setting.default}'`);
    output[setting.local] = args[setting.env] || args[setting.local] || process.env[setting.env] || process.env[setting.local] || settings[setting.env] || settings[setting.local] || setting.default;

    // Make sure these settings are properly formatted
    if(output[setting.local]) {
      // Make sure these settings are properly formatted to JSON
      let jsonSettings = ['authentication', 'customServer'];
      if(jsonSettings.includes(setting.local) && output[setting.local]) {
        if(typeof output[setting.local] !== "object") {
          console.log(`${setting.local}/${setting.env} must be a JSON object. Attempting to convert it for you.`);
          try {
            let parsed = JSON.parse(output[setting.local]);
            output[setting.local] = parsed;
          }
          catch(e) {
            console.log(`- Unable to parse '${output[setting.local]}'`);
            console.log(`- Please check your syntax. Reverting to default.`);
          }
          console.log(`- Done.`);
        }
      }
      // Make sure these settings are properly formatted to an Array
      // This currently is only coded to handle the servers setting which is an Array of JSON objects
      let arraySettings = ['servers'];
      if(arraySettings.includes(setting.local)) {
        if(!Array.isArray(output[setting.local])) {
          console.log(`${setting.local}/${setting.env} must be an Array of JSON objects. Attempting to convert it for you.`);

          let temp = output[setting.local].trim();

          temp = temp.replace("[", "");
          temp = temp.replace("]", "");
          let tempArr = temp.split("},");

          if(tempArr.length > 0) {
            let tempOutArr = [];
            tempArr.forEach(element => {
              try {
                if(!element.endsWith('}')) {
                  element = element + '}';
                }
                let parsed = JSON.parse(element);

                tempOutArr.push(parsed);
              }
              catch(e) {
                console.log(`- Unable to parse '${element}'`);
                console.log(`- Please check your syntax. Skipping...`);
              }
            });

            if(tempOutArr.length == 0) {
              console.log(`- No elements to use. Reverting to default.`);
            }
            output[setting.local] = tempOutArr;
          }
          else {
            console.log(`No array elements found =(. Reverting to default.`);
            output[setting.local] = setting.default;
          }
          console.log(`- Done.`);
        }
      }
    }
    // Backwards compatibilty for PORT ENV setting
    if(setting.local == 'webapp_port' && output[setting.local] == 8088) {
      let port = args['PORT'] || process.env['PORT'] || settings['PORT'];
      if(port && port !== 8088) {
        console.log(`Please change 'PORT' to 'WEB_PORT'. Setting WEB_PORT to '${port}'`)
        output[setting.local] = port;
      }
    }

    // Remove trailing slashes, if they exist
    if ((setting.local == 'webroot' || setting.local == 'accessUrl') && output[setting.local].endsWith("/")) {
      console.log(`${setting.local}/${setting.env} should not end in '/'. Removing trailing slash(es) for you.`);
      output[setting.local] = output[setting.local].replace(/\/+$/, "");
      console.log(`- Done.`);
    }
    // Add leading slash, if not provided
    if (setting.local == 'webroot' && output[setting.local].length > 1 && !output[setting.local].startsWith("/")) {
      console.log(`${setting.local}/${setting.env} should always start with '/'. Adding the leading slash for you.`);
      // Make sure it starts with one leading slash
      output[setting.local] = `/${output[setting.local]}`;
      console.log(`- Done.`);
    }
    // Make sure 'webroot' and 'serverroot' aren't set to '/'. Revert to default if they do.
    if ((setting.local == 'webroot' || setting.local == 'serverroot') && output[setting.local] == '/') {
      console.log(`${setting.local}/${setting.env} cannot be set to '/'. Reverting to default: '${setting.default}'`);
      output[setting.local] = setting.default;
      console.log(`- Done.`);
    }
    process.env[setting.env] = typeof output[setting.local] === 'object' ? JSON.stringify(output[setting.local]) : output[setting.local];
  }
  //console.log('Our settings are', output)
  return output;
};
<|endoftext|>


<commit_before>
var Xpos = 0;
var Ypos = 0;
function GetXY(event) {
  if (window.event) {
    Xpos = window.event.clientX + document.documentElement.scrollLeft
                             + document.body.scrollLeft;
    Ypos = window.event.clientY + document.documentElement.scrollTop +
                             + document.body.scrollTop;
  }
  else {
    Xpos = event.clientX + window.scrollX;
    Ypos = event.clientY + window.scrollY;
  }
}
function getFrameWidth(w) {
  if (! w) w=window;
      winW = w.innerWidth;
      if (! winW)
            winW = w.document.body.offsetWidth;

      return (winW);
  }

function getFrameHeight(w) {
  if (! w) w=window;
      winH= w.innerHeight;
      if (! winH)
           winH = w.document.body.offsetHeight;
      return (winH);
  }

// get document object from iframe object
function getIdocument(ifr) {
if (ifr.contentDocument) {
    // For NS6
    return ifr.contentDocument;
  } else if (ifr.contentWindow) {
    // For IE5.5 and IE6
    return ifr.contentWindow.document;
  } else if (ifr.document) {
    // For IE5
    return ifr.document;
  } else {
    return false;
  }
}

function CenterDiv(eid) {
      var winH=getFrameHeight();
      var winW=getFrameWidth();

      if (document.getElementById) { // DOM3 = IE5, NS6
         var divlogo = document.getElementById(eid);
	 divlogo.style.display = 'inline';

         if ((winH>0) && (winW>0)) {

           divlogo.style.top = winH/2 - (divlogo.offsetHeight/2)+ document.body.scrollTop;
           divlogo.style.left = winW/2 - (divlogo.offsetWidth/2);

         }

       }
    return true;
}

function getKeyPress(event)
{
  var intKeyCode;

  if (window.event) {
    intKeyCode = window.event.keyCode;
    alert(window.event.type);
  } else {
    intKeyCode = event.which;
    alert(event.type);
  }
  alert('key:'+intKeyCode);
  return intKeyCode;
}

function autoHresize() {
  if (window != top) return;

  var dw=0;
  var dh=0;
  availHeight=self.screen.availHeight-300;

  if (document.body.scrollHeight > availHeight)
    dh=availHeight-document.body.clientHeight;
  else
    dh=document.body.scrollHeight-document.body.clientHeight;

  if (dh > 0) {
    window.resizeBy(dw,dh);

    // double resize for mozilla ?
    if (document.body.scrollHeight < availHeight) {
      if (document.body.scrollHeight > document.body.clientHeight) {

	dh=document.body.scrollHeight-document.body.clientHeight;
	if (dh > 0) window.resizeBy(dw,dh);
      }
    }
  }
}

function autoVresize() {
  if (window != top) return;
  var dw=0;
  var dh=0;
  if (document.body.scrollWidth > self.screen.availWidth)
    dw=self.screen.availWidth-document.body.clientWidth;
  else
    dw=document.body.scrollWidth-document.body.clientWidth;

  if (dw > 0) window.resizeBy(dw,dh);
}

function autoWresize() {
  autoVresize();
  autoHresize();
}
<commit_msg>correct for getFrameHeigth/Width for IE with DTD strict<commit_after>
var Xpos = 0;
var Ypos = 0;
function GetXY(event) {
  if (window.event) {
    Xpos = window.event.clientX + document.documentElement.scrollLeft
                             + document.body.scrollLeft;
    Ypos = window.event.clientY + document.documentElement.scrollTop +
                             + document.body.scrollTop;
  }
  else {
    Xpos = event.clientX + window.scrollX;
    Ypos = event.clientY + window.scrollY;
  }
}
function getFrameWidth(w) {
  if (! w) w=window;
      winW = w.innerWidth;
      if (! winW) winW = w.document.documentElement.offsetWidth;
      if (! winW) winW = w.document.body.offsetWidth;

      return (winW);
  }

function getFrameHeight(w) {
  if (! w) w=window;
      winH= w.innerHeight;
      if (! winH) winH = w.document.documentElement.offsetHeight;
      if (! winH) winH = w.document.body.offsetHeight;
      return (winH);
  }

// get document object from iframe object
function getIdocument(ifr) {
if (ifr.contentDocument) {
    // For NS6
    return ifr.contentDocument;
  } else if (ifr.contentWindow) {
    // For IE5.5 and IE6
    return ifr.contentWindow.document;
  } else if (ifr.document) {
    // For IE5
    return ifr.document;
  } else {
    return false;
  }
}

function CenterDiv(eid) {
      var winH=getFrameHeight();
      var winW=getFrameWidth();

      if (document.getElementById) { // DOM3 = IE5, NS6
         var divlogo = document.getElementById(eid);
	 divlogo.style.display = 'inline';

         if ((winH>0) && (winW>0)) {

           divlogo.style.top = winH/2 - (divlogo.offsetHeight/2)+ document.body.scrollTop;
           divlogo.style.left = winW/2 - (divlogo.offsetWidth/2);

         }

       }
    return true;
}

function getKeyPress(event)
{
  var intKeyCode;

  if (window.event) {
    intKeyCode = window.event.keyCode;
    alert(window.event.type);
  } else {
    intKeyCode = event.which;
    alert(event.type);
  }
  alert('key:'+intKeyCode);
  return intKeyCode;
}

function autoHresize() {
  if (window != top) return;

  var dw=0;
  var dh=0;
  availHeight=self.screen.availHeight-300;

  if (document.body.scrollHeight > availHeight)
    dh=availHeight-document.body.clientHeight;
  else
    dh=document.body.scrollHeight-document.body.clientHeight;

  if (dh > 0) {
    window.resizeBy(dw,dh);

    // double resize for mozilla ?
    if (document.body.scrollHeight < availHeight) {
      if (document.body.scrollHeight > document.body.clientHeight) {

	dh=document.body.scrollHeight-document.body.clientHeight;
	if (dh > 0) window.resizeBy(dw,dh);
      }
    }
  }
}

function autoVresize() {
  if (window != top) return;
  var dw=0;
  var dh=0;
  if (document.body.scrollWidth > self.screen.availWidth)
    dw=self.screen.availWidth-document.body.clientWidth;
  else
    dw=document.body.scrollWidth-document.body.clientWidth;

  if (dw > 0) window.resizeBy(dw,dh);
}

function autoWresize() {
  autoVresize();
  autoHresize();
}
<|endoftext|>


<commit_before>
App.ValidatedSelectYearComponent = Ember.Select.extend({

  years: [],

  content: function() {
    var years    = this.get('years'),
        thisYear = new Date().getFullYear();
    for ( var i = 0; i < 12; i++ ) {
      years.push(thisYear + i);
    }
    return years;
  }.property('years')

});
<commit_msg>Issue #556 - years to strings<commit_after>
App.ValidatedSelectYearComponent = Ember.Select.extend({

  years: [],

  content: function() {
    var years    = this.get('years'),
        thisYear = new Date().getFullYear();
    for ( var i = 0; i < 12; i++ ) {
      years.push( (thisYear + i).toString() );
    }
    return years;
  }.property('years')

});
<|endoftext|>


<commit_before>// TODO
//
// - DOMRange
//   - kill DOMAugmenter
//   - consider moving to _callbacks (including memberOut)
// - port attributes
// - EACH untested as far as:
//   - toHTML
//   - cleanup
//   - most things really...
// - Do you get access to the DOMRange from "destroyed"?

Blaze.View = function (kind, render) {
  if (! (this instanceof Blaze.View))
    // called without `new`
    return new Blaze.View(kind, render);

  if (typeof kind === 'function') {
    // omitted "kind" argument
    render = kind;
    kind = '';
  }
  this.kind = kind;
  this.render = render;

  this._callbacks = {
    created: null,
    materialized: null,
    rendered: null,
    destroyed: null };
};

Blaze.View.prototype.render = function () { return null; };

Blaze.View.prototype.isCreated = false;
Blaze.View.prototype.isDestroyed = false;

Blaze.View.prototype.onCreated = function (cb) {
  this._callbacks.created = this._callbacks.created || [];
  this._callbacks.created.push(cb);
};
Blaze.View.prototype.onMaterialized = function (cb) {
  this._callbacks.materialized = this._callbacks.materialized || [];
  this._callbacks.materialized.push(cb);
};
Blaze.View.prototype.onRendered = function (cb) {
  this._callbacks.rendered = this._callbacks.rendered || [];
  this._callbacks.rendered.push(cb);
};
Blaze.View.prototype.onDestroyed = function (cb) {
  this._callbacks.destroyed = this._callbacks.destroyed || [];
  this._callbacks.destroyed.push(cb);
};

Blaze.View.prototype.autorun = function (f) {
  var self = this;

  if (! self.isCreated)
    throw new Error("View#autorun must be called from the created callback at the earliest");

  var c = Deps.nonreactive(function () {
    return Deps.autorun(function viewAutorun(c) {
      return Blaze.withCurrentView(self, function () {
        return f.call(self, c);
      });
    });
  });
  self.onDestroyed(function () { c.stop(); });
};

Blaze._fireCallbacks = function (view, which) {
  Blaze.withCurrentView(view, function () {
    Deps.nonreactive(function fireCallbacks() {
      var cbs = view._callbacks[which];
      for (var i = 0, N = (cbs && cbs.length); i < N; i++)
        cbs[i].call(view);
    });
  });
};

Blaze.materializeView = function (view, parentView) {
  view.parentView = (parentView || null);

  if (view.isCreated)
    throw new Error("Can't render the same View twice");
  view.isCreated = true;

  Blaze._fireCallbacks(view, 'created');

  var domrange = new Blaze.DOMRange;
  view.domrange = domrange;
  domrange.view = view;

  var needsRenderedCallback = false;
  var scheduleRenderedCallback = function () {
    if (needsRenderedCallback && ! view.isDestroyed &&
        view._callbacks.rendered && view._callbacks.rendered.length) {
      Deps.afterFlush(function callRendered() {
        if (needsRenderedCallback && ! view.isDestroyed) {
          needsRenderedCallback = false;
          Blaze._fireCallbacks(view, 'rendered');
        }
      });
    }
  };

  var lastHtmljs;
  view.autorun(function doRender(c) {
    // `view.autorun` sets the current view.
    // Any dependencies that should invalidate this Computation come
    // from this line:
    var htmljs = view.render();

    Deps.nonreactive(function doMaterialize() {
      var materializer = new Blaze.DOMMaterializer({parentView: view});
      var rangesAndNodes = materializer.visit(htmljs, []);
      if (c.firstRun || ! Blaze._isContentEqual(lastHtmljs, htmljs)) {
        domrange.setMembers(rangesAndNodes);
        Blaze._fireCallbacks(view, 'materialized');
        needsRenderedCallback = true;
        if (! c.firstRun)
          scheduleRenderedCallback();
      }
    });
    lastHtmljs = htmljs;
  });

  domrange.onAttached(function attached(range, element) {
    Blaze.DOMBackend.Teardown.onElementTeardown(element, function teardown() {
      Blaze.destroyView(view, true /* _viaTeardown */);
    });

    scheduleRenderedCallback();
  });

  return domrange;
};

// Renders a view to text or HTML.  Calls `created` callbacks
// and `render`, but not `materialized` or `rendered` callbacks.
// After rendering, immediately calls `destroyed` callbacks and
//
Blaze._stringifyView = function (view, parentView, stringifier) {
  view.parentView = (parentView || null);

  if (view.isCreated)
    throw new Error("Can't render the same View twice");
  view.isCreated = true;

  Blaze._fireCallbacks(view, 'created');

  var htmljs = Blaze.withCurrentView(view, function () {
    return view.render();
  });
  var result = stringifier.visit(htmljs);

  Blaze.destroyView(view);

  return result;
};

Blaze.viewToHTML = function (view, parentView) {
  return Blaze._stringifyView(
    view, parentView,
    new Blaze.HTMLStringifier({parentView: parentView}));
};

Blaze.viewToText = function (view, parentView, textMode) {
  if ((parentView != null) && ! (parentView instanceof Blaze.View)) {
    // omitted parentView argument
    textMode = parentView;
    parentView = null;
  }

  if (! textMode)
    throw new Error("textMode required");
  if (! (textMode === HTML.TEXTMODE.STRING ||
         textMode === HTML.TEXTMODE.RCDATA ||
         textMode === HTML.TEXTMODE.ATTRIBUTE))
    throw new Error("Unknown textMode: " + textMode);

  return Blaze._stringifyView(
    view, parentView,
    new Blaze.TextStringifier({textMode: textMode,
                               parentView: parentView}));
};

Blaze.destroyView = function (view, _viaTeardown) {
  if (view.isDestroyed)
    return;
  view.isDestroyed = true;

  // Destroy views and elements recursively.  If _viaTeardown,
  // only recurse up to views, not elements, because we assume
  // the backend (jQuery) is recursing over the elements already.
  if (view.domrange) {
    var members = view.domrange.members;
    for (var i = 0; i < members.length; i++) {
      var m = members[i];
      if (m instanceof Blaze.DOMRange) {
        if (m.view)
          Blaze.destroyView(m.view, _viaTeardown);
      } else if (! _viaTeardown && m.nodeType === 1) {
        Blaze.destroyNode(m);
      }
    }
  }

  Blaze._fireCallbacks(view, 'destroyed');
};

Blaze.destroyNode = function (node) {
  if (node.nodeType === 1)
    Blaze.DOMBackend.Teardown.tearDownElement(node);
};

// Are the HTMLjs entities `a` and `b` the same?  We could be
// more elaborate here but the point is to catch the most basic
// cases.
Blaze._isContentEqual = function (a, b) {
  if (a instanceof HTML.Raw) {
    return (b instanceof HTML.Raw) && (a.value === b.value);
  } else if (a == null) {
    return (b == null);
  } else {
    return (a === b) &&
      ((typeof a === 'number') || (typeof a === 'boolean') ||
       (typeof a === 'string'));
  }
};

Blaze.currentView = null;

Blaze.withCurrentView = function (view, func) {
  var oldView = Blaze.currentView;
  try {
    Blaze.currentView = view;
    return func();
  } finally {
    Blaze.currentView = oldView;
  }
};

Blaze.render3 = function (contentFunc) {
  return Blaze.materializeView(Blaze.View('render', contentFunc));
};

Blaze.toHTML3 = function (contentFunc) {
  return Blaze.viewToHTML(Blaze.View('toHTML', contentFunc));
};

Blaze.With3 = function (data, contentFunc) {
  var view = Blaze.View('with', contentFunc);

  view.dataVar = new Blaze.ReactiveVar;

  view.onCreated(function () {
    if (typeof data === 'function') {
      view.autorun(function () {
        view.dataVar.set(data());
      });
    } else {
        view.dataVar.set(data);
    }
  });

  return view;
};

Blaze.getCurrentData = function () {
  var theWith = Blaze.getCurrentView('with');
  return theWith ? theWith.dataVar.get() : null;
};

// Gets the current view or its nearest ancestor of kind
// `kind`.
Blaze.getCurrentView = function (kind) {
  var view = Blaze.currentView;
  // Better to fail in cases where it doesn't make sense
  // to use Blaze.getCurrentView().  There will be a current
  // view anywhere it does.  You can check Blaze.currentView
  // if you want to know whether there is one or not.
  if (! view)
    throw new Error("There is no current view");

  if (kind) {
    while (view && view.kind !== kind)
      view = view.parentView;
    return view || null;
  } else {
    // Blaze.getCurrentView() with no arguments just returns
    // Blaze.currentView.
    return view;
  }
};

Blaze.getParentView = function (view, kind) {
  var v = view.parentView;

  if (kind) {
    while (v && v.kind !== kind)
      v = v.parentView;
  }

  return v || null;
};

Blaze._calculateCondition = function (cond) {
  if (cond instanceof Array && cond.length === 0)
    cond = false;
  return !! cond;
};

Blaze.If3 = function (conditionFunc, contentFunc, elseFunc, _not) {
  var conditionVar = new Blaze.ReactiveVar;

  var view = Blaze.View(_not ? 'unless' : 'if', function () {
    this.autorun(function () {
      var cond = Blaze._calculateCondition(conditionFunc());
      conditionVar.set(_not ? (! cond) : cond);
    });
    return conditionVar.get() ? contentFunc() :
      (elseFunc ? elseFunc() : null);
  });
  view.__conditionVar = conditionVar;

  return view;
};

Blaze.Unless3 = function (conditionFunc, contentFunc, elseFunc) {
  return Blaze.If3(conditionFunc, contentFunc, elseFunc, true /*_not*/);
};

Blaze.Each3 = function (argFunc, contentFunc, elseFunc) {
  var eachView = Blaze.View('each', function () {
    var subviews = this.initialSubviews;
    this.initialSubviews = null;
    return subviews;
  });
  eachView.initialSubviews = [];
  eachView.numItems = 0;
  eachView.inElseMode = false;
  eachView.stopHandle = null;
  eachView.contentFunc = contentFunc;
  eachView.elseFunc = elseFunc;
  eachView.argVar = new Blaze.ReactiveVar;

  eachView.onCreated(function () {
    // We evaluate argFunc in an autorun to make sure
    // Blaze.currentView is always set when it runs (rather than
    // passing argFunc straight to ObserveSequence).
    eachView.autorun(function () {
      eachView.argVar.set(argFunc());
    });

    eachView.stopHandle = ObserveSequence.observe(function () {
      return eachView.argVar.get();
    }, {
      addedAt: function (id, item, index) {
        var newItemView = Blaze.With3(item, eachView.contentFunc);
        eachView.numItems++;

        if (eachView.domrange) {
          if (eachView.inElseMode) {
            eachView.domrange.removeMember(0);
            eachView.inElseMode = false;
          }

          var range = Blaze.materializeView(newItemView, eachView);
          eachView.domrange.addMember(range, index);
        } else {
          eachView.initialSubviews.splice(index, 0, newItemView);
        }
      },
      removedAt: function (id, item, index) {
        eachView.numItems--;
        if (eachView.domrange) {
          eachView.domrange.removeMember(index);
          if (eachView.elseFunc && eachView.numItems === 0) {
            eachView.inElseMode = true;
            eachView.domrange.addMember(
              Blaze.materializeView(
                Blaze.View('each_else',eachView.elseFunc),
                eachView));
        }
        } else {
          eachView.initialSubviews.splice(index, 1);
        }
      },
      changedAt: function (id, newItem, oldItem, index) {
        var itemView;
        if (eachView.domrange) {
          itemView = eachView.domrange.getMember(index).view;
        } else {
          itemView = eachView.initialSubviews[index];
        }
        itemView.dataVar.set(newItem);
      },
      movedTo: function (id, item, fromIndex, toIndex) {
        if (eachView.domrange) {
          eachView.domrange.moveMember(fromIndex, toIndex);
        } else {
          var subviews = eachView.initialSubviews;
          var itemView = subviews[fromIndex];
          subviews.splice(fromIndex, 1);
          subviews.splice(toIndex, 0, itemView);
        }
      }
    });

    if (eachView.elseFunc && eachView.numItems === 0) {
      eachView.inElseMode = true;
      eachView.initialSubviews[0] =
        Blaze.View('each_else', eachView.elseFunc);
    }
  });

  eachView.onDestroyed(function () {
    if (eachView.stopHandle)
      eachView.stopHandle.stop();
  });

  return eachView;
};
<commit_msg>Fix scope of block helper argument calculation<commit_after>// TODO
//
// - DOMRange
//   - kill DOMAugmenter
//   - consider moving to _callbacks (including memberOut)
// - port attributes
// - EACH untested as far as:
//   - toHTML
//   - cleanup
//   - most things really...
// - Do you get access to the DOMRange from "destroyed"?

Blaze.View = function (kind, render) {
  if (! (this instanceof Blaze.View))
    // called without `new`
    return new Blaze.View(kind, render);

  if (typeof kind === 'function') {
    // omitted "kind" argument
    render = kind;
    kind = '';
  }
  this.kind = kind;
  this.render = render;

  this._callbacks = {
    created: null,
    materialized: null,
    rendered: null,
    destroyed: null };
};

Blaze.View.prototype.render = function () { return null; };

Blaze.View.prototype.isCreated = false;
Blaze.View.prototype.isDestroyed = false;

Blaze.View.prototype.onCreated = function (cb) {
  this._callbacks.created = this._callbacks.created || [];
  this._callbacks.created.push(cb);
};
Blaze.View.prototype.onMaterialized = function (cb) {
  this._callbacks.materialized = this._callbacks.materialized || [];
  this._callbacks.materialized.push(cb);
};
Blaze.View.prototype.onRendered = function (cb) {
  this._callbacks.rendered = this._callbacks.rendered || [];
  this._callbacks.rendered.push(cb);
};
Blaze.View.prototype.onDestroyed = function (cb) {
  this._callbacks.destroyed = this._callbacks.destroyed || [];
  this._callbacks.destroyed.push(cb);
};

Blaze.View.prototype.autorun = function (f, _inViewScope) {
  var self = this;

  if (! self.isCreated)
    throw new Error("View#autorun must be called from the created callback at the earliest");

  var c = Deps.nonreactive(function () {
    return Deps.autorun(function viewAutorun(c) {
      return Blaze.withCurrentView(_inViewScope || self, function () {
        return f.call(self, c);
      });
    });
  });
  self.onDestroyed(function () { c.stop(); });
};

Blaze._fireCallbacks = function (view, which) {
  Blaze.withCurrentView(view, function () {
    Deps.nonreactive(function fireCallbacks() {
      var cbs = view._callbacks[which];
      for (var i = 0, N = (cbs && cbs.length); i < N; i++)
        cbs[i].call(view);
    });
  });
};

Blaze.materializeView = function (view, parentView) {
  view.parentView = (parentView || null);

  if (view.isCreated)
    throw new Error("Can't render the same View twice");
  view.isCreated = true;

  Blaze._fireCallbacks(view, 'created');

  var domrange = new Blaze.DOMRange;
  view.domrange = domrange;
  domrange.view = view;

  var needsRenderedCallback = false;
  var scheduleRenderedCallback = function () {
    if (needsRenderedCallback && ! view.isDestroyed &&
        view._callbacks.rendered && view._callbacks.rendered.length) {
      Deps.afterFlush(function callRendered() {
        if (needsRenderedCallback && ! view.isDestroyed) {
          needsRenderedCallback = false;
          Blaze._fireCallbacks(view, 'rendered');
        }
      });
    }
  };

  var lastHtmljs;
  view.autorun(function doRender(c) {
    // `view.autorun` sets the current view.
    // Any dependencies that should invalidate this Computation come
    // from this line:
    var htmljs = view.render();

    Deps.nonreactive(function doMaterialize() {
      var materializer = new Blaze.DOMMaterializer({parentView: view});
      var rangesAndNodes = materializer.visit(htmljs, []);
      if (c.firstRun || ! Blaze._isContentEqual(lastHtmljs, htmljs)) {
        domrange.setMembers(rangesAndNodes);
        Blaze._fireCallbacks(view, 'materialized');
        needsRenderedCallback = true;
        if (! c.firstRun)
          scheduleRenderedCallback();
      }
    });
    lastHtmljs = htmljs;
  });

  domrange.onAttached(function attached(range, element) {
    Blaze.DOMBackend.Teardown.onElementTeardown(element, function teardown() {
      Blaze.destroyView(view, true /* _viaTeardown */);
    });

    scheduleRenderedCallback();
  });

  return domrange;
};

// Renders a view to text or HTML.  Calls `created` callbacks
// and `render`, but not `materialized` or `rendered` callbacks.
// After rendering, immediately calls `destroyed` callbacks and
//
Blaze._stringifyView = function (view, parentView, stringifier) {
  view.parentView = (parentView || null);

  if (view.isCreated)
    throw new Error("Can't render the same View twice");
  view.isCreated = true;

  Blaze._fireCallbacks(view, 'created');

  var htmljs = Blaze.withCurrentView(view, function () {
    return view.render();
  });
  var result = stringifier.visit(htmljs);

  Blaze.destroyView(view);

  return result;
};

Blaze.viewToHTML = function (view, parentView) {
  return Blaze._stringifyView(
    view, parentView,
    new Blaze.HTMLStringifier({parentView: view}));
};

Blaze.viewToText = function (view, parentView, textMode) {
  if ((parentView != null) && ! (parentView instanceof Blaze.View)) {
    // omitted parentView argument
    textMode = parentView;
    parentView = null;
  }

  if (! textMode)
    throw new Error("textMode required");
  if (! (textMode === HTML.TEXTMODE.STRING ||
         textMode === HTML.TEXTMODE.RCDATA ||
         textMode === HTML.TEXTMODE.ATTRIBUTE))
    throw new Error("Unknown textMode: " + textMode);

  return Blaze._stringifyView(
    view, parentView,
    new Blaze.TextStringifier({textMode: textMode,
                               parentView: view}));
};

Blaze.destroyView = function (view, _viaTeardown) {
  if (view.isDestroyed)
    return;
  view.isDestroyed = true;

  // Destroy views and elements recursively.  If _viaTeardown,
  // only recurse up to views, not elements, because we assume
  // the backend (jQuery) is recursing over the elements already.
  if (view.domrange) {
    var members = view.domrange.members;
    for (var i = 0; i < members.length; i++) {
      var m = members[i];
      if (m instanceof Blaze.DOMRange) {
        if (m.view)
          Blaze.destroyView(m.view, _viaTeardown);
      } else if (! _viaTeardown && m.nodeType === 1) {
        Blaze.destroyNode(m);
      }
    }
  }

  Blaze._fireCallbacks(view, 'destroyed');
};

Blaze.destroyNode = function (node) {
  if (node.nodeType === 1)
    Blaze.DOMBackend.Teardown.tearDownElement(node);
};

// Are the HTMLjs entities `a` and `b` the same?  We could be
// more elaborate here but the point is to catch the most basic
// cases.
Blaze._isContentEqual = function (a, b) {
  if (a instanceof HTML.Raw) {
    return (b instanceof HTML.Raw) && (a.value === b.value);
  } else if (a == null) {
    return (b == null);
  } else {
    return (a === b) &&
      ((typeof a === 'number') || (typeof a === 'boolean') ||
       (typeof a === 'string'));
  }
};

Blaze.currentView = null;

Blaze.withCurrentView = function (view, func) {
  var oldView = Blaze.currentView;
  try {
    Blaze.currentView = view;
    return func();
  } finally {
    Blaze.currentView = oldView;
  }
};

Blaze.render3 = function (contentFunc) {
  return Blaze.materializeView(Blaze.View('render', contentFunc));
};

Blaze.toHTML3 = function (contentFunc) {
  return Blaze.viewToHTML(Blaze.View('toHTML', contentFunc));
};

Blaze.With3 = function (data, contentFunc) {
  var view = Blaze.View('with', contentFunc);

  view.dataVar = new Blaze.ReactiveVar;

  view.onCreated(function () {
    if (typeof data === 'function') {
      view.autorun(function () {
        view.dataVar.set(data());
      }, view.parentView);
    } else {
        view.dataVar.set(data);
    }
  });

  return view;
};

Blaze.getCurrentData = function () {
  var theWith = Blaze.getCurrentView('with');
  return theWith ? theWith.dataVar.get() : null;
};

// Gets the current view or its nearest ancestor of kind
// `kind`.
Blaze.getCurrentView = function (kind) {
  var view = Blaze.currentView;
  // Better to fail in cases where it doesn't make sense
  // to use Blaze.getCurrentView().  There will be a current
  // view anywhere it does.  You can check Blaze.currentView
  // if you want to know whether there is one or not.
  if (! view)
    throw new Error("There is no current view");

  if (kind) {
    while (view && view.kind !== kind)
      view = view.parentView;
    return view || null;
  } else {
    // Blaze.getCurrentView() with no arguments just returns
    // Blaze.currentView.
    return view;
  }
};

Blaze.getParentView = function (view, kind) {
  var v = view.parentView;

  if (kind) {
    while (v && v.kind !== kind)
      v = v.parentView;
  }

  return v || null;
};

Blaze._calculateCondition = function (cond) {
  if (cond instanceof Array && cond.length === 0)
    cond = false;
  return !! cond;
};

Blaze.If3 = function (conditionFunc, contentFunc, elseFunc, _not) {
  var conditionVar = new Blaze.ReactiveVar;

  var view = Blaze.View(_not ? 'unless' : 'if', function () {
    this.autorun(function () {
      var cond = Blaze._calculateCondition(conditionFunc());
      conditionVar.set(_not ? (! cond) : cond);
    }, this.parentView);
    return conditionVar.get() ? contentFunc() :
      (elseFunc ? elseFunc() : null);
  });
  view.__conditionVar = conditionVar;

  return view;
};

Blaze.Unless3 = function (conditionFunc, contentFunc, elseFunc) {
  return Blaze.If3(conditionFunc, contentFunc, elseFunc, true /*_not*/);
};

Blaze.Each3 = function (argFunc, contentFunc, elseFunc) {
  var eachView = Blaze.View('each', function () {
    var subviews = this.initialSubviews;
    this.initialSubviews = null;
    return subviews;
  });
  eachView.initialSubviews = [];
  eachView.numItems = 0;
  eachView.inElseMode = false;
  eachView.stopHandle = null;
  eachView.contentFunc = contentFunc;
  eachView.elseFunc = elseFunc;
  eachView.argVar = new Blaze.ReactiveVar;

  eachView.onCreated(function () {
    // We evaluate argFunc in an autorun to make sure
    // Blaze.currentView is always set when it runs (rather than
    // passing argFunc straight to ObserveSequence).
    eachView.autorun(function () {
      eachView.argVar.set(argFunc());
    }, eachView.parentView);

    eachView.stopHandle = ObserveSequence.observe(function () {
      return eachView.argVar.get();
    }, {
      addedAt: function (id, item, index) {
        var newItemView = Blaze.With3(item, eachView.contentFunc);
        eachView.numItems++;

        if (eachView.domrange) {
          if (eachView.inElseMode) {
            eachView.domrange.removeMember(0);
            eachView.inElseMode = false;
          }

          var range = Blaze.materializeView(newItemView, eachView);
          eachView.domrange.addMember(range, index);
        } else {
          eachView.initialSubviews.splice(index, 0, newItemView);
        }
      },
      removedAt: function (id, item, index) {
        eachView.numItems--;
        if (eachView.domrange) {
          eachView.domrange.removeMember(index);
          if (eachView.elseFunc && eachView.numItems === 0) {
            eachView.inElseMode = true;
            eachView.domrange.addMember(
              Blaze.materializeView(
                Blaze.View('each_else',eachView.elseFunc),
                eachView));
        }
        } else {
          eachView.initialSubviews.splice(index, 1);
        }
      },
      changedAt: function (id, newItem, oldItem, index) {
        var itemView;
        if (eachView.domrange) {
          itemView = eachView.domrange.getMember(index).view;
        } else {
          itemView = eachView.initialSubviews[index];
        }
        itemView.dataVar.set(newItem);
      },
      movedTo: function (id, item, fromIndex, toIndex) {
        if (eachView.domrange) {
          eachView.domrange.moveMember(fromIndex, toIndex);
        } else {
          var subviews = eachView.initialSubviews;
          var itemView = subviews[fromIndex];
          subviews.splice(fromIndex, 1);
          subviews.splice(toIndex, 0, itemView);
        }
      }
    });

    if (eachView.elseFunc && eachView.numItems === 0) {
      eachView.inElseMode = true;
      eachView.initialSubviews[0] =
        Blaze.View('each_else', eachView.elseFunc);
    }
  });

  eachView.onDestroyed(function () {
    if (eachView.stopHandle)
      eachView.stopHandle.stop();
  });

  return eachView;
};
<|endoftext|>