<commit_before>/**
 * @fileOverview Requirejs module containing device modifier to launch native external media players
 *
 * @preserve Copyright (c) 2014 British Broadcasting Corporation
 * (http://www.bbc.co.uk) and TAL Contributors (1)
 *
 * (1) TAL Contributors are listed in the AUTHORS file and at
 *     https://github.com/fmtvp/TAL/AUTHORS - please extend this file,
 *     not this notice.
 *
 * @license Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * All rights reserved
 * Please contact us for an alternative licence
 */

require.def(
    "antie/devices/mediaplayer/html5",
    [
        "antie/runtimecontext",
        "antie/devices/device",
        "antie/devices/mediaplayer/mediaplayer"
    ],
    function(RuntimeContext, Device, MediaPlayer) {
        "use strict";

        /**
         * Main MediaPlayer implementation for HTML5 devices.
         * Use this device modifier if a device implements the HTML5 media playback standard.
         * It must support creation of <video> and <audio> elements, and those objects must expose an
         * API in accordance with the HTML5 media specification.
         * @name antie.devices.mediaplayer.html5
         * @class
         * @extends antie.devices.mediaplayer.MediaPlayer.prototype
         */
        var Player = MediaPlayer.extend({

            init: function() {
                this._super();
                this._state = MediaPlayer.STATE.EMPTY;
            },

            /**
            * @inheritDoc
            */
            setSource: function(mediaType, url, mimeType) {
                if (this.getState() === MediaPlayer.STATE.EMPTY) {
                    this._type = mediaType;
                    this._source = url;
                    this._mimeType = mimeType;
                    var device = RuntimeContext.getDevice();

                    var idSuffix = mediaType === MediaPlayer.TYPE.AUDIO ? "Audio" : "Video";

                    this._mediaElement = device._createElement(this._type, "mediaPlayer" + idSuffix);
                    this._mediaElement.autoplay = false;
                    this._mediaElement.style.position = "absolute";
                    this._mediaElement.style.top = "0px";
                    this._mediaElement.style.left = "0px";
                    this._mediaElement.style.width = "100%";
                    this._mediaElement.style.height = "100%";

                    var self = this;
                    this._wrapOnFinishedBuffering = function(event) { self._onFinishedBuffering(); };
                    this._wrapOnError = function(event) { self._onDeviceError(); };
                    this._wrapOnEndOfMedia = function(event) { self._onEndOfMedia(); };
                    this._wrapOnDeviceBuffering = function(event) { self._onDeviceBuffering(); };
                    this._wrapOnStatus = function(event) { self._onStatus(); };
                    this._wrapOnMetadata = function(event) { self._onMetadata(); };
                    this._mediaElement.addEventListener("canplay", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.addEventListener("seeked", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.addEventListener("playing", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.addEventListener("error", this._wrapOnError, false);
                    this._mediaElement.addEventListener("ended", this._wrapOnEndOfMedia, false);
                    this._mediaElement.addEventListener("waiting", this._wrapOnDeviceBuffering, false);
                    this._mediaElement.addEventListener("timeupdate", this._wrapOnStatus, false);
                    this._mediaElement.addEventListener("loadedmetadata", this._wrapOnMetadata, false);

                    var body = document.getElementsByTagName("body")[0];
                    device.prependChildElement(body, this._mediaElement);

                    this._mediaElement.preload = "auto";
                    this._mediaElement.src = url;
                    this._mediaElement.load();

                    this._toStopped();
                } else {
                    this._toError("Cannot set source unless in the '" + MediaPlayer.STATE.EMPTY + "' state");
                }
            },

            /**
            * @inheritDoc
            */
            playFrom: function(seconds) {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                this._targetSeekTime = seconds;
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                    case MediaPlayer.STATE.PAUSED:
                    case MediaPlayer.STATE.COMPLETE:
                        this._toBuffering();
                        this._playFromIfReady();
                        break;

                    case MediaPlayer.STATE.BUFFERING:
                        this._playFromIfReady();
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._toBuffering();
                        if (this._isNearToCurrentTime(seconds)) {
                            this._toPlaying();
                        } else {
                            this._playFromIfReady();
                        }
                        break;

                    default:
                        this._toError("Cannot playFrom while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            beginPlayback: function(seconds) {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                        this._toBuffering();
                        this._mediaElement.play();
                        break;

                    default:
                        this._toError("Cannot beginPlayback while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            pause: function() {
                this._postBufferingState = MediaPlayer.STATE.PAUSED;
                switch (this.getState()) {
                    case MediaPlayer.STATE.PAUSED:
                        break;

                    case MediaPlayer.STATE.BUFFERING:
                        if (this._readyToPlayFrom) {
                            // If we are not ready to playFrom, then calling pause would seek to the start of media, which we might not want.
                            this._mediaElement.pause();
                        }
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._mediaElement.pause();
                        this._toPaused();
                        break;

                    default:
                        this._toError("Cannot pause while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            resume : function() {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                switch (this.getState()) {
                    case MediaPlayer.STATE.PLAYING:
                    case MediaPlayer.STATE.BUFFERING:
                        break;

                    case MediaPlayer.STATE.PAUSED:
                        this._mediaElement.play();
                        this._toPlaying();
                        break;

                    default:
                        this._toError("Cannot resume while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            stop: function() {
                switch (this.getState()) {
                    case MediaPlayer.STATE.BUFFERING:
                    case MediaPlayer.STATE.PLAYING:
                    case MediaPlayer.STATE.PAUSED:
                    case MediaPlayer.STATE.COMPLETE:
                        this._mediaElement.pause();
                        this._mediaElement.currentTime = 0;
                        this._toStopped();
                        break;

                    default:
                        this._toError("Cannot stop while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            reset: function() {
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                    case MediaPlayer.STATE.ERROR:
                        this._toEmpty();
                        break;

                    default:
                        this._toError("Cannot reset while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            getSource: function() {
                return this._source;
            },

            /**
            * @inheritDoc
            */
            getMimeType: function() {
                return this._mimeType;
            },

            /**
            * @inheritDoc
            */
            getCurrentTime: function() {
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                    case MediaPlayer.STATE.ERROR:
                        break;

                    default:
                        if (this._mediaElement) {
                            return this._mediaElement.currentTime;
                        }
                        break;
                }
                return undefined;
            },

            /**
            * @inheritDoc
            */
            getRange: function() {
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                    case MediaPlayer.STATE.ERROR:
                        break;

                    default:
                        return this._getSeekableRange();
                }
                return undefined;
            },

            _getSeekableRange: function() {
                if (this._mediaElement) {
                    if (this._mediaElement.seekable && this._mediaElement.seekable.length > 0) {
                        return {
                            start: this._mediaElement.seekable.start(0),
                            end: this._mediaElement.seekable.end(0)
                        };
                    } else if (this._mediaElement.duration !== undefined) {
                        return {
                            start: 0,
                            end: this._mediaElement.duration
                        };
                    } else {
                        RuntimeContext.getDevice().getLogger().warn("No 'duration' or 'seekable' on media element");
                    }
                }
                return undefined;
            },

            /**
            * @inheritDoc
            */
            getState: function() {
                return this._state;
            },

            _onFinishedBuffering: function() {
                this._exitBuffering();
            },

            _onDeviceError: function() {
                this._toError("Media element emitted error with code: " + this._mediaElement.error.code);
            },

            /**
             * @protected
             */
            _onDeviceBuffering: function() {
                if (this.getState() === MediaPlayer.STATE.PLAYING) {
                    this._toBuffering();
                }
            },

            _onEndOfMedia: function() {
                this._toComplete();
            },

            _onStatus: function() {
                if (this.getState() === MediaPlayer.STATE.PLAYING) {
                    this._emitEvent(MediaPlayer.EVENT.STATUS);
                }
            },

            _onMetadata: function() {
                this._readyToPlayFrom = true;
                if (this._waitingToPlayFrom()) {
                    this._deferredPlayFrom();
                }
            },

            _exitBuffering: function () {
                if (this.getState() !== MediaPlayer.STATE.BUFFERING) {
                    return;

                } else if (this._postBufferingState === MediaPlayer.STATE.PAUSED) {
                    this._toPaused();

                } else {
                    this._toPlaying();
                }
            },

            _playFromIfReady: function() {
                if (this._readyToPlayFrom) {
                    if (this._waitingToPlayFrom()) {
                        this._deferredPlayFrom();
                    }
                }
            },

            _waitingToPlayFrom: function() {
                return this._targetSeekTime !== undefined;
            },

            _deferredPlayFrom: function() {
                this._seekTo(this._targetSeekTime);
                this._mediaElement.play();
                if (this._postBufferingState === MediaPlayer.STATE.PAUSED) {
                    this._mediaElement.pause();
                }
                this._targetSeekTime = undefined;
            },

            _seekTo: function(seconds) {
                this._mediaElement.currentTime = this._getClampedTime(seconds);
            },

            _wipe: function() {
                this._type = undefined;
                this._source = undefined;
                this._mimeType = undefined;
                this._targetSeekTime = undefined;
                this._destroyMediaElement();
                clearInterval(this._sentinelInterval);
                this._readyToPlayFrom = false;
            },

            _destroyMediaElement: function() {
                if (this._mediaElement) {
                    this._mediaElement.removeEventListener("canplay", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.removeEventListener("seeked", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.removeEventListener("playing", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.removeEventListener("error", this._wrapOnError, false);
                    this._mediaElement.removeEventListener("ended", this._wrapOnEndOfMedia, false);
                    this._mediaElement.removeEventListener("waiting", this._wrapOnDeviceBuffering, false);
                    this._mediaElement.removeEventListener("timeupdate", this._wrapOnStatus, false);
                    this._mediaElement.removeEventListener("loadedmetadata", this._wrapOnMetadata, false);

                    this._mediaElement.removeAttribute('src');
                    this._mediaElement.load();

                    var device = RuntimeContext.getDevice();
                    device.removeElement(this._mediaElement);

                    delete this._mediaElement;
                }
            },

            _toStopped: function() {
                this._state = MediaPlayer.STATE.STOPPED;
                this._emitEvent(MediaPlayer.EVENT.STOPPED);
            },

            _toBuffering: function() {
                this._state = MediaPlayer.STATE.BUFFERING;
                this._emitEvent(MediaPlayer.EVENT.BUFFERING);
            },

            _toPlaying: function() {
                this._state = MediaPlayer.STATE.PLAYING;
                this._emitEvent(MediaPlayer.EVENT.PLAYING);
                this._setSentinels();
            },

            _toPaused: function() {
                this._state = MediaPlayer.STATE.PAUSED;
                this._emitEvent(MediaPlayer.EVENT.PAUSED);
            },

            _toComplete: function() {
                this._state = MediaPlayer.STATE.COMPLETE;
                this._emitEvent(MediaPlayer.EVENT.COMPLETE);
            },

            _toEmpty: function() {
                this._wipe();
                this._state = MediaPlayer.STATE.EMPTY;
            },

            _toError: function(errorMessage) {
                RuntimeContext.getDevice().getLogger().error(errorMessage);
                this._wipe();
                this._state = MediaPlayer.STATE.ERROR;
                this._emitEvent(MediaPlayer.EVENT.ERROR);
            },

            _enterBufferingSentinel: function() {
                if(!this._hasSentinelTimeAdvanced) {
                    this._toBuffering();
                }
            },

            _setSentinels: function() {
                var self = this;
                this._lastSentinelTime = this.getCurrentTime();
                this._sentinelInterval = setInterval(function() {
                    var newTime = self.getCurrentTime();
                    self._hasSentinelTimeAdvanced = (newTime > self._lastSentinelTime);
                    self._lastSentinelTime = newTime;

                    self._enterBufferingSentinel();
                }, 1100);
            }
        });

        var instance = new Player();

        // Mixin this MediaPlayer implementation, so that device.getMediaPlayer() returns the correct implementation for the device
        Device.prototype.getMediaPlayer = function() {
            return instance;
        };

        return Player;
    }

);
<commit_msg>Extracted clearSentinels method<commit_after>/**
 * @fileOverview Requirejs module containing device modifier to launch native external media players
 *
 * @preserve Copyright (c) 2014 British Broadcasting Corporation
 * (http://www.bbc.co.uk) and TAL Contributors (1)
 *
 * (1) TAL Contributors are listed in the AUTHORS file and at
 *     https://github.com/fmtvp/TAL/AUTHORS - please extend this file,
 *     not this notice.
 *
 * @license Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * All rights reserved
 * Please contact us for an alternative licence
 */

require.def(
    "antie/devices/mediaplayer/html5",
    [
        "antie/runtimecontext",
        "antie/devices/device",
        "antie/devices/mediaplayer/mediaplayer"
    ],
    function(RuntimeContext, Device, MediaPlayer) {
        "use strict";

        /**
         * Main MediaPlayer implementation for HTML5 devices.
         * Use this device modifier if a device implements the HTML5 media playback standard.
         * It must support creation of <video> and <audio> elements, and those objects must expose an
         * API in accordance with the HTML5 media specification.
         * @name antie.devices.mediaplayer.html5
         * @class
         * @extends antie.devices.mediaplayer.MediaPlayer.prototype
         */
        var Player = MediaPlayer.extend({

            init: function() {
                this._super();
                this._state = MediaPlayer.STATE.EMPTY;
            },

            /**
            * @inheritDoc
            */
            setSource: function(mediaType, url, mimeType) {
                if (this.getState() === MediaPlayer.STATE.EMPTY) {
                    this._type = mediaType;
                    this._source = url;
                    this._mimeType = mimeType;
                    var device = RuntimeContext.getDevice();

                    var idSuffix = mediaType === MediaPlayer.TYPE.AUDIO ? "Audio" : "Video";

                    this._mediaElement = device._createElement(this._type, "mediaPlayer" + idSuffix);
                    this._mediaElement.autoplay = false;
                    this._mediaElement.style.position = "absolute";
                    this._mediaElement.style.top = "0px";
                    this._mediaElement.style.left = "0px";
                    this._mediaElement.style.width = "100%";
                    this._mediaElement.style.height = "100%";

                    var self = this;
                    this._wrapOnFinishedBuffering = function(event) { self._onFinishedBuffering(); };
                    this._wrapOnError = function(event) { self._onDeviceError(); };
                    this._wrapOnEndOfMedia = function(event) { self._onEndOfMedia(); };
                    this._wrapOnDeviceBuffering = function(event) { self._onDeviceBuffering(); };
                    this._wrapOnStatus = function(event) { self._onStatus(); };
                    this._wrapOnMetadata = function(event) { self._onMetadata(); };
                    this._mediaElement.addEventListener("canplay", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.addEventListener("seeked", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.addEventListener("playing", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.addEventListener("error", this._wrapOnError, false);
                    this._mediaElement.addEventListener("ended", this._wrapOnEndOfMedia, false);
                    this._mediaElement.addEventListener("waiting", this._wrapOnDeviceBuffering, false);
                    this._mediaElement.addEventListener("timeupdate", this._wrapOnStatus, false);
                    this._mediaElement.addEventListener("loadedmetadata", this._wrapOnMetadata, false);

                    var body = document.getElementsByTagName("body")[0];
                    device.prependChildElement(body, this._mediaElement);

                    this._mediaElement.preload = "auto";
                    this._mediaElement.src = url;
                    this._mediaElement.load();

                    this._toStopped();
                } else {
                    this._toError("Cannot set source unless in the '" + MediaPlayer.STATE.EMPTY + "' state");
                }
            },

            /**
            * @inheritDoc
            */
            playFrom: function(seconds) {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                this._targetSeekTime = seconds;
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                    case MediaPlayer.STATE.PAUSED:
                    case MediaPlayer.STATE.COMPLETE:
                        this._toBuffering();
                        this._playFromIfReady();
                        break;

                    case MediaPlayer.STATE.BUFFERING:
                        this._playFromIfReady();
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._toBuffering();
                        if (this._isNearToCurrentTime(seconds)) {
                            this._toPlaying();
                        } else {
                            this._playFromIfReady();
                        }
                        break;

                    default:
                        this._toError("Cannot playFrom while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            beginPlayback: function(seconds) {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                        this._toBuffering();
                        this._mediaElement.play();
                        break;

                    default:
                        this._toError("Cannot beginPlayback while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            pause: function() {
                this._postBufferingState = MediaPlayer.STATE.PAUSED;
                switch (this.getState()) {
                    case MediaPlayer.STATE.PAUSED:
                        break;

                    case MediaPlayer.STATE.BUFFERING:
                        if (this._readyToPlayFrom) {
                            // If we are not ready to playFrom, then calling pause would seek to the start of media, which we might not want.
                            this._mediaElement.pause();
                        }
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._mediaElement.pause();
                        this._toPaused();
                        break;

                    default:
                        this._toError("Cannot pause while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            resume : function() {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                switch (this.getState()) {
                    case MediaPlayer.STATE.PLAYING:
                    case MediaPlayer.STATE.BUFFERING:
                        break;

                    case MediaPlayer.STATE.PAUSED:
                        this._mediaElement.play();
                        this._toPlaying();
                        break;

                    default:
                        this._toError("Cannot resume while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            stop: function() {
                switch (this.getState()) {
                    case MediaPlayer.STATE.BUFFERING:
                    case MediaPlayer.STATE.PLAYING:
                    case MediaPlayer.STATE.PAUSED:
                    case MediaPlayer.STATE.COMPLETE:
                        this._mediaElement.pause();
                        this._mediaElement.currentTime = 0;
                        this._toStopped();
                        break;

                    default:
                        this._toError("Cannot stop while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            reset: function() {
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                    case MediaPlayer.STATE.ERROR:
                        this._toEmpty();
                        break;

                    default:
                        this._toError("Cannot reset while in the '" + this.getState() + "' state");
                        break;
                }
            },

            /**
            * @inheritDoc
            */
            getSource: function() {
                return this._source;
            },

            /**
            * @inheritDoc
            */
            getMimeType: function() {
                return this._mimeType;
            },

            /**
            * @inheritDoc
            */
            getCurrentTime: function() {
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                    case MediaPlayer.STATE.ERROR:
                        break;

                    default:
                        if (this._mediaElement) {
                            return this._mediaElement.currentTime;
                        }
                        break;
                }
                return undefined;
            },

            /**
            * @inheritDoc
            */
            getRange: function() {
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                    case MediaPlayer.STATE.ERROR:
                        break;

                    default:
                        return this._getSeekableRange();
                }
                return undefined;
            },

            _getSeekableRange: function() {
                if (this._mediaElement) {
                    if (this._mediaElement.seekable && this._mediaElement.seekable.length > 0) {
                        return {
                            start: this._mediaElement.seekable.start(0),
                            end: this._mediaElement.seekable.end(0)
                        };
                    } else if (this._mediaElement.duration !== undefined) {
                        return {
                            start: 0,
                            end: this._mediaElement.duration
                        };
                    } else {
                        RuntimeContext.getDevice().getLogger().warn("No 'duration' or 'seekable' on media element");
                    }
                }
                return undefined;
            },

            /**
            * @inheritDoc
            */
            getState: function() {
                return this._state;
            },

            _onFinishedBuffering: function() {
                this._exitBuffering();
            },

            _onDeviceError: function() {
                this._toError("Media element emitted error with code: " + this._mediaElement.error.code);
            },

            /**
             * @protected
             */
            _onDeviceBuffering: function() {
                if (this.getState() === MediaPlayer.STATE.PLAYING) {
                    this._toBuffering();
                }
            },

            _onEndOfMedia: function() {
                this._toComplete();
            },

            _onStatus: function() {
                if (this.getState() === MediaPlayer.STATE.PLAYING) {
                    this._emitEvent(MediaPlayer.EVENT.STATUS);
                }
            },

            _onMetadata: function() {
                this._readyToPlayFrom = true;
                if (this._waitingToPlayFrom()) {
                    this._deferredPlayFrom();
                }
            },

            _exitBuffering: function () {
                if (this.getState() !== MediaPlayer.STATE.BUFFERING) {
                    return;

                } else if (this._postBufferingState === MediaPlayer.STATE.PAUSED) {
                    this._toPaused();

                } else {
                    this._toPlaying();
                }
            },

            _playFromIfReady: function() {
                if (this._readyToPlayFrom) {
                    if (this._waitingToPlayFrom()) {
                        this._deferredPlayFrom();
                    }
                }
            },

            _waitingToPlayFrom: function() {
                return this._targetSeekTime !== undefined;
            },

            _deferredPlayFrom: function() {
                this._seekTo(this._targetSeekTime);
                this._mediaElement.play();
                if (this._postBufferingState === MediaPlayer.STATE.PAUSED) {
                    this._mediaElement.pause();
                }
                this._targetSeekTime = undefined;
            },

            _seekTo: function(seconds) {
                this._mediaElement.currentTime = this._getClampedTime(seconds);
            },

            _wipe: function() {
                this._type = undefined;
                this._source = undefined;
                this._mimeType = undefined;
                this._targetSeekTime = undefined;
                this._destroyMediaElement();
                this._readyToPlayFrom = false;
                this._clearSentinels();
            },

            _destroyMediaElement: function() {
                if (this._mediaElement) {
                    this._mediaElement.removeEventListener("canplay", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.removeEventListener("seeked", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.removeEventListener("playing", this._wrapOnFinishedBuffering, false);
                    this._mediaElement.removeEventListener("error", this._wrapOnError, false);
                    this._mediaElement.removeEventListener("ended", this._wrapOnEndOfMedia, false);
                    this._mediaElement.removeEventListener("waiting", this._wrapOnDeviceBuffering, false);
                    this._mediaElement.removeEventListener("timeupdate", this._wrapOnStatus, false);
                    this._mediaElement.removeEventListener("loadedmetadata", this._wrapOnMetadata, false);

                    this._mediaElement.removeAttribute('src');
                    this._mediaElement.load();

                    var device = RuntimeContext.getDevice();
                    device.removeElement(this._mediaElement);

                    delete this._mediaElement;
                }
            },

            _toStopped: function() {
                this._state = MediaPlayer.STATE.STOPPED;
                this._emitEvent(MediaPlayer.EVENT.STOPPED);
            },

            _toBuffering: function() {
                this._state = MediaPlayer.STATE.BUFFERING;
                this._emitEvent(MediaPlayer.EVENT.BUFFERING);
            },

            _toPlaying: function() {
                this._state = MediaPlayer.STATE.PLAYING;
                this._emitEvent(MediaPlayer.EVENT.PLAYING);
                this._setSentinels();
            },

            _toPaused: function() {
                this._state = MediaPlayer.STATE.PAUSED;
                this._emitEvent(MediaPlayer.EVENT.PAUSED);
            },

            _toComplete: function() {
                this._state = MediaPlayer.STATE.COMPLETE;
                this._emitEvent(MediaPlayer.EVENT.COMPLETE);
            },

            _toEmpty: function() {
                this._wipe();
                this._state = MediaPlayer.STATE.EMPTY;
            },

            _toError: function(errorMessage) {
                RuntimeContext.getDevice().getLogger().error(errorMessage);
                this._wipe();
                this._state = MediaPlayer.STATE.ERROR;
                this._emitEvent(MediaPlayer.EVENT.ERROR);
            },

            _enterBufferingSentinel: function() {
                if(!this._hasSentinelTimeAdvanced) {
                    this._toBuffering();
                }
            },

            _clearSentinels: function() {
                clearInterval(this._sentinelInterval);
            },

            _setSentinels: function() {
                var self = this;
                this._clearSentinels();
                this._lastSentinelTime = this.getCurrentTime();
                this._sentinelInterval = setInterval(function() {
                    var newTime = self.getCurrentTime();
                    self._hasSentinelTimeAdvanced = (newTime > self._lastSentinelTime);
                    self._lastSentinelTime = newTime;

                    self._enterBufferingSentinel();
                }, 1100);
            }
        });

        var instance = new Player();

        // Mixin this MediaPlayer implementation, so that device.getMediaPlayer() returns the correct implementation for the device
        Device.prototype.getMediaPlayer = function() {
            return instance;
        };

        return Player;
    }

);
<|endoftext|>


<commit_before>// For token sale holders:
// 1. Add the address users are sending to
// 2. Add the gas limit users should use to send successfully (this avoids OOG errors)
// 3. Add any data if applicable
// 4. Add a message if you want.

// Token Calendar:
// http://www.tokensalecalendar.com/
// If you aren't on the above, you should get on it.

module.exports = [
{
  to:         '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
  gasLimit:   21000,
  data:       '',
  msg:        'Thank you for donating to MyEtherWallet. TO THE MOON!'
},
{
  // DCORP
  to:         '0xD42433A8a0A1CEB73c8ff4e432463e2ee109039d',
  gasLimit:   100000,
  data:       '',
  msg:        'DCORP. Ends June 30, 2017.'
},
{
  // BANCOR
  to:         '0xBbc79794599b19274850492394004087cBf89710',
  gasLimit:   200000,
  data:       '',
  msg:        'Bancor. Open June 12, 10:00 GMT - 11:00 GMT. Use gas slider in the footer to increase gas PRICE to 50 GWEI.'
},
{
  // SuperDao
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'SuperDAO. Starts June 13, 2017.'
},
{
  // SONM
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'SuperDAO. Starts June 13, 2017.'
},
{
  // Status
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Status. Starts June 17, 2017.'
},
{
  // OpenANX
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'OpenANX. Starts June 22, 2017.'
},
{
  // Adchain
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Adchain. Starts June 26, 2017.'
},
{
  // OmiseGo
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'OmiseGo. Starts June 27, 2017.'
},
{
  // Santiment
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Santiment. Starts June 30, 2017.'
},
{
  // Moeda
  to:         '0x4870E705a3def9DDa6da7A953D1cd3CCEDD08573',
  gasLimit:   200000,
  data:       '',
  msg:        'Moeda. Ends at block 4,111,557.'
}
]

<commit_msg>update bancor time<commit_after>// For token sale holders:
// 1. Add the address users are sending to
// 2. Add the gas limit users should use to send successfully (this avoids OOG errors)
// 3. Add any data if applicable
// 4. Add a message if you want.

// Token Calendar:
// http://www.tokensalecalendar.com/
// If you aren't on the above, you should get on it.

module.exports = [
{
  to:         '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
  gasLimit:   21000,
  data:       '',
  msg:        'Thank you for donating to MyEtherWallet. TO THE MOON!'
},
{
  // DCORP
  to:         '0xD42433A8a0A1CEB73c8ff4e432463e2ee109039d',
  gasLimit:   100000,
  data:       '',
  msg:        'DCORP. Ends June 30, 2017.'
},
{
  // BANCOR
  to:         '0xBbc79794599b19274850492394004087cBf89710',
  gasLimit:   200000,
  data:       '',
  msg:        'Bancor. Open June 12, 14:00 GMT - 15:00 GMT. Use gas slider in the footer to increase GAS PRICE to 50 GWEI. GAS LIMIT: 200000.'
},
{
  // SuperDao
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'SuperDAO. Starts June 13, 2017.'
},
{
  // SONM
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'SuperDAO. Starts June 13, 2017.'
},
{
  // Status
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Status. Starts June 17, 2017.'
},
{
  // OpenANX
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'OpenANX. Starts June 22, 2017.'
},
{
  // Adchain
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Adchain. Starts June 26, 2017.'
},
{
  // OmiseGo
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'OmiseGo. Starts June 27, 2017.'
},
{
  // Santiment
  to:         '0x0000',
  gasLimit:   200000,
  data:       '',
  msg:        'Santiment. Starts June 30, 2017.'
},
{
  // Moeda
  to:         '0x4870E705a3def9DDa6da7A953D1cd3CCEDD08573',
  gasLimit:   200000,
  data:       '',
  msg:        'Moeda. Ends at block 4,111,557.'
}
]

<|endoftext|>


<commit_before>/**
 * @file ext-panning.js
 *
 * @license MIT
 *
 * @copyright 2013 Luis Aguirre
 *
 */
/*
  This is a very basic SVG-Edit extension to let tablet/mobile devices pan without problem
*/

const loadExtensionTranslation = async function (lang) {
  let translationModule;
  try {
    translationModule = await import(`./locale/${encodeURIComponent(lang)}.js`);
  } catch (_error) {
    // eslint-disable-next-line no-console
    console.error(`Missing translation (${lang}) - using 'en'`);
    translationModule = await import(`./locale/en.js`);
  }
  return translationModule.default;
};

export default {
  name: 'panning',
  async init ({importLocale}) {
    const svgEditor = this;
    const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
    const {svgCanvas} = svgEditor;
    const buttons = [{
      id: 'ext-panning',
      icon: 'panning.png',
      type: 'mode',
      events: {
        click () {
          svgCanvas.setMode('ext-panning');
        }
      }
    }];
    return {
      newUI: true,
      name: strings.name,
      buttons: strings.buttons.map((button, i) => {
        return Object.assign(buttons[i], button);
      }),
      mouseDown () {
        if (svgCanvas.getMode() === 'ext-panning') {
          svgEditor.setPanning(true);
          return {started: true};
        }
        return undefined;
      },
      mouseUp () {
        if (svgCanvas.getMode() === 'ext-panning') {
          svgEditor.setPanning(false);
          return {
            keep: false,
            element: null
          };
        }
        return undefined;
      }
    };
  }
};
<commit_msg>fix panning<commit_after>/**
 * @file ext-panning.js
 *
 * @license MIT
 *
 * @copyright 2013 Luis Aguirre
 *
 */
/*
  This is a very basic SVG-Edit extension to let tablet/mobile devices pan without problem
*/

const loadExtensionTranslation = async function (lang) {
  let translationModule;
  try {
    translationModule = await import(`./locale/${encodeURIComponent(lang)}.js`);
  } catch (_error) {
    // eslint-disable-next-line no-console
    console.error(`Missing translation (${lang}) - using 'en'`);
    translationModule = await import(`./locale/en.js`);
  }
  return translationModule.default;
};

export default {
  name: 'panning',
  async init ({importLocale}) {
    const svgEditor = this;
    const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang'));
    const {svgCanvas} = svgEditor;
    const events = {
      id: 'ext-panning',
      click () {
        svgCanvas.setMode('ext-panning');
      }
    };
    return {
      newUI: true,
      name: strings.name,
      events,
      mouseDown () {
        if (svgCanvas.getMode() === 'ext-panning') {
          svgEditor.setPanning(true);
          return {started: true};
        }
        return undefined;
      },
      mouseUp () {
        if (svgCanvas.getMode() === 'ext-panning') {
          svgEditor.setPanning(false);
          return {
            keep: false,
            element: null
          };
        }
        return undefined;
      }
    };
  }
};
<|endoftext|>


<commit_before>'use strict';
/*jshint -W061 */

var $ = require('jquery');
var SHA256 = require('crypto-js/sha256');

var links = [
    'dx.doi.org',
    'github.com',
    'bitbucket.(com|org)',
    'r-project.org',
    'sourceforge.net'
];

var linksRegex = new RegExp('^https?://([^.]*\\.)?(' +
    links.join('|') +
    ')',
    'i' // Ignore case
);

var rules = {
    'PLOS': {
        url: /plos.*\.org\/article\/info/,
        journal: '$(".logo:first img").attr("alt")',
        title: '$(".header h1").text().trim()',
        authors: '$(".authors:first .person").map(function(e) { return $(this).children().remove().end().text().trim().replace(/[\\s,]+$/g, ""); }).get().join(", ")',
        abstract: '$(".abstract").text()',
        fulltext: '$(".article").html()',
        year: '$(".date-doi-line li:first").text().split(", ")[1]',
        doi: '$(".header > ul.date-doi-line > li:nth-child(2)").text().substr(5)'
    },
    'eLife': {
        url: /elifesciences\.org\/content\//,
        journal: '"eLife"',
        title: '$(".page-title").text()',
        authors: '$(".elife-article-author-item").map(function() { return $(this).text() }).get().join(", ")',
        abstract: '$("#abstract").text()',
        fulltext: '$("#main-text").html() + $("#references").html()',
        year: '$(".highwire-doi-epubdate-data").text().split(", ")[1]',
        doi: '$(".elife-doi-doi").text().replace("http://dx.doi.org/", "")'
    },
    'ScienceDirect': {
        url: /sciencedirect.com\/science\/article\/pii\//,
        journal: '$(".centerPane .head .title a span").text()',
        title: '$("h1.svTitle").text()',
        authors: '$(".authorGroup .authorName").map(function() { return $(this).text()}).get().join(", ")',
        abstract: '$(".abstract.svAbstract").text()',
        fulltext: '$(".svArticle.section").html() + $(".refText").html()',
        year: '$(".volIssue").text().match(/ ([1-2]\\d\\d\\d)/)[1]',
        doi: '$(".doiLink .doi").text().substr(6)'
    },
    'peerJ': {
        url: /peerj.com\/articles\//,
        journal: '"PeerJ"',
        title: '$("h1.article-title").text()',
        authors: '$(".article-authors span.contrib .name").map(function() { return $(this).text() }).get().join(", ")',
        abstract: '$("article .abstract").text()',
        fulltext: '$("article main").html() + $("footer .ref-list").html()',
        year: '$(".article-dates dd time").get()[0].innerHTML.split("-")[0]',
        doi: '$(".self-citation a:first").text().replace("http://dx.doi.org/", "")'
    }
};

var supported = function supported(url) {
    var support;
    $.each(rules, function(rule) {
        if(url.match(rules[rule].url)) {
            console.log('Matches rule: ' + rule);
            support = rule;
            return false;
        }
    });
    return support;
};

var extract = function extract(document, rule) {
    console.log('Extracting ' + document.URL + ' with '+ rule);
    rule = rules[rule];
    var message = {
        journal: eval(rule.journal),
        title: eval(rule.title),
        authors: eval(rule.authors),
        abstract: eval(rule.abstract),
        year: eval(rule.year),
        doi: eval(rule.doi),
        url: document.URL
    };

    if (message.doi) {
        message.id = message.doi;
    }
    else {
        message.id = SHA256(message);
    }

    message.fulltext = $(eval(rule.fulltext));
    message.method = 'POST';
    message.links = message.fulltext.find('a').
        map(function(i,e) {
            var url = e.getAttribute('href');
            if(url && url.match(linksRegex)) {
                return url;
            }
        }
    ).get();

    // We don't need HTML anymore and text is preferred for indexing
    message.fulltext = message.fulltext.text();

    if(message.fulltext.length < 1000) {
        console.log('You do not seem to have fulltext access to this journal.');
        return false;
    } else {
        return message;
    }
};

module.exports.supported = supported;
module.exports.extract = extract;<commit_msg>Add support for recent Institute of Physics (IOP) journals articles<commit_after>'use strict';
/*jshint -W061 */

var $ = require('jquery');
var SHA256 = require('crypto-js/sha256');

var links = [
    'dx.doi.org',
    'github.com',
    'bitbucket.(com|org)',
    'r-project.org',
    'sourceforge.net'
];

var linksRegex = new RegExp('^https?://([^.]*\\.)?(' +
    links.join('|') +
    ')',
    'i' // Ignore case
);

var rules = {
    'PLOS': {
        url: /plos.*\.org\/article\/info/,
        journal: '$(".logo:first img").attr("alt")',
        title: '$(".header h1").text().trim()',
        authors: '$(".authors:first .person").map(function(e) { return $(this).children().remove().end().text().trim().replace(/[\\s,]+$/g, ""); }).get().join(", ")',
        abstract: '$(".abstract").text()',
        fulltext: '$(".article").html()',
        year: '$(".date-doi-line li:first").text().split(", ")[1]',
        doi: '$(".header > ul.date-doi-line > li:nth-child(2)").text().substr(5)'
    },
    'eLife': {
        url: /elifesciences\.org\/content\//,
        journal: '"eLife"',
        title: '$(".page-title").text()',
        authors: '$(".elife-article-author-item").map(function() { return $(this).text() }).get().join(", ")',
        abstract: '$("#abstract").text()',
        fulltext: '$("#main-text").html() + $("#references").html()',
        year: '$(".highwire-doi-epubdate-data").text().split(", ")[1]',
        doi: '$(".elife-doi-doi").text().replace("http://dx.doi.org/", "")'
    },
    'ScienceDirect': {
        url: /sciencedirect.com\/science\/article\/pii\//,
        journal: '$(".centerPane .head .title a span").text()',
        title: '$("h1.svTitle").text()',
        authors: '$(".authorGroup .authorName").map(function() { return $(this).text()}).get().join(", ")',
        abstract: '$(".abstract.svAbstract").text()',
        fulltext: '$(".svArticle.section").html() + $(".refText").html()',
        year: '$(".volIssue").text().match(/ ([1-2]\\d\\d\\d)/)[1]',
        doi: '$(".doiLink .doi").text().substr(6)'
    },
    'peerJ': {
        url: /peerj.com\/articles\//,
        journal: '"PeerJ"',
        title: '$("h1.article-title").text()',
        authors: '$(".article-authors span.contrib .name").map(function() { return $(this).text() }).get().join(", ")',
        abstract: '$("article .abstract").text()',
        fulltext: '$("article main").html() + $("footer .ref-list").html()',
        year: '$(".article-dates dd time").get()[0].innerHTML.split("-")[0]',
        doi: '$(".self-citation a:first").text().replace("http://dx.doi.org/", "")'
    },
    'IOPScience': {
        url: /iopscience.iop.org\/.*?\/article/,
        journal: '$("ul.breadcrumbs li:nth-child(1) a").text()',
        title: '$("div.publishingInfo h2").text().trim()',
        authors: '$("p.authors").clone().children().remove().end().text().replace(" and ", ", ")',
        abstract: '$("div.abst div div").text().trim()',
        fulltext: '$("div.section:nth(1)").html() + $("dl.citationlist").html()',
        year: '$("div.publishingInfo p:contains(\'©\')").html().split("<br>")[0].trim().split(" ")[1]',
        doi: '$("div.publishingInfo p a:contains(\'doi\')").text().split(":")[1]'
    }
};

var supported = function supported(url) {
    var support;
    $.each(rules, function(rule) {
        if(url.match(rules[rule].url)) {
            console.log('Matches rule: ' + rule);
            support = rule;
            return false;
        }
    });
    return support;
};

var extract = function extract(document, rule) {
    console.log('Extracting ' + document.URL + ' with '+ rule);
    rule = rules[rule];
    var message = {
        journal: eval(rule.journal),
        title: eval(rule.title),
        authors: eval(rule.authors),
        abstract: eval(rule.abstract),
        year: eval(rule.year),
        doi: eval(rule.doi),
        url: document.URL
    };

    if (message.doi) {
        message.id = message.doi;
    }
    else {
        message.id = SHA256(message);
    }

    message.fulltext = $(eval(rule.fulltext));
    message.method = 'POST';
    message.links = message.fulltext.find('a').
        map(function(i,e) {
            var url = e.getAttribute('href');
            if(url && url.match(linksRegex)) {
                return url;
            }
        }
    ).get();

    // We don't need HTML anymore and text is preferred for indexing
    message.fulltext = message.fulltext.text();

    if(message.fulltext.length < 1000) {
        console.log('You do not seem to have fulltext access to this journal.');
        return false;
    } else {
        return message;
    }
};

module.exports.supported = supported;
module.exports.extract = extract;<|endoftext|>


<commit_before>/**
 * Map control, shared between mapmaker and reader modes.
 * Functionality specific to mapmaker or reader should be placed in separte plugins.
 */
define(["backbone", "backbone.marionette", "leaflet", "d3", "communicator",
        "config", "jquery", "underscore", "erfgeoviewer.common",
        "leaflet.markercluster", "leaflet.smoothmarkerbouncing", "leaflet.proj",
        "leaflet.fullscreen", 'models/state',
        "tpl!template/map.html", "vendor/sparql-geojson"],
  function(Backbone, Marionette, L, d3, Communicator, Config, $, _, App,
           LeafletMarkerCluster, LeafletBouncing, LeafletProjections,
           LeafletFullscreen, State, Template) {

  return Marionette.ItemView.extend({

    // Marionette properties.
    events: {},
    template: Template,

    // ID of dom element where Leaflet will be rendered.
    mapboxContainer: "map",

    // Map models to geometry by cid
    geometryMap: [],

    layers: {},

    // Marionette layout instance.
    layout: null,

    // Instance of a PopupView.
    popup: null,

    updateOnPositionChange: true,

    initialize: function(o) {

      var self = this;
      _.bindAll(this, 'updateMapSize', 'addMarker', 'attachMoveEndListener');

      this.layout = o.layout;

      $( window ).resize( _.throttle( this.updateMapSize, 150 ) );

      // this.registerAutoWidthMarker();
      this.registerLeafletZoomThrottle(200);

      /**
       * Event listeners
       */
      Communicator.mediator.on( 'map:fitAll', function() {
        var bounds;

        _.each(self.layers, function(layer) {
          var layerBounds = layer.getBounds();

          if (layerBounds.isValid()) {
            if (!bounds) {
              bounds = layerBounds;
            }
            else {
              bounds.extend(layerBounds);
            }
          }
        });

        if (bounds.isValid()) {
          self.map.fitBounds(bounds, { padding: [10, 10] });
        }
      });
      Communicator.mediator.on('map:setPosition', function(options) {
        self.map.setView(options.centerPoint, options.zoom);
      });
      Communicator.mediator.on('map:resetEditorPosition', function() {
        if (State.getPlugin('map_settings').model.get('editorCenterPoint')) {
          self.map.setView(State.getPlugin('map_settings').model.get('editorCenterPoint'), State.getPlugin('map_settings').model.get('editorZoom'));
        }
      });
      Communicator.mediator.on('map:setUpdateOnPositionChange', function(value) {
        self.updateOnPositionChange = value;
      });
      Communicator.mediator.on( 'map:moveend', function(map) {
        if (self.updateOnPositionChange) {
          State.getPlugin('map_settings').model.set({
            editorCenterPoint: map.getCenter(),
            editorZoom: map.getZoom()
          });

//          self.state.save();
        }
      });
      Communicator.mediator.on( 'map:move', function() {
        self.map.fireEvent('moveend');
      });
      Communicator.mediator.on("map:panTo", function(o) {
        var latlng = L.latLng( [o.lat, o.lng] );
        self.map.panTo(latlng);
      });
      Communicator.mediator.on("map:zoomTo", function(zoom) {
        self.map.setZoom(zoom);
      });
      Communicator.mediator.on("map:zoomIn", function() {
        this.map.setZoom(this.map.getZoom() + 1);
      }, this);
      Communicator.mediator.on("map:zoomOut", function() {
        this.map.setZoom(this.map.getZoom() - 1);
      }, this);
      Communicator.mediator.on("map:updateSize", function() {
        _.throttle( self.updateMapSize, 150 )
      });
      Communicator.reqres.setHandler( "getMap", function() { return self.map; });

      /**
       * State management.
       */
      State.getPlugin('geojson_features').collection.on('add', this.addMarker, this);

      State.getPlugin('geojson_features').collection.on('remove', this.removeMarker, this);

      State.getPlugin('geojson_features').collection.on('reset', this.initFeatures, this);

      State.getPlugin('map_settings').model.on('change:primaryColor', this.updatePrimaryColor, this);

      State.getPlugin('map_settings').model.on('change:baseMap', this.setBaseMap, this);

      Communicator.mediator.on('map:ready', function() {
        this.initFeatures(State.getPlugin('geojson_features').collection);
      }, this);

      this.initMap = _.bind(this._initMap, this);

      State.on('change:mapSettings', this.initMap)
    },

    initFeatures: function(collection) {
      var self = this;

      // Clear map
      _.each(_.keys(self.layers), function(group) {
        self.layers[group].clearLayers();
      });

      // Iterate over models
      collection.each(function(feature) {
        if (!feature.get('spatial') && (!feature.get('latitude') || !feature.get('longitude'))) return false;

        self.addMarker(feature);
      });
    },

    _initMap: function() {
      State.off('change:map_settings', this.initMap);

      this.map.setView( State.getPlugin('map_settings').model.get('editorCenterPoint') || [52.121580, 5.6304], State.getPlugin('map_settings').model.get('editorZoom') || 8 );
    },

    getGeoJSON: function(marker) {
      var spatial,
        geojson;

      if (spatial = marker.get( 'spatial' )) {
        switch (marker.get( 'geometryType' )) {
          case 'POINT':
            defaultProperties = {
              title: marker.get('title'),
              'marker-color': marker.get('color')
            };
            if (marker.get('icon')) defaultProperties['marker-symbol'] = marker.get('icon');
            break;

          default:
            defaultProperties = {
              title: marker.get('title'),
              fill: marker.get('color'),
              stroke: marker.get('color'),
              'marker-color': marker.get('color'),
              'marker-symbol': marker.get('icon')
            };
            break;
        }

        geojson = _.extend(sparqlToGeoJSON(marker.get( 'spatial' )[0]), { properties: defaultProperties });
      }
      else {
        geojson = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [marker.get( 'longitude' )[0], marker.get( 'latitude' )[0]]
          },
          properties: {
            title: marker.get('title')
          }
        };
        if (marker.get('color')) geojson.properties['marker-color'] = marker.get('color');
        if (marker.get('icon')) geojson.properties['marker-symbol'] = marker.get('icon');
      }

      return geojson;
    },

    /**
     * Take model of marker and add to map.
     * @param m - model
     */
    addMarker: function(marker) {
      var self = this,
        markers = (_.isArray(marker)) ? marker : [marker],
        geojson,
        spatial;

      _.each(markers, function(m) {
        m.on('change', self.updateMarker, self);

        if (_.isEmpty(m.get( 'spatial' )) && (!m.get( 'latitude' ) || !m.get( 'longitude' ))) {
          console.log('invalid marker:', m);
          return false;
        }
        geojson = m.convertToGeoJSON();
        var marker = L.mapbox.featureLayer();
        marker.setGeoJSON(geojson);
        marker.on("click", function() {
          Communicator.mediator.trigger("marker:click", m)
        });
        self.addMarkerGroup(marker, m.get('layerGroup'));

        self.geometryMap.push({
          cid: m.cid,
          featureLayer: marker
        });
      });
    },

    updatePrimaryColor: function() {
      var features = State.getPlugin('geojson_features').collection.where({ userColor: null });

      _.each(features, _.bind(function(feature) {
        this.updateMarker(feature);
      }, this));
    },

    updateMarker: function(m) {
      // Retrieve object from geometry mapping
      var marker = _.findWhere(this.geometryMap, { cid: m.cid });

      if (_.isObject(marker)) {
        this.removeMarker(m);
        this.addMarker(m);
      }
    },

    /**
     * Take model of marker and remove it from the map.
     */
    removeMarker: function(m) {
      // Retrieve object from geometry mapping
      var marker = _.findWhere(this.geometryMap, { cid: m.cid });

      if (_.isObject(marker)) {
        this.removeMarkerGroup(marker.featureLayer, m.get('layerGroup'));
        this.geometryMap = _.without(this.geometryMap, marker);
      }
    },

    /**
     * Adds a layer to a layergroup. If layergroup {key} doesn't exist, it will
     * be created.
     * @param layer - layer to be added
     * @param key - id of layergroup
     */
    addLayer: function(layer, key) {
      key = key || 'default';
      if ( _.isUndefined(this.layers[key]) )
        this.layers[key] = L.layerGroup().addTo(this.map);
      this.layers[key].addLayer(layer);
    },

    addMarkerGroup: function(layer, key) {
      key = key || 'default';
      if ( _.isUndefined(this.layers[key]) )
        this.layers[key] = new L.MarkerClusterGroup({
          iconCreateFunction: function (cluster) {
            var childCount = cluster.getChildCount();

            var c = ' marker-cluster-';
            if (childCount < 10) {
              c += 'small';
            } else if (childCount < 100) {
              c += 'medium';
            } else {
              c += 'large';
            }

            return new L.DivIcon({ html: '
' + childCount + '
', className: 'color-primary marker-cluster' + c, iconSize: new L.Point(40, 40) });
          }
        }).addTo(this.map);
      this.layers[key].addLayer(layer);
    },

    removeMarkerGroup: function(layer, key) {
      key = key || 'default';
      this.layers[key].removeLayer(layer);
    },

    onShow: function() {

      var self = this;

      // Load map.
      this.updateMapSize();
      L.mapbox.accessToken = Config.mapbox.accessToken;
      this.map = L.mapbox.map(this.mapboxContainer, null, {
        boxZoom: true,
        worldCopyJump: true,
        fullscreenControl: true
      });
      this.setBaseMap();

      if (App.mode == 'mapmaker') {
        this.map.setView( State.getPlugin('map_settings').model.get('editorCenterPoint') || [52.121580, 5.6304], State.getPlugin('map_settings').model.get('editorZoom') || 8 );
      }
      else {
        this.map.setView( State.getPlugin('map_settings').model.get('centerPoint') || [52.121580, 5.6304], State.getPlugin('map_settings').model.get('zoom') || 8 );
      }

      Communicator.mediator.trigger('map:ready', this.map);

      // Initialize markers
      this.layers.markers = new L.MarkerClusterGroup().addTo(this.map);
      this.layers.markers.addTo(this.map);

      // Event handlers
      this.map.on('click', function(e) {
        Communicator.mediator.trigger( "map:tile-layer-clicked" );
      });
      this.map.on('moveend', this.attachMoveEndListener);

    },

    attachMoveEndListener: function(e) {
      var self = this;

      this.map.off('moveend', this.attachMoveEndListener);

      this.map.on('moveend', function() {
        Communicator.mediator.trigger( "map:moveend", self.map )
      });
    },

    /**
     * Create an alternative to the L.divIcon marker, which does not support variable widths
     */
    registerAutoWidthMarker: function() {
      var self = this;
      L.DivIconAutosize = L.Icon.extend({
        options: {
          iconSize: [12, 12], // also can be set through CSS
          /*
           iconAnchor: (Point)
           popupAnchor: (Point)
           html: (String)
           bgPos: (Point)
           */
          iconHeight: 30,
          iconAnchorHeight: 44,
          className: 'leaflet-div-icon',
          html: false
        },

        createIcon: function (oldIcon) {
          var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
            options = this.options;

          div.innerHTML = options.html !== false ? options.html : '';
          this.options.iconSize = [undefined, this.options.iconHeight];
          this.options.iconAnchor = [105, this.options.iconAnchorHeight];

          if (options.bgPos) {
            div.style.backgroundPosition = (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px';
          }

          this._setIconStyles(div, 'icon');

          // Temporarily attach div to the dom so we can calculate the width.
          var $temp = $(div).clone().appendTo( '#' + self.mapboxContainer );
          $temp.width('auto');
          var w = $temp.outerWidth();
          $temp.remove();

          this.options.iconSize = [w, this.options.iconHeight];
          this.options.iconAnchor = [w / 2, this.options.iconAnchorHeight];

          // Necessary to set this twice, limitation of Leaflet
          this._setIconStyles(div, 'icon');

          return div;
        },

        createShadow: function () {
          return null;
        }
      });

      L.divIconAutosize = function (options) {
        return new L.DivIconAutosize(options);
      };
    },

    /**
     * Reduces the speed of Leaflet's mouse zooming.
     */
    registerLeafletZoomThrottle: function(throttle) {
      var lastScroll = new Date().getTime();
      L.Map.ScrollWheelZoom.prototype._onWheelScroll = function (e) {
        if (new Date().getTime() - lastScroll < throttle) { return; }
        var delta = L.DomEvent.getWheelDelta(e);
        var debounce = this._map.options.wheelDebounceTime;

        this._delta += delta;
        this._lastMousePos = this._map.mouseEventToContainerPoint(e);

        if (!this._startTime) {
          this._startTime = +new Date();
        }

        var left = Math.max(debounce - (+new Date() - this._startTime), 0);

        clearTimeout(this._timer);
        lastScroll = new Date().getTime();
        this._timer = setTimeout(L.bind(this._performZoom, this), left);

        L.DomEvent.stop(e);
      }
    },

    setBaseMap: function() {

      var self = this,
        tileId = State.getPlugin('map_settings').model.get('baseMap'),
        tile = _.findWhere( Config.tiles, {id: tileId} );

      if (!tile) return;

      if (self.baseLayer) self.map.removeLayer(self.baseLayer);
      if (tile.type == "mapbox") {
        self.baseLayer = L.mapbox.tileLayer( tile.id ).addTo( self.map );
      } else {
        // Mapbox does not respect detectRetina option. See issue #8581.
        var tileOptions = {
          attribution: tile.tilejson.attribution || 'Geen toerekening',
          minZoom: tile.tilejson.minZoom || 0,
          maxZoom: tile.tilejson.maxZoom || 18
        };
        self.baseLayer = L.tileLayer( tile.tilejson.tiles, tileOptions ).addTo( self.map );
      }

    },

    updateMapSize: function() {

      this.height = $( window ).height();
      if ($('header').css('display') != 'none') {
        this.height -= $( 'header' ).height();
      }
      this.width = $( window ).width();
      $( '#' + this.mapboxContainer ).css( 'height', this.height );

    }

  });

});<commit_msg>Only fit bounds on feature list if there are any.<commit_after>/**
 * Map control, shared between mapmaker and reader modes.
 * Functionality specific to mapmaker or reader should be placed in separte plugins.
 */
define(["backbone", "backbone.marionette", "leaflet", "d3", "communicator",
        "config", "jquery", "underscore", "erfgeoviewer.common",
        "leaflet.markercluster", "leaflet.smoothmarkerbouncing", "leaflet.proj",
        "leaflet.fullscreen", 'models/state',
        "tpl!template/map.html", "vendor/sparql-geojson"],
  function(Backbone, Marionette, L, d3, Communicator, Config, $, _, App,
           LeafletMarkerCluster, LeafletBouncing, LeafletProjections,
           LeafletFullscreen, State, Template) {

  return Marionette.ItemView.extend({

    // Marionette properties.
    events: {},
    template: Template,

    // ID of dom element where Leaflet will be rendered.
    mapboxContainer: "map",

    // Map models to geometry by cid
    geometryMap: [],

    layers: {},

    // Marionette layout instance.
    layout: null,

    // Instance of a PopupView.
    popup: null,

    updateOnPositionChange: true,

    initialize: function(o) {

      var self = this;
      _.bindAll(this, 'updateMapSize', 'addMarker', 'attachMoveEndListener');

      this.layout = o.layout;

      $( window ).resize( _.throttle( this.updateMapSize, 150 ) );

      // this.registerAutoWidthMarker();
      this.registerLeafletZoomThrottle(200);

      /**
       * Event listeners
       */
      Communicator.mediator.on( 'map:fitAll', function() {
        var bounds;

        _.each(self.layers, function(layer) {
          var layerBounds = layer.getBounds();

          if (layerBounds.isValid()) {
            if (!bounds) {
              bounds = layerBounds;
            }
            else {
              bounds.extend(layerBounds);
            }
          }
        });

        if (bounds instanceof L.LatLngBounds) {
          self.map.fitBounds(bounds, { padding: [10, 10] });
        }
      });
      Communicator.mediator.on('map:setPosition', function(options) {
        self.map.setView(options.centerPoint, options.zoom);
      });
      Communicator.mediator.on('map:resetEditorPosition', function() {
        if (State.getPlugin('map_settings').model.get('editorCenterPoint')) {
          self.map.setView(State.getPlugin('map_settings').model.get('editorCenterPoint'), State.getPlugin('map_settings').model.get('editorZoom'));
        }
      });
      Communicator.mediator.on('map:setUpdateOnPositionChange', function(value) {
        self.updateOnPositionChange = value;
      });
      Communicator.mediator.on( 'map:moveend', function(map) {
        if (self.updateOnPositionChange) {
          State.getPlugin('map_settings').model.set({
            editorCenterPoint: map.getCenter(),
            editorZoom: map.getZoom()
          });

//          self.state.save();
        }
      });
      Communicator.mediator.on( 'map:move', function() {
        self.map.fireEvent('moveend');
      });
      Communicator.mediator.on("map:panTo", function(o) {
        var latlng = L.latLng( [o.lat, o.lng] );
        self.map.panTo(latlng);
      });
      Communicator.mediator.on("map:zoomTo", function(zoom) {
        self.map.setZoom(zoom);
      });
      Communicator.mediator.on("map:zoomIn", function() {
        this.map.setZoom(this.map.getZoom() + 1);
      }, this);
      Communicator.mediator.on("map:zoomOut", function() {
        this.map.setZoom(this.map.getZoom() - 1);
      }, this);
      Communicator.mediator.on("map:updateSize", function() {
        _.throttle( self.updateMapSize, 150 )
      });
      Communicator.reqres.setHandler( "getMap", function() { return self.map; });

      /**
       * State management.
       */
      State.getPlugin('geojson_features').collection.on('add', this.addMarker, this);

      State.getPlugin('geojson_features').collection.on('remove', this.removeMarker, this);

      State.getPlugin('geojson_features').collection.on('reset', this.initFeatures, this);

      State.getPlugin('map_settings').model.on('change:primaryColor', this.updatePrimaryColor, this);

      State.getPlugin('map_settings').model.on('change:baseMap', this.setBaseMap, this);

      Communicator.mediator.on('map:ready', function() {
        this.initFeatures(State.getPlugin('geojson_features').collection);
      }, this);

      this.initMap = _.bind(this._initMap, this);

      State.on('change:mapSettings', this.initMap)
    },

    initFeatures: function(collection) {
      var self = this;

      // Clear map
      _.each(_.keys(self.layers), function(group) {
        self.layers[group].clearLayers();
      });

      // Iterate over models
      collection.each(function(feature) {
        if (!feature.get('spatial') && (!feature.get('latitude') || !feature.get('longitude'))) return false;

        self.addMarker(feature);
      });
    },

    _initMap: function() {
      State.off('change:map_settings', this.initMap);

      this.map.setView( State.getPlugin('map_settings').model.get('editorCenterPoint') || [52.121580, 5.6304], State.getPlugin('map_settings').model.get('editorZoom') || 8 );
    },

    getGeoJSON: function(marker) {
      var spatial,
        geojson;

      if (spatial = marker.get( 'spatial' )) {
        switch (marker.get( 'geometryType' )) {
          case 'POINT':
            defaultProperties = {
              title: marker.get('title'),
              'marker-color': marker.get('color')
            };
            if (marker.get('icon')) defaultProperties['marker-symbol'] = marker.get('icon');
            break;

          default:
            defaultProperties = {
              title: marker.get('title'),
              fill: marker.get('color'),
              stroke: marker.get('color'),
              'marker-color': marker.get('color'),
              'marker-symbol': marker.get('icon')
            };
            break;
        }

        geojson = _.extend(sparqlToGeoJSON(marker.get( 'spatial' )[0]), { properties: defaultProperties });
      }
      else {
        geojson = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [marker.get( 'longitude' )[0], marker.get( 'latitude' )[0]]
          },
          properties: {
            title: marker.get('title')
          }
        };
        if (marker.get('color')) geojson.properties['marker-color'] = marker.get('color');
        if (marker.get('icon')) geojson.properties['marker-symbol'] = marker.get('icon');
      }

      return geojson;
    },

    /**
     * Take model of marker and add to map.
     * @param m - model
     */
    addMarker: function(marker) {
      var self = this,
        markers = (_.isArray(marker)) ? marker : [marker],
        geojson,
        spatial;

      _.each(markers, function(m) {
        m.on('change', self.updateMarker, self);

        if (_.isEmpty(m.get( 'spatial' )) && (!m.get( 'latitude' ) || !m.get( 'longitude' ))) {
          console.log('invalid marker:', m);
          return false;
        }
        geojson = m.convertToGeoJSON();
        var marker = L.mapbox.featureLayer();
        marker.setGeoJSON(geojson);
        marker.on("click", function() {
          Communicator.mediator.trigger("marker:click", m)
        });
        self.addMarkerGroup(marker, m.get('layerGroup'));

        self.geometryMap.push({
          cid: m.cid,
          featureLayer: marker
        });
      });
    },

    updatePrimaryColor: function() {
      var features = State.getPlugin('geojson_features').collection.where({ userColor: null });

      _.each(features, _.bind(function(feature) {
        this.updateMarker(feature);
      }, this));
    },

    updateMarker: function(m) {
      // Retrieve object from geometry mapping
      var marker = _.findWhere(this.geometryMap, { cid: m.cid });

      if (_.isObject(marker)) {
        this.removeMarker(m);
        this.addMarker(m);
      }
    },

    /**
     * Take model of marker and remove it from the map.
     */
    removeMarker: function(m) {
      // Retrieve object from geometry mapping
      var marker = _.findWhere(this.geometryMap, { cid: m.cid });

      if (_.isObject(marker)) {
        this.removeMarkerGroup(marker.featureLayer, m.get('layerGroup'));
        this.geometryMap = _.without(this.geometryMap, marker);
      }
    },

    /**
     * Adds a layer to a layergroup. If layergroup {key} doesn't exist, it will
     * be created.
     * @param layer - layer to be added
     * @param key - id of layergroup
     */
    addLayer: function(layer, key) {
      key = key || 'default';
      if ( _.isUndefined(this.layers[key]) )
        this.layers[key] = L.layerGroup().addTo(this.map);
      this.layers[key].addLayer(layer);
    },

    addMarkerGroup: function(layer, key) {
      key = key || 'default';
      if ( _.isUndefined(this.layers[key]) )
        this.layers[key] = new L.MarkerClusterGroup({
          iconCreateFunction: function (cluster) {
            var childCount = cluster.getChildCount();

            var c = ' marker-cluster-';
            if (childCount < 10) {
              c += 'small';
            } else if (childCount < 100) {
              c += 'medium';
            } else {
              c += 'large';
            }

            return new L.DivIcon({ html: '
' + childCount + '
', className: 'color-primary marker-cluster' + c, iconSize: new L.Point(40, 40) });
          }
        }).addTo(this.map);
      this.layers[key].addLayer(layer);
    },

    removeMarkerGroup: function(layer, key) {
      key = key || 'default';
      this.layers[key].removeLayer(layer);
    },

    onShow: function() {

      var self = this;

      // Load map.
      this.updateMapSize();
      L.mapbox.accessToken = Config.mapbox.accessToken;
      this.map = L.mapbox.map(this.mapboxContainer, null, {
        boxZoom: true,
        worldCopyJump: true,
        fullscreenControl: true
      });
      this.setBaseMap();

      if (App.mode == 'mapmaker') {
        this.map.setView( State.getPlugin('map_settings').model.get('editorCenterPoint') || [52.121580, 5.6304], State.getPlugin('map_settings').model.get('editorZoom') || 8 );
      }
      else {
        this.map.setView( State.getPlugin('map_settings').model.get('centerPoint') || [52.121580, 5.6304], State.getPlugin('map_settings').model.get('zoom') || 8 );
      }

      Communicator.mediator.trigger('map:ready', this.map);

      // Initialize markers
      this.layers.markers = new L.MarkerClusterGroup().addTo(this.map);
      this.layers.markers.addTo(this.map);

      // Event handlers
      this.map.on('click', function(e) {
        Communicator.mediator.trigger( "map:tile-layer-clicked" );
      });
      this.map.on('moveend', this.attachMoveEndListener);

    },

    attachMoveEndListener: function(e) {
      var self = this;

      this.map.off('moveend', this.attachMoveEndListener);

      this.map.on('moveend', function() {
        Communicator.mediator.trigger( "map:moveend", self.map )
      });
    },

    /**
     * Create an alternative to the L.divIcon marker, which does not support variable widths
     */
    registerAutoWidthMarker: function() {
      var self = this;
      L.DivIconAutosize = L.Icon.extend({
        options: {
          iconSize: [12, 12], // also can be set through CSS
          /*
           iconAnchor: (Point)
           popupAnchor: (Point)
           html: (String)
           bgPos: (Point)
           */
          iconHeight: 30,
          iconAnchorHeight: 44,
          className: 'leaflet-div-icon',
          html: false
        },

        createIcon: function (oldIcon) {
          var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
            options = this.options;

          div.innerHTML = options.html !== false ? options.html : '';
          this.options.iconSize = [undefined, this.options.iconHeight];
          this.options.iconAnchor = [105, this.options.iconAnchorHeight];

          if (options.bgPos) {
            div.style.backgroundPosition = (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px';
          }

          this._setIconStyles(div, 'icon');

          // Temporarily attach div to the dom so we can calculate the width.
          var $temp = $(div).clone().appendTo( '#' + self.mapboxContainer );
          $temp.width('auto');
          var w = $temp.outerWidth();
          $temp.remove();

          this.options.iconSize = [w, this.options.iconHeight];
          this.options.iconAnchor = [w / 2, this.options.iconAnchorHeight];

          // Necessary to set this twice, limitation of Leaflet
          this._setIconStyles(div, 'icon');

          return div;
        },

        createShadow: function () {
          return null;
        }
      });

      L.divIconAutosize = function (options) {
        return new L.DivIconAutosize(options);
      };
    },

    /**
     * Reduces the speed of Leaflet's mouse zooming.
     */
    registerLeafletZoomThrottle: function(throttle) {
      var lastScroll = new Date().getTime();
      L.Map.ScrollWheelZoom.prototype._onWheelScroll = function (e) {
        if (new Date().getTime() - lastScroll < throttle) { return; }
        var delta = L.DomEvent.getWheelDelta(e);
        var debounce = this._map.options.wheelDebounceTime;

        this._delta += delta;
        this._lastMousePos = this._map.mouseEventToContainerPoint(e);

        if (!this._startTime) {
          this._startTime = +new Date();
        }

        var left = Math.max(debounce - (+new Date() - this._startTime), 0);

        clearTimeout(this._timer);
        lastScroll = new Date().getTime();
        this._timer = setTimeout(L.bind(this._performZoom, this), left);

        L.DomEvent.stop(e);
      }
    },

    setBaseMap: function() {

      var self = this,
        tileId = State.getPlugin('map_settings').model.get('baseMap'),
        tile = _.findWhere( Config.tiles, {id: tileId} );

      if (!tile) return;

      if (self.baseLayer) self.map.removeLayer(self.baseLayer);
      if (tile.type == "mapbox") {
        self.baseLayer = L.mapbox.tileLayer( tile.id ).addTo( self.map );
      } else {
        // Mapbox does not respect detectRetina option. See issue #8581.
        var tileOptions = {
          attribution: tile.tilejson.attribution || 'Geen toerekening',
          minZoom: tile.tilejson.minZoom || 0,
          maxZoom: tile.tilejson.maxZoom || 18
        };
        self.baseLayer = L.tileLayer( tile.tilejson.tiles, tileOptions ).addTo( self.map );
      }

    },

    updateMapSize: function() {

      this.height = $( window ).height();
      if ($('header').css('display') != 'none') {
        this.height -= $( 'header' ).height();
      }
      this.width = $( window ).width();
      $( '#' + this.mapboxContainer ).css( 'height', this.height );

    }

  });

});<|endoftext|>


<commit_before>/* global FastBoot */
import Ember from "ember";

const { deprecate, computed, get } = Ember;
const { deprecatingAlias, readOnly } = computed;

const RequestObject = Ember.Object.extend({
  init() {
    this._super(...arguments);

    let request = this.request;
    delete this.request;

    this.cookies = request.cookies;
    this.headers = request.headers;
    this.queryParams = request.queryParams;
    this.path = request.path;
    this.protocol = request.protocol;
    this._host = function() {
      return request.host();
    };
  },

  host: computed(function() {
    return this._host();
  })
});

const Shoebox = Ember.Object.extend({
  put(key, value) {
    Ember.assert('shoebox.put is only invoked from the FastBoot rendered application', this.get('fastboot.isFastBoot'));
    Ember.assert('the provided key is a string', typeof key === 'string');

    let fastbootInfo = this.get('fastboot._fastbootInfo');
    if (!fastbootInfo.shoebox) { fastbootInfo.shoebox = {}; }

    fastbootInfo.shoebox[key] = value;
  },

  retrieve(key) {
    if (this.get('fastboot.isFastBoot')) {
      let shoebox = this.get('fastboot._fastbootInfo.shoebox');
      if (!shoebox) { return; }

      return shoebox[key];
    }

    let shoeboxItem = this.get(key);
    if (shoeboxItem) { return shoeboxItem; }

    let el = document.querySelector(`#shoebox-${key}`);
    if (!el) { return; }
    let valueString = el.textContent;
    if (!valueString) { return; }

    shoeboxItem = JSON.parse(valueString);
    this.set(key, shoeboxItem);

    return shoeboxItem;
  }
});

const FastBootService = Ember.Service.extend({
  cookies: deprecatingAlias('request.cookies', { id: 'fastboot.cookies-to-request', until: '0.9.9' }),
  headers: deprecatingAlias('request.headers', { id: 'fastboot.headers-to-request', until: '0.9.9' }),
  isFastBoot: typeof FastBoot !== 'undefined',

  init() {
    this._super(...arguments);

    let shoebox = Shoebox.create({ fastboot: this });
    this.set('shoebox', shoebox);
  },

  host: computed(function() {
    deprecate(
      'Usage of fastboot service\'s `host` property is deprecated.  Please use `request.host` instead.',
      false,
      { id: 'fastboot.host-to-request', until: '0.9.9' }
    );

    return this._fastbootInfo.request.host();
  }),

  response: readOnly('_fastbootInfo.response'),
  metadata: readOnly('_fastbootInfo.metadata'),

  request: computed(function() {
    if (!this.isFastBoot) return null;
    return RequestObject.create({ request: get(this, '_fastbootInfo.request') });
  }),

  deferRendering(promise) {
    Ember.assert('deferRendering requires a promise or thennable object', typeof promise.then === 'function');
    this._fastbootInfo.deferRendering(promise);
  }
});

export default FastBootService;
<commit_msg>cleanup how `assert` referenced<commit_after>/* global FastBoot */
import Ember from "ember";

const { deprecate, computed, get, assert } = Ember;
const { deprecatingAlias, readOnly } = computed;

const RequestObject = Ember.Object.extend({
  init() {
    this._super(...arguments);

    let request = this.request;
    delete this.request;

    this.cookies = request.cookies;
    this.headers = request.headers;
    this.queryParams = request.queryParams;
    this.path = request.path;
    this.protocol = request.protocol;
    this._host = function() {
      return request.host();
    };
  },

  host: computed(function() {
    return this._host();
  })
});

const Shoebox = Ember.Object.extend({
  put(key, value) {
    assert('shoebox.put is only invoked from the FastBoot rendered application', this.get('fastboot.isFastBoot'));
    assert('the provided key is a string', typeof key === 'string');

    let fastbootInfo = this.get('fastboot._fastbootInfo');
    if (!fastbootInfo.shoebox) { fastbootInfo.shoebox = {}; }

    fastbootInfo.shoebox[key] = value;
  },

  retrieve(key) {
    if (this.get('fastboot.isFastBoot')) {
      let shoebox = this.get('fastboot._fastbootInfo.shoebox');
      if (!shoebox) { return; }

      return shoebox[key];
    }

    let shoeboxItem = this.get(key);
    if (shoeboxItem) { return shoeboxItem; }

    let el = document.querySelector(`#shoebox-${key}`);
    if (!el) { return; }
    let valueString = el.textContent;
    if (!valueString) { return; }

    shoeboxItem = JSON.parse(valueString);
    this.set(key, shoeboxItem);

    return shoeboxItem;
  }
});

const FastBootService = Ember.Service.extend({
  cookies: deprecatingAlias('request.cookies', { id: 'fastboot.cookies-to-request', until: '0.9.9' }),
  headers: deprecatingAlias('request.headers', { id: 'fastboot.headers-to-request', until: '0.9.9' }),
  isFastBoot: typeof FastBoot !== 'undefined',

  init() {
    this._super(...arguments);

    let shoebox = Shoebox.create({ fastboot: this });
    this.set('shoebox', shoebox);
  },

  host: computed(function() {
    deprecate(
      'Usage of fastboot service\'s `host` property is deprecated.  Please use `request.host` instead.',
      false,
      { id: 'fastboot.host-to-request', until: '0.9.9' }
    );

    return this._fastbootInfo.request.host();
  }),

  response: readOnly('_fastbootInfo.response'),
  metadata: readOnly('_fastbootInfo.metadata'),

  request: computed(function() {
    if (!this.isFastBoot) return null;
    return RequestObject.create({ request: get(this, '_fastbootInfo.request') });
  }),

  deferRendering(promise) {
    assert('deferRendering requires a promise or thennable object', typeof promise.then === 'function');
    this._fastbootInfo.deferRendering(promise);
  }
});

export default FastBootService;
<|endoftext|>


<commit_before>import Ember from 'ember';
import Config from '../config/environment';

export default Ember.Service.extend({
    pageHasAnalytics: function() {
        return window.mixpanel && typeof window.mixpanel === "object" && Config.mixpanel.enabled;
    },

    logTrackingEnabled: function() {
        return !!Config && !! Config.mixpanel.LOG_EVENT_TRACKING;
    },

    logTracking: function() {
        Ember.Logger.info('[Mixpanel] ', arguments);
    },

    trackPageView: function(page, overrides = {}) {
        if (this.pageHasAnalytics()) {
            if (!page) {
                var loc = window.location;
                page = loc.hash ? loc.hash.substring(1) : loc.pathname + loc.search;
            }

            window.mixpanel.track("visit", Ember.merge({pageName: page}, overrides));
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('page view', page);
        }
    },

    trackEvent: function(event, properties, options, callback) {
        if (this.pageHasAnalytics()) {
            window.mixpanel.track(event, properties, options, callback);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking(event, properties, options);
        }
    },

    identify: function(userId, traits, options, callback) {
        if (this.pageHasAnalytics()) {
            window.mixpanel.identify(userId, traits, options, callback);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('identify user', userId, traits, options);
        }
    },

    alias: function(userId, previousId, options, callback) {
        if (this.pageHasAnalytics()) {
            window.mixpanel.alias(userId, previousId, options, callback);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('alias user', userId, previousId, options);
        }
    },

    register: function(traits, options, callback) {
        if (this.pageHasAnalytics()) {
            window.mixpanel.register(traits, options, callback);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('register user', traits, options);
        }
    },

    peopleSet: function(attributes) {

        if (this.pageHasAnalytics()) {
            window.mixpanel.people.set(attributes);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('people.set', attributes);
        }
    },

    peopleIncrement: function() {

      if (this.pageHasAnalytics()) {
          window.mixpanel.people.increment.apply(this, arguments);
      }

      if (this.logTrackingEnabled()) {
          this.logTracking('people.increment', arguments);
      }
    },

    // Mixpanel does not have an api to increment super property directly.
    // Instread, they recommend using get_property to fetch the value, increment
    // it and then register it. More details at https://mixpanel.com/blog/2015/01/29/incremental-super-properties

    superIncrement: function(property, increment = 1) {
      if (this.pageHasAnalytics()) {
          var currentValue = window.mixpanel.get_property(property);
          var updatedValue = {};

          if(currentValue && typeof(currentValue) === 'number') {
            updatedValue[property] = currentValue + increment;
          }

          else {
            updatedValue[property] = increment;
          }

          window.mixpanel.register(updatedValue);
      }

      if (this.logTrackingEnabled()) {
          this.logTracking('increment super property', property, increment);
      }
    }

});
<commit_msg>change context on apply<commit_after>import Ember from 'ember';
import Config from '../config/environment';

export default Ember.Service.extend({
    pageHasAnalytics: function() {
        return window.mixpanel && typeof window.mixpanel === "object" && Config.mixpanel.enabled;
    },

    logTrackingEnabled: function() {
        return !!Config && !! Config.mixpanel.LOG_EVENT_TRACKING;
    },

    logTracking: function() {
        Ember.Logger.info('[Mixpanel] ', arguments);
    },

    trackPageView: function(page, overrides = {}) {
        if (this.pageHasAnalytics()) {
            if (!page) {
                var loc = window.location;
                page = loc.hash ? loc.hash.substring(1) : loc.pathname + loc.search;
            }

            window.mixpanel.track("visit", Ember.merge({pageName: page}, overrides));
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('page view', page);
        }
    },

    trackEvent: function(event, properties, options, callback) {
        if (this.pageHasAnalytics()) {
            window.mixpanel.track(event, properties, options, callback);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking(event, properties, options);
        }
    },

    identify: function(userId, traits, options, callback) {
        if (this.pageHasAnalytics()) {
            window.mixpanel.identify(userId, traits, options, callback);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('identify user', userId, traits, options);
        }
    },

    alias: function(userId, previousId, options, callback) {
        if (this.pageHasAnalytics()) {
            window.mixpanel.alias(userId, previousId, options, callback);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('alias user', userId, previousId, options);
        }
    },

    register: function(traits, options, callback) {
        if (this.pageHasAnalytics()) {
            window.mixpanel.register(traits, options, callback);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('register user', traits, options);
        }
    },

    peopleSet: function(attributes) {

        if (this.pageHasAnalytics()) {
            window.mixpanel.people.set(attributes);
        }

        if (this.logTrackingEnabled()) {
            this.logTracking('people.set', attributes);
        }
    },

    peopleIncrement: function() {

      if (this.pageHasAnalytics()) {
          window.mixpanel.people.increment.apply(window.mixpanel.people, arguments);
      }

      if (this.logTrackingEnabled()) {
          this.logTracking('people.increment', arguments);
      }
    },

    // Mixpanel does not have an api to increment super property directly.
    // Instread, they recommend using get_property to fetch the value, increment
    // it and then register it. More details at https://mixpanel.com/blog/2015/01/29/incremental-super-properties

    superIncrement: function(property, increment = 1) {
      if (this.pageHasAnalytics()) {
          var currentValue = window.mixpanel.get_property(property);
          var updatedValue = {};

          if(currentValue && typeof(currentValue) === 'number') {
            updatedValue[property] = currentValue + increment;
          }

          else {
            updatedValue[property] = increment;
          }

          window.mixpanel.register(updatedValue);
      }

      if (this.logTrackingEnabled()) {
          this.logTracking('increment super property', property, increment);
      }
    }

});
<|endoftext|>