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

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

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

            /**
            * @inheritDoc
            */
            setSource: function (mediaType, url, mimeType) {
                if (this.getState() === MediaPlayer.STATE.EMPTY) {
                    this._type = mediaType;
                    this._source = url;
                    this._mimeType = mimeType;
                    this._createElement();
                    this._registerEventHandlers();
                    this._addElementToDOM();
                    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:
                    case MediaPlayer.STATE.BUFFERING:
                        break;

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

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

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

                    case MediaPlayer.STATE.STOPPED:
                        // Seeking past 0 requires calling play first when media has not been loaded
                        this._toBuffering();
                        this._playAndSetDeferredSeek(seconds);
                        break;

                    case MediaPlayer.STATE.COMPLETE:
                        this._toBuffering();
                        this._mediaElement.stop();
                        this._playAndSetDeferredSeek(seconds);
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._toBuffering();
                        var seekResult = this._mediaElement.seek(this._getClampedTime(seconds) * 1000);
                        if(seekResult === false) {
                            this._toPlaying();
                        }
                        break;

                    case MediaPlayer.STATE.PAUSED:
                        // TODO: consider always deferring play(1) until playStateChange to 2 OR deferring seek until playStateChange to 3, 4, or 1
                        this._toBuffering();
                        this._seekAndPlayFromPaused(this._getClampedTime(seconds) * 1000);
                        break;

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

            /**
            * @inheritDoc
            */
            pause: function () {
                this._postBufferingState = MediaPlayer.STATE.PAUSED;
                // TODO: Spec says we can pause from buffering. Will this help observed device issues?
                switch (this.getState()) {
                    case MediaPlayer.STATE.BUFFERING:
                    case MediaPlayer.STATE.PAUSED:
                        break;

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

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

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

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

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

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

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

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

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

                    case MediaPlayer.STATE.COMPLETE:
                        if (this._range) {
                            return this._range.end;
                        }

                    default:
                        if (this._mediaElement) {
                            return this._mediaElement.playPosition / 1000;
                        }
                        break;
                }
                return undefined;
            },

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

                    default:
                        return this._range;
                };
                return undefined;
            },

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

            _onFinishedBuffering: function() {
                this._cacheRange();

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

                if(this._waitingToSeek()) {
                    this._toBuffering();
                    this._performDeferredSeek();
                } else if (this._waitingToPause()) {
                    this._toPaused();
                    this._mediaElement.play(0);
                } else {
                    this._toPlaying();
                }
            },

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

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

            _onEndOfMedia: function() {
                if (this.getState() !== MediaPlayer.STATE.COMPLETE) {
                    this._toComplete();
                }
            },

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

            _createElement: function() {
                var device = RuntimeContext.getDevice();
                this._mediaElement = device._createElement("object", "mediaPlayer");
                this._mediaElement.type = this._mimeType;
                this._mediaElement.data = this._source;
                this._mediaElement.style.position = "absolute";
                this._mediaElement.style.top = "0px";
                this._mediaElement.style.left = "0px";
                this._mediaElement.style.width = "100%";
                this._mediaElement.style.height = "100%";
                //this._mediaElement.setFullScreen(true);
            },

            _registerEventHandlers: function() {
                var self = this;
                var DEVICE_UPDATE_PERIOD_MS = 500;

                this._mediaElement.onPlayStateChange = function() {
                    switch (self._mediaElement.playState) {
                        case Player.PLAY_STATE_STOPPED:
                            break;
                        case Player.PLAY_STATE_PLAYING:
                            self._onFinishedBuffering();
                            break;
                        case Player.PLAY_STATE_PAUSED:
                            break;
                        case Player.PLAY_STATE_CONNECTING:
                            break;
                        case Player.PLAY_STATE_BUFFERING:
                            self._onDeviceBuffering();
                            break;
                        case Player.PLAY_STATE_FINISHED:
                            self._onEndOfMedia();
                            break;
                        case Player.PLAY_STATE_ERROR:
                            self._onDeviceError();
                            break;
                        default:
                            // do nothing
                            break;
                    }
                }

                this._updateInterval = window.setInterval(function() {
                    self._onStatus();
                }, DEVICE_UPDATE_PERIOD_MS);
            },

            _addElementToDOM: function() {
                var device = RuntimeContext.getDevice();
                var body = document.getElementsByTagName("body")[0];
                device.prependChildElement(body, this._mediaElement);
            },

            _cacheRange: function() {
                if(this._mediaElement) {
                    this._range =  {
                        start: 0,
                        end: this._mediaElement.playTime / 1000
                    };
                }
            },

            _seekAndPlayFromPaused: function(seconds) {
                this._mediaElement.seek(seconds);
                this._mediaElement.play(1);
            },

            _playAndSetDeferredSeek: function(seconds) {
                this._mediaElement.play(1);
                if (seconds > 0) {
                    this._deferSeekingTo = seconds;
                }
            },

            _waitingToSeek: function() {
                return (this._deferSeekingTo !== undefined);
            },

            _performDeferredSeek: function() {
                this._mediaElement.seek(this._getClampedTime(this._deferSeekingTo) * 1000);
                this._deferSeekingTo = undefined;
            },

            _waitingToPause: function() {
                return (this._postBufferingState === MediaPlayer.STATE.PAUSED);
            },

            _wipe: function () {
                this._type = undefined;
                this._source = undefined;
                this._mimeType = undefined;
                if(this._mediaElement) {
                    window.clearInterval(this._updateInterval);
                    this._destroyMediaElement();
                }
            },

            _destroyMediaElement: function() {
                var device = RuntimeContext.getDevice();
                delete this._mediaElement.onPlayStateChange;
                device.removeElement(this._mediaElement);
                this._mediaElement = undefined;
            },

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

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

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

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

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

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

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

        Player.PLAY_STATE_STOPPED = 0;
        Player.PLAY_STATE_PLAYING = 1;
        Player.PLAY_STATE_PAUSED = 2;
        Player.PLAY_STATE_CONNECTING = 3;
        Player.PLAY_STATE_BUFFERING = 4;
        Player.PLAY_STATE_FINISHED = 5;
        Player.PLAY_STATE_ERROR = 6;

        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;
    }

    // 7.14.1.1 State diagram for A/V control objects
    // Contains useful state diagram
    // 7.14.3 Extensions to A/V object for trickmodes
    // TODO: Implement onPlaySpeedChanged() to give us some logging, and check some devices to see if it is used
    // playSpeeds array might be useful - if '0' is not in the list, then perhaps we cannot pause?
    // 7.14.8 Extensions to A/V object for UI feedback of buffering A/V content
    // TODO: Implement onReadyToPlay() to give us some logging, and check some devices to see if it is used
    // 7.14.9 DOM 2 events for A/V object
    // playState can change whilst onPlayStateChange is executing

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

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

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

            /**
            * @inheritDoc
            */
            setSource: function (mediaType, url, mimeType) {
                if (this.getState() === MediaPlayer.STATE.EMPTY) {
                    this._type = mediaType;
                    this._source = url;
                    this._mimeType = mimeType;
                    this._createElement();
                    this._registerEventHandlers();
                    this._addElementToDOM();
                    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:
                    case MediaPlayer.STATE.BUFFERING:
                        break;

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

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

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

                    case MediaPlayer.STATE.STOPPED:
                        // Seeking past 0 requires calling play first when media has not been loaded
                        this._toBuffering();
                        this._playAndSetDeferredSeek(seconds);
                        break;

                    case MediaPlayer.STATE.COMPLETE:
                        this._toBuffering();
                        this._mediaElement.stop();
                        this._playAndSetDeferredSeek(seconds);
                        break;

                    case MediaPlayer.STATE.PLAYING:
                        this._toBuffering();
                        var seekResult = this._mediaElement.seek(this._getClampedTime(seconds) * 1000);
                        if(seekResult === false) {
                            this._toPlaying();
                        }
                        break;

                    case MediaPlayer.STATE.PAUSED:
                        // TODO: consider always deferring play(1) until playStateChange to 2 OR deferring seek until playStateChange to 3, 4, or 1
                        this._toBuffering();
                        this._seekAndPlayFromPaused(this._getClampedTime(seconds) * 1000);
                        break;

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

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

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

            /**
            * @inheritDoc
            */
            pause: function () {
                this._postBufferingState = MediaPlayer.STATE.PAUSED;
                // TODO: Spec says we can pause from buffering. Will this help observed device issues?
                switch (this.getState()) {
                    case MediaPlayer.STATE.BUFFERING:
                    case MediaPlayer.STATE.PAUSED:
                        break;

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

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

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

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

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

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

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

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

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

                    case MediaPlayer.STATE.COMPLETE:
                        if (this._range) {
                            return this._range.end;
                        }

                    default:
                        if (this._mediaElement) {
                            return this._mediaElement.playPosition / 1000;
                        }
                        break;
                }
                return undefined;
            },

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

                    default:
                        return this._range;
                };
                return undefined;
            },

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

            _onFinishedBuffering: function() {
                this._cacheRange();

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

                if(this._waitingToSeek()) {
                    this._toBuffering();
                    this._performDeferredSeek();
                } else if (this._waitingToPause()) {
                    this._toPaused();
                    this._mediaElement.play(0);
                } else {
                    this._toPlaying();
                }
            },

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

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

            _onEndOfMedia: function() {
                if (this.getState() !== MediaPlayer.STATE.COMPLETE) {
                    this._toComplete();
                }
            },

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

            _createElement: function() {
                var device = RuntimeContext.getDevice();
                this._mediaElement = device._createElement("object", "mediaPlayer");
                this._mediaElement.type = this._mimeType;
                this._mediaElement.data = this._source;
                this._mediaElement.style.position = "absolute";
                this._mediaElement.style.top = "0px";
                this._mediaElement.style.left = "0px";
                this._mediaElement.style.width = "100%";
                this._mediaElement.style.height = "100%";
                //this._mediaElement.setFullScreen(true);
            },

            _registerEventHandlers: function() {
                var self = this;
                var DEVICE_UPDATE_PERIOD_MS = 500;

                this._mediaElement.onPlayStateChange = function() {
                    switch (self._mediaElement.playState) {
                        case Player.PLAY_STATE_STOPPED:
                            break;
                        case Player.PLAY_STATE_PLAYING:
                            self._onFinishedBuffering();
                            break;
                        case Player.PLAY_STATE_PAUSED:
                            break;
                        case Player.PLAY_STATE_CONNECTING:
                            break;
                        case Player.PLAY_STATE_BUFFERING:
                            self._onDeviceBuffering();
                            break;
                        case Player.PLAY_STATE_FINISHED:
                            self._onEndOfMedia();
                            break;
                        case Player.PLAY_STATE_ERROR:
                            self._onDeviceError();
                            break;
                        default:
                            // do nothing
                            break;
                    }
                }

                this._updateInterval = window.setInterval(function() {
                    self._onStatus();
                }, DEVICE_UPDATE_PERIOD_MS);
            },

            _addElementToDOM: function() {
                var device = RuntimeContext.getDevice();
                var body = document.getElementsByTagName("body")[0];
                device.prependChildElement(body, this._mediaElement);
            },

            _cacheRange: function() {
                if(this._mediaElement) {
                    this._range =  {
                        start: 0,
                        end: this._mediaElement.playTime / 1000
                    };
                }
            },

            _seekAndPlayFromPaused: function(seconds) {
                this._mediaElement.seek(seconds);
                this._mediaElement.play(1);
            },

            _playAndSetDeferredSeek: function(seconds) {
                this._mediaElement.play(1);
                if (seconds > 0) {
                    this._deferSeekingTo = seconds;
                }
            },

            _waitingToSeek: function() {
                return (this._deferSeekingTo !== undefined);
            },

            _performDeferredSeek: function() {
                this._mediaElement.seek(this._getClampedTime(this._deferSeekingTo) * 1000);
                this._deferSeekingTo = undefined;
            },

            _waitingToPause: function() {
                return (this._postBufferingState === MediaPlayer.STATE.PAUSED);
            },

            _wipe: function () {
                this._type = undefined;
                this._source = undefined;
                this._mimeType = undefined;
                if(this._mediaElement) {
                    window.clearInterval(this._updateInterval);
                    this._destroyMediaElement();
                }
            },

            _destroyMediaElement: function() {
                var device = RuntimeContext.getDevice();
                delete this._mediaElement.onPlayStateChange;
                device.removeElement(this._mediaElement);
                this._mediaElement = undefined;
            },

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

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

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

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

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

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

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

        Player.PLAY_STATE_STOPPED = 0;
        Player.PLAY_STATE_PLAYING = 1;
        Player.PLAY_STATE_PAUSED = 2;
        Player.PLAY_STATE_CONNECTING = 3;
        Player.PLAY_STATE_BUFFERING = 4;
        Player.PLAY_STATE_FINISHED = 5;
        Player.PLAY_STATE_ERROR = 6;

        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;
    }

    // 7.14.1.1 State diagram for A/V control objects
    // Contains useful state diagram
    // 7.14.3 Extensions to A/V object for trickmodes
    // TODO: Implement onPlaySpeedChanged() to give us some logging, and check some devices to see if it is used
    // playSpeeds array might be useful - if '0' is not in the list, then perhaps we cannot pause?
    // 7.14.8 Extensions to A/V object for UI feedback of buffering A/V content
    // TODO: Implement onReadyToPlay() to give us some logging, and check some devices to see if it is used
    // 7.14.9 DOM 2 events for A/V object
    // playState can change whilst onPlayStateChange is executing

);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Relay from 'react-relay';
import { Image, Modal } from 'react-bootstrap';
import EntryViewer from './EntryViewer';
import moment from 'moment';

export class Entry extends Component {
    constructor(props) {
        super(props);

        this.state = {};

        this.getStyles = this.getStyles.bind(this);
        this.showViewer = this.showViewer.bind(this);
        this.hideViewer = this.hideViewer.bind(this);
    }

    formatTimestamp() {
        return moment(this.props.entry.timestamp).fromNow();
    }

    getStyles() {
        var thumb = this.props.entry.media.videoThumbnail || this.props.entry.media.imageThumbnail;
        return (thumb)
            ? { backgroundImage: 'url('+ thumb + ')' }
            : {};
    }

    showViewer(e) {
        e.preventDefault();
        if (this.props.withViewer) {
            this.setState({showEntryViewer: true});
        }
    }

    hideViewer() {
        this.setState({showEntryViewer: false});
    }

    render() {
        var hasMedia = this.props.entry.media.image || this.props.entry.media.video;
        var className = 'entry entry--' + this.props.entry.sentiment.type;

        if (hasMedia) {
            className += ' entry--has-media';
        }

        return (

                            { this.props.entry.media.text &&

{this.props.entry.media.text}

                            }

                        <Image
                            src={'/static/' + this.props.entry.sentiment.type + '.png'}
                            alt={this.props.entry.sentiment.type}
                        />

{this.formatTimestamp()}

                                {this.props.entry.topics.map((t) => t.name).join(', ')}

                        { this.props.withViewer &&
                            <Modal
                                show={this.state.showEntryViewer}
                                bsSize="large"
                                backdrop={true}
                                onHide={this.hideViewer}
                            >
                                <EntryViewer entry={this.props.entry} />
                            </Modal>
                        }

        );
    }
}

Entry.propTypes = {
    entry: React.PropTypes.object.isRequired,
    withViewer: React.PropTypes.bool
}

Entry.defaultProps = {
    withViewer: true
}

export const EntryContainer = Relay.createContainer(Entry, {
    fragments: {
        entry: () => Relay.QL`
        fragment on Entry {
            id
            media {
                text
                video
                videoThumbnail
                image
                imageThumbnail
            }
            topics {
                name
            }
            sentiment {
                type
            },
            created
            updated
        }`,
    }
});
<commit_msg>Fixed a bug where the timestamp of an entry was being generated from the current time and not the creation time.<commit_after>import React, { Component } from 'react';
import Relay from 'react-relay';
import { Image, Modal } from 'react-bootstrap';
import EntryViewer from './EntryViewer';
import moment from 'moment';

export class Entry extends Component {
    constructor(props) {
        super(props);

        this.state = {};

        this.getStyles = this.getStyles.bind(this);
        this.showViewer = this.showViewer.bind(this);
        this.hideViewer = this.hideViewer.bind(this);
    }

    formatTimestamp() {
        return moment(this.props.entry.created).fromNow();
    }

    getStyles() {
        var thumb = this.props.entry.media.videoThumbnail || this.props.entry.media.imageThumbnail;
        return (thumb)
            ? { backgroundImage: 'url('+ thumb + ')' }
            : {};
    }

    showViewer(e) {
        e.preventDefault();
        if (this.props.withViewer) {
            this.setState({showEntryViewer: true});
        }
    }

    hideViewer() {
        this.setState({showEntryViewer: false});
    }

    render() {
        var hasMedia = this.props.entry.media.image || this.props.entry.media.video;
        var className = 'entry entry--' + this.props.entry.sentiment.type;

        if (hasMedia) {
            className += ' entry--has-media';
        }

        return (

                            { this.props.entry.media.text &&

{this.props.entry.media.text}

                            }

                        <Image
                            src={'/static/' + this.props.entry.sentiment.type + '.png'}
                            alt={this.props.entry.sentiment.type}
                        />

{this.formatTimestamp()}

                                {this.props.entry.topics.map((t) => t.name).join(', ')}

                        { this.props.withViewer &&
                            <Modal
                                show={this.state.showEntryViewer}
                                bsSize="large"
                                backdrop={true}
                                onHide={this.hideViewer}
                            >
                                <EntryViewer entry={this.props.entry} />
                            </Modal>
                        }

        );
    }
}

Entry.propTypes = {
    entry: React.PropTypes.object.isRequired,
    withViewer: React.PropTypes.bool
}

Entry.defaultProps = {
    withViewer: true
}

export const EntryContainer = Relay.createContainer(Entry, {
    fragments: {
        entry: () => Relay.QL`
        fragment on Entry {
            id
            media {
                text
                video
                videoThumbnail
                image
                imageThumbnail
            }
            topics {
                name
            }
            sentiment {
                type
            },
            created
            updated
        }`,
    }
});
<|endoftext|>


<commit_before>var BrehonContract = artifacts.require("./BrehonContract.sol");

contract('BrehonContract', function (accounts) {
  it('should set appealLevel to -1', function () {
    return BrehonContract.deployed().then(function (instance) {
      return instance.appealLevel.call().then(function (appealLevel) {
        assert.equal(appealLevel.valueOf(), -1);
      });
    });
  });

  it("should set partyA's address properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[0], accounts[0]);
      });
    });
  });

  it("should set partyA's deposit to 0", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[1].valueOf(), 0);
      });
    });
  });

  it("should set partyA's contractAccepted to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[2], false);
      });
    });
  });

  it("should set partyA's primaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[3], false);
      });
    });
  });

  it("should set partyA's secondaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[4], false);
      });
    });
  });

  it("should set partyA's tertiaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[5], false);
      });
    });
  });

  it("should set partyB's address properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[0], accounts[1]);
      });
    });
  });

  it("should set partyB's deposit to 0", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[1].valueOf(), 0);
      });
    });
  });

  it("should set partyB's contractAccepted to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[2], false);
      });
    });
  });

  it("should set partyB's primaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[3], false);
      });
    });
  });

  it("should set partyB's secondaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[4], false);
      });
    });
  });

  it("should set partyB's tertiaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[5], false);
      });
    });
  });
});
<commit_msg>Added test suite for primary and secondary brehon's deployment setting<commit_after>var BrehonContract = artifacts.require("./BrehonContract.sol");

contract('BrehonContract constructor', function (accounts) {
  it('should set appealLevel to -1', function () {
    return BrehonContract.deployed().then(function (instance) {
      return instance.appealLevel.call().then(function (appealLevel) {
        assert.equal(appealLevel.valueOf(), -1);
      });
    });
  });

  it("should set partyA's address properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[0], accounts[0]);
      });
    });
  });

  it("should set partyA's deposit to 0", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[1].valueOf(), 0);
      });
    });
  });

  it("should set partyA's contractAccepted to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[2], false);
      });
    });
  });

  it("should set partyA's primaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[3], false);
      });
    });
  });

  it("should set partyA's secondaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[4], false);
      });
    });
  });

  it("should set partyA's tertiaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyA.call().then(function (partyA) {
        assert.equal(partyA[5], false);
      });
    });
  });

  it("should set partyB's address properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[0], accounts[1]);
      });
    });
  });

  it("should set partyB's deposit to 0", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[1].valueOf(), 0);
      });
    });
  });

  it("should set partyB's contractAccepted to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[2], false);
      });
    });
  });

  it("should set partyB's primaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[3], false);
      });
    });
  });

  it("should set partyB's secondaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[4], false);
      });
    });
  });

  it("should set partyB's tertiaryBrehonApproval to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.partyB.call().then(function (partyB) {
        assert.equal(partyB[5], false);
      });
    });
  });

  it("should set primaryBrehon's address properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.primaryBrehon.call().then(function (primaryBrehon) {
        assert.equal(primaryBrehon[0], accounts[2]);
      });
    });
  });

  it("should set primaryBrehon's contractAcceptance to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.primaryBrehon.call().then(function (primaryBrehon) {
        assert.equal(primaryBrehon[1], false);
      });
    });
  });

  it("should set primaryBrehon's fixedFee properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.primaryBrehon.call().then(function (primaryBrehon) {
        assert.equal(primaryBrehon[2].valueOf(), '100000000000000000');
      });
    });
  });

  it("should set primaryBrehon's disputeFee properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.primaryBrehon.call().then(function (primaryBrehon) {
        assert.equal(primaryBrehon[3].valueOf(), '1000000000000000000');
      });
    });
  });

  it("should set secondaryBrehon's address properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.secondaryBrehon.call().then(function (secondaryBrehon) {
        assert.equal(secondaryBrehon[0], accounts[3]);
      });
    });
  });

  it("should set secondaryBrehon's contractAcceptance to false", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.secondaryBrehon.call().then(function (secondaryBrehon) {
        assert.equal(secondaryBrehon[1], false);
      });
    });
  });

  it("should set secondaryBrehon's fixedFee properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.secondaryBrehon.call().then(function (secondaryBrehon) {
        assert.equal(secondaryBrehon[2].valueOf(), '100000000000000000');
      });
    });
  });

  it("should set secondaryBrehon's disputeFee properly", function () {
    return BrehonContract.deployed().then(function (brehonContract) {
      return brehonContract.secondaryBrehon.call().then(function (secondaryBrehon) {
        assert.equal(secondaryBrehon[3].valueOf(), '1000000000000000000');
      });
    });
  });
});

contract('BrehonContract constructor', function (accounts) {
  it('should set appealLevel to -1', function () {
    return BrehonContract.deployed().then(function (instance) {
      return instance.appealLevel.call().then(function (appealLevel) {
        assert.equal(appealLevel.valueOf(), -1);
      });
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var gulp = require('gulp');

var conf = require('./conf');
var bowerInfo = require('../bower.json');

var $ = require('gulp-load-plugins')();

gulp.task('scripts', function () {
  return gulp.src(path.join(conf.paths.src, '/**/*.coffee'))
    // TODO: make source maps actually work.
    // .pipe($.sourcemaps.init())
    // .pipe($.sourcemaps.write())
    // TODO: configure .jshintrc rules, and re-enable.
    // .pipe($.coffeelint())
    // .pipe($.coffeelint.reporter())
    .pipe($.coffee()).on('error', conf.errorHandler('CoffeeScript'))
    .pipe(gulp.dest(path.join(conf.paths.tmp, '/scripts')))
    .pipe($.size())
});

gulp.task('locales', function () {
  return gulp.src(path.join(conf.paths.src, '**/locales/*.json'))
    .pipe($.angularTranslate({
      module: 'maestrano.impac',
      standalone: false,
      filename: 'impac-angular.locales.js'}))
    .pipe(gulp.dest(conf.paths.tmp + '/scripts')) // Output locales.js
})

// Scripts to run impac.version in the console (need to refresh first)
// Needs to be called AFTER scripts due to permissions errors
gulp.task('version', ['scripts'], function () {
  var func = '(function () {console.info("' + bowerInfo.description + ' - v' + bowerInfo.version + '"); window["impac"] = {"version": "' + bowerInfo.version + '"};}).call();';

  return fs.writeFileSync(path.join(conf.paths.tmp, '/scripts', '/version.js'), func);
});

<commit_msg>Extend gulp locales task to build src json to dist<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var gulp = require('gulp');

var conf = require('./conf');
var bowerInfo = require('../bower.json');

var $ = require('gulp-load-plugins')();

gulp.task('scripts', function () {
  return gulp.src(path.join(conf.paths.src, '/**/*.coffee'))
    // TODO: make source maps actually work.
    // .pipe($.sourcemaps.init())
    // .pipe($.sourcemaps.write())
    // TODO: configure .jshintrc rules, and re-enable.
    // .pipe($.coffeelint())
    // .pipe($.coffeelint.reporter())
    .pipe($.coffee()).on('error', conf.errorHandler('CoffeeScript'))
    .pipe(gulp.dest(path.join(conf.paths.tmp, '/scripts')))
    .pipe($.size())
});

gulp.task('locales', function () {
  return gulp.src(path.join(conf.paths.src, '**/locales/*.json'))
    .pipe(gulp.dest(conf.paths.dist))
    .pipe($.angularTranslate({
      module: 'maestrano.impac',
      standalone: false,
      filename: 'impac-angular.locales.js'}))
    .pipe(gulp.dest(conf.paths.tmp + '/scripts')) // Output locales.js
})

// Scripts to run impac.version in the console (need to refresh first)
// Needs to be called AFTER scripts due to permissions errors
gulp.task('version', ['scripts'], function () {
  var func = '(function () {console.info("' + bowerInfo.description + ' - v' + bowerInfo.version + '"); window["impac"] = {"version": "' + bowerInfo.version + '"};}).call();';

  return fs.writeFileSync(path.join(conf.paths.tmp, '/scripts', '/version.js'), func);
});

<|endoftext|>


<commit_before>const React = require('react');
const FocusTrap = require('focus-trap-react');

const Button = require('./Button');
const Icon = require('./Icon');

const StyleConstants = require('../constants/Style');

const Modal = React.createClass({
  propTypes: {
    buttons: React.PropTypes.arrayOf(React.PropTypes.shape({
      actionText: React.PropTypes.string,
      className: React.PropTypes.string,
      isActive: React.PropTypes.bool,
      icon: React.PropTypes.string,
      label: React.PropTypes.string,
      onClick: React.PropTypes.func,
      primaryColor: React.PropTypes.string,
      style: React.PropTypes.object,
      type: React.PropTypes.oneOf(['primary', 'secondary'])
    })),
    color: React.PropTypes.string,
    contentStyle: React.PropTypes.object,
    footerContent: React.PropTypes.node,
    footerStyle: React.PropTypes.object,
    isRelative: React.PropTypes.bool,
    onRequestClose: React.PropTypes.func,
    showCloseIcon: React.PropTypes.bool,
    showFooter: React.PropTypes.bool,
    showScrim: React.PropTypes.bool,
    showTitleBar: React.PropTypes.bool,
    style: React.PropTypes.object,
    title: React.PropTypes.string,
    tooltip: React.PropTypes.string,
    tooltipLabel: React.PropTypes.string,
    tooltipTitle: React.PropTypes.string
  },

  getDefaultProps () {
    return {
      buttons: [],
      color: StyleConstants.Colors.PRIMARY,
      isRelative: false,
      showCloseIcon: true,
      showFooter: false,
      showScrim: true,
      showTitleBar: false,
      title: '',
      tooltip: null,
      tooltipLabel: '',
      tooltipTitle: null
    };
  },

  getInitialState () {
    return {
      showTooltip: false
    };
  },

  componentDidMount () {
    this._modal.focus();
  /*eslint-disable */
    if (this.props.hasOwnProperty('isOpen')) {
      console.warn('WARNING: The prop "isOpen" is deprecated in this version of the component. Please handle Modal opening from its parent.');
    }
  /*eslint-enable */
  },

  _handleTooltipToggle (show) {
    this.setState({
      showTooltip: show
    });
  },

  _renderTitleBar () {
    if (this.props.showTitleBar) {
      const styles = this.styles();

      return (

          {this.props.title}

      );
    } else {
      return null;
    }
  },

  _renderFooter () {
    if (this.props.showFooter) {
      const styles = this.styles();

      return (

          {this._renderTooltipIconAndLabel()}
          {this._renderFooterContent()}

            {this.props.buttons.map((button, i) => {
              return (
                <Button
                  actionText={button.actionText}
                  className={'mx-modal-button ' + button.className}
                  icon={button.icon}
                  isActive={button.isActive}
                  key={button.type + i}
                  onClick={button.onClick}
                  primaryColor={button.primaryColor}
                  style={Object.assign({}, styles.button, button.style)}
                  type={button.type}
                >
                  {button.label}
                </Button>
              );
            })}

      );
    } else {
      return null;
    }
  },

  _renderFooterContent () {
    const styles = this.styles();

    return (

        {this.props.footerContent}

    );
  },

  _renderTooltip () {
    if (this.state.showTooltip) {
      const styles = this.styles();

      return (

            {this.props.tooltipTitle}

            {this.props.tooltip}

      );
    } else {
      return null;
    }
  },

  _renderTooltipIconAndLabel () {
    if (this.props.tooltip) {
      const styles = this.styles();

      return (

          <Icon
            className='mx-modal-tooltip-label-icon'
            elementProps={{
              onMouseOut: this._handleTooltipToggle.bind(null, false),
              onMouseOver: this._handleTooltipToggle.bind(null, true)
            }}
            size={18}
            style={{ color: this.props.color }}
            type='info'
          />

            {this.props.tooltipLabel}

      );
    } else {
      return null;
    }
  },

  render () {
    const styles = this.styles();

    return (
      <FocusTrap>

 this._modal = ref}
            style={Object.assign({}, styles.container, this.props.style)}
          >
            {this._renderTitleBar()}

              {this.props.children}
              {this._renderTooltip()}

            {this._renderFooter()}
            {this.props.showCloseIcon && (
              <Icon
                className='mx-modal-close'
                elementProps={{
                  tabIndex: 0,
                  'aria-label': 'Close Modal',
                  role: 'button',
                  onClick: this.props.onRequestClose,
                  onKeyUp: (e) => e.keyCode === 13 && this.props.onRequestClose()
                }}
                size={24}
                style={styles.close}
                type='close-solid'
              />
            )}

      </FocusTrap>
    );
  },

  styles () {
    return {
      scrim: {
        zIndex: 1000,
        position: 'fixed',
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
        textAlign: 'center'
      },
      relative: {
        position: 'absolute'
      },
      overlay: {
        backgroundColor: this.props.showScrim ? StyleConstants.Colors.SCRIM : 'transparent'
      },
      close: {
        position: 'absolute',
        top: 0,
        right: 0,
        margin: '-12px -12px 0 0',
        cursor: 'pointer',
        color: StyleConstants.Colors.CHARCOAL
      },
      container: {
        fontFamily: StyleConstants.FontFamily,
        boxSizing: 'border-box',
        position: 'relative',
        zIndex: 1001,
        backgroundColor: StyleConstants.Colors.WHITE,
        boxShadow: StyleConstants.ShadowHigh,
        borderRadius: 2,
        top: 20,
        maxWidth: 'calc(100% - 40px)',
        display: 'inline-block',
        textAlign: 'left'
      },
      titleBar: {
        backgroundColor: StyleConstants.Colors.PORCELAIN,
        borderTopLeftRadius: 2,
        borderTopRightRadius: 2,
        padding: '15px 20px',
        color: StyleConstants.Colors.ASH,
        fontSize: StyleConstants.FontSizes.SMALL,
        textTransform: 'uppercase',
        letterSpacing: 1
      },
      content: {
        position: 'relative',
        maxHeight: 'calc(100% - 140px)',
        overflow: 'auto'
      },
      footer: {
        backgroundColor: StyleConstants.Colors.PORCELAIN,
        borderBottomLeftRadius: 2,
        borderBottomRightRadius: 2,
        padding: '10px 20px',
        display: 'flex',
        justifyContent: 'space-between'
      },
      footerContent: {
        padding: '5px 0',
        textAlign: 'left'
      },
      tooltipLabel: {
        padding: '5px 0'
      },
      tooltipLabelText: {
        fontSize: StyleConstants.FontSizes.SMALL
      },
      tooltip: {
        backgroundColor: StyleConstants.Colors.PORCELAIN,
        borderColor: StyleConstants.Colors.FOG,
        borderStyle: 'solid',
        borderWidth: 1,
        boxSizing: 'border-box',
        bottom: 10,
        left: 10,
        position: 'absolute',
        width: 250,
        maxWidth: '100%',
        padding: 10
      },
      tooltipTitle: {
        fontSize: StyleConstants.FontSizes.MEDIUM,
        marginBottom: 5
      },
      tooltipContent: {
        color: StyleConstants.Colors.ASH,
        fontSize: StyleConstants.FontSizes.SMALL,
        lineHeight: '1.5em',
        textAlign: 'left'
      },
      buttons: {
        textAlign: 'right'
      },
      button: {
        marginLeft: 5
      },
      small: {
        width: 400,
        textAlign: 'center'
      }
    };
  }
});

module.exports = Modal;
<commit_msg>arialabel prop, focus content on mount<commit_after>const React = require('react');
const FocusTrap = require('focus-trap-react');

const Button = require('./Button');
const Icon = require('./Icon');

const StyleConstants = require('../constants/Style');

const Modal = React.createClass({
  propTypes: {
    ariaLabel: React.PropTypes.string,
    buttons: React.PropTypes.arrayOf(React.PropTypes.shape({
      actionText: React.PropTypes.string,
      className: React.PropTypes.string,
      isActive: React.PropTypes.bool,
      icon: React.PropTypes.string,
      label: React.PropTypes.string,
      onClick: React.PropTypes.func,
      primaryColor: React.PropTypes.string,
      style: React.PropTypes.object,
      type: React.PropTypes.oneOf(['primary', 'secondary'])
    })),
    color: React.PropTypes.string,
    contentStyle: React.PropTypes.object,
    footerContent: React.PropTypes.node,
    footerStyle: React.PropTypes.object,
    isRelative: React.PropTypes.bool,
    onRequestClose: React.PropTypes.func,
    showCloseIcon: React.PropTypes.bool,
    showFooter: React.PropTypes.bool,
    showScrim: React.PropTypes.bool,
    showTitleBar: React.PropTypes.bool,
    style: React.PropTypes.object,
    title: React.PropTypes.string,
    tooltip: React.PropTypes.string,
    tooltipLabel: React.PropTypes.string,
    tooltipTitle: React.PropTypes.string
  },

  getDefaultProps () {
    return {
      ariaLabel: '',
      buttons: [],
      color: StyleConstants.Colors.PRIMARY,
      isRelative: false,
      showCloseIcon: true,
      showFooter: false,
      showScrim: true,
      showTitleBar: false,
      title: '',
      tooltip: null,
      tooltipLabel: '',
      tooltipTitle: null
    };
  },

  getInitialState () {
    return {
      showTooltip: false
    };
  },

  componentDidMount () {
    this._modalContent.focus();
  /*eslint-disable */
    if (this.props.hasOwnProperty('isOpen')) {
      console.warn('WARNING: The prop "isOpen" is deprecated in this version of the component. Please handle Modal opening from its parent.');
    }
  /*eslint-enable */
  },

  _handleTooltipToggle (show) {
    this.setState({
      showTooltip: show
    });
  },

  _renderTitleBar () {
    if (this.props.showTitleBar) {
      const styles = this.styles();

      return (

          {this.props.title}

      );
    } else {
      return null;
    }
  },

  _renderFooter () {
    if (this.props.showFooter) {
      const styles = this.styles();

      return (

          {this._renderTooltipIconAndLabel()}
          {this._renderFooterContent()}

            {this.props.buttons.map((button, i) => {
              return (
                <Button
                  actionText={button.actionText}
                  className={'mx-modal-button ' + button.className}
                  icon={button.icon}
                  isActive={button.isActive}
                  key={button.type + i}
                  onClick={button.onClick}
                  primaryColor={button.primaryColor}
                  style={Object.assign({}, styles.button, button.style)}
                  type={button.type}
                >
                  {button.label}
                </Button>
              );
            })}

      );
    } else {
      return null;
    }
  },

  _renderFooterContent () {
    const styles = this.styles();

    return (

        {this.props.footerContent}

    );
  },

  _renderTooltip () {
    if (this.state.showTooltip) {
      const styles = this.styles();

      return (

            {this.props.tooltipTitle}

            {this.props.tooltip}

      );
    } else {
      return null;
    }
  },

  _renderTooltipIconAndLabel () {
    if (this.props.tooltip) {
      const styles = this.styles();

      return (

          <Icon
            className='mx-modal-tooltip-label-icon'
            elementProps={{
              onMouseOut: this._handleTooltipToggle.bind(null, false),
              onMouseOver: this._handleTooltipToggle.bind(null, true)
            }}
            size={18}
            style={{ color: this.props.color }}
            type='info'
          />

            {this.props.tooltipLabel}

      );
    } else {
      return null;
    }
  },

  render () {
    const styles = this.styles();

    return (
      <FocusTrap>

            {this._renderTitleBar()}

 this._modalContent = ref}
              style={Object.assign({}, styles.content, this.props.contentStyle)}
              tabIndex={0}
            >
              {this.props.children}
              {this._renderTooltip()}

            {this._renderFooter()}
            {this.props.showCloseIcon && (
              <Icon
                className='mx-modal-close'
                elementProps={{
                  tabIndex: 0,
                  'aria-label': 'Close Modal',
                  role: 'button',
                  onClick: this.props.onRequestClose,
                  onKeyUp: (e) => e.keyCode === 13 && this.props.onRequestClose()
                }}
                size={24}
                style={styles.close}
                type='close-solid'
              />
            )}

      </FocusTrap>
    );
  },

  styles () {
    return {
      scrim: {
        zIndex: 1000,
        position: 'fixed',
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
        textAlign: 'center'
      },
      relative: {
        position: 'absolute'
      },
      overlay: {
        backgroundColor: this.props.showScrim ? StyleConstants.Colors.SCRIM : 'transparent'
      },
      close: {
        position: 'absolute',
        top: 0,
        right: 0,
        margin: '-12px -12px 0 0',
        cursor: 'pointer',
        color: StyleConstants.Colors.CHARCOAL
      },
      container: {
        fontFamily: StyleConstants.FontFamily,
        boxSizing: 'border-box',
        position: 'relative',
        zIndex: 1001,
        backgroundColor: StyleConstants.Colors.WHITE,
        boxShadow: StyleConstants.ShadowHigh,
        borderRadius: 2,
        top: 20,
        maxWidth: 'calc(100% - 40px)',
        display: 'inline-block',
        textAlign: 'left'
      },
      titleBar: {
        backgroundColor: StyleConstants.Colors.PORCELAIN,
        borderTopLeftRadius: 2,
        borderTopRightRadius: 2,
        padding: '15px 20px',
        color: StyleConstants.Colors.ASH,
        fontSize: StyleConstants.FontSizes.SMALL,
        textTransform: 'uppercase',
        letterSpacing: 1
      },
      content: {
        position: 'relative',
        maxHeight: 'calc(100% - 140px)',
        overflow: 'auto'
      },
      footer: {
        backgroundColor: StyleConstants.Colors.PORCELAIN,
        borderBottomLeftRadius: 2,
        borderBottomRightRadius: 2,
        padding: '10px 20px',
        display: 'flex',
        justifyContent: 'space-between'
      },
      footerContent: {
        padding: '5px 0',
        textAlign: 'left'
      },
      tooltipLabel: {
        padding: '5px 0'
      },
      tooltipLabelText: {
        fontSize: StyleConstants.FontSizes.SMALL
      },
      tooltip: {
        backgroundColor: StyleConstants.Colors.PORCELAIN,
        borderColor: StyleConstants.Colors.FOG,
        borderStyle: 'solid',
        borderWidth: 1,
        boxSizing: 'border-box',
        bottom: 10,
        left: 10,
        position: 'absolute',
        width: 250,
        maxWidth: '100%',
        padding: 10
      },
      tooltipTitle: {
        fontSize: StyleConstants.FontSizes.MEDIUM,
        marginBottom: 5
      },
      tooltipContent: {
        color: StyleConstants.Colors.ASH,
        fontSize: StyleConstants.FontSizes.SMALL,
        lineHeight: '1.5em',
        textAlign: 'left'
      },
      buttons: {
        textAlign: 'right'
      },
      button: {
        marginLeft: 5
      },
      small: {
        width: 400,
        textAlign: 'center'
      }
    };
  }
});

module.exports = Modal;
<|endoftext|>


<commit_before>// CURRENT SETUP FOR TESTING ON STAGING SITE:
// GitHub OAuth app points to http://staging.foodoasis.la/add/
// Gatekeeper hosted at https://jimchat-learntocode.herokuapp.com/

// For testing, we'll use this as the master repo:
var GITHUB_REPO = 'site';
var GITHUB_OWNER = 'foodoasisla';
var GATEKEEPER_URL = 'https://foodoasisla-gatekeeper.herokuapp.com/authenticate/';

// VARIABLES FOR CURRENT USER:
var gitHubAccessToken;
var userName;
var userProfileLink;
var userPhoto;
var userForkedRepoName;
var pullRequestLink;

// VARIABLES FOR GIT COMMIT PROCESS
var pullRequestTitle = "Suggesting a New Healthy Food Location!"; // for testing!
var pullRequestBody = '';
var notesFileSha;
var newCommitSha;

// Elements and user input:
var messageSection = document.getElementById("messageSection");
var loginSection = document.getElementById("loginSection");
var inputSection = document.getElementById("inputSection");
var userNameSpan = document.getElementById("userNameSpan");

// Get the temporary GitHub code from URL params, as in ?code=gitHubTemporaryCodeHere
var gitHubTemporaryCodeArray = window.location.href.match(/\?code=(.*)/);

// If code exists (meaning the user clicked the login button, gave access in GitHub, and was redirected):
if (gitHubTemporaryCodeArray) {
  console.log('gitHubTemporaryCodeArray: ' + gitHubTemporaryCodeArray);
  // Hide login section if user has started the login process
  loginSection.classList.add('hidden');

  // Display loading message
  messageSection.classList.remove('hidden');
  messageSection.innerHTML = "
...Loading...
";

  // Step 1: Authenticate the user with GitHub
  // (Gatekeeper exchanges temporary code for an access token, using the stored client ID and client secret)
  get(GATEKEEPER_URL + gitHubTemporaryCodeArray[1])
  .then(JSON.parse).then(function (authResponse){
    console.log('Authentication response from Gatekeeper:\n');
    console.log(authResponse);

    // Save the access token for later API calls!
    gitHubAccessToken = authResponse.token;

    // Step 2: Fork the base repo
    return postWithToken('https://api.github.com/repos/' + GITHUB_OWNER + '/' + GITHUB_REPO + '/forks', {}, gitHubAccessToken)

  }).then(JSON.parse).then(function (forkResponse){
    console.log('GitHub response after forking the base repo:\n');
    console.log(forkResponse);

    // Hide the "loading" message when done authenticating and forking
    messageSection.classList.add('hidden');

    // Save username and name of newly-forked repo
    userName = forkResponse.owner.login;
    userProfileLink = forkResponse.owner.html_url;
    userPhoto = forkResponse.owner.avatar_url;
    userForkedRepoName = forkResponse.name;

    // Display username
    userNameSpan.textContent = userName;
    // Display inputSection
    inputSection.classList.remove('hidden');

  }).catch(logAndDisplayError);

}

// When user clicks "submit" button, post to GitHub!
document.getElementById('submit').addEventListener('click', submitToGitHub);

function submitToGitHub() {
  // If user hasn't signed in first, notify user to do so before submitting notes!
  if (!gitHubAccessToken) {
  	messageSection.innerHTML = "
Please log in with GitHub first! Then you can submit your suggestion.
";
  	return;	// Exit from this function, skipping the code below
  }

  // Get user input
  var userText = document.getElementById("userText").value;

  // Display loading message
  messageSection.innerHTML = "
...Loading...
";
  messageSection.classList.remove('hidden');

  // Create and format content for new file with user input
  var fileContents = userText + '\r\n';

  // Encode into base64
  fileContents = window.btoa(fileContents);

  var newFileName = '_locations/' + userName + '.md';
  var updateFileData = {"path": newFileName, "message": "Test creaeting file via GitHub API", "content": fileContents};

  // Step 3: Commit to the repo, creating new file
  postWithToken('https://api.github.com/repos/' + userName + '/' + userForkedRepoName + '/contents/' + newFileName, updateFileData, gitHubAccessToken, "PUT")
  .then(JSON.parse).then(function (updateResponse){
    console.log('GitHub response after creating the file:\n');
    console.log(updateResponse);

    // Step 4: Create a new pull request
    var pullRequestData = {"title": pullRequestTitle, "body": pullRequestBody, "base": "master", "head": userName + ":master"};
    return postWithToken('https://api.github.com/repos/' + GITHUB_OWNER + '/' + GITHUB_REPO + '/pulls', pullRequestData, gitHubAccessToken);

  }).then(JSON.parse).then(function (pullResponse){
    console.log('GitHub response after creating the pull request:\n');
    console.log(pullResponse);

    // If a new pull request was successfully created, save its public link
    if (pullResponse.html_url) {
      pullRequestLink = pullResponse.html_url;
    }

    // Step 5: Display success message with link to pull request
    messageSection.classList.remove('hidden');
  	messageSection.innerHTML = '
Thanks! Your suggestion has been submitted!

View your suggestion on GitHub here! Once approved, your suggestion will appear on our map.
';

    // TODO: Prevent "pull request already exists" error?

  }).catch(logAndDisplayError); // Log error to console and display on the web page too

} // end of submitToGitHub function

function logAndDisplayError (errorMessage) {
	console.log(errorMessage);
  messageSection.classList.remove('hidden');
	messageSection.innerHTML = '
' + errorMessage + '
';
}

/* -------------------------------------------------
	HELPER FUNCTIONS
---------------------------------------------------- */

// Returns a promise, as a simple wrapper around XMLHTTPRequest
// via http://eloquentjavascript.net/17_http.html
function get(url) {
  return new Promise(function(succeed, fail) {
    var req = new XMLHttpRequest();
    req.open("GET", url, true);
    req.addEventListener("load", function() {
      if (req.status < 400)
        succeed(req.responseText);
      else
        fail(new Error("Request failed: " + req.statusText));
    });
    req.addEventListener("error", function() {
      fail(new Error("Network error"));
    });
    req.send(null);
  });
}

function getWithCustomHeader(url, customHeader) {
  return new Promise(function(succeed, fail) {
    var req = new XMLHttpRequest();
    req.open("GET", url, true);

    req.setRequestHeader('Accept', 'application/vnd.github.v3.html');

    req.addEventListener("load", function() {
      if (req.status < 400)
        succeed(req.responseText);
      else
        fail(new Error("Request failed: " + req.statusText));
    });
    req.addEventListener("error", function() {
      fail(new Error("Network error"));
    });
    req.send(null);
  });
}

// Returns a promise for a POST request
function postWithToken(url, postDataObject, accessToken, method) {
  return new Promise(function(succeed, fail) {
    var req = new XMLHttpRequest();

    req.open(method || "POST", url, true);

    // Set header for POST, like sending form data
    req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    // Set header for GitHub auth
    req.setRequestHeader('Authorization', 'token ' + accessToken);

    req.addEventListener("load", function() {
      // NOTE: Exception for "A pull request already exists" 422 error!
      if (req.status < 400 || ( req.status == 422 && JSON.parse(req.responseText).errors[0].message.includes("A pull request already exists") ) ) {
        succeed(req.responseText);
      } else {
        fail(new Error("Request failed: " + req.statusText));
      }
    });
    req.addEventListener("error", function() {
      fail(new Error("Network error"));
    });

    req.send(JSON.stringify(postDataObject));
  });
}
<commit_msg>File name and PR title now based on location title<commit_after>// CURRENT SETUP FOR TESTING ON STAGING SITE:
// GitHub OAuth app points to http://staging.foodoasis.la/add/
// Gatekeeper hosted at https://jimchat-learntocode.herokuapp.com/

// For testing, we'll use this as the master repo:
var GITHUB_REPO = 'site';
var GITHUB_OWNER = 'foodoasisla';
var GATEKEEPER_URL = 'https://foodoasisla-gatekeeper.herokuapp.com/authenticate/';

// VARIABLES FOR CURRENT USER:
var gitHubAccessToken;
var userName;
var userProfileLink;
var userPhoto;
var userForkedRepoName;
var pullRequestLink;

// VARIABLES FOR GIT COMMIT PROCESS
var pullRequestTitle = "Suggesting New Location: "; // for testing!
var pullRequestBody = '';
var notesFileSha;
var newCommitSha;

// Elements and user input:
var messageSection = document.getElementById("messageSection");
var loginSection = document.getElementById("loginSection");
var inputSection = document.getElementById("inputSection");
var userNameSpan = document.getElementById("userNameSpan");

// Get the temporary GitHub code from URL params, as in ?code=gitHubTemporaryCodeHere
var gitHubTemporaryCodeArray = window.location.href.match(/\?code=(.*)/);

// If code exists (meaning the user clicked the login button, gave access in GitHub, and was redirected):
if (gitHubTemporaryCodeArray) {
  console.log('gitHubTemporaryCodeArray: ' + gitHubTemporaryCodeArray);
  // Hide login section if user has started the login process
  loginSection.classList.add('hidden');

  // Display loading message
  messageSection.classList.remove('hidden');
  messageSection.innerHTML = "
...Loading...
";

  // Step 1: Authenticate the user with GitHub
  // (Gatekeeper exchanges temporary code for an access token, using the stored client ID and client secret)
  get(GATEKEEPER_URL + gitHubTemporaryCodeArray[1])
  .then(JSON.parse).then(function (authResponse){
    console.log('Authentication response from Gatekeeper:\n');
    console.log(authResponse);

    // Save the access token for later API calls!
    gitHubAccessToken = authResponse.token;

    // Step 2: Fork the base repo
    return postWithToken('https://api.github.com/repos/' + GITHUB_OWNER + '/' + GITHUB_REPO + '/forks', {}, gitHubAccessToken)

  }).then(JSON.parse).then(function (forkResponse){
    console.log('GitHub response after forking the base repo:\n');
    console.log(forkResponse);

    // Hide the "loading" message when done authenticating and forking
    messageSection.classList.add('hidden');

    // Save username and name of newly-forked repo
    userName = forkResponse.owner.login;
    userProfileLink = forkResponse.owner.html_url;
    userPhoto = forkResponse.owner.avatar_url;
    userForkedRepoName = forkResponse.name;

    // Display username
    userNameSpan.textContent = userName;
    // Display inputSection
    inputSection.classList.remove('hidden');

  }).catch(logAndDisplayError);

}

// When user clicks "submit" button, post to GitHub!
document.getElementById('submit').addEventListener('click', submitToGitHub);

function submitToGitHub() {
  // If user hasn't signed in first, notify user to do so before submitting notes!
  if (!gitHubAccessToken) {
  	messageSection.innerHTML = "
Please log in with GitHub first! Then you can submit your suggestion.
";
  	return;	// Exit from this function, skipping the code below
  }

  // Get user input
  var userText = document.getElementById("userText").value;
  var locationTitle = document.getElementById("locationTitle").value;

  pullRequestTitle += locationTitle;

  // Convert to safe (well, safe ENOUGH for now) file name.
  // via https://stackoverflow.com/questions/8485027/javascript-url-safe-filename-safe-string
  var newFileName = '_locations/' + locationTitle.replace(/[^a-z0-9]/gi, '-').toLowerCase() + '.md';

  // Display loading message
  messageSection.innerHTML = "
...Loading...
";
  messageSection.classList.remove('hidden');

  // Create and format content for new file with user input
  var fileContents = userText + '\r\n';

  // Encode into base64
  fileContents = window.btoa(fileContents);

  var updateFileData = {"path": newFileName, "message": "Test creaeting file via GitHub API", "content": fileContents};

  // Step 3: Commit to the repo, creating new file
  postWithToken('https://api.github.com/repos/' + userName + '/' + userForkedRepoName + '/contents/' + newFileName, updateFileData, gitHubAccessToken, "PUT")
  .then(JSON.parse).then(function (updateResponse){
    console.log('GitHub response after creating the file:\n');
    console.log(updateResponse);

    // Step 4: Create a new pull request
    var pullRequestData = {"title": pullRequestTitle, "body": pullRequestBody, "base": "master", "head": userName + ":master"};
    return postWithToken('https://api.github.com/repos/' + GITHUB_OWNER + '/' + GITHUB_REPO + '/pulls', pullRequestData, gitHubAccessToken);

  }).then(JSON.parse).then(function (pullResponse){
    console.log('GitHub response after creating the pull request:\n');
    console.log(pullResponse);

    // If a new pull request was successfully created, save its public link
    if (pullResponse.html_url) {
      pullRequestLink = pullResponse.html_url;
    }

    // Step 5: Display success message with link to pull request
    messageSection.classList.remove('hidden');
  	messageSection.innerHTML = '
Thanks! Your suggestion has been submitted!

View your suggestion on GitHub here! Once approved, your suggestion will appear on our map.
';

    // TODO: Prevent "pull request already exists" error?

  }).catch(logAndDisplayError); // Log error to console and display on the web page too

} // end of submitToGitHub function

function logAndDisplayError (errorMessage) {
	console.log(errorMessage);
  messageSection.classList.remove('hidden');
	messageSection.innerHTML = '
' + errorMessage + '
';
}

/* -------------------------------------------------
	HELPER FUNCTIONS
---------------------------------------------------- */

// Returns a promise, as a simple wrapper around XMLHTTPRequest
// via http://eloquentjavascript.net/17_http.html
function get(url) {
  return new Promise(function(succeed, fail) {
    var req = new XMLHttpRequest();
    req.open("GET", url, true);
    req.addEventListener("load", function() {
      if (req.status < 400)
        succeed(req.responseText);
      else
        fail(new Error("Request failed: " + req.statusText));
    });
    req.addEventListener("error", function() {
      fail(new Error("Network error"));
    });
    req.send(null);
  });
}

function getWithCustomHeader(url, customHeader) {
  return new Promise(function(succeed, fail) {
    var req = new XMLHttpRequest();
    req.open("GET", url, true);

    req.setRequestHeader('Accept', 'application/vnd.github.v3.html');

    req.addEventListener("load", function() {
      if (req.status < 400)
        succeed(req.responseText);
      else
        fail(new Error("Request failed: " + req.statusText));
    });
    req.addEventListener("error", function() {
      fail(new Error("Network error"));
    });
    req.send(null);
  });
}

// Returns a promise for a POST request
function postWithToken(url, postDataObject, accessToken, method) {
  return new Promise(function(succeed, fail) {
    var req = new XMLHttpRequest();

    req.open(method || "POST", url, true);

    // Set header for POST, like sending form data
    req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    // Set header for GitHub auth
    req.setRequestHeader('Authorization', 'token ' + accessToken);

    req.addEventListener("load", function() {
      // NOTE: Exception for "A pull request already exists" 422 error!
      if (req.status < 400 || ( req.status == 422 && JSON.parse(req.responseText).errors[0].message.includes("A pull request already exists") ) ) {
        succeed(req.responseText);
      } else {
        fail(new Error("Request failed: " + req.statusText));
      }
    });
    req.addEventListener("error", function() {
      fail(new Error("Network error"));
    });

    req.send(JSON.stringify(postDataObject));
  });
}
<|endoftext|>