<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/samsung_maple",
    [
        "antie/devices/device",
        "antie/devices/mediaplayer/mediaplayer",
        "antie/runtimecontext"
    ],
    function(Device, MediaPlayer, RuntimeContext) {
        "use strict";

        /**
         * Main MediaPlayer implementation for Samsung devices implementing the Maple API.
         * Use this device modifier if a device implements the Samsung Maple media playback standard.
         * It must support creation of <object> elements with appropriate SAMSUNG_INFOLINK classids.
         * Those objects must expose an API in accordance with the Samsung Maple media specification.
         * @name antie.devices.mediaplayer.samsung_maple
         * @class
         * @extends antie.devices.mediaplayer.MediaPlayer.prototype
         */
        var Player = MediaPlayer.extend({

            init: function() {
                this._super();
                this._state = MediaPlayer.STATE.EMPTY;
                this._playerPlugin = document.getElementById('playerPlugin');
                this._deferSeekingTo = null;
                this._postBufferingState = null;
                this._tryingToPause = false;
                this._currentTimeKnown = false;
            },

            /**
            * @inheritDoc
            */
            setSource: function (mediaType, url, mimeType) {
                if (this.getState() === MediaPlayer.STATE.EMPTY) {
                    this._type = mediaType;
                    this._source = url;
                    this._mimeType = mimeType;
                    this._registerEventHandlers();
                    this._toStopped();
                } else {
                    this._toError("Cannot set source unless in the '" + MediaPlayer.STATE.EMPTY + "' state");
                }
            },

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

                    case MediaPlayer.STATE.BUFFERING:
                        if (this._tryingToPause) {
                            this._tryingToPause = false;
                            this._toPlaying();
                        }
                        break;

                    case MediaPlayer.STATE.PAUSED:
                        this._playerPlugin.Resume();
                        this._toPlaying();
                        break;

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

            /**
            * @inheritDoc
            */
            playFrom: function (seconds) {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                var seekingTo = this._range ? this._getClampedTime(seconds) : seconds;
                if (seekingTo !== seconds) {
                    RuntimeContext.getDevice().getLogger().debug("playFrom " + seconds + " clamped to " + seekingTo + " - seekable range is { start: " + this._range.start + ", end: " + this._range.end + " }");
                }

                switch (this.getState()) {
                    case MediaPlayer.STATE.BUFFERING:
                        this._deferSeekingTo = seekingTo;
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._toBuffering();
                        if (!this._currentTimeKnown) {
                            this._deferSeekingTo = seekingTo;
                        } else if (this._isNearToCurrentTime(seekingTo)) {
                            this._toPlaying();
                        } else {
                            this._seekToWithFailureStateTransition(seekingTo);
                        }
                        break;

                    case MediaPlayer.STATE.PAUSED:
                        this._toBuffering();
                        if (!this._currentTimeKnown) {
                            this._deferSeekingTo = seekingTo;
                        } else if (this._isNearToCurrentTime(seekingTo)) {
                            this._playerPlugin.Resume();
                            this._toPlaying();
                        } else {
                            this._seekToWithFailureStateTransition(seekingTo);
                            this._playerPlugin.Resume();
                        }
                        break;

                    case MediaPlayer.STATE.STOPPED:
                        this._setDisplayFullScreenForVideo();
                        this._playerPlugin.ResumePlay(this._wrappedSource(), seekingTo);
                        this._toBuffering();
                        break;

                    case MediaPlayer.STATE.COMPLETE:
                        this._playerPlugin.Stop();
                        this._setDisplayFullScreenForVideo();
                        this._playerPlugin.ResumePlay(this._wrappedSource(), seekingTo);
                        this._toBuffering();
                        break;

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

            /**
            * @inheritDoc
            */
            beginPlayback: function() {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                        this._toBuffering();
                        this._setDisplayFullScreenForVideo();
                        this._playerPlugin.Play(this._wrappedSource());
                        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.BUFFERING:
                    case MediaPlayer.STATE.PAUSED:
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._tryPauseWithStateTransition();
                        break;

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

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

                    case MediaPlayer.STATE.BUFFERING:
                    case MediaPlayer.STATE.PLAYING:
                    case MediaPlayer.STATE.PAUSED:
                    case MediaPlayer.STATE.COMPLETE:
                        this._stopPlayer();
                        this._toStopped();
                        break;

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

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

                    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 () {
                if (this.getState() === MediaPlayer.STATE.STOPPED) {
                    return undefined;
                } else {
                    return this._currentTime;
                }
            },

            /**
            * @inheritDoc
            */
            getSeekableRange: function () {
                return this._range;
            },

            /**
             * @inheritDoc
             */
            getDuration: function() {
                if (this._range) {
                    return this._range.end;
                }
                return undefined;
            },

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

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

                if (this._deferSeekingTo === null) {
                    if (this._postBufferingState === MediaPlayer.STATE.PAUSED) {
                        this._tryPauseWithStateTransition();
                    } else {
                        this._toPlaying();
                    }
                }
            },

            _onDeviceError: function(message) {
                this._reportError(message);
            },

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

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

            _stopPlayer: function() {
                this._playerPlugin.Stop();
                this._currentTimeKnown = false;
            },

            _tryPauseWithStateTransition: function() {
                var success = this._isSuccessCode(this._playerPlugin.Pause());
                if (success) {
                    this._toPaused();
                }

                this._tryingToPause = !success;
            },

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

            _onMetadata: function() {
                this._range = {
                    start: 0,
                    end: this._playerPlugin.GetDuration() / 1000
                };
            },

            _onCurrentTime: function(timeInMillis) {
                this._currentTime = timeInMillis / 1000;
                this._onStatus();
                this._currentTimeKnown = true;

                if (this._deferSeekingTo !== null) {
                    this._deferredSeek();
                }

                if (this._tryingToPause) {
                    this._tryPauseWithStateTransition();
                }
            },

            _deferredSeek: function() {
                var clampedTime = this._getClampedTime(this._deferSeekingTo);
                if (clampedTime !== this._deferSeekingTo) {
                    RuntimeContext.getDevice().getLogger().debug("playFrom " + this._deferSeekingTo + " clamped to " + clampedTime + " - seekable range is { start: " + this._range.start + ", end: " + this._range.end + " }");
                }

                var isNearCurrentTime = this._isNearToCurrentTime(clampedTime);

                if (isNearCurrentTime) {
                    this._toPlaying();
                    this._deferSeekingTo = null;
                } else {
                    var seekResult = this._seekTo(clampedTime);
                    if (seekResult) {
                        this._deferSeekingTo = null;
                    }
                }
            },

            _registerEventHandlers: function() {
                var self = this;

                window.SamsungMapleOnRenderError = function () {
                    self._onDeviceError("Media element emitted OnRenderError");
                };
                this._playerPlugin.OnRenderError = 'SamsungMapleOnRenderError';

                window.SamsungMapleOnConnectionFailed = function () {
                    self._onDeviceError("Media element emitted OnConnectionFailed");
                };
                this._playerPlugin.OnConnectionFailed = 'SamsungMapleOnConnectionFailed';

                window.SamsungMapleOnNetworkDisconnected = function () {
                    self._onDeviceError("Media element emitted OnNetworkDisconnected");
                };
                this._playerPlugin.OnNetworkDisconnected = 'SamsungMapleOnNetworkDisconnected';

                window.SamsungMapleOnStreamNotFound = function () {
                    self._onDeviceError("Media element emitted OnStreamNotFound");
                };
                this._playerPlugin.OnStreamNotFound = 'SamsungMapleOnStreamNotFound';

                window.SamsungMapleOnAuthenticationFailed = function () {
                    self._onDeviceError("Media element emitted OnAuthenticationFailed");
                };
                this._playerPlugin.OnAuthenticationFailed = 'SamsungMapleOnAuthenticationFailed';

                window.SamsungMapleOnRenderingComplete = function () {
                    self._onEndOfMedia();
                };
                this._playerPlugin.OnRenderingComplete = 'SamsungMapleOnRenderingComplete';

                window.SamsungMapleOnBufferingStart = function () {
                    self._onDeviceBuffering();
                };
                this._playerPlugin.OnBufferingStart = 'SamsungMapleOnBufferingStart';

                window.SamsungMapleOnBufferingComplete = function () {
                    self._onFinishedBuffering();
                };
                this._playerPlugin.OnBufferingComplete = 'SamsungMapleOnBufferingComplete';

                window.SamsungMapleOnStreamInfoReady = function () {
                    self._onMetadata();
                };
                this._playerPlugin.OnStreamInfoReady = 'SamsungMapleOnStreamInfoReady';

                window.SamsungMapleOnCurrentPlayTime = function (timeInMillis) {
                    self._onCurrentTime(timeInMillis);
                };
                this._playerPlugin.OnCurrentPlayTime = 'SamsungMapleOnCurrentPlayTime';

                this._onWindowHide = function () {
                    self.stop();
                };

                window.addEventListener('hide', this._onWindowHide, false);
                window.addEventListener('unload', this._onWindowHide, false);
            },

            _unregisterEventHandlers: function() {
                var eventHandlers = [
                    'SamsungMapleOnRenderError',
                    'SamsungMapleOnRenderingComplete',
                    'SamsungMapleOnBufferingStart',
                    'SamsungMapleOnBufferingComplete',
                    'SamsungMapleOnStreamInfoReady',
                    'SamsungMapleOnCurrentPlayTime',
                    'SamsungMapleOnConnectionFailed',
                    'SamsungMapleOnNetworkDisconnected',
                    'SamsungMapleOnStreamNotFound',
                    'SamsungMapleOnAuthenticationFailed'
                ];

                for (var i = 0; i < eventHandlers.length; i++){
                    var handler = eventHandlers[i];
                    var hook = handler.substring("SamsungMaple".length);
                    this._playerPlugin[hook] = undefined;

                    delete window[handler];
                }

                window.removeEventListener('hide', this._onWindowHide, false);
                window.removeEventListener('unload', this._onWindowHide, false);
            },

            _wipe: function () {
                this._stopPlayer();
                this._type = undefined;
                this._source = undefined;
                this._mimeType = undefined;
                this._currentTime = undefined;
                this._range = undefined;
                this._deferSeekingTo = null;
                this._tryingToPause = false;
                this._currentTimeKnown = false;
                this._unregisterEventHandlers();
            },

            _seekTo: function(seconds) {
                var offset = seconds - this.getCurrentTime();
                var success = this._isSuccessCode(this._jump(offset));

                if (success) {
                    this._currentTime = seconds;
                }

                return success;
            },

            _seekToWithFailureStateTransition: function(seconds) {
                var success = this._seekTo(seconds);
                if (!success) {
                    this._toPlaying();
                }
            },

            _jump: function (offsetSeconds) {
                if (offsetSeconds > 0) {
                    return this._playerPlugin.JumpForward(offsetSeconds);
                } else {
                    return this._playerPlugin.JumpBackward(Math.abs(offsetSeconds));
                }
            },

            _isHlsMimeType: function () {
                var mime = this._mimeType.toLowerCase()
                return mime === "application/vnd.apple.mpegurl"
                    || mime === "application/x-mpegurl";
            },

            _wrappedSource: function () {
                var source = this._source;
                if (this._isHlsMimeType()) {
                    source += "|COMPONENT=HLS";
                }
                return source;
            },

            _reportError: function(errorMessage) {
                RuntimeContext.getDevice().getLogger().error(errorMessage);
                this._emitEvent(MediaPlayer.EVENT.ERROR);
            },

            _toStopped: function () {
                this._currentTime = 0;
                this._range = undefined;
                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);
            },

            _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) {
                this._wipe();
                this._state = MediaPlayer.STATE.ERROR;
                this._reportError(errorMessage);
                throw "ApiError: " + errorMessage;
            },

            _setDisplayFullScreenForVideo: function() {
                if (this._type === MediaPlayer.TYPE.VIDEO) {
                    var dimensions = RuntimeContext.getDevice().getScreenSize();
                    this._playerPlugin.SetDisplayArea(0, 0, dimensions.width, dimensions.height);
                }
            },

            _isSuccessCode: function(code) {
                var samsung2010ErrorCode = -1;
                return code && code !== samsung2010ErrorCode;
            },

            /**
             * @constant {Number} Time (in seconds) compared to current time within which seeking has no effect.
             * On a sample device (Samsung FoxP 2013), seeking by two seconds worked 90% of the time, but seeking
             * by 2.5 seconds was always seen to work.
             */
            CURRENT_TIME_TOLERANCE: 2.5
        });

        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>Refactored Samsung Maple's clamped playFrom debug message code<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/samsung_maple",
    [
        "antie/devices/device",
        "antie/devices/mediaplayer/mediaplayer",
        "antie/runtimecontext"
    ],
    function(Device, MediaPlayer, RuntimeContext) {
        "use strict";

        /**
         * Main MediaPlayer implementation for Samsung devices implementing the Maple API.
         * Use this device modifier if a device implements the Samsung Maple media playback standard.
         * It must support creation of <object> elements with appropriate SAMSUNG_INFOLINK classids.
         * Those objects must expose an API in accordance with the Samsung Maple media specification.
         * @name antie.devices.mediaplayer.samsung_maple
         * @class
         * @extends antie.devices.mediaplayer.MediaPlayer.prototype
         */
        var Player = MediaPlayer.extend({

            init: function() {
                this._super();
                this._state = MediaPlayer.STATE.EMPTY;
                this._playerPlugin = document.getElementById('playerPlugin');
                this._deferSeekingTo = null;
                this._postBufferingState = null;
                this._tryingToPause = false;
                this._currentTimeKnown = false;
            },

            /**
            * @inheritDoc
            */
            setSource: function (mediaType, url, mimeType) {
                if (this.getState() === MediaPlayer.STATE.EMPTY) {
                    this._type = mediaType;
                    this._source = url;
                    this._mimeType = mimeType;
                    this._registerEventHandlers();
                    this._toStopped();
                } else {
                    this._toError("Cannot set source unless in the '" + MediaPlayer.STATE.EMPTY + "' state");
                }
            },

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

                    case MediaPlayer.STATE.BUFFERING:
                        if (this._tryingToPause) {
                            this._tryingToPause = false;
                            this._toPlaying();
                        }
                        break;

                    case MediaPlayer.STATE.PAUSED:
                        this._playerPlugin.Resume();
                        this._toPlaying();
                        break;

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

            /**
            * @inheritDoc
            */
            playFrom: function (seconds) {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                var seekingTo = this._range ? this._getClampedTimeForPlayFrom(seconds) : seconds;

                switch (this.getState()) {
                    case MediaPlayer.STATE.BUFFERING:
                        this._deferSeekingTo = seekingTo;
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._toBuffering();
                        if (!this._currentTimeKnown) {
                            this._deferSeekingTo = seekingTo;
                        } else if (this._isNearToCurrentTime(seekingTo)) {
                            this._toPlaying();
                        } else {
                            this._seekToWithFailureStateTransition(seekingTo);
                        }
                        break;

                    case MediaPlayer.STATE.PAUSED:
                        this._toBuffering();
                        if (!this._currentTimeKnown) {
                            this._deferSeekingTo = seekingTo;
                        } else if (this._isNearToCurrentTime(seekingTo)) {
                            this._playerPlugin.Resume();
                            this._toPlaying();
                        } else {
                            this._seekToWithFailureStateTransition(seekingTo);
                            this._playerPlugin.Resume();
                        }
                        break;

                    case MediaPlayer.STATE.STOPPED:
                        this._setDisplayFullScreenForVideo();
                        this._playerPlugin.ResumePlay(this._wrappedSource(), seekingTo);
                        this._toBuffering();
                        break;

                    case MediaPlayer.STATE.COMPLETE:
                        this._playerPlugin.Stop();
                        this._setDisplayFullScreenForVideo();
                        this._playerPlugin.ResumePlay(this._wrappedSource(), seekingTo);
                        this._toBuffering();
                        break;

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

            /**
            * @inheritDoc
            */
            beginPlayback: function() {
                this._postBufferingState = MediaPlayer.STATE.PLAYING;
                switch (this.getState()) {
                    case MediaPlayer.STATE.STOPPED:
                        this._toBuffering();
                        this._setDisplayFullScreenForVideo();
                        this._playerPlugin.Play(this._wrappedSource());
                        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.BUFFERING:
                    case MediaPlayer.STATE.PAUSED:
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._tryPauseWithStateTransition();
                        break;

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

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

                    case MediaPlayer.STATE.BUFFERING:
                    case MediaPlayer.STATE.PLAYING:
                    case MediaPlayer.STATE.PAUSED:
                    case MediaPlayer.STATE.COMPLETE:
                        this._stopPlayer();
                        this._toStopped();
                        break;

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

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

                    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 () {
                if (this.getState() === MediaPlayer.STATE.STOPPED) {
                    return undefined;
                } else {
                    return this._currentTime;
                }
            },

            /**
            * @inheritDoc
            */
            getSeekableRange: function () {
                return this._range;
            },

            /**
             * @inheritDoc
             */
            getDuration: function() {
                if (this._range) {
                    return this._range.end;
                }
                return undefined;
            },

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

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

                if (this._deferSeekingTo === null) {
                    if (this._postBufferingState === MediaPlayer.STATE.PAUSED) {
                        this._tryPauseWithStateTransition();
                    } else {
                        this._toPlaying();
                    }
                }
            },

            _onDeviceError: function(message) {
                this._reportError(message);
            },

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

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

            _stopPlayer: function() {
                this._playerPlugin.Stop();
                this._currentTimeKnown = false;
            },

            _tryPauseWithStateTransition: function() {
                var success = this._isSuccessCode(this._playerPlugin.Pause());
                if (success) {
                    this._toPaused();
                }

                this._tryingToPause = !success;
            },

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

            _onMetadata: function() {
                this._range = {
                    start: 0,
                    end: this._playerPlugin.GetDuration() / 1000
                };
            },

            _onCurrentTime: function(timeInMillis) {
                this._currentTime = timeInMillis / 1000;
                this._onStatus();
                this._currentTimeKnown = true;

                if (this._deferSeekingTo !== null) {
                    this._deferredSeek();
                }

                if (this._tryingToPause) {
                    this._tryPauseWithStateTransition();
                }
            },

            _deferredSeek: function() {
                var clampedTime = this._getClampedTimeForPlayFrom(this._deferSeekingTo);
                var isNearCurrentTime = this._isNearToCurrentTime(clampedTime);

                if (isNearCurrentTime) {
                    this._toPlaying();
                    this._deferSeekingTo = null;
                } else {
                    var seekResult = this._seekTo(clampedTime);
                    if (seekResult) {
                        this._deferSeekingTo = null;
                    }
                }
            },

            _getClampedTimeForPlayFrom: function (seconds) {
                var clampedTime = this._getClampedTime(seconds);
                if (clampedTime !== seconds) {
                    RuntimeContext.getDevice().getLogger().debug("playFrom " + seconds+ " clamped to " + clampedTime + " - seekable range is { start: " + this._range.start + ", end: " + this._range.end + " }");
                }
                return clampedTime;
            },

            _registerEventHandlers: function() {
                var self = this;

                window.SamsungMapleOnRenderError = function () {
                    self._onDeviceError("Media element emitted OnRenderError");
                };
                this._playerPlugin.OnRenderError = 'SamsungMapleOnRenderError';

                window.SamsungMapleOnConnectionFailed = function () {
                    self._onDeviceError("Media element emitted OnConnectionFailed");
                };
                this._playerPlugin.OnConnectionFailed = 'SamsungMapleOnConnectionFailed';

                window.SamsungMapleOnNetworkDisconnected = function () {
                    self._onDeviceError("Media element emitted OnNetworkDisconnected");
                };
                this._playerPlugin.OnNetworkDisconnected = 'SamsungMapleOnNetworkDisconnected';

                window.SamsungMapleOnStreamNotFound = function () {
                    self._onDeviceError("Media element emitted OnStreamNotFound");
                };
                this._playerPlugin.OnStreamNotFound = 'SamsungMapleOnStreamNotFound';

                window.SamsungMapleOnAuthenticationFailed = function () {
                    self._onDeviceError("Media element emitted OnAuthenticationFailed");
                };
                this._playerPlugin.OnAuthenticationFailed = 'SamsungMapleOnAuthenticationFailed';

                window.SamsungMapleOnRenderingComplete = function () {
                    self._onEndOfMedia();
                };
                this._playerPlugin.OnRenderingComplete = 'SamsungMapleOnRenderingComplete';

                window.SamsungMapleOnBufferingStart = function () {
                    self._onDeviceBuffering();
                };
                this._playerPlugin.OnBufferingStart = 'SamsungMapleOnBufferingStart';

                window.SamsungMapleOnBufferingComplete = function () {
                    self._onFinishedBuffering();
                };
                this._playerPlugin.OnBufferingComplete = 'SamsungMapleOnBufferingComplete';

                window.SamsungMapleOnStreamInfoReady = function () {
                    self._onMetadata();
                };
                this._playerPlugin.OnStreamInfoReady = 'SamsungMapleOnStreamInfoReady';

                window.SamsungMapleOnCurrentPlayTime = function (timeInMillis) {
                    self._onCurrentTime(timeInMillis);
                };
                this._playerPlugin.OnCurrentPlayTime = 'SamsungMapleOnCurrentPlayTime';

                this._onWindowHide = function () {
                    self.stop();
                };

                window.addEventListener('hide', this._onWindowHide, false);
                window.addEventListener('unload', this._onWindowHide, false);
            },

            _unregisterEventHandlers: function() {
                var eventHandlers = [
                    'SamsungMapleOnRenderError',
                    'SamsungMapleOnRenderingComplete',
                    'SamsungMapleOnBufferingStart',
                    'SamsungMapleOnBufferingComplete',
                    'SamsungMapleOnStreamInfoReady',
                    'SamsungMapleOnCurrentPlayTime',
                    'SamsungMapleOnConnectionFailed',
                    'SamsungMapleOnNetworkDisconnected',
                    'SamsungMapleOnStreamNotFound',
                    'SamsungMapleOnAuthenticationFailed'
                ];

                for (var i = 0; i < eventHandlers.length; i++){
                    var handler = eventHandlers[i];
                    var hook = handler.substring("SamsungMaple".length);
                    this._playerPlugin[hook] = undefined;

                    delete window[handler];
                }

                window.removeEventListener('hide', this._onWindowHide, false);
                window.removeEventListener('unload', this._onWindowHide, false);
            },

            _wipe: function () {
                this._stopPlayer();
                this._type = undefined;
                this._source = undefined;
                this._mimeType = undefined;
                this._currentTime = undefined;
                this._range = undefined;
                this._deferSeekingTo = null;
                this._tryingToPause = false;
                this._currentTimeKnown = false;
                this._unregisterEventHandlers();
            },

            _seekTo: function(seconds) {
                var offset = seconds - this.getCurrentTime();
                var success = this._isSuccessCode(this._jump(offset));

                if (success) {
                    this._currentTime = seconds;
                }

                return success;
            },

            _seekToWithFailureStateTransition: function(seconds) {
                var success = this._seekTo(seconds);
                if (!success) {
                    this._toPlaying();
                }
            },

            _jump: function (offsetSeconds) {
                if (offsetSeconds > 0) {
                    return this._playerPlugin.JumpForward(offsetSeconds);
                } else {
                    return this._playerPlugin.JumpBackward(Math.abs(offsetSeconds));
                }
            },

            _isHlsMimeType: function () {
                var mime = this._mimeType.toLowerCase()
                return mime === "application/vnd.apple.mpegurl"
                    || mime === "application/x-mpegurl";
            },

            _wrappedSource: function () {
                var source = this._source;
                if (this._isHlsMimeType()) {
                    source += "|COMPONENT=HLS";
                }
                return source;
            },

            _reportError: function(errorMessage) {
                RuntimeContext.getDevice().getLogger().error(errorMessage);
                this._emitEvent(MediaPlayer.EVENT.ERROR);
            },

            _toStopped: function () {
                this._currentTime = 0;
                this._range = undefined;
                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);
            },

            _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) {
                this._wipe();
                this._state = MediaPlayer.STATE.ERROR;
                this._reportError(errorMessage);
                throw "ApiError: " + errorMessage;
            },

            _setDisplayFullScreenForVideo: function() {
                if (this._type === MediaPlayer.TYPE.VIDEO) {
                    var dimensions = RuntimeContext.getDevice().getScreenSize();
                    this._playerPlugin.SetDisplayArea(0, 0, dimensions.width, dimensions.height);
                }
            },

            _isSuccessCode: function(code) {
                var samsung2010ErrorCode = -1;
                return code && code !== samsung2010ErrorCode;
            },

            /**
             * @constant {Number} Time (in seconds) compared to current time within which seeking has no effect.
             * On a sample device (Samsung FoxP 2013), seeking by two seconds worked 90% of the time, but seeking
             * by 2.5 seconds was always seen to work.
             */
            CURRENT_TIME_TOLERANCE: 2.5
        });

        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>Template.afArrayField_bootstrap3.helpers({
  panelClass: function() {
    return this.atts.panelClass || 'panel-default';
  },
  headingClass: function() {
    return return this.atts.headingClass || '';
  }
});
<commit_msg>fix Unexpected token (6:11)<commit_after>Template.afArrayField_bootstrap3.helpers({
  panelClass: function() {
    return this.atts.panelClass || 'panel-default';
  },
  headingClass: function() {
    return this.atts.headingClass || '';
  }
});
<|endoftext|>


<commit_before>/*!
 * Giu
 *
 * An opinionated Swiss-army knife for building React application GUIs.
 *
 * @copyright Guillermo Grau Panea 2016
 * @license MIT
 */

// @flow

/* eslint-disable import/newline-after-import, import/first */

// NB: Flow complains when we take a default export from another module
// and reexport as a named export from this main API,
// e.g. export Select from './inputs/select';
// That's why we do it in two steps here

// Reexported libraries
import tinycolor from 'tinycolor2';
export { tinycolor };

// Components
import Giu from './components/giu';
export { Giu };
import { ThemeContext } from './gral/themeContext';
export { ThemeContext };

import Select from './inputs/select';
export { Select };
export { LIST_SEPARATOR } from './inputs/selectCustom';
export {
  TextInput,
  PasswordInput,
  NumberInput,
  RangeInput,
} from './inputs/textNumberRangeInput';
import DateInput from './inputs/dateInput';
export { DateInput };
import Textarea from './inputs/textarea';
export { Textarea };
import Checkbox from './inputs/checkbox';
export { Checkbox };
import FileInput from './inputs/fileInput';
export { FileInput };
import RadioGroup from './inputs/radioGroup';
export { RadioGroup };
import ColorInput from './inputs/colorInput';
export { ColorInput };

export {
  Modals,
  reducer as modalReducer,
  actions as modalActions,
  modalPush,
  modalPop,
  isModalActive,
} from './components/modals';
import Modal from './components/modal';
export { Modal };
export {
  Notifications,
  reducer as notifReducer,
  actions as notifActions,
  notify,
  notifRetain,
  notifDelete,
  notifDeleteByName,
} from './components/notifications';
import Notification from './components/notification';
export { Notification };
export {
  Floats,
  floatAdd,
  floatDelete,
  floatUpdate,
  floatReposition,
} from './components/floats';
export {
  Hints,
  reducer,
  actions,
  hintDefine,
  hintDisableAll,
  hintReset,
  hintShow,
  hintHide,
  isHintShown,
} from './components/hints';
import HintScreen from './components/hintScreen';
export { HintScreen };

import DataTable, { SORT_MANUALLY } from './components/dataTable';
export { DataTable, SORT_MANUALLY };
import VirtualScroller from './components/virtualScroller';
export { VirtualScroller };

import DropDownMenu from './components/dropDownMenu';
export { DropDownMenu };
import Button from './components/button';
export { Button };
import Icon from './components/icon';
export { Icon };
import Spinner from './components/spinner';
export { Spinner };
import LargeMessage from './components/largeMessage';
export { LargeMessage };
import Progress from './components/progress';
export { Progress };
import HeightMeasurer from './components/heightMeasurer';
export { HeightMeasurer };
import AnimatedCounter from './components/animatedCounter';
export { AnimatedCounter };

// HOCs and wrappers
import input from './hocs/input';
export { input };

// Styles
export {
  flexContainer,
  flexItem,
  boxWithShadow,
  isDark,
  isLight,
  darken,
  lighten,
  addStylesToPage,
} from './gral/styles';
export {
  COLORS,
  KEYS,
  FONTS,
  getScrollbarWidth,
  IS_MAC,
  IS_IOS,
  IS_MOBILE_OR_TABLET,
} from './gral/constants';

// Other
export {
  bindAll,
  cancelEvent,
  preventDefault,
  stopPropagation,
  cancelBodyScrolling,
  windowHeightWithoutScrollbar,
  windowWidthWithoutScrollbar,
  simplifyString,
} from './gral/helpers';
export * from './gral/wait';
export * from './gral/validators';
export { isAnyModifierPressed } from './gral/keys';
export { isVisible, scrollIntoView } from './gral/visibility';
export * from './gral/storage';
<commit_msg>Export new Input HOC<commit_after>/*!
 * Giu
 *
 * An opinionated Swiss-army knife for building React application GUIs.
 *
 * @copyright Guillermo Grau Panea 2016
 * @license MIT
 */

// @flow

/* eslint-disable import/newline-after-import, import/first */

// NB: Flow complains when we take a default export from another module
// and reexport as a named export from this main API,
// e.g. export Select from './inputs/select';
// That's why we do it in two steps here

// Reexported libraries
import tinycolor from 'tinycolor2';
export { tinycolor };

// Components
import Giu from './components/giu';
export { Giu };
import { ThemeContext } from './gral/themeContext';
export { ThemeContext };

import Select from './inputs/select';
export { Select };
export { LIST_SEPARATOR } from './inputs/selectCustom';
export {
  TextInput,
  PasswordInput,
  NumberInput,
  RangeInput,
} from './inputs/textNumberRangeInput';
import DateInput from './inputs/dateInput';
export { DateInput };
import Textarea from './inputs/textarea';
export { Textarea };
import Checkbox from './inputs/checkbox';
export { Checkbox };
import FileInput from './inputs/fileInput';
export { FileInput };
import RadioGroup from './inputs/radioGroup';
export { RadioGroup };
import ColorInput from './inputs/colorInput';
export { ColorInput };

export {
  Modals,
  reducer as modalReducer,
  actions as modalActions,
  modalPush,
  modalPop,
  isModalActive,
} from './components/modals';
import Modal from './components/modal';
export { Modal };
export {
  Notifications,
  reducer as notifReducer,
  actions as notifActions,
  notify,
  notifRetain,
  notifDelete,
  notifDeleteByName,
} from './components/notifications';
import Notification from './components/notification';
export { Notification };
export {
  Floats,
  floatAdd,
  floatDelete,
  floatUpdate,
  floatReposition,
} from './components/floats';
export {
  Hints,
  reducer,
  actions,
  hintDefine,
  hintDisableAll,
  hintReset,
  hintShow,
  hintHide,
  isHintShown,
} from './components/hints';
import HintScreen from './components/hintScreen';
export { HintScreen };

import DataTable, { SORT_MANUALLY } from './components/dataTable';
export { DataTable, SORT_MANUALLY };
import VirtualScroller from './components/virtualScroller';
export { VirtualScroller };

import DropDownMenu from './components/dropDownMenu';
export { DropDownMenu };
import Button from './components/button';
export { Button };
import Icon from './components/icon';
export { Icon };
import Spinner from './components/spinner';
export { Spinner };
import LargeMessage from './components/largeMessage';
export { LargeMessage };
import Progress from './components/progress';
export { Progress };
import HeightMeasurer from './components/heightMeasurer';
export { HeightMeasurer };
import AnimatedCounter from './components/animatedCounter';
export { AnimatedCounter };

// HOCs and wrappers
import Input from './hocs/input';
export { Input };

// Styles
export {
  flexContainer,
  flexItem,
  boxWithShadow,
  isDark,
  isLight,
  darken,
  lighten,
  addStylesToPage,
} from './gral/styles';
export {
  COLORS,
  KEYS,
  FONTS,
  getScrollbarWidth,
  IS_MAC,
  IS_IOS,
  IS_MOBILE_OR_TABLET,
} from './gral/constants';

// Other
export {
  bindAll,
  cancelEvent,
  preventDefault,
  stopPropagation,
  cancelBodyScrolling,
  windowHeightWithoutScrollbar,
  windowWidthWithoutScrollbar,
  simplifyString,
} from './gral/helpers';
export * from './gral/wait';
export * from './gral/validators';
export { isAnyModifierPressed } from './gral/keys';
export { isVisible, scrollIntoView } from './gral/visibility';
export * from './gral/storage';
<|endoftext|>


<commit_before>/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
require(['jquery', 'resource', 'resourcePicker'], function ($, $resource) {
  'use strict';

  // Declare the configuration namespace.
  CKEDITOR.config['xwiki-resource'] = CKEDITOR.config['xwiki-resource'] || {
    __namespace: true
  };

  CKEDITOR.plugins.add('xwiki-resource', {
    requires: 'xwiki-marker,xwiki-dialog,xwiki-localization'
  });

  CKEDITOR.plugins.xwikiResource = {
    parseResourceReference: function(serializedResourceReference) {
      var parts = serializedResourceReference.split('|-|');
      var resourceReference = {
        typed: parts[0] === 'true',
        type: parts[1],
        reference: parts[2],
        parameters: {}
      };
      if (parts.length > 3) {
        var serializedParameters = parts.slice(3).join('|-|');
        CKEDITOR.plugins.xwikiMarker.parseParameters(serializedParameters, resourceReference.parameters);
      }
      if (resourceReference.type === 'mailto') {
        // HACK: Add support for mailto parameters (e.g. subject and body).
        var queryStringIndex = resourceReference.reference.lastIndexOf('?');
        if (queryStringIndex >= 0) {
          var queryString = resourceReference.reference.substr(queryStringIndex + 1);
          resourceReference.reference = resourceReference.reference.substr(0, queryStringIndex);
          CKEDITOR.tools.extend(resourceReference.parameters, parseQueryString(queryString), true);
        }
      }
      return resourceReference;
    },

    serializeResourceReference: function(resourceReference) {
      var components = [
        !!resourceReference.typed,
        resourceReference.type,
        resourceReference.reference
      ];
      var parameters = resourceReference.parameters;
      if (resourceReference.type === 'mailto' && resourceReference.reference.indexOf('?') < 0) {
        // HACK: Add support for mailto parameters (e.g. subject and body).
        parameters = CKEDITOR.tools.extend({}, parameters);
        var queryStringParameters = {};
        ['subject', 'body'].forEach(function(id) {
          var value = parameters[id];
          if (value !== null && value !== undefined) {
            queryStringParameters[id] = value;
          }
          delete parameters[id];
        });
        var queryString = $.param(queryStringParameters);
        if (queryString.length > 0) {
          //The replace is a workaround for a jquery issue: https://github.com/jquery/jquery/issues/2658
          components[2] += '?' + queryString.replace(/\+/g, '%20');
        }
      }
      var serializedParameters = CKEDITOR.plugins.xwikiMarker.serializeParameters(parameters);
      if (serializedParameters) {
        components.push(serializedParameters);
      }
      return components.join('|-|');
    },

    createResourcePicker: function(pickerDefinition) {
      var basePickerDefinition = {
        //
        // Standard fields
        //
        id: 'resourceReference',
        type: 'html',
        html: '
' +
                '' +
                '<input type="text" />' +
              '
',
        // Focus the resource picker when the dialog is loaded.
        tabIndex: 0,
        onLoad: function() {
          // Register the event listeners and create the resource picker. We register the changeResourceType listener
          // before creating the resource picker because we need to catch the first changeResourceType event in order to
          // update the label (it won't be fired again when we set the value of the resource input unless the resource
          // type is different).
          $(this.getElement().$).on('changeResourceType', $.proxy(this, 'onResourceTypeChange'))
            .find('input').resourcePicker({resourceTypes: this.resourceTypes});
          // We register the selectResource event listener after creating the resource picker because we don't care
          // about the first selectResource event since we're going to trigger another one anyway by setting the value
          // of the resource input when the dialog is shown.
          $(this.getElement().$).on('selectResource', $.proxy(this, 'onSelectResource'));
          // Fix the tab-key navigation.
          var resourceTypeDropDownToggle = this.getElement().findOne('.dropdown-toggle');
          var resourceTypeButton = this.getElement().findOne('button.resourceType');
          var resourceReferenceInput = this.getElement().findOne('input.resourceReference');
          var tabIndex = this.tabIndex;
          [resourceTypeDropDownToggle, resourceTypeButton, resourceReferenceInput].forEach(function(field) {
            var dialog = this;
            if (tabIndex >= 0 && tabIndex < dialog._.focusList.length) {
              dialog.addFocusable(field, tabIndex);
              field._focusable = dialog._.focusList[tabIndex];
            } else {
              dialog.addFocusable(field);
              field._focusable = dialog._.focusList[dialog._.focusList.length - 1];
            }
            field.on('focus', function() {
              dialog._.currentFocusIndex = this._focusable.focusIndex;
            });
          }, this.getDialog());
          // Fix reference input id.
          var id = CKEDITOR.tools.getNextId();
          resourceReferenceInput.setAttribute('id', id);
          this.getElement().findOne('label').setAttribute('for', id);
        },
        validate: function() {
          var resourceReference = this.getValue();
          if (resourceReference.reference === '') {
            // Check if the selected resource type supports empty references.
            var resourceTypeConfig = $resource.types[resourceReference.type] || {};
            if (resourceTypeConfig.allowEmptyReference !== true) {
              return this.getDialog().getParentEditor().localization.get('xwiki-resource.notSpecified',
                this.getLabelElement().getText());
            }
          }
          return true;
        },
        getValue: function() {
          var resourcePickerInput = this.getResourcePickerInput();
          // Make sure the resource picker has updated the resource picker input. This is needed in Internet Explorer
          // where the dialog buttons are not selectable and so the change event is not fired before the click event.
          $(resourcePickerInput.$).trigger('beforeGetValue');
          var serializedResourceReference = resourcePickerInput.getValue();
          var separatorIndex = serializedResourceReference.indexOf(':');
          var resourceReference = {
            type: serializedResourceReference.substr(0, separatorIndex),
            reference: serializedResourceReference.substr(separatorIndex + 1)
          };
          if (this.selectedResource && this.selectedResource.reference.type === resourceReference.type &&
              this.selectedResource.reference.reference === resourceReference.reference) {
            // Preserve the typed field if the resource type and reference have not changed.
            resourceReference.typed = this.selectedResource.reference.typed;
          }
          return resourceReference;
        },
        setValue: function(resourceReference) {
          // Reset the resource picker if no resource reference is provided.
          resourceReference = resourceReference || {
            type: this.resourceTypes[0],
            reference: '',
            // Make sure the picker doesn't try to resolve the empty reference.
            isNew: true
          };
          var serializedResourceReference = (resourceReference.type || '') + ':' + (resourceReference.reference || '');
          $(this.getResourcePickerInput().$).val(serializedResourceReference).trigger('selectResource', {
            reference: resourceReference
          });
        },
        //
        // Custom fields
        //
        getResourcePickerInput: function() {
          return this.getElement().findOne('input');
        },
        getLabelElement: function() {
          return this.getElement().findOne('.cke_dialog_ui_labeled_label');
        },
        onResourceTypeChange: function(event, data) {
          // Update the label.
          var resourceTypeConfig = $resource.types[data.newValue] || {label: data.newValue};
          this.getLabelElement().setText(resourceTypeConfig.label);
        },
        onSelectResource: function(event, resource) {
          this.selectedResource = resource;
        }
      };
      pickerDefinition = pickerDefinition || {};
      // We may need to access the base definition in order to override some operation.
      pickerDefinition.base = basePickerDefinition;
      return CKEDITOR.tools.extend(pickerDefinition, basePickerDefinition);
    },

    getResourceURL: function(resourceReference, editor) {
      if (['url', 'path', 'unc', 'unknown'].indexOf(resourceReference.type) >= 0) {
        return resourceReference.reference;
      } else if (['mailto', 'data'].indexOf(resourceReference.type) >= 0) {
        return resourceReference.type + ':' + resourceReference.reference;
      } else {
        var dispatcherURL = editor.config['xwiki-resource'].dispatcher;
        dispatcherURL += dispatcherURL.indexOf('?') < 0 ? '?' : '&';
        return dispatcherURL + $.param(resourceReference);
      }
    },

    updateResourcePickerOnFileBrowserSelect: function(dialogDefinition, resourcePickerElementId, fileBrowserElementId) {
      var fileBrowserElement = dialogDefinition.getContents(fileBrowserElementId[0]).get(fileBrowserElementId[1]);
      if (fileBrowserElement && fileBrowserElement.filebrowser) {
        var fileBrowserConfig = fileBrowserElement.filebrowser;
        if (typeof fileBrowserConfig === 'string') {
          fileBrowserConfig = {target: fileBrowserConfig};
          fileBrowserElement.filebrowser = fileBrowserConfig;
        }
        var oldOnSelect = fileBrowserConfig.onSelect;
        fileBrowserConfig.onSelect = function(fileURL, data) {
          if (data.resourceReference) {
            // Update the resource picker.
            this.getDialog().setValueOf(resourcePickerElementId[0], resourcePickerElementId[1], data.resourceReference);
          }
          if (typeof oldOnSelect === 'function') {
            return oldOnSelect.call(this, fileURL, data);
          }
        };
      }
    },

    bindResourcePicker: function(element, resourcePickerId, onlyTheReferencePart) {
      // Use the resource picker value when the given element is committed.
      var oldCommit = element.commit;
      element.commit = function() {
        var resourceReference = this.getDialog().getValueOf(resourcePickerId[0], resourcePickerId[1]);
        if (onlyTheReferencePart  === true) {
          this.setValue(resourceReference.reference);
        } else {
          this.setValue(CKEDITOR.plugins.xwikiResource.getResourceURL(resourceReference,
            this.getDialog().getParentEditor()));
        }
        if (typeof oldCommit === 'function') {
          oldCommit.apply(this, arguments);
        }
      };
      // We validate the resource reference instead.
      delete element.validate;
      // Hide the element. We show the resource picker instead.
      element.hidden = true;
    }
  };

  var parseQueryString = function(queryString) {
    var parameters = {};
    // Replace plus signs in the query string with spaces.
    queryString = queryString.replace(/\+/g, ' ');
    queryString.split('&').forEach(function(pair) {
      var parts = pair.split('=');
      var name = decodeURIComponent(parts[0]);
      var value = parts.length > 1 ? decodeURIComponent(parts[1]) : '';
      // We know the query string supports multiple parameter values but we don't have this need right now.
      parameters[name] = value;
    });
    return parameters;
  };
});
<commit_msg>    CKEDITOR-226  Creating a mailto link in ckeditor with subject and body params encode spaces as "+" instead of "%20"<commit_after>/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
require(['jquery', 'resource', 'resourcePicker'], function ($, $resource) {
  'use strict';

  // Declare the configuration namespace.
  CKEDITOR.config['xwiki-resource'] = CKEDITOR.config['xwiki-resource'] || {
    __namespace: true
  };

  CKEDITOR.plugins.add('xwiki-resource', {
    requires: 'xwiki-marker,xwiki-dialog,xwiki-localization'
  });

  CKEDITOR.plugins.xwikiResource = {
    parseResourceReference: function(serializedResourceReference) {
      var parts = serializedResourceReference.split('|-|');
      var resourceReference = {
        typed: parts[0] === 'true',
        type: parts[1],
        reference: parts[2],
        parameters: {}
      };
      if (parts.length > 3) {
        var serializedParameters = parts.slice(3).join('|-|');
        CKEDITOR.plugins.xwikiMarker.parseParameters(serializedParameters, resourceReference.parameters);
      }
      if (resourceReference.type === 'mailto') {
        // HACK: Add support for mailto parameters (e.g. subject and body).
        var queryStringIndex = resourceReference.reference.lastIndexOf('?');
        if (queryStringIndex >= 0) {
          var queryString = resourceReference.reference.substr(queryStringIndex + 1);
          resourceReference.reference = resourceReference.reference.substr(0, queryStringIndex);
          CKEDITOR.tools.extend(resourceReference.parameters, parseQueryString(queryString), true);
        }
      }
      return resourceReference;
    },

    serializeResourceReference: function(resourceReference) {
      var components = [
        !!resourceReference.typed,
        resourceReference.type,
        resourceReference.reference
      ];
      var parameters = resourceReference.parameters;
      if (resourceReference.type === 'mailto' && resourceReference.reference.indexOf('?') < 0) {
        // HACK: Add support for mailto parameters (e.g. subject and body).
        parameters = CKEDITOR.tools.extend({}, parameters);
        var queryStringParameters = {};
        ['subject', 'body'].forEach(function(id) {
          var value = parameters[id];
          if (value !== null && value !== undefined) {
            queryStringParameters[id] = value;
          }
          delete parameters[id];
        });
        var queryString = $.param(queryStringParameters);
        if (queryString.length > 0) {
          // The replace is a workaround for a jquery issue: https://github.com/jquery/jquery/issues/2658
          components[2] += '?' + queryString.replace(/\+/g, '%20');
        }
      }
      var serializedParameters = CKEDITOR.plugins.xwikiMarker.serializeParameters(parameters);
      if (serializedParameters) {
        components.push(serializedParameters);
      }
      return components.join('|-|');
    },

    createResourcePicker: function(pickerDefinition) {
      var basePickerDefinition = {
        //
        // Standard fields
        //
        id: 'resourceReference',
        type: 'html',
        html: '
' +
                '' +
                '<input type="text" />' +
              '
',
        // Focus the resource picker when the dialog is loaded.
        tabIndex: 0,
        onLoad: function() {
          // Register the event listeners and create the resource picker. We register the changeResourceType listener
          // before creating the resource picker because we need to catch the first changeResourceType event in order to
          // update the label (it won't be fired again when we set the value of the resource input unless the resource
          // type is different).
          $(this.getElement().$).on('changeResourceType', $.proxy(this, 'onResourceTypeChange'))
            .find('input').resourcePicker({resourceTypes: this.resourceTypes});
          // We register the selectResource event listener after creating the resource picker because we don't care
          // about the first selectResource event since we're going to trigger another one anyway by setting the value
          // of the resource input when the dialog is shown.
          $(this.getElement().$).on('selectResource', $.proxy(this, 'onSelectResource'));
          // Fix the tab-key navigation.
          var resourceTypeDropDownToggle = this.getElement().findOne('.dropdown-toggle');
          var resourceTypeButton = this.getElement().findOne('button.resourceType');
          var resourceReferenceInput = this.getElement().findOne('input.resourceReference');
          var tabIndex = this.tabIndex;
          [resourceTypeDropDownToggle, resourceTypeButton, resourceReferenceInput].forEach(function(field) {
            var dialog = this;
            if (tabIndex >= 0 && tabIndex < dialog._.focusList.length) {
              dialog.addFocusable(field, tabIndex);
              field._focusable = dialog._.focusList[tabIndex];
            } else {
              dialog.addFocusable(field);
              field._focusable = dialog._.focusList[dialog._.focusList.length - 1];
            }
            field.on('focus', function() {
              dialog._.currentFocusIndex = this._focusable.focusIndex;
            });
          }, this.getDialog());
          // Fix reference input id.
          var id = CKEDITOR.tools.getNextId();
          resourceReferenceInput.setAttribute('id', id);
          this.getElement().findOne('label').setAttribute('for', id);
        },
        validate: function() {
          var resourceReference = this.getValue();
          if (resourceReference.reference === '') {
            // Check if the selected resource type supports empty references.
            var resourceTypeConfig = $resource.types[resourceReference.type] || {};
            if (resourceTypeConfig.allowEmptyReference !== true) {
              return this.getDialog().getParentEditor().localization.get('xwiki-resource.notSpecified',
                this.getLabelElement().getText());
            }
          }
          return true;
        },
        getValue: function() {
          var resourcePickerInput = this.getResourcePickerInput();
          // Make sure the resource picker has updated the resource picker input. This is needed in Internet Explorer
          // where the dialog buttons are not selectable and so the change event is not fired before the click event.
          $(resourcePickerInput.$).trigger('beforeGetValue');
          var serializedResourceReference = resourcePickerInput.getValue();
          var separatorIndex = serializedResourceReference.indexOf(':');
          var resourceReference = {
            type: serializedResourceReference.substr(0, separatorIndex),
            reference: serializedResourceReference.substr(separatorIndex + 1)
          };
          if (this.selectedResource && this.selectedResource.reference.type === resourceReference.type &&
              this.selectedResource.reference.reference === resourceReference.reference) {
            // Preserve the typed field if the resource type and reference have not changed.
            resourceReference.typed = this.selectedResource.reference.typed;
          }
          return resourceReference;
        },
        setValue: function(resourceReference) {
          // Reset the resource picker if no resource reference is provided.
          resourceReference = resourceReference || {
            type: this.resourceTypes[0],
            reference: '',
            // Make sure the picker doesn't try to resolve the empty reference.
            isNew: true
          };
          var serializedResourceReference = (resourceReference.type || '') + ':' + (resourceReference.reference || '');
          $(this.getResourcePickerInput().$).val(serializedResourceReference).trigger('selectResource', {
            reference: resourceReference
          });
        },
        //
        // Custom fields
        //
        getResourcePickerInput: function() {
          return this.getElement().findOne('input');
        },
        getLabelElement: function() {
          return this.getElement().findOne('.cke_dialog_ui_labeled_label');
        },
        onResourceTypeChange: function(event, data) {
          // Update the label.
          var resourceTypeConfig = $resource.types[data.newValue] || {label: data.newValue};
          this.getLabelElement().setText(resourceTypeConfig.label);
        },
        onSelectResource: function(event, resource) {
          this.selectedResource = resource;
        }
      };
      pickerDefinition = pickerDefinition || {};
      // We may need to access the base definition in order to override some operation.
      pickerDefinition.base = basePickerDefinition;
      return CKEDITOR.tools.extend(pickerDefinition, basePickerDefinition);
    },

    getResourceURL: function(resourceReference, editor) {
      if (['url', 'path', 'unc', 'unknown'].indexOf(resourceReference.type) >= 0) {
        return resourceReference.reference;
      } else if (['mailto', 'data'].indexOf(resourceReference.type) >= 0) {
        return resourceReference.type + ':' + resourceReference.reference;
      } else {
        var dispatcherURL = editor.config['xwiki-resource'].dispatcher;
        dispatcherURL += dispatcherURL.indexOf('?') < 0 ? '?' : '&';
        return dispatcherURL + $.param(resourceReference);
      }
    },

    updateResourcePickerOnFileBrowserSelect: function(dialogDefinition, resourcePickerElementId, fileBrowserElementId) {
      var fileBrowserElement = dialogDefinition.getContents(fileBrowserElementId[0]).get(fileBrowserElementId[1]);
      if (fileBrowserElement && fileBrowserElement.filebrowser) {
        var fileBrowserConfig = fileBrowserElement.filebrowser;
        if (typeof fileBrowserConfig === 'string') {
          fileBrowserConfig = {target: fileBrowserConfig};
          fileBrowserElement.filebrowser = fileBrowserConfig;
        }
        var oldOnSelect = fileBrowserConfig.onSelect;
        fileBrowserConfig.onSelect = function(fileURL, data) {
          if (data.resourceReference) {
            // Update the resource picker.
            this.getDialog().setValueOf(resourcePickerElementId[0], resourcePickerElementId[1], data.resourceReference);
          }
          if (typeof oldOnSelect === 'function') {
            return oldOnSelect.call(this, fileURL, data);
          }
        };
      }
    },

    bindResourcePicker: function(element, resourcePickerId, onlyTheReferencePart) {
      // Use the resource picker value when the given element is committed.
      var oldCommit = element.commit;
      element.commit = function() {
        var resourceReference = this.getDialog().getValueOf(resourcePickerId[0], resourcePickerId[1]);
        if (onlyTheReferencePart  === true) {
          this.setValue(resourceReference.reference);
        } else {
          this.setValue(CKEDITOR.plugins.xwikiResource.getResourceURL(resourceReference,
            this.getDialog().getParentEditor()));
        }
        if (typeof oldCommit === 'function') {
          oldCommit.apply(this, arguments);
        }
      };
      // We validate the resource reference instead.
      delete element.validate;
      // Hide the element. We show the resource picker instead.
      element.hidden = true;
    }
  };

  var parseQueryString = function(queryString) {
    var parameters = {};
    // Replace plus signs in the query string with spaces.
    queryString = queryString.replace(/\+/g, ' ');
    queryString.split('&').forEach(function(pair) {
      var parts = pair.split('=');
      var name = decodeURIComponent(parts[0]);
      var value = parts.length > 1 ? decodeURIComponent(parts[1]) : '';
      // We know the query string supports multiple parameter values but we don't have this need right now.
      parameters[name] = value;
    });
    return parameters;
  };
});
<|endoftext|>


<commit_before>define(
    ['jquery'],
    function($) {
        var cssFile = "/styles.css";
        var emtpyFunc = function() {};

        var currentBaseUrl = window.location.href.replace(/\/[^\/]+\.html/ig, "");
        var jsonUrl, customCssUrl;
        var allQueryParams;

        function getQueryParams() {
            var vars = {};
            var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,
                function(m,key,value) {
                    vars[key] = decodeURIComponent(value);
                });
            allQueryParams = vars;
        }

        var getUrlVar = function(name) {
            if (!allQueryParams) {
                getQueryParams();
            }
            return allQueryParams[name];
        };

        var setUrlVar = function(name, value) {
            if (!allQueryParams) {
                getQueryParams();
            }
            allQueryParams[name] = value;
            var newUrl = window.location.href.replace(/\?\S*/g, "");
            var first = true;
            for (paramName in allQueryParams) {
                if (allQueryParams[paramName]) {
                    newUrl += first ? "?" : "&";
                    first = false;
                    newUrl += paramName + "=" + encodeURIComponent(allQueryParams[paramName]);
                }
            }
            // write modified querystring without reloading the page
            if (history.pushState) {
                window.history.pushState({path:newUrl},'',newUrl);
            }
        };

        function setUrls(newUrl, allowOverride) {
             jsonUrl = newUrl;
             if (allowOverride && getUrlVar("conference") != undefined) {
                 jsonUrl = jsonUrl.replace(/\d+$/g, getUrlVar("conference"));
                 console.log('detected conference id from url parameter: ' + getUrlVar("conference"))
             }
             customCssUrl = jsonUrl + cssFile;
         }

        // temporarily for retrieving conference id from url parameter for switching between conferences,
        // can be removed when conference switch is implemented in html5 client

        function loadInitData(callback) {
            $.ajax({
                url: currentBaseUrl + '/rest/init.json',
                dataType: 'json',
                success: function (data) {
                    if (data.id != undefined) {
                        setUrls(jsonUrl.replace(/\d+$/g, data.id), false);
                        console.log('detected conference id from init call: ' + data.id)
                    }
                    if (callback) {
                        callback(jsonUrl, customCssUrl);
                    }
                },
                error: function() {
                }
            });
        }

        setUrls("${dukecon.server.jsonUrl}", true);
        loadInitData();

        return {
            getJsonUrl: function(callback) {
                callback = callback || emptyFunc;
                if (!jsonUrl) {
                    loadInitData(function(jsonUrl, unused) {
                        callback(jsonUrl);
                    });
                }
                else {
                    callback(jsonUrl);
                }
            },
            getCustomCssUrl: function(callback) {
                callback = callback || emptyFunc;
                if (!customCssUrl) {
                    loadInitData(function(unused, customCssUrl) {
                        callback(customCssUrl);
                    });
                }
                else {
                    callback(customCssUrl);
                }
            },
            getUrlParam: getUrlVar,
            setUrlParam: setUrlVar
        };
    }
);
<commit_msg>Compute conference data base url with xxx/ and xxx/index.html correctly<commit_after>define(
    ['jquery'],
    function($) {
        var cssFile = "/styles.css";
        var emtpyFunc = function() {};

        var currentBaseUrl = window.location.href.replace(/\/[^\/]+\.html/ig, "").replace(/\/$/ig, "");
        console.log('Using currentBaseUrl: "' + currentBaseUrl + '"');
        var jsonUrl, customCssUrl;
        var allQueryParams;

        function getQueryParams() {
            var vars = {};
            var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,
                function(m,key,value) {
                    vars[key] = decodeURIComponent(value);
                });
            allQueryParams = vars;
        }

        var getUrlVar = function(name) {
            if (!allQueryParams) {
                getQueryParams();
            }
            return allQueryParams[name];
        };

        var setUrlVar = function(name, value) {
            if (!allQueryParams) {
                getQueryParams();
            }
            allQueryParams[name] = value;
            var newUrl = window.location.href.replace(/\?\S*/g, "");
            var first = true;
            for (paramName in allQueryParams) {
                if (allQueryParams[paramName]) {
                    newUrl += first ? "?" : "&";
                    first = false;
                    newUrl += paramName + "=" + encodeURIComponent(allQueryParams[paramName]);
                }
            }
            // write modified querystring without reloading the page
            if (history.pushState) {
                window.history.pushState({path:newUrl},'',newUrl);
            }
        };

        function setUrls(newUrl, allowOverride) {
             jsonUrl = newUrl;
             if (allowOverride && getUrlVar("conference") != undefined) {
                 jsonUrl = jsonUrl.replace(/\d+$/g, getUrlVar("conference"));
                 console.log('detected conference id from url parameter: ' + getUrlVar("conference"));
             }
             customCssUrl = jsonUrl + cssFile;
         }

        // temporarily for retrieving conference id from url parameter for switching between conferences,
        // can be removed when conference switch is implemented in html5 client

        function loadInitData(callback) {
            $.ajax({
                url: currentBaseUrl + '/rest/init.json',
                dataType: 'json',
                success: function (data) {
                    if (data.id != undefined) {
                        setUrls(jsonUrl.replace(/\d+$/g, data.id), false);
                        console.log('detected conference id from init call: ' + data.id)
                    }
                    if (callback) {
                        callback(jsonUrl, customCssUrl);
                    }
                },
                error: function() {
                }
            });
        }

        setUrls("${dukecon.server.jsonUrl}", true);
        loadInitData();

        return {
            getJsonUrl: function(callback) {
                callback = callback || emptyFunc;
                if (!jsonUrl) {
                    loadInitData(function(jsonUrl, unused) {
                        callback(jsonUrl);
                    });
                }
                else {
                    callback(jsonUrl);
                }
            },
            getCustomCssUrl: function(callback) {
                callback = callback || emptyFunc;
                if (!customCssUrl) {
                    loadInitData(function(unused, customCssUrl) {
                        callback(customCssUrl);
                    });
                }
                else {
                    callback(customCssUrl);
                }
            },
            getUrlParam: getUrlVar,
            setUrlParam: setUrlVar
        };
    }
);
<|endoftext|>


<commit_before>'use strict'

// modules > native
const p = require('path')
const fs = require('fs')

// modules > 3rd party
const _ = require('lodash')
const chalk = require('chalk')
const serializeError = require('serialize-error')

// modules > gulp
const gulp = require('gulp')
const gutil = require('gulp-util')
const { rollup } = require('rollup')
// const sourcemaps = require('gulp-sourcemaps')

// modules > local
const errorHandler = require('../util/error-handler')

const { rollup: config } = require('../config')

const TASK_NAME = 'rollup'

const cache = {}

config.inputs = config.inputs || [config.input]

/* TODO only rebuild changed bundle when watching.
 * should probably be done with gulp.watch(pattern, (file) => {})
 *
 * if (file && !_.isEmpty(cache)) {
 *  const filesInBundle = cache[].modules.map(input => input.id)
 *  if (filesInBundle.includes(file)) etc etc
 * }
 */

const omitKeys = [
  'dest',
  'entries',
  // 'input',
  'inputs',
  'merge',
  // 'output',
  'outputs',
  'src',
  'suffix',
]

function task (entry, cb) {
  // if (config.suffix) {
  //   fs.writeFile(`${config.dest}.json`, JSON.stringify({ suffix: config.suffix }), (err) => err && console.error(err))
  // }

  rollup(Object.assign(_.omit(entry, omitKeys), {
    cache: cache[entry.input],
  }))
    .then((bundle) => {
      cache[entry.input] = bundle

      const count = bundle.modules.filter((module) => !module.id.startsWith('\u0000commonjs-proxy')).length

      bundle.write(entry.output).then(() => {
        gutil.log(`${chalk.cyan(TASK_NAME)} bundled ${chalk.blue(count)} files into ${chalk.magenta(entry.output.file)}.`)

        cb()
      })
    })
    .catch((err) => {
      err = Object.assign(serializeError(err), {
        task: `${TASK_NAME}:${entry.input}`,
      })

      errorHandler(err)

      cb()
    })
}

function createTask (entry) {
  if (entry.merge && !Array.isArray(config)) {
    entry = _.mergeWith({}, _.omit(config, [ 'entries', 'input', 'inputs', 'file', 'outputs' ]), entry, (a, b) => (Array.isArray(a) ? b : undefined))
  }

  // initialize all plugins. if the plugins property is an array, the plugins have already been initialized.
  const plugins = Array.isArray(entry.plugins) ? entry.plugins : _.map(entry.plugins, (pluginConfig, pluginName) => {
    pluginName = _.kebabCase(pluginName)

    if (_.isPlainObject(pluginConfig)) {
      let plugin

      try {
        plugin = require(pluginName)
      } catch (e) {
        try {
          plugin = require(`rollup-plugin-${pluginName}`)
        } catch (e) {
          throw new Error(`Unknown plugin "${pluginName}"`)
        }
      }

      return plugin(pluginConfig)
    }

    // if not a plainObject, assume pluginConfig is actually an initialized plugin
    return pluginConfig
  })

  if (Array.isArray(entry.inputs)) {
    return entry.inputs.map((input, i) => {
      if (!entry.outputs || !entry.outputs[i]) {
        throw new Error(`Output file not defined for "${input}"`)
      }

      const output = Object.assign({
        file: entry.outputs[i],
      }, entry.output)

      return createTask(Object.assign({}, _.omit(entry, [ 'entries', 'inputs', 'outputs', 'merge' ]), { input, output, plugins }))
    })
  }

  let input = entry.input
  let file = entry.file || entry.output.file

  if (entry.src || config.src) {
    input = p.join(entry.src || config.src, input)
  }

  if (entry.dest || config.dest) {
    file = p.join(entry.dest || config.dest, file)
  }

  const output = Object.assign({}, entry.output, { file })

  let taskName = `${TASK_NAME}:${entry.input}`

  gulp.task(taskName, task.bind(null, Object.assign({}, entry, { plugins, input, output })))

  return taskName
}

let entries = Array.isArray(config) ? config : config.entries || [ config ]

const tasks = entries.map(createTask)

gulp.task(TASK_NAME, gulp.parallel(_.flatten(tasks)))
<commit_msg>Enable suffix option for rollup task<commit_after>'use strict'

// modules > native
const p = require('path')
const fs = require('fs')

// modules > 3rd party
const _ = require('lodash')
const chalk = require('chalk')
const serializeError = require('serialize-error')

// modules > gulp
const gulp = require('gulp')
const gutil = require('gulp-util')
const { rollup } = require('rollup')
// const sourcemaps = require('gulp-sourcemaps')

// modules > local
const errorHandler = require('../util/error-handler')

const { rollup: config } = require('../config')

const TASK_NAME = 'rollup'

const cache = {}

config.inputs = config.inputs || [config.input]

/* TODO only rebuild changed bundle when watching.
 * should probably be done with gulp.watch(pattern, (file) => {})
 *
 * if (file && !_.isEmpty(cache)) {
 *  const filesInBundle = cache[].modules.map(input => input.id)
 *  if (filesInBundle.includes(file)) etc etc
 * }
 */

const omitKeys = [
  'dest',
  'entries',
  // 'input',
  'inputs',
  'merge',
  // 'output',
  'outputs',
  'src',
  'suffix',
]

function task (entry, cb) {
  if (entry.suffix) {
    fs.writeFile(`${config.dest}.json`, JSON.stringify({ suffix: entry.suffix }), (err) => err && console.error(err))
  }

  rollup(Object.assign(_.omit(entry, omitKeys), {
    cache: cache[entry.input],
  }))
    .then((bundle) => {
      cache[entry.input] = bundle

      const count = bundle.modules.filter((module) => !module.id.startsWith('\u0000commonjs-proxy')).length

      bundle.write(entry.output).then(() => {
        gutil.log(`${chalk.cyan(TASK_NAME)} bundled ${chalk.blue(count)} files into ${chalk.magenta(entry.output.file)}.`)

        cb()
      })
    })
    .catch((err) => {
      err = Object.assign(serializeError(err), {
        task: `${TASK_NAME}:${entry.input}`,
      })

      errorHandler(err)

      cb()
    })
}

function createTask (entry) {
  if (entry.merge && !Array.isArray(config)) {
    entry = _.mergeWith({}, _.omit(config, [ 'entries', 'input', 'inputs', 'file', 'outputs' ]), entry, (a, b) => (Array.isArray(a) ? b : undefined))
  }

  // initialize all plugins. if the plugins property is an array, the plugins have already been initialized.
  const plugins = Array.isArray(entry.plugins) ? entry.plugins : _.map(entry.plugins, (pluginConfig, pluginName) => {
    pluginName = _.kebabCase(pluginName)

    if (_.isPlainObject(pluginConfig)) {
      let plugin

      try {
        plugin = require(pluginName)
      } catch (e) {
        try {
          plugin = require(`rollup-plugin-${pluginName}`)
        } catch (e) {
          throw new Error(`Unknown plugin "${pluginName}"`)
        }
      }

      return plugin(pluginConfig)
    }

    // if not a plainObject, assume pluginConfig is actually an initialized plugin
    return pluginConfig
  })

  if (Array.isArray(entry.inputs)) {
    return entry.inputs.map((input, i) => {
      if (!entry.outputs || !entry.outputs[i]) {
        throw new Error(`Output file not defined for "${input}"`)
      }

      const output = Object.assign({
        file: entry.outputs[i],
      }, entry.output)

      return createTask(Object.assign({}, _.omit(entry, [ 'entries', 'inputs', 'outputs', 'merge' ]), { input, output, plugins }))
    })
  }

  let input = entry.input
  let file = entry.file || entry.output.file

  if (entry.suffix || config.suffix) {
    const obj = p.parse(file)

    file = `${obj.dir}/${obj.name}${config.suffix || entry.suffix}${obj.ext}`
  }

  if (entry.src || config.src) {
    input = p.join(entry.src || config.src, input)
  }

  if (entry.dest || config.dest) {
    file = p.join(entry.dest || config.dest, file)
  }

  const output = Object.assign({}, entry.output, { file })

  let taskName = `${TASK_NAME}:${entry.input}`

  gulp.task(taskName, task.bind(null, Object.assign({}, entry, { plugins, input, output })))

  return taskName
}

let entries = Array.isArray(config) ? config : config.entries || [ config ]

const tasks = entries.map(createTask)

gulp.task(TASK_NAME, gulp.parallel(_.flatten(tasks)))
<|endoftext|>


<commit_before>/**
 * Sprite generator for Tâmia
 *
 * @requires GraphicsMagick or Cairo
 * @author Artem Sapegin (http://sapegin.me)
 */

/*jshint node:true */
module.exports = function(grunt) {
	'use strict';

	var fs = require('fs');
	var path = require('path');
	var spritesmith = require('spritesmith');
	var crypto = require('crypto');
	var _ = require('lodash');
	var async = grunt.util.async;

	grunt.template.addDelimiters('tamia-sprite', '{%', '%}');

	grunt.registerMultiTask('sprite', 'Sprite generator for Tâmia', function() {
		this.requiresConfig([ this.name, this.target, 'src' ].join('.'));
		this.requiresConfig([ this.name, this.target, 'dest' ].join('.'));

		var allDone = this.async();
		var params = _.defaults(this.data, {
			target: this.target,
			engine: 'auto',
			algorithm: 'binary-tree',
			padding: 3,
			destStyl: this.data.dest.replace(/\.png$/, '.styl'),
			template: '{%=target%}_{%=name%} = {%=x%}px {%=y%}px {%=width%}px {%=height%}px',
			fingerprintTemplate: '{%=target%}_fingerprint = "{%=fingerprint%}"',
		});

		var files = this.filesSrc;
		if (!files.length) {
			grunt.warn('Source files not found.');
			allDone();
		}

		// Separate retina files
		var filesRetina = _.filter(files, isRetina);
		files = _.filter(files, isNotRetina);

		if (filesRetina.length && filesRetina.length !== files.length) {
			grunt.warn('Number of normal and Retina images should be equal.');
			allDone();
		}

		var md5 = crypto.createHash('md5');
		var coordinates;
		async.parallel([
			function(done) {
				generateImage(_.extend({}, params, {
					src: files
				}), function(coords) {
					coordinates = coords;
					done();
				});
			},
			function(done) {
				generateImage(_.extend({}, params, {
					src: filesRetina,
					dest: params.dest.replace(/\.png$/, '@2x.png')
				}), done);
			}
		], function() {
			generateStylesheet(params, coordinates);
			allDone();
		});

		function generateImage(options, done) {
			if (!options.src.length) return done();

			spritesmith({
				src: options.src,
				engine: options.engine,
				algorithm: options.algorithm,
				padding: options.padding,
				exportOpts: {
					format: 'png'
				}
			}, function (err, result) {
				if (err) {
					grunt.fatal(err);
					return done();
				}

				// Save sprite image
				grunt.file.mkdir(path.dirname(options.dest));
				fs.writeFileSync(options.dest, result.image, 'binary');
				md5.update(result.image);

				grunt.log.writeln('Sprite ' + options.dest.cyan + ' created.');

				done(result.coordinates);
			});
		}

		function generateStylesheet(options, coordinates) {
			// Generate Stylus variables
			var lines = _.map(coordinates, function(coords, filename) {
				var name = path.basename(filename, '.png');
				return grunt.template.process(options.template, {delimiters: 'tamia-sprite', data: {
					target: options.target,
					name: name,
					x: -coords.x,
					y: -coords.y,
					width: coords.width,
					height: coords.height
				}});
			});

			// Add fingerprint variable
			lines.push(grunt.template.process(options.fingerprintTemplate, {delimiters: 'tamia-sprite', data: {
				target: options.target,
				fingerprint: md5.digest('hex')
			}}));

			// Save variables
			grunt.file.write(options.destStyl, lines.join('\n'));

			grunt.log.writeln('Stylesheet ' + options.destStyl.cyan + ' created.');
		}

		function isRetina(name) {
			return name.indexOf('@2x') !== -1;
		}

		function isNotRetina(name) {
			return !isRetina(name);
		}
	});
};
<commit_msg>Refactoring.<commit_after>/**
 * Sprite generator for Tâmia
 *
 * @requires GraphicsMagick or Cairo
 * @author Artem Sapegin (http://sapegin.me)
 */

/*jshint node:true */
module.exports = function(grunt) {
	'use strict';

	var fs = require('fs');
	var path = require('path');
	var spritesmith = require('spritesmith');
	var crypto = require('crypto');
	var _ = require('lodash');
	var async = grunt.util.async;

	grunt.template.addDelimiters('tamia-sprite', '{%', '%}');

	grunt.registerMultiTask('sprite', 'Sprite generator for Tâmia', function() {
		this.requiresConfig([ this.name, this.target, 'src' ].join('.'));
		this.requiresConfig([ this.name, this.target, 'dest' ].join('.'));

		var allDone = this.async();
		var params = _.defaults(this.data, {
			target: this.target,
			engine: 'auto',
			algorithm: 'binary-tree',
			padding: 3,
			destStyl: this.data.dest.replace(/\.png$/, '.styl'),
			template: '{%=target%}_{%=name%} = {%=x%}px {%=y%}px {%=width%}px {%=height%}px',
			fingerprintTemplate: '{%=target%}_fingerprint = "{%=fingerprint%}"',
		});

		var files = this.filesSrc;
		if (!files.length) {
			grunt.warn('Source files not found.');
			allDone();
		}

		// Separate retina files
		var filesRetina = _.filter(files, isRetina);
		files = _.reject(files, isRetina);

		if (filesRetina.length && filesRetina.length !== files.length) {
			grunt.warn('Number of normal and Retina images should be equal.');
			allDone();
		}

		var md5 = crypto.createHash('md5');
		var coordinates;
		async.parallel([
			function(done) {
				generateImage(_.extend({}, params, {
					src: files
				}), function(coords) {
					coordinates = coords;
					done();
				});
			},
			function(done) {
				generateImage(_.extend({}, params, {
					src: filesRetina,
					dest: params.dest.replace(/\.png$/, '@2x.png')
				}), done);
			}
		], function() {
			generateStylesheet(params, coordinates);
			allDone();
		});

		function generateImage(options, done) {
			if (!options.src.length) return done();

			spritesmith({
				src: options.src,
				engine: options.engine,
				algorithm: options.algorithm,
				padding: options.padding,
				exportOpts: {
					format: 'png'
				}
			}, function (err, result) {
				if (err) {
					grunt.fatal(err);
					return done();
				}

				// Save sprite image
				grunt.file.mkdir(path.dirname(options.dest));
				fs.writeFileSync(options.dest, result.image, 'binary');
				md5.update(result.image);

				grunt.log.writeln('Sprite ' + options.dest.cyan + ' created.');

				done(result.coordinates);
			});
		}

		function generateStylesheet(options, coordinates) {
			// Generate Stylus variables
			var lines = _.map(coordinates, function(coords, filename) {
				var name = path.basename(filename, '.png');
				return grunt.template.process(options.template, {delimiters: 'tamia-sprite', data: {
					target: options.target,
					name: name,
					x: -coords.x,
					y: -coords.y,
					width: coords.width,
					height: coords.height
				}});
			});

			// Add fingerprint variable
			lines.push(grunt.template.process(options.fingerprintTemplate, {delimiters: 'tamia-sprite', data: {
				target: options.target,
				fingerprint: md5.digest('hex')
			}}));

			// Save variables
			grunt.file.write(options.destStyl, lines.join('\n'));

			grunt.log.writeln('Stylesheet ' + options.destStyl.cyan + ' created.');
		}

		function isRetina(name) {
			return name.indexOf('@2x') !== -1;
		}
	});
};
<|endoftext|>