<commit_before>// Popcorn Youtube Player Wrapper

var onYouTubePlayerReady;

( function( Popcorn ) {
  /**
   * Youtube wrapper for popcorn.
   * This plug-in adds capability for Popcorn.js to deal with Youtube
   * videos. This plug-in also doesn't use Popcorn's plugin() API and
   * instead hacks directly into Popcorn's core.
   *
   * To use this plug-in, onYouTubePlayerReady() event handler needs to be
   * called by the Youtube video player, before videos can be registered.
   * Once videos are registered, calls to them can be made the same way as
   * regular Popcorn objects. Also note that enablejsapi=1 needs to be added
   * to the embed code, in order for Youtube's JavaScript API to work.
   *
   * Note that there are a few methods, properties and events that are not
   * supported. See the bottom of this plug-in for a complete list.
   */

  // Intended
  var undef;

  // Config parameters
  // 33 ms per update is suitable for 30 fps
  // 0.05 sec tolerance between old and new times to determine if currentTime has been set programatically
  // 250 ms progress interval as specified by WHATWG
  var timeupdateInterval = 33,
      timeCheckInterval = 0.5,
      progressInterval = 250;

  // Ready State Constants
  var READY_STATE_HAVE_NOTHING = 0,
      READY_STATE_HAVE_METADATA = 1,
      READY_STATE_HAVE_CURRENT_DATA = 2,
      READY_STATE_HAVE_FUTURE_DATA = 3,
      READY_STATE_HAVE_ENOUGH_DATA = 4;

  // Youtube State Constants
  var YOUTUBE_STATE_UNSTARTED = -1,
      YOUTUBE_STATE_ENDED = 0,
      YOUTUBE_STATE_PLAYING = 1,
      YOUTUBE_STATE_PAUSED = 2,
      YOUTUBE_STATE_BUFFERING = 3,
      YOUTUBE_STATE_CUED = 5;

  var urlRegex = /^.*[\/=](.{11})/;

  // Collection of all Youtube players
  var registry = {},
      loadedPlayers = {};

  var abs = Math.abs;

  Popcorn.getScript( "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js" );

  // Extract the id from a web url
  function extractIdFromUrl( url ) {
    if ( !url ) {
      return;
    }

    var matches = urlRegex.exec( url );

    // Return id, which comes after first equals sign
    return matches ? matches[1] : "";
  }

  // Extract the id from a player url
  function extractIdFromUri( url ) {
    if ( !url ) {
      return;
    }

    var matches = urlRegex.exec( url );

    // Return id, which comes after first equals sign
    return matches ? matches[1] : "";
  }

  function getPlayerAddress( vidId, playerId ) {
    if( !vidId ) {
      return;
    }

    return "http://www.youtube.com/e/" + id;
  }

  function makeSWF( url, container ) {
    var bounds,
        params,
        flashvars,
        attributes,
        self = this;

    if ( !window.swfobject ) {
      setTimeout( function() {
        makeSWF.call( self, url, container );
      }, 1 );
      return;
    }

    bounds = container.getBoundingClientRect();

    // Determine width/height/etc based on container
    this.width = container.style.width || 460;
    this.height = container.style.height || 350;

    // Just in case we got the attributes as strings. We'll need to do math with these later
    this.width = parseFloat(this.width);
    this.height = parseFloat(this.height);

    // For calculating position relative to video (like subtitles)
    this.offsetWidth = this.width;
    this.offsetHeight = this.height;
    this.offsetLeft = bounds.left;
    this.offsetTop = bounds.top;

    this.offsetParent = container.offsetParent;

    flashvars = {
      playerapiid: this.playerId
    };
    params = {
      allowscriptaccess: 'always',
      allowfullscreen: 'true',
      // This is so we can overlay html on top of Flash
      wmode: 'transparent'
    };

    attributes = {
      id: this.playerId
    };

    swfobject.embedSWF( "http://www.youtube.com/e/" + this.vidId +"?enablejsapi=1&playerapiid=" + this.playerId + "&version=3",
                      this.playerId, this.width, this.height, "8", null, flashvars, params, attributes );
  }

  // Called when a player is loaded
  // Playerid must match the element id
  onYouTubePlayerReady = function ( playerId ) {
    var vid = registry[playerId];

    loadedPlayers[playerId] = 1;

    // Video hadn't loaded yet when ctor was called
    vid.video = document.getElementById( playerId );
    vid.duration = vid.video.getDuration();

    // Issue load event
    vid.dispatchEvent( 'load' );
    vid.dispatchEvent( "durationchange" );
  };

  Popcorn.youtube = function( elementId, url, options ) {
    return new Popcorn.youtube.init( elementId, url, options );
  };

  Popcorn.youtube.init = function( elementId, url, options ) {
    if ( !elementId ) {
      throw "Element id is invalid.";
    } else if ( /file/.test( location.protocol ) ) {
      throw "This must be run from a web server.";
    }

    options = options || {};

    var self = this;

    this.playerId = elementId + Popcorn.guid();
    this.readyState = READY_STATE_HAVE_NOTHING;
    this._eventListeners = {};
    this.loadStarted = false;
    this.loadedData = false;
    this.fullyLoaded = false;

    // If supplied as number, append  'px' on end
    // If suppliied as '###' or '###px', convert to number and append 'px' back on end
    options.width = options.width && (+options.width)+"px";
    options.height = options.height && (+options.height)+"px";

    this._target = document.getElementById( elementId );
    this._container = document.createElement( "div" );
    this._container.id = this.playerId;
    this._container.style.height = this._target.style.height = options.height || this._target.style.height || "350px";
    this._container.style.width  = this._target.style.width  = options.width || this._target.style.width  || "460px";
    this._target.appendChild( this._container );

    this.offsetHeight = +this._target.offsetHeight;
    this.offsetWidth = +this._target.offsetWidth;

    this.currentTime = this.previousCurrentTime = 0;
    this.volume = this.previousVolume = this.preMuteVol = 1;
    this.duration = 0;

    this.vidId = extractIdFromUrl( url ) || extractIdFromUri( url );

    if ( !this.vidId ) {
      throw "Could not find video id";
    }

    this.addEventListener( "load", function() {
      // For calculating position relative to video (like subtitles)
      this.offsetWidth = this.video.offsetWidth;
      this.offsetHeight = this.video.offsetHeight;
      this.offsetParent = this.video.offsetParent;

      // Set up stuff that requires the API to be loaded
      this.registerYoutubeEventHandlers();
      this.registerInternalEventHandlers();
    });

    (function() {
      var hasBeenCalled = 0;

      self.addEventListener( "playing", function() {
        if (hasBeenCalled) {
          return;
        }

        hasBeenCalled = 1;
        self.duration = self.video.getDuration();
        self.dispatchEvent( "durationchange" );

      });
    })();

    if ( loadedPlayers[this.playerId] ) {
      this.video = registry[this.playerId].video;

      this.vidId = this.vidId || extractIdFromUrl( this._container.getAttribute( "src" ) ) || extractIdFromUri( this._container.getAttribute( "src" ) );

      if (this.vidId !== registry[this.playerId].vidId ) {
        this.video.cueVideoById( this.vidId );
      } else {
        // Same video, new ctor. Force a seek to the beginning
        this.previousCurrentTime = 1;
      }

      this.dispatchEvent( 'load' );
    } else if ( this._container ) {
      makeSWF.call( this, url, this._container );
    } else {
      // Container not yet loaded, get it on DOMDontentLoad
      document.addEventListener( "DOMContentLoaded", function() {
        self._container = document.getElementById( elementId );

        if ( !self._container ) {
          throw "Could not find container!";
        }

        makeSWF.call( self, url, self._container );
      }, false);
    }

    registry[this.playerId] = this;
  };
  // end Popcorn.youtube.init

  Popcorn.extend( Popcorn.youtube.init.prototype, {

    // For internal use only.
    // Register handlers to YouTube events.
    registerYoutubeEventHandlers: function() {
      var youcorn = this,
          stateChangeHandler = 'Popcorn.youtube.stateChangeEventHandler',
          errorHandler = 'Popcorn.youtube.errorEventHandler';

      this.video.addEventListener( 'onStateChange', stateChangeHandler );
      this.video.addEventListener( 'onError', errorHandler );

      /**
       * Since Flash can only call named functions, they are declared
       * separately here.
       */
      Popcorn.youtube.stateChangeEventHandler = function( state ) {
        // In case ctor has been called many times for many ctors
        // Only use latest ctor call for each player id
        var self = registry[youcorn.playerId];

        if ( state === YOUTUBE_STATE_UNSTARTED ) {
          self.readyState = READY_STATE_HAVE_METADATA;
          self.dispatchEvent( 'loadedmetadata' );
        } else if ( state === YOUTUBE_STATE_ENDED ) {
          self.dispatchEvent( 'ended' );
        } else if ( state === YOUTUBE_STATE_PLAYING ) {
          // Being able to play means current data is loaded.
          if ( !this.loadedData ) {
            this.loadedData = true;
            self.dispatchEvent( 'loadeddata' );
          }

          self.readyState = READY_STATE_HAVE_CURRENT_DATA;
          self.dispatchEvent( 'playing' );
        } else if ( state === YOUTUBE_STATE_PAUSED ) {
          self.dispatchEvent( 'pause' );
        } else if ( state === YOUTUBE_STATE_BUFFERING ) {
          self.dispatchEvent( 'waiting' );
        } else if ( state === YOUTUBE_STATE_CUED ) {
          // not handled
        }
      };

      Popcorn.youtube.errorEventHandler = function( state ) {
        youcorn.dispatchEvent( 'error' );
      };
    },

    // For internal use only.
    // Start current time and loading progress syncing intervals.
    registerInternalEventHandlers: function() {
      this.addEventListener( 'playing', function() {
        this.startTimeUpdater();
      });
      this.addEventListener( 'loadedmetadata', function() {
        this.startProgressUpdater();
      });
    },

    play: function() {
      // In case called before video is loaded, defer acting
      if ( !loadedPlayers[this.playerId] ) {
        this.addEventListener( "load", function() {
          this.play();
        });
        return;
      }

      this.dispatchEvent( 'play' );
      this.video.playVideo();
    },

    pause: function() {
      // In case called before video is loaded, defer acting
      if ( !loadedPlayers[this.playerId] ) {
        this.addEventListener( "load", this.pause );
        return;
      }

      this.video.pauseVideo();
      // pause event is raised by Youtube.
    },

    load: function() {
      // In case called before video is loaded, defer acting
      if ( !loadedPlayers[this.playerId] ) {
        this.addEventListener( "load", function() {
          this.load();
        });
        return;
      }

      this.video.playVideo();
      this.video.pauseVideo();
    },

    seekTo: function( time ) {
      var playing = this.video.getPlayerState() == YOUTUBE_STATE_PLAYING;
      this.video.seekTo( time, true );

      // Prevent Youtube's behaviour to start playing video after seeking.
      if ( !playing ) {
        this.video.pauseVideo();
      }

      // Data need to be loaded again.
      if ( !this.fullyLoaded ) {
        this.loadedData = false;
      }

      // Raise event.
      this.dispatchEvent( 'seeked' );
    },

    // Mute is toggleable
    mute: function() {
      // In case called before video is loaded, defer acting
      if ( !loadedPlayers[this.playerId] ) {
        this.addEventListener( "load", this.mute );
        return;
      }

      if ( this.volume !== 0 ) {
        this.preMuteVol = this.volume;
        this.setVolume( 0 );
      } else {
        this.setVolume( this.preMuteVol );
      }
    },

    // Expects beteween 0 and 1
    setVolume: function( vol ) {
      this.volume = this.previousVolume = vol;
      this.video.setVolume( vol * 100 );
      this.dispatchEvent( 'volumechange' );
    },

    addEventListener: function( evt, func ) {
      var evtName = evt.type || evt;

      if ( !this._eventListeners[evtName] ) {
        this._eventListeners[evtName] = [];
      }

      this._eventListeners[evtName].push( func );
    },

    /**
     * Notify event listeners about an event.
     */
    dispatchEvent: function( name ) {
      var evtName = name.type || name;
      if ( !this._eventListeners[evtName] ) {
        return;
      }

      var self = this;

      Popcorn.forEach( this._eventListeners[evtName], function( evt ) {
        evt.call( self, null );
      });
    },

    /* Unsupported methods. */

    defaultPlaybackRate: function( arg ) {
    },

    playbackRate: function( arg ) {
    },

    getBoundingClientRect: function() {
      var b,
          self = this;

      if ( this.video ) {
        b = this.video.getBoundingClientRect();

        return {
          bottom: b.bottom,
          left: b.left,
          right: b.right,
          top: b.top,

          //  These not guaranteed to be in there
          width: b.width || ( b.right - b.left ),
          height: b.height || ( b.bottom - b.top )
        };
      } else {
        b = self._container.getBoundingClientRect();

        // Update bottom, right for expected values once the container loads
        return {
          left: b.left,
          top: b.top,
          width: self._target.offsetWidth,
          height: self._target.offsetHeight,
          bottom: b.top + self._target.offsetWidth,
          right: b.left + self._target.offsetHeight
        };
      }
    },

    startTimeUpdater: function() {
      var state = this.video.getPlayerState(),
          self = this,
          seeked = 0;

      if ( abs( this.currentTime - this.previousCurrentTime ) > timeCheckInterval ) {
        // Has programatically set the currentTime
        this.previousCurrentTime = this.currentTime - timeCheckInterval;
        this.seekTo( this.currentTime );
        seeked = 1;
      } else {
        this.previousCurrentTime = this.currentTime;
        this.currentTime = this.video.getCurrentTime();
      }

      if ( this.volume !== this.previousVolume ) {
        this.setVolume( this.volume );
      }

      if ( state !== YOUTUBE_STATE_ENDED && state !== YOUTUBE_STATE_PAUSED || seeked ) {
        this.dispatchEvent( 'timeupdate' );
      }

      if( state !== YOUTUBE_STATE_ENDED ) {
        setTimeout( function() {
          self.startTimeUpdater.call(self);
        }, timeupdateInterval);
      }
    },

    startProgressUpdater: function() {
      var bytesLoaded = this.video.getVideoBytesLoaded(),
          bytesToLoad = this.video.getVideoBytesTotal(),
          self = this;

      // do nothing if size is not yet determined
      if ( bytesToLoad === 0 ) {
        return;
      }

      // raise an event if load has just started
      if ( !this.loadStarted ) {
        this.loadStarted = true;
        this.dispatchEvent( 'loadstart' );
      }

      // fully loaded
      if ( bytesLoaded >= bytesToLoad ) {
        this.fullyLoaded = true;
        this.readyState = READY_STATE_HAVE_ENOUGH_DATA;
        this.dispatchEvent( 'canplaythrough' );
        return;
      }

      this.dispatchEvent( 'progress' );

      setTimeout( function() {
        self.startProgressUpdater.call( self );
      }, progressInterval);
    }
  }); // end Popcorn.extend

  /* Unsupported properties and events. */

  /**
   * Unsupported events are:
   * * suspend
   * * abort
   * * emptied
   * * stalled
   * * canplay
   * * seeking
   * * ratechange
   */

})( Popcorn );

<commit_msg>Added flag that is used in butter to track whether the state of the video is paused or not<commit_after>// Popcorn Youtube Player Wrapper

var onYouTubePlayerReady;

( function( Popcorn ) {
  /**
   * Youtube wrapper for popcorn.
   * This plug-in adds capability for Popcorn.js to deal with Youtube
   * videos. This plug-in also doesn't use Popcorn's plugin() API and
   * instead hacks directly into Popcorn's core.
   *
   * To use this plug-in, onYouTubePlayerReady() event handler needs to be
   * called by the Youtube video player, before videos can be registered.
   * Once videos are registered, calls to them can be made the same way as
   * regular Popcorn objects. Also note that enablejsapi=1 needs to be added
   * to the embed code, in order for Youtube's JavaScript API to work.
   *
   * Note that there are a few methods, properties and events that are not
   * supported. See the bottom of this plug-in for a complete list.
   */

  // Intended
  var undef;

  // Config parameters
  // 33 ms per update is suitable for 30 fps
  // 0.05 sec tolerance between old and new times to determine if currentTime has been set programatically
  // 250 ms progress interval as specified by WHATWG
  var timeupdateInterval = 33,
      timeCheckInterval = 0.5,
      progressInterval = 250;

  // Ready State Constants
  var READY_STATE_HAVE_NOTHING = 0,
      READY_STATE_HAVE_METADATA = 1,
      READY_STATE_HAVE_CURRENT_DATA = 2,
      READY_STATE_HAVE_FUTURE_DATA = 3,
      READY_STATE_HAVE_ENOUGH_DATA = 4;

  // Youtube State Constants
  var YOUTUBE_STATE_UNSTARTED = -1,
      YOUTUBE_STATE_ENDED = 0,
      YOUTUBE_STATE_PLAYING = 1,
      YOUTUBE_STATE_PAUSED = 2,
      YOUTUBE_STATE_BUFFERING = 3,
      YOUTUBE_STATE_CUED = 5;

  var urlRegex = /^.*[\/=](.{11})/;

  // Collection of all Youtube players
  var registry = {},
      loadedPlayers = {};

  var abs = Math.abs;

  Popcorn.getScript( "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js" );

  // Extract the id from a web url
  function extractIdFromUrl( url ) {
    if ( !url ) {
      return;
    }

    var matches = urlRegex.exec( url );

    // Return id, which comes after first equals sign
    return matches ? matches[1] : "";
  }

  // Extract the id from a player url
  function extractIdFromUri( url ) {
    if ( !url ) {
      return;
    }

    var matches = urlRegex.exec( url );

    // Return id, which comes after first equals sign
    return matches ? matches[1] : "";
  }

  function getPlayerAddress( vidId, playerId ) {
    if( !vidId ) {
      return;
    }

    return "http://www.youtube.com/e/" + id;
  }

  function makeSWF( url, container ) {
    var bounds,
        params,
        flashvars,
        attributes,
        self = this;

    if ( !window.swfobject ) {
      setTimeout( function() {
        makeSWF.call( self, url, container );
      }, 1 );
      return;
    }

    bounds = container.getBoundingClientRect();

    // Determine width/height/etc based on container
    this.width = container.style.width || 460;
    this.height = container.style.height || 350;

    // Just in case we got the attributes as strings. We'll need to do math with these later
    this.width = parseFloat(this.width);
    this.height = parseFloat(this.height);

    // For calculating position relative to video (like subtitles)
    this.offsetWidth = this.width;
    this.offsetHeight = this.height;
    this.offsetLeft = bounds.left;
    this.offsetTop = bounds.top;

    this.offsetParent = container.offsetParent;

    flashvars = {
      playerapiid: this.playerId
    };
    params = {
      allowscriptaccess: 'always',
      allowfullscreen: 'true',
      // This is so we can overlay html on top of Flash
      wmode: 'transparent'
    };

    attributes = {
      id: this.playerId
    };

    swfobject.embedSWF( "http://www.youtube.com/e/" + this.vidId +"?enablejsapi=1&playerapiid=" + this.playerId + "&version=3",
                      this.playerId, this.width, this.height, "8", null, flashvars, params, attributes );
  }

  // Called when a player is loaded
  // Playerid must match the element id
  onYouTubePlayerReady = function ( playerId ) {
    var vid = registry[playerId];

    loadedPlayers[playerId] = 1;

    // Video hadn't loaded yet when ctor was called
    vid.video = document.getElementById( playerId );
    vid.duration = vid.video.getDuration();

    // Issue load event
    vid.dispatchEvent( 'load' );
    vid.dispatchEvent( "durationchange" );
  };

  Popcorn.youtube = function( elementId, url, options ) {
    return new Popcorn.youtube.init( elementId, url, options );
  };

  Popcorn.youtube.init = function( elementId, url, options ) {
    if ( !elementId ) {
      throw "Element id is invalid.";
    } else if ( /file/.test( location.protocol ) ) {
      throw "This must be run from a web server.";
    }

    options = options || {};

    var self = this;

    this.playerId = elementId + Popcorn.guid();
    this.readyState = READY_STATE_HAVE_NOTHING;
    this._eventListeners = {};
    this.loadStarted = false;
    this.loadedData = false;
    this.fullyLoaded = false;
    this.paused = true;

    // If supplied as number, append  'px' on end
    // If suppliied as '###' or '###px', convert to number and append 'px' back on end
    options.width = options.width && (+options.width)+"px";
    options.height = options.height && (+options.height)+"px";

    this._target = document.getElementById( elementId );
    this._container = document.createElement( "div" );
    this._container.id = this.playerId;
    this._container.style.height = this._target.style.height = options.height || this._target.style.height || "350px";
    this._container.style.width  = this._target.style.width  = options.width || this._target.style.width  || "460px";
    this._target.appendChild( this._container );

    this.offsetHeight = +this._target.offsetHeight;
    this.offsetWidth = +this._target.offsetWidth;

    this.currentTime = this.previousCurrentTime = 0;
    this.volume = this.previousVolume = this.preMuteVol = 1;
    this.duration = 0;

    this.vidId = extractIdFromUrl( url ) || extractIdFromUri( url );

    if ( !this.vidId ) {
      throw "Could not find video id";
    }

    this.addEventListener( "load", function() {
      // For calculating position relative to video (like subtitles)
      this.offsetWidth = this.video.offsetWidth;
      this.offsetHeight = this.video.offsetHeight;
      this.offsetParent = this.video.offsetParent;

      // Set up stuff that requires the API to be loaded
      this.registerYoutubeEventHandlers();
      this.registerInternalEventHandlers();
    });

    (function() {
      var hasBeenCalled = 0;

      self.addEventListener( "playing", function() {
        if (hasBeenCalled) {
          return;
        }

        hasBeenCalled = 1;
        self.duration = self.video.getDuration();
        self.dispatchEvent( "durationchange" );

      });
    })();

    if ( loadedPlayers[this.playerId] ) {
      this.video = registry[this.playerId].video;

      this.vidId = this.vidId || extractIdFromUrl( this._container.getAttribute( "src" ) ) || extractIdFromUri( this._container.getAttribute( "src" ) );

      if (this.vidId !== registry[this.playerId].vidId ) {
        this.video.cueVideoById( this.vidId );
      } else {
        // Same video, new ctor. Force a seek to the beginning
        this.previousCurrentTime = 1;
      }

      this.dispatchEvent( 'load' );
    } else if ( this._container ) {
      makeSWF.call( this, url, this._container );
    } else {
      // Container not yet loaded, get it on DOMDontentLoad
      document.addEventListener( "DOMContentLoaded", function() {
        self._container = document.getElementById( elementId );

        if ( !self._container ) {
          throw "Could not find container!";
        }

        makeSWF.call( self, url, self._container );
      }, false);
    }

    registry[this.playerId] = this;
  };
  // end Popcorn.youtube.init

  Popcorn.extend( Popcorn.youtube.init.prototype, {

    // For internal use only.
    // Register handlers to YouTube events.
    registerYoutubeEventHandlers: function() {
      var youcorn = this,
          stateChangeHandler = 'Popcorn.youtube.stateChangeEventHandler',
          errorHandler = 'Popcorn.youtube.errorEventHandler';

      this.video.addEventListener( 'onStateChange', stateChangeHandler );
      this.video.addEventListener( 'onError', errorHandler );

      /**
       * Since Flash can only call named functions, they are declared
       * separately here.
       */
      Popcorn.youtube.stateChangeEventHandler = function( state ) {
        // In case ctor has been called many times for many ctors
        // Only use latest ctor call for each player id
        var self = registry[youcorn.playerId];

        if ( state === YOUTUBE_STATE_UNSTARTED ) {
          self.readyState = READY_STATE_HAVE_METADATA;
          self.dispatchEvent( 'loadedmetadata' );
        } else if ( state === YOUTUBE_STATE_ENDED ) {
          self.dispatchEvent( 'ended' );
        } else if ( state === YOUTUBE_STATE_PLAYING ) {
          // Being able to play means current data is loaded.
          if ( !this.loadedData ) {
            this.loadedData = true;
            self.dispatchEvent( 'loadeddata' );
          }

          self.readyState = READY_STATE_HAVE_CURRENT_DATA;
          self.dispatchEvent( 'playing' );
        } else if ( state === YOUTUBE_STATE_PAUSED ) {
          self.dispatchEvent( 'pause' );
        } else if ( state === YOUTUBE_STATE_BUFFERING ) {
          self.dispatchEvent( 'waiting' );
        } else if ( state === YOUTUBE_STATE_CUED ) {
          // not handled
        }
      };

      Popcorn.youtube.errorEventHandler = function( state ) {
        youcorn.dispatchEvent( 'error' );
      };
    },

    // For internal use only.
    // Start current time and loading progress syncing intervals.
    registerInternalEventHandlers: function() {
      this.addEventListener( 'playing', function() {
        this.startTimeUpdater();
      });
      this.addEventListener( 'loadedmetadata', function() {
        this.startProgressUpdater();
      });
    },

    play: function() {
      // In case called before video is loaded, defer acting
      if ( !loadedPlayers[this.playerId] ) {
        this.addEventListener( "load", function() {
          this.play();
        });
        return;
      }

      this.dispatchEvent( 'play' );
      this.video.playVideo();
    },

    pause: function() {
      // In case called before video is loaded, defer acting
      if ( !loadedPlayers[this.playerId] ) {
        this.addEventListener( "load", this.pause );
        return;
      }

      this.video.pauseVideo();
      // pause event is raised by Youtube.
    },

    load: function() {
      // In case called before video is loaded, defer acting
      if ( !loadedPlayers[this.playerId] ) {
        this.addEventListener( "load", function() {
          this.load();
        });
        return;
      }

      this.video.playVideo();
      this.video.pauseVideo();
    },

    seekTo: function( time ) {
      var playing = this.video.getPlayerState() == YOUTUBE_STATE_PLAYING;
      this.video.seekTo( time, true );

      // Prevent Youtube's behaviour to start playing video after seeking.
      if ( !playing ) {
        this.video.paused = true;
        this.video.pauseVideo();
      } else {
        this.video.paused = false;
      }

      // Data need to be loaded again.
      if ( !this.fullyLoaded ) {
        this.loadedData = false;
      }

      // Raise event.
      this.dispatchEvent( 'seeked' );
    },

    // Mute is toggleable
    mute: function() {
      // In case called before video is loaded, defer acting
      if ( !loadedPlayers[this.playerId] ) {
        this.addEventListener( "load", this.mute );
        return;
      }

      if ( this.volume !== 0 ) {
        this.preMuteVol = this.volume;
        this.setVolume( 0 );
      } else {
        this.setVolume( this.preMuteVol );
      }
    },

    // Expects beteween 0 and 1
    setVolume: function( vol ) {
      this.volume = this.previousVolume = vol;
      this.video.setVolume( vol * 100 );
      this.dispatchEvent( 'volumechange' );
    },

    addEventListener: function( evt, func ) {
      var evtName = evt.type || evt;

      if ( !this._eventListeners[evtName] ) {
        this._eventListeners[evtName] = [];
      }

      this._eventListeners[evtName].push( func );
    },

    /**
     * Notify event listeners about an event.
     */
    dispatchEvent: function( name ) {
      var evtName = name.type || name;
      if ( !this._eventListeners[evtName] ) {
        return;
      }

      var self = this;

      Popcorn.forEach( this._eventListeners[evtName], function( evt ) {
        evt.call( self, null );
      });
    },

    /* Unsupported methods. */

    defaultPlaybackRate: function( arg ) {
    },

    playbackRate: function( arg ) {
    },

    getBoundingClientRect: function() {
      var b,
          self = this;

      if ( this.video ) {
        b = this.video.getBoundingClientRect();

        return {
          bottom: b.bottom,
          left: b.left,
          right: b.right,
          top: b.top,

          //  These not guaranteed to be in there
          width: b.width || ( b.right - b.left ),
          height: b.height || ( b.bottom - b.top )
        };
      } else {
        b = self._container.getBoundingClientRect();

        // Update bottom, right for expected values once the container loads
        return {
          left: b.left,
          top: b.top,
          width: self._target.offsetWidth,
          height: self._target.offsetHeight,
          bottom: b.top + self._target.offsetWidth,
          right: b.left + self._target.offsetHeight
        };
      }
    },

    startTimeUpdater: function() {
      var state = this.video.getPlayerState(),
          self = this,
          seeked = 0;

      if ( abs( this.currentTime - this.previousCurrentTime ) > timeCheckInterval ) {
        // Has programatically set the currentTime
        this.previousCurrentTime = this.currentTime - timeCheckInterval;
        this.seekTo( this.currentTime );
        seeked = 1;
      } else {
        this.previousCurrentTime = this.currentTime;
        this.currentTime = this.video.getCurrentTime();
      }

      if ( this.volume !== this.previousVolume ) {
        this.setVolume( this.volume );
      }

      if ( state !== YOUTUBE_STATE_ENDED && state !== YOUTUBE_STATE_PAUSED || seeked ) {
        this.dispatchEvent( 'timeupdate' );
      }

      if( state !== YOUTUBE_STATE_ENDED ) {
        setTimeout( function() {
          self.startTimeUpdater.call(self);
        }, timeupdateInterval);
      }
    },

    startProgressUpdater: function() {
      var bytesLoaded = this.video.getVideoBytesLoaded(),
          bytesToLoad = this.video.getVideoBytesTotal(),
          self = this;

      // do nothing if size is not yet determined
      if ( bytesToLoad === 0 ) {
        return;
      }

      // raise an event if load has just started
      if ( !this.loadStarted ) {
        this.loadStarted = true;
        this.dispatchEvent( 'loadstart' );
      }

      // fully loaded
      if ( bytesLoaded >= bytesToLoad ) {
        this.fullyLoaded = true;
        this.readyState = READY_STATE_HAVE_ENOUGH_DATA;
        this.dispatchEvent( 'canplaythrough' );
        return;
      }

      this.dispatchEvent( 'progress' );

      setTimeout( function() {
        self.startProgressUpdater.call( self );
      }, progressInterval);
    }
  }); // end Popcorn.extend

  /* Unsupported properties and events. */

  /**
   * Unsupported events are:
   * * suspend
   * * abort
   * * emptied
   * * stalled
   * * canplay
   * * seeking
   * * ratechange
   */

})( Popcorn );

<|endoftext|>


<commit_before>const inquirer = require('inquirer')
const CanvasApi = require('kth-canvas-api')
process.env['NODE_ENV'] = 'production'
const csvFile = require('../csvFile')

async function createFile () {
  const {apiUrl} = await inquirer.prompt(
    {
      message: 'Vilken miljö?',
      name: 'apiUrl',
      choices: [
        {name: 'test', value: 'https://kth.test.instructure.com/api/v1'},
        {name: 'prod', value: 'https://kth.instructure.com/api/v1'},
        {name: 'beta', value: 'https://kth.beta.instructure.com/api/v1'}
      ],
      type: 'list'
    })

  const {apiKey} = await inquirer.prompt({
    message: 'Klistra in api nyckel till Canvas här',
    name: 'apiKey',
    type: 'string'
  })

  const canvasApi = new CanvasApi(apiUrl, apiKey)
  try {
    const sectionFileName = 'csv/unenrollObserversSections.csv'
    const coursesFileName = 'csv/unenrollObserversCourses.csv'

    await csvFile.writeLine(['section_id', 'user_id', 'role', 'status'], sectionFileName)
    await csvFile.writeLine(['course_id', 'user_id', 'role', 'status'], coursesFileName)

    const courses = await canvasApi.recursePages(`${apiUrl}/accounts/1/courses?per_page=100`)
    // const courses = await canvasApi.requestUrl(`accounts/1/courses?per_page=100`)

    // const courses = await canvasApi.requestUrl(`accounts/1/courses?per_page=100`)
    // const course = courses[0]
    // course.id = 2339

    for (let course of courses) {
      try {
        const enrollments = await canvasApi.recursePages(`${apiUrl}/courses/${course.id}/enrollments?type[]=ObserverEnrollment&per_page=100`)
        // const enrollments = await canvasApi.recursePages(`${apiUrl}/courses/${course.id}/enrollments?per_page=100`)
        for (let enrollment of enrollments) {
          console.log(':::::::::::::. enrollment:', enrollment, '------------------')
          if (enrollment.sis_section_id) {
            console.log('Yes, enrollment is in a section', enrollment.sis_section_id)
              // TODO Only removing observers from sections. Guess we should also remove from courses?
            await csvFile.writeLine([enrollment.sis_section_id, enrollment.sis_user_id, 'ObserverEnrollment', 'deleted'], sectionFileName)
          } else {
            console.log('enrollment is in a course?')
          }
        }
      } catch (e) {
        console.log('an error occured, continue', e)
      }
    }
    console.log('Done. Now upload the sis file to canvas.')
  } catch (err) {
    console.error(err)
  }
}
createFile()
<commit_msg>start with unenrolling our antagna role<commit_after>const inquirer = require('inquirer')
const CanvasApi = require('kth-canvas-api')
process.env['NODE_ENV'] = 'production'
const csvFile = require('../csvFile')

async function createFile () {
  const {apiUrl} = await inquirer.prompt(
    {
      message: 'Vilken miljö?',
      name: 'apiUrl',
      choices: [
        {name: 'test', value: 'https://kth.test.instructure.com/api/v1'},
        {name: 'prod', value: 'https://kth.instructure.com/api/v1'},
        {name: 'beta', value: 'https://kth.beta.instructure.com/api/v1'}
      ],
      type: 'list'
    })

  const {apiKey} = await inquirer.prompt({
    message: 'Klistra in api nyckel till Canvas här',
    name: 'apiKey',
    type: 'string'
  })

  const canvasApi = new CanvasApi(apiUrl, apiKey)
  try {
    const sectionFileName = 'csv/unenrollObserversSections.csv'
    const coursesFileName = 'csv/unenrollObserversCourses.csv'

    await csvFile.writeLine(['section_id', 'user_id', 'role', 'status'], sectionFileName)
    await csvFile.writeLine(['course_id', 'user_id', 'role', 'status'], coursesFileName)

    const courses = await canvasApi.recursePages(`${apiUrl}/accounts/1/courses?per_page=100`)
    // const courses = await canvasApi.requestUrl(`accounts/1/courses?per_page=100`)

    // const courses = await canvasApi.requestUrl(`accounts/1/courses?per_page=100`)
    // const course = courses[0]
    // course.id = 2339

    for (let course of courses) {
      try {
        const enrollments = await canvasApi.recursePages(`${apiUrl}/courses/${course.id}/enrollments?type[]=ObserverEnrollment&per_page=100`)
        // const enrollments = await canvasApi.recursePages(`${apiUrl}/courses/${course.id}/enrollments?per_page=100`)
        for (let enrollment of enrollments) {
          console.log('enrollment:', enrollment)

          if (enrollment.role === 'Applied pending registration (Observer)') {
              // TODO Only removing observers from sections. Guess we should also remove from courses?
              if(enrollment.sis_section_id){
                await csvFile.writeLine([enrollment.sis_section_id, enrollment.sis_user_id, 'Applied pending registration (Observer)', 'deleted'], sectionFileName)
              }else{
                console.log('user is not enrolled in a section...')
              }

          } else {
            console.log('enrollment is not an antagen.', enrollment)
          }
        }
      } catch (e) {
        console.log('an error occured, continue', e)
      }
    }
    console.log('Done. Now upload the sis file to canvas.')
  } catch (err) {
    console.error(err)
  }
}
createFile()
<|endoftext|>


<commit_before>import { takeLatest } from 'redux-saga'
import { call, put } from 'redux-saga/effects'
import { getSiadWallet } from './helpers.js'
import * as actions from '../actions/locking.js'
import * as constants from '../constants/locking.js'
import Siad from 'sia.js'
const IPC = require('electron').ipcRenderer;

// Asynchronously get Siad's wallet status, and elegantly handle any side effects
function *getLockStatus(action) {
	try {
		// Sync up Siad config with the main UI.
		const config = yield call(IPC.sendSync, 'config', 'siad');
		yield Siad.configure(config);
		const response = yield getSiadWallet(Siad);
		if (!response.unlocked) {
			yield put(actions.lockWallet());
		} else {
			yield put(actions.unlockWallet());
		}
	} catch (e) {
		yield put(actions.walletError(e));
	}
}
// Consume any GET_LOCK_STATUS actions
export function* watchGetLockStatus() {
	yield *takeLatest(constants.GET_LOCK_STATUS, getLockStatus);
}<commit_msg>use siadError in lockingSaga<commit_after>import { takeLatest } from 'redux-saga';
import { call, put } from 'redux-saga/effects';
import { getSiadWallet } from './helpers.js';
import * as actions from '../actions/locking.js';
import * as constants from '../constants/locking.js';
import { siadError } from '../actions/error.js';
import Siad from 'sia.js'
const IPC = require('electron').ipcRenderer;

// Asynchronously get Siad's wallet status, and elegantly handle any side effects
function *getLockStatus(action) {
	try {
		// Sync up Siad config with the main UI.
		const config = yield call(IPC.sendSync, 'config', 'siad');
		yield Siad.configure(config);
		const response = yield getSiadWallet(Siad);
		if (!response.unlocked) {
			yield put(actions.lockWallet());
		} else {
			yield put(actions.unlockWallet());
		}
	} catch (e) {
		yield put(siadError(e));
	}
}
// Consume any GET_LOCK_STATUS actions
export function* watchGetLockStatus() {
	yield *takeLatest(constants.GET_LOCK_STATUS, getLockStatus);
}<|endoftext|>


<commit_before>import { blizzardApiResponseLatencyHistogram } from 'helpers/metrics';
import RequestTimeoutError from 'helpers/request/RequestTimeoutError';
import RequestSocketTimeoutError from 'helpers/request/RequestSocketTimeoutError';
import RequestConnectionResetError from 'helpers/request/RequestConnectionResetError';
import RequestUnknownError from 'helpers/request/RequestUnknownError';
import retryingRequest from './retryingRequest';

const availableRegions = {
  eu: 'ru_RU',
  us: 'en_US',
  tw: 'zh_TW',
  kr: 'ko_KR',
};

const USER_AGENT = process.env.USER_AGENT;
const clientToken = {};

const get = (url, metricLabels, region, skipAccessToken = false) => {
  let end;
  return retryingRequest({
    url,
    headers: {
      'User-Agent': USER_AGENT,
    },
    gzip: true,
    // we'll be making several requests, so pool connections
    forever: true,
    timeout: 4000, // ms after which to abort the request, when a character is uncached it's not uncommon to take ~2sec
    shouldRetry: error => {
      const { statusCode } = error;
      //generally 404 should probably just not be retried
      return statusCode !== 404;
    },
    getUrlWithAccessToken: async () => {
      console.log(`skippingAccessToken: ${skipAccessToken}`);
      if(!skipAccessToken){
        const accessToken = await getAccessToken(region);
        return `${url}&access_token=${accessToken}`;
      } else {
        return url;
      }
    },
    onBeforeAttempt: () => {
      end = blizzardApiResponseLatencyHistogram.startTimer(metricLabels);
    },
    onFailure: async err => {
      if (err.statusCode === 401) {
        delete clientToken[region];
        end({
          statusCode: 401,
        });
      } else if (err instanceof RequestTimeoutError) {
        end({
          statusCode: 'timeout',
        });
      } else if (err instanceof RequestSocketTimeoutError) {
        end({
          statusCode: 'socket timeout',
        });
      } else if (err instanceof RequestConnectionResetError) {
        end({
          statusCode: 'connection reset',
        });
      } else if (err instanceof RequestUnknownError) {
        end({
          statusCode: 'unknown',
        });
      } else {
        end({
          statusCode: err.statusCode,
        });
      }
      return null;
    },
    onSuccess: () => {
      end({
        statusCode: 200,
      });
    },
  });
};
const makeBaseUrl = region => `https://${region}.api.blizzard.com`;

const getAccessToken = async (region) => {
  if (clientToken[region]) {
    return clientToken[region];
  }

  const url = `https://${region}.battle.net/oauth/token?grant_type=client_credentials&client_id=${process.env.BATTLE_NET_API_CLIENT_ID}&client_secret=${process.env.BATTLE_NET_API_CLIENT_SECRET}`;

  const tokenRequest = await get(
    url,
    {
      category: 'token',
      region,
    },
    region,
    true,
  );

  const tokenData = JSON.parse(tokenRequest);
  clientToken[region] = tokenData.access_token;
  return clientToken[region];
};

export async function fetchCharacter(region, realm, name, fields = '') {
  region = region.toLowerCase();
  if (!availableRegions[region]) {
    throw new Error('Region not recognized.');
  }
  const url = `${makeBaseUrl(region)}/wow/character/${encodeURIComponent(realm)}/${encodeURIComponent(name)}?locale=${availableRegions[region]}&fields=${fields}`;

  return get(
    url,
    {
      category: 'character',
      region,
    },
    region,
  );
}

export async function fetchItem(region, itemId) {
  region = region.toLowerCase();
  if (!availableRegions[region]) {
    throw new Error('Region not recognized.');
  }
  const url = `${makeBaseUrl(region)}/wow/item/${encodeURIComponent(itemId)}?locale=${availableRegions[region]}`;

  return get(
    url,
    {
      category: 'item',
      region,
    },
    region,
  );
}

export async function fetchSpell(region, spellId) {
  region = region.toLowerCase();
  if (!availableRegions[region]) {
    throw new Error('Region not recognized.');
  }
  const url = `${makeBaseUrl(region)}/wow/spell/${encodeURIComponent(spellId)}?locale=${availableRegions[region]}`;

  return get(
    url,
    {
      category: 'spell',
      region,
    },
    region,
  );
}
<commit_msg>Update server/helpers/wowCommunityApi.js<commit_after>import { blizzardApiResponseLatencyHistogram } from 'helpers/metrics';
import RequestTimeoutError from 'helpers/request/RequestTimeoutError';
import RequestSocketTimeoutError from 'helpers/request/RequestSocketTimeoutError';
import RequestConnectionResetError from 'helpers/request/RequestConnectionResetError';
import RequestUnknownError from 'helpers/request/RequestUnknownError';
import retryingRequest from './retryingRequest';

const availableRegions = {
  eu: 'ru_RU',
  us: 'en_US',
  tw: 'zh_TW',
  kr: 'ko_KR',
};

const USER_AGENT = process.env.USER_AGENT;
const clientToken = {};

const get = (url, metricLabels, region, skipAccessToken = false) => {
  let end;
  return retryingRequest({
    url,
    headers: {
      'User-Agent': USER_AGENT,
    },
    gzip: true,
    // we'll be making several requests, so pool connections
    forever: true,
    timeout: 4000, // ms after which to abort the request, when a character is uncached it's not uncommon to take ~2sec
    shouldRetry: error => {
      const { statusCode } = error;
      //generally 404 should probably just not be retried
      return statusCode !== 404;
    },
    getUrlWithAccessToken: async () => {
      if(!skipAccessToken){
        const accessToken = await getAccessToken(region);
        return `${url}&access_token=${accessToken}`;
      } else {
        return url;
      }
    },
    onBeforeAttempt: () => {
      end = blizzardApiResponseLatencyHistogram.startTimer(metricLabels);
    },
    onFailure: async err => {
      if (err.statusCode === 401) {
        delete clientToken[region];
        end({
          statusCode: 401,
        });
      } else if (err instanceof RequestTimeoutError) {
        end({
          statusCode: 'timeout',
        });
      } else if (err instanceof RequestSocketTimeoutError) {
        end({
          statusCode: 'socket timeout',
        });
      } else if (err instanceof RequestConnectionResetError) {
        end({
          statusCode: 'connection reset',
        });
      } else if (err instanceof RequestUnknownError) {
        end({
          statusCode: 'unknown',
        });
      } else {
        end({
          statusCode: err.statusCode,
        });
      }
      return null;
    },
    onSuccess: () => {
      end({
        statusCode: 200,
      });
    },
  });
};
const makeBaseUrl = region => `https://${region}.api.blizzard.com`;

const getAccessToken = async (region) => {
  if (clientToken[region]) {
    return clientToken[region];
  }

  const url = `https://${region}.battle.net/oauth/token?grant_type=client_credentials&client_id=${process.env.BATTLE_NET_API_CLIENT_ID}&client_secret=${process.env.BATTLE_NET_API_CLIENT_SECRET}`;

  const tokenRequest = await get(
    url,
    {
      category: 'token',
      region,
    },
    region,
    true,
  );

  const tokenData = JSON.parse(tokenRequest);
  clientToken[region] = tokenData.access_token;
  return clientToken[region];
};

export async function fetchCharacter(region, realm, name, fields = '') {
  region = region.toLowerCase();
  if (!availableRegions[region]) {
    throw new Error('Region not recognized.');
  }
  const url = `${makeBaseUrl(region)}/wow/character/${encodeURIComponent(realm)}/${encodeURIComponent(name)}?locale=${availableRegions[region]}&fields=${fields}`;

  return get(
    url,
    {
      category: 'character',
      region,
    },
    region,
  );
}

export async function fetchItem(region, itemId) {
  region = region.toLowerCase();
  if (!availableRegions[region]) {
    throw new Error('Region not recognized.');
  }
  const url = `${makeBaseUrl(region)}/wow/item/${encodeURIComponent(itemId)}?locale=${availableRegions[region]}`;

  return get(
    url,
    {
      category: 'item',
      region,
    },
    region,
  );
}

export async function fetchSpell(region, spellId) {
  region = region.toLowerCase();
  if (!availableRegions[region]) {
    throw new Error('Region not recognized.');
  }
  const url = `${makeBaseUrl(region)}/wow/spell/${encodeURIComponent(spellId)}?locale=${availableRegions[region]}`;

  return get(
    url,
    {
      category: 'spell',
      region,
    },
    region,
  );
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Return version info based on package.json and the git hash
 *
 * We figure out the Git hash in the following order:
 *
 * (1) read config/version.json if exists (ie. staging, production)
 * (2) figure it out from git (either regular '.git', or
 *     '/home/app/git' for AwsBox)
 */
'use strict';

var fs = require('fs');
var path = require('path');
var util = require('util');
var child_process = require('child_process');
var Promise = require('bluebird');
var logger = require('intel').getLogger('server.ver.json');

var version = require('../../../package.json').version;

exports.method = 'get';
exports.path = '/ver.json';

function getCommitHashFromVersionJson() {
  return Promise.attempt(function () {
    var configFile = path.join(__dirname, '..', '..', '..', 'config', 'version.json')
    if (fs.existsSync(configFile)) {
      var commitHash;
      try {
        commitHash = require(configFile).version.hash;
      } catch (e) {
        logger.error('could not read version.hash from version.json');
      }
      return commitHash;
    }
  });
}

function getGitDir() {
  if (! fs.existsSync(path.join(__dirname, '..', '..', '..', '.git'))) {
    // try at '/home/app/git' for AwsBox deploys
    return path.sep + path.join('home', 'app', 'git');
  }
}

function getCommitHashFromGit() {
  var deferred = Promise.defer();

  var gitDir = getGitDir();
  var cmd = util.format('git %s rev-parse HEAD', gitDir ? '--git-dir=' + gitDir : '');

  child_process.exec(cmd, function (err, stdout) {
    deferred.resolve(stdout.replace(/\s+/, ''));
  });

  return deferred.promise;
}

var promise;
function getVersionInfo() {
  // only resolve once, the data does not need to be re-calculated.
  if (promise) {
    return promise;
  }

  // (1) read config/version.json if exists (ie. staging, production)
  promise = getCommitHashFromVersionJson()
              .then(function (commitHash) {
                if (commitHash) {
                  return commitHash;
                }
                // (2) figure it out from git (either regular '.git',
                // or '/home/app/git' for AwsBox)
                return getCommitHashFromGit();
              })
              .then(function (commitHash) {
                logger.info('version set to: %s', version);
                logger.info('commit hash set to: %s', commitHash);
                return {
                  version: version,
                  commit: commitHash
                };
              });

  return promise;
}

// seed the info on startup.
getVersionInfo();

exports.process = function (req, res) {
  getVersionInfo()
    .then(function (versionInfo) {
      // charset must be set on json responses.
      res.charset = 'utf-8';
      res.json(versionInfo);
    });
};

<commit_msg>show the git sha recorded in app/bower_components/fxa-content-server-l10n/.bower.json<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Return version info based on package.json and the git hash
 *
 * We figure out the Git hash in the following order:
 *
 * (1) read config/version.json if exists (ie. staging, production)
 * (2) figure it out from git (either regular '.git', or
 *     '/home/app/git' for AwsBox)
 */
'use strict';

var fs = require('fs');
var path = require('path');
var util = require('util');
var child_process = require('child_process');
var Promise = require('bluebird');
var logger = require('intel').getLogger('server.ver.json');

var version = require('../../../package.json').version;

exports.method = 'get';
exports.path = '/ver.json';

function getCommitHashFromVersionJson() {
  return Promise.attempt(function () {
    var configFile = path.join(__dirname, '..', '..', '..', 'config', 'version.json')
    if (fs.existsSync(configFile)) {
      var commitHash;
      try {
        commitHash = require(configFile).version.hash;
      } catch (e) {
        logger.error('could not read version.hash from version.json');
      }
      return commitHash;
    }
  });
}

function getGitDir() {
  if (! fs.existsSync(path.join(__dirname, '..', '..', '..', '.git'))) {
    // try at '/home/app/git' for AwsBox deploys
    return path.sep + path.join('home', 'app', 'git');
  }
}

function getCommitHashFromGit() {
  var deferred = Promise.defer();

  var gitDir = getGitDir();
  var cmd = util.format('git %s rev-parse HEAD', gitDir ? '--git-dir=' + gitDir : '');

  child_process.exec(cmd, function (err, stdout) {
    deferred.resolve(stdout.replace(/\s+/, ''));
  });

  return deferred.promise;
}

var l10nVersion = (function getL10NVersion() {
  var version;
  try {
    var bowerPath = '../../../app/bower_components/fxa-content-server-l10n/.bower.json';
    var bowerInfo = require(bowerPath);
    version = bowerInfo && bowerInfo._release;
  } catch(e) {
  }
  return version || 'unknown';
})();

var promise;
function getVersionInfo() {
  // only resolve once, the data does not need to be re-calculated.
  if (promise) {
    return promise;
  }

  // (1) read config/version.json if exists (ie. staging, production)
  promise = getCommitHashFromVersionJson()
              .then(function (commitHash) {
                if (commitHash) {
                  return commitHash;
                }
                // (2) figure it out from git (either regular '.git',
                // or '/home/app/git' for AwsBox)
                return getCommitHashFromGit();
              })
              .then(function (commitHash) {
                logger.info('version set to: %s', version);
                logger.info('commit hash set to: %s', commitHash);
                logger.info('fxa-content-server-l10n commit hash set to: %s', l10nVersion);
                return {
                  version: version,
                  commit: commitHash,
                  l10n: l10nVersion
                };
              });

  return promise;
}

// seed the info on startup.
getVersionInfo();

exports.process = function (req, res) {
  getVersionInfo()
    .then(function (versionInfo) {
      // charset must be set on json responses.
      res.charset = 'utf-8';
      res.json(versionInfo);
    });
};

<|endoftext|>


<commit_before>'use strict'

const { dump } = require('../../../lib/contract')

const crutch = (req, res, next) => {
  const cache = dump()

  res.send(cache)
  next()
}

module.exports = crutch
<commit_msg>Adding content type to crutch index response<commit_after>'use strict'

const { dump } = require('../../../lib/contract')

const crutch = (req, res, next) => {
  const cache = dump()

  res.setHeader('Content-Type', 'application/json');
  res.send(cache)
  next()
}

module.exports = crutch
<|endoftext|>


<commit_before>// @flow

import type { UploadInput } from '../creators/upload-creator';

import sharp from 'sharp';

import { fileInfoFromData } from 'lib/utils/file-utils';

const fiveMegabytes = 5 * 1024 * 1024;

async function validateAndConvert(
  initialBuffer: Buffer,
  initialName: string,
  size: number, // in bytes
): Promise<?UploadInput> {
  const fileInfo = fileInfoFromData(initialBuffer, initialName);
  if (!fileInfo) {
    return null;
  }

  const { mime } = fileInfo;
  if (mime === "image/heic") {
    // This should've gotten converted on the client
    return null;
  }
  if (size < fiveMegabytes && (mime === "image/png" || mime === "image/jpeg")) {
    return { ...fileInfo, buffer: initialBuffer };
  }

  let sharpImage;
  try {
    sharpImage = sharp(initialBuffer);
  } catch (e) {
    return null;
  }
  sharpImage = sharpImage.resize(
    3000,
    2000,
    { fit: 'inside', withoutEnlargement: true },
  );

  if (mime === "image/png" || mime === "image/gif") {
    sharpImage = sharpImage.png();
  } else {
    sharpImage = sharpImage.jpeg({ quality: 92 });
  }

  const convertedBuffer = await sharpImage.toBuffer();
  const convertedFileInfo = fileInfoFromData(convertedBuffer, initialName);
  if (!convertedFileInfo) {
    return null;
  }
  return { ...convertedFileInfo, buffer: convertedBuffer };
}

export {
  validateAndConvert,
};
<commit_msg>[server] Maintain own list of valid MIME types<commit_after>// @flow

import type { UploadInput } from '../creators/upload-creator';

import sharp from 'sharp';

import { fileInfoFromData } from 'lib/utils/file-utils';

const fiveMegabytes = 5 * 1024 * 1024;

const allowedMimeTypes = new Set([
  "image/png",
  "image/jpeg",
  "image/gif",
]);

async function validateAndConvert(
  initialBuffer: Buffer,
  initialName: string,
  size: number, // in bytes
): Promise<?UploadInput> {
  const fileInfo = fileInfoFromData(initialBuffer, initialName);
  if (!fileInfo) {
    return null;
  }

  const { mime } = fileInfo;
  if (!allowedMimeTypes.has(mime)) {
    // This should've gotten converted on the client
    return null;
  }
  if (size < fiveMegabytes && (mime === "image/png" || mime === "image/jpeg")) {
    return { ...fileInfo, buffer: initialBuffer };
  }

  let sharpImage;
  try {
    sharpImage = sharp(initialBuffer);
  } catch (e) {
    return null;
  }
  sharpImage = sharpImage.resize(
    3000,
    2000,
    { fit: 'inside', withoutEnlargement: true },
  );

  if (mime === "image/png" || mime === "image/gif") {
    sharpImage = sharpImage.png();
  } else {
    sharpImage = sharpImage.jpeg({ quality: 92 });
  }

  const convertedBuffer = await sharpImage.toBuffer();
  const convertedFileInfo = fileInfoFromData(convertedBuffer, initialName);
  if (!convertedFileInfo) {
    return null;
  }
  return { ...convertedFileInfo, buffer: convertedBuffer };
}

export {
  validateAndConvert,
};
<|endoftext|>


<commit_before>const path = require('path');
var nodeExternals = require('webpack-node-externals');
const debug = process.env.DEBUGGING || false;
const merge = require('webpack-merge');
const webpack = require('webpack');
const Dotenv = require('dotenv-webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');

const config = {
    builders: {
        web: {
            entry: './src/index.tsx',
            output: {
                chunkFilename: '[name].bundle.js',
            },
            stack: ['web', 'react'],
            tsLoaderOptions: {
                configFileName: "./tsconfig.json"
            },
            webpackDevPort: 3000,
            openBrowser: true,
            defines: {
                __CLIENT__: true,
            },
            test: {
                role: ['build', 'watch']
            },
            htmlTemplate: "../../tools/html-plugin-template.ejs",
            // Wait for backend to start prior to letting webpack load frontend page
            waitOn: ['tcp:localhost:8080'],
            enabled: true,
            webpackConfig: {
                plugins: [
                    new LodashModuleReplacementPlugin({
                        // Necessary as a workaround for https://github.com/apollographql/react-apollo/issues/1831
                        flattening: true
                      }),
                ],
                // for additional webpack configuration.
                resolve: process.env.NODE_ENV !== 'production'
                    ? {
                        alias: {
                            'react-dom': '@hot-loader/react-dom'
                        }
                    }
                    : {},
            }
        },
        server: {
            entry: './src/backend/app.ts',
            stack: ['server'],
            tsLoaderOptions: {
                configFileName: "./tsconfig.json"
            },
            defines: {
                __SERVER__: true,
            },
            enabled: false,
            webpackConfig: {
                output: {
                    filename: 'main.js',
                },
                plugins: [
                    new CopyWebpackPlugin([{
                        from: '../../tools/esm-wrapper.js',
                        to: 'index.js',
                    }]),
                    new LodashModuleReplacementPlugin({
                        // Necessary as a workaround for https://github.com/apollographql/react-apollo/issues/1831
                        flattening: true
                      }),
                ],
                externals: [
                    nodeExternals(),
                    nodeExternals({ whitelist: [/webpack\/hot/i, /babel-polyfill/], modulesDir: "../../node_modules" })
                ],
            }
        },
        test: {
            stack: ['server'],
            roles: ['test'],
            defines: {
                __TEST__: true
            }
        }
    },
    options: {
        stack: [
            "apollo",
            "ts",
            "react",
            "webpack",
            "css"
        ],
        cache: '../../.cache',
        backendBuildDir: "dist",
        frontendBuildDir: "dist",
        dllBuildDir: "dist/.build/dll",
        ssr: false,
        backendUrl: "http://localhost:8080",
        webpackDll: true,
        reactHotLoader: true,
        useDefaultPostCss: true,
        persistGraphQL: false,
        frontendRefreshOnBackendChange: true,
        nodeDebugger: false,
        overridesConfig: "./tools/webpackAppConfig.js",
        plugins: [
            new Dotenv({
                path: process.env.ENV_FILE
            })
        ],
        defines: {
            __DEV__: process.env.NODE_ENV === 'development',
            __GRAPHQL_URL__: '"http://localhost:8080/graphql"',
        }
    }
};
if (process.env.NODE_ENV === 'development') {
    config.builders.web.webpackConfig = {
        plugins: [
            new Dotenv({
                path: process.env.ENV_FILE
            })
        ],
    }
}

if (process.env.SSR) {
    config.builders.server.enabled = true;
    config.options.defines.__BACKEND_URL__ = '"http://localhost:3010"';
    config.options.ssr = true;
    config.options.backendUrl = "http://localhost:3010";
}
if (process.env.NODE_ENV !== 'development') {
    config.builders.server.enabled = true;
    config.options.defines.__BACKEND_URL__ = '"http://localhost:3010"';
    config.options.ssr = true;
    config.options.backendUrl = "http://localhost:3010";
}

if (process.env.NODE_ENV === 'production') {
    // Generating source maps for production will slowdown compilation for roughly 25%
    config.options.sourceMap = false;
}

config.options.devProxy = config.options.ssr;

const extraDefines = {
    __SSR__: config.options.ssr,
    __PERSIST_GQL__: `'${config.options.persistGraphQL}'`,
    __FRONTEND_BUILD_DIR__: `'${config.options.frontendBuildDir}'`,
    __DLL_BUILD_DIR__: `'${config.options.dllBuildDir}'`,
    __DEBUGGING__: `'${debug}'`
};

if (process.env.NODE_ENV !== 'production') {

    if (!config.options.ssr) {
        console.log('Warning! exposing env variables in UI, only run in development.');
        var dotenv = require('dotenv-safe')
            .config(
                {
                    allowEmptyValues: true,
                    path: process.env.ENV_FILE,
                    example: '../../config/development/dev.env',
                });
        const envConfig = {
            plugins: [
                new webpack.DefinePlugin({
                    "__ENV__": JSON.stringify(dotenv.parsed)
                }),
            ],
        }
        config.builders.web.webpackConfig = merge(config.builders.web.webpackConfig, envConfig);
    }
}

config.options.defines = Object.assign(config.options.defines, extraDefines);

module.exports = config;<commit_msg>fix webpack config fix<commit_after>const path = require('path');
var nodeExternals = require('webpack-node-externals');
const debug = process.env.DEBUGGING || false;
const merge = require('webpack-merge');
const webpack = require('webpack');
const Dotenv = require('dotenv-webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');

const config = {
    builders: {
        web: {
            entry: './src/index.tsx',
            output: {
                chunkFilename: '[name].bundle.js',
            },
            stack: ['web', 'react'],
            tsLoaderOptions: {
                configFileName: "./tsconfig.json"
            },
            webpackDevPort: 3000,
            openBrowser: true,
            defines: {
                __CLIENT__: true,
            },
            test: {
                role: ['build', 'watch']
            },
            htmlTemplate: "../../tools/html-plugin-template.ejs",
            // Wait for backend to start prior to letting webpack load frontend page
            waitOn: ['tcp:localhost:8080'],
            enabled: true,
            webpackConfig: {
                plugins: [
                    new LodashModuleReplacementPlugin({
                        // Necessary as a workaround for https://github.com/apollographql/react-apollo/issues/1831
                        flattening: true
                      }),
                ],
                // for additional webpack configuration.
                resolve: process.env.NODE_ENV !== 'production'
                    ? {
                        alias: {
                            'react-dom': '@hot-loader/react-dom'
                        }
                    }
                    : {},
            }
        },
        server: {
            entry: './src/backend/app.ts',
            stack: ['server'],
            tsLoaderOptions: {
                configFileName: "./tsconfig.json"
            },
            defines: {
                __SERVER__: true,
            },
            enabled: false,
            webpackConfig: {
                output: {
                    filename: 'main.js',
                },
                plugins: [
                    new CopyWebpackPlugin([{
                        from: '../../tools/esm-wrapper.js',
                        to: 'index.js',
                    }]),
                    new LodashModuleReplacementPlugin({
                        // Necessary as a workaround for https://github.com/apollographql/react-apollo/issues/1831
                        flattening: true
                      }),
                ],
                externals: [
                    nodeExternals(),
                    nodeExternals({ whitelist: [/webpack\/hot/i, /babel-polyfill/], modulesDir: "../../node_modules" })
                ],
            }
        },
        test: {
            stack: ['server'],
            roles: ['test'],
            defines: {
                __TEST__: true
            }
        }
    },
    options: {
        stack: [
            "apollo",
            "ts",
            "react",
            "webpack",
            "css"
        ],
        cache: '../../.cache',
        backendBuildDir: "dist",
        frontendBuildDir: "dist",
        dllBuildDir: "dist/.build/dll",
        ssr: false,
        backendUrl: "http://localhost:8080",
        webpackDll: true,
        reactHotLoader: true,
        useDefaultPostCss: true,
        persistGraphQL: false,
        frontendRefreshOnBackendChange: true,
        nodeDebugger: false,
        overridesConfig: "./tools/webpackAppConfig.js",
        plugins: [
            new Dotenv({
                path: process.env.ENV_FILE
            })
        ],
        defines: {
            __DEV__: process.env.NODE_ENV === 'development',
            __GRAPHQL_URL__: '"http://localhost:8080/graphql"',
        }
    }
};
if (process.env.NODE_ENV === 'development') {
    const dotEnvPlugin = {
        plugins: [
            new Dotenv({
                path: process.env.ENV_FILE
            })
        ],
    }
    config.builders.web.webpackConfig = merge(config.builders.web.webpackConfig, dotEnvPlugin);

}

if (process.env.SSR) {
    config.builders.server.enabled = true;
    config.options.defines.__BACKEND_URL__ = '"http://localhost:3010"';
    config.options.ssr = true;
    config.options.backendUrl = "http://localhost:3010";
}
if (process.env.NODE_ENV !== 'development') {
    config.builders.server.enabled = true;
    config.options.defines.__BACKEND_URL__ = '"http://localhost:3010"';
    config.options.ssr = true;
    config.options.backendUrl = "http://localhost:3010";
}

if (process.env.NODE_ENV === 'production') {
    // Generating source maps for production will slowdown compilation for roughly 25%
    config.options.sourceMap = false;
}

config.options.devProxy = config.options.ssr;

const extraDefines = {
    __SSR__: config.options.ssr,
    __PERSIST_GQL__: `'${config.options.persistGraphQL}'`,
    __FRONTEND_BUILD_DIR__: `'${config.options.frontendBuildDir}'`,
    __DLL_BUILD_DIR__: `'${config.options.dllBuildDir}'`,
    __DEBUGGING__: `'${debug}'`
};

if (process.env.NODE_ENV !== 'production') {

    if (!config.options.ssr) {
        console.log('Warning! exposing env variables in UI, only run in development.');
        var dotenv = require('dotenv-safe')
            .config(
                {
                    allowEmptyValues: true,
                    path: process.env.ENV_FILE,
                    example: '../../config/development/dev.env',
                });
        const envPlugin = {
            plugins: [
                new webpack.DefinePlugin({
                    "__ENV__": JSON.stringify(dotenv.parsed)
                }),
            ],
        }
        config.builders.web.webpackConfig = merge(config.builders.web.webpackConfig, envPlugin);
    }
}

config.options.defines = Object.assign(config.options.defines, extraDefines);
module.exports = config;<|endoftext|>


<commit_before>import * as utils from 'base/utils';
import Model from 'base/model';

//classes are vzb-portrait, vzb-landscape...
var class_prefix = 'vzb-';
var class_presentation = 'presentation';
var class_rtl = 'rtl';
var class_portrait = 'vzb-portrait';
var class_landscape = 'vzb-landscape';

var UI = Model.extend({

  screen_profiles: {
    small: {
      min_width: 0,
      min_height: 0
    },
    medium: {
      min_width: 600,
      min_height: 400
    },
    large: {
      min_width: 900,
      min_height: 520
    }
  },

  getClassDefaults: function() {
    var defaults = {
      presentation: false,
      buttons: [],
      dialogs: {
        popup: [],
        sidebar: [],
        moreoptions: []
      },
      splash: false
    };
    return utils.deepExtend(this._super(), defaults);
  },

  /**
   * Initializes the layout manager
   */
  init: function(name, values, parent, bind) {

    this._type = 'ui';
    this._container = null;
    //dom element
    this._curr_profile = null;
    this._prev_size = {};

    //resize when window resizes
    window.addEventListener('resize', this.resizeHandler.bind(this));
    bind['change:presentation'] = this.updatePresentation.bind(this);

    this._super(name, values, parent, bind);
  },

  resizeHandler: function() {
    if(this._container) {
      this.setSize();
    }
  },

  /**
   * Calculates the size of the newly resized container
   */
  setSize: function(force) {
    var _this = this;
    var width = this._container.clientWidth;
    var height = this._container.clientHeight;

    /**
     * issue #1118
     * check if device is iPhone then add top margin for searchbar if it visible
     */
    if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent) // browser is safari
      && navigator.userAgent.match(/iPhone/i) // checking device
    ) {
      this._container.style.top =  0;
      if (this._container.clientWidth > this._container.clientHeight // landscape mode
        && this._container.clientWidth < 700) {  // small device
        var bodyHeight = this._container.clientHeight;
        var windowHeight = window.innerHeight;
        if (2 < (bodyHeight - windowHeight) && (bodyHeight - windowHeight) <= 45) { // check searchbar is visible
          this._container.style.top =  44 + "px";
          document.body.scrollTop = 44; // scrolling empty space
        }
      }
    }

    if(!force && this._prev_size && this._prev_size.width === width && this._prev_size.height === height) {
      return;
    }

    // choose profile depending on size
    utils.forEach(this.screen_profiles, function(range, size) {
      //remove class
      utils.removeClass(_this._container, class_prefix + size);
      //find best fit
      if(width >= range.min_width && height >= range.min_height) {
        _this._curr_profile = size;
      }
    });

    //update size class
    utils.addClass(this._container, class_prefix + this._curr_profile);

    //toggle, untoggle classes based on orientation
    utils.classed(this._container, class_portrait, width < height);
    utils.classed(this._container, class_landscape, !(width < height));

    this._prev_size.width = width;
    this._prev_size.height = height;
    this.trigger('resize');
  },

  /**
   * Sets the container for this layout
   * @param container DOM element
   */
  setContainer: function(container) {
    this._container = container;
    this.setSize();
    this.updatePresentation();
  },

  /**
   * Sets the presentation mode for this layout
   * @param {Bool} presentation mode on or off
   */
  updatePresentation: function() {
    utils.classed(this._container, class_prefix + class_presentation, this.presentation);
    this.trigger('resize');
  },

  getPresentationMode: function() {
    return this.presentation;
  },

  setRTL: function(flag) {
    utils.classed(this._container, class_prefix + class_rtl, flag);
  },

  /**
   * Gets the current selected profile
   * @returns {String} name of current profile
   */
  currentProfile: function() {
    return this._curr_profile;
  },

  clear: function() {
    window.removeEventListener('resize', this.resizeHandler);
  }

});

export default UI;
<commit_msg>fix 'resize' unbind #2337 (#2343)<commit_after>import * as utils from 'base/utils';
import Model from 'base/model';

//classes are vzb-portrait, vzb-landscape...
var class_prefix = 'vzb-';
var class_presentation = 'presentation';
var class_rtl = 'rtl';
var class_portrait = 'vzb-portrait';
var class_landscape = 'vzb-landscape';

var UI = Model.extend({

  screen_profiles: {
    small: {
      min_width: 0,
      min_height: 0
    },
    medium: {
      min_width: 600,
      min_height: 400
    },
    large: {
      min_width: 900,
      min_height: 520
    }
  },

  getClassDefaults: function() {
    var defaults = {
      presentation: false,
      buttons: [],
      dialogs: {
        popup: [],
        sidebar: [],
        moreoptions: []
      },
      splash: false
    };
    return utils.deepExtend(this._super(), defaults);
  },

  /**
   * Initializes the layout manager
   */
  init: function(name, values, parent, bind) {

    this._type = 'ui';
    this._container = null;
    //dom element
    this._curr_profile = null;
    this._prev_size = {};

    //resize when window resizes
    this.resizeHandler = this.resizeHandler.bind(this);
    window.addEventListener('resize', this.resizeHandler);
    bind['change:presentation'] = this.updatePresentation.bind(this);

    this._super(name, values, parent, bind);
  },

  resizeHandler: function() {
    if(this._container) {
      this.setSize();
    }
  },

  /**
   * Calculates the size of the newly resized container
   */
  setSize: function(force) {
    var _this = this;
    var width = this._container.clientWidth;
    var height = this._container.clientHeight;

    /**
     * issue #1118
     * check if device is iPhone then add top margin for searchbar if it visible
     */
    if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent) // browser is safari
      && navigator.userAgent.match(/iPhone/i) // checking device
    ) {
      this._container.style.top =  0;
      if (this._container.clientWidth > this._container.clientHeight // landscape mode
        && this._container.clientWidth < 700) {  // small device
        var bodyHeight = this._container.clientHeight;
        var windowHeight = window.innerHeight;
        if (2 < (bodyHeight - windowHeight) && (bodyHeight - windowHeight) <= 45) { // check searchbar is visible
          this._container.style.top =  44 + "px";
          document.body.scrollTop = 44; // scrolling empty space
        }
      }
    }

    if(!force && this._prev_size && this._prev_size.width === width && this._prev_size.height === height) {
      return;
    }

    // choose profile depending on size
    utils.forEach(this.screen_profiles, function(range, size) {
      //remove class
      utils.removeClass(_this._container, class_prefix + size);
      //find best fit
      if(width >= range.min_width && height >= range.min_height) {
        _this._curr_profile = size;
      }
    });

    //update size class
    utils.addClass(this._container, class_prefix + this._curr_profile);

    //toggle, untoggle classes based on orientation
    utils.classed(this._container, class_portrait, width < height);
    utils.classed(this._container, class_landscape, !(width < height));

    this._prev_size.width = width;
    this._prev_size.height = height;
    this.trigger('resize');
  },

  /**
   * Sets the container for this layout
   * @param container DOM element
   */
  setContainer: function(container) {
    this._container = container;
    this.setSize();
    this.updatePresentation();
  },

  /**
   * Sets the presentation mode for this layout
   * @param {Bool} presentation mode on or off
   */
  updatePresentation: function() {
    utils.classed(this._container, class_prefix + class_presentation, this.presentation);
    this.trigger('resize');
  },

  getPresentationMode: function() {
    return this.presentation;
  },

  setRTL: function(flag) {
    utils.classed(this._container, class_prefix + class_rtl, flag);
  },

  /**
   * Gets the current selected profile
   * @returns {String} name of current profile
   */
  currentProfile: function() {
    return this._curr_profile;
  },

  clear: function() {
    window.removeEventListener('resize', this.resizeHandler);
  }

});

export default UI;
<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular
    .module('dopplerRelay')
    .controller('PlanCtrl', PlanCtrl);

  PlanCtrl.$inject = [
    '$scope',
    '$location',
    '$rootScope',
    'auth',
    '$translate',
    '$timeout',
    'settings',
    '$filter',
    'reports',
    'moment',
    'utils'
  ];

  function PlanCtrl($scope, $location, $rootScope, auth, $translate, $timeout, settings, $filter, reports, moment, utils) {
    var vm = this;
    $rootScope.setSubmenues([
      { text: 'submenu_my_profile', url: 'settings/my-profile', active: false },
      { text: 'submenu_my_plan', url: 'settings/my-plan', active: true }
    ]);
    vm.hideDragMe = false;
    vm.activationPromise = activate();
    var defaultPlanDeliveries = '60000';
    var planItems;
    vm.langUsed = $translate.use();
    vm.showPricingChart = showPricingChart;
    vm.pricingChartDisplayed = false;
    vm.planInfoLoader = true;
    vm.planStatusInfoLoader = true;
    vm.ipsPlanCount = 0;

    function activate() {
      var getCurrentPlanInfo = settings.getCurrentPlanInfo().then(function(response) {
        vm.currentPlanPrice = response.data.fee + (response.data.ips_count * response.data.cost_by_ip || 0);
        vm.currentPlanEmailsAmount = response.data.includedDeliveries;
        vm.currentPlanEmailPrice = response.data.extraDeliveryCost;
        vm.currency = response.data.currency;
        vm.isFreeTrial = response.data.fee && response.data.includedDeliveries ? false : true;
        vm.currentIpsPlanCount = response.data.ips_count || 0;
        vm.hasScheduledPlan = !response.data.endDate;
        vm.hideDragMe = !vm.isFreeTrial;
        defaultPlanDeliveries = !vm.isFreeTrial ? response.data.includedDeliveries.toString() : defaultPlanDeliveries;
        if (!vm.hasScheduledPlan) {
          settings.getNextPlan().then(function(response){
            vm.nextPlanIncludedDeliveries = response.data.includedDeliveries;
          });
        }
      })
      .finally(function () {
        vm.planInfoLoader = false;
      });
      var getPlansAvailable = settings.getPlansAvailable().then(function(response) {
        planItems = response.data.items;
      });
      return Promise.all([getPlansAvailable, getCurrentPlanInfo, getMonthConsumption()]).then(function(){
        ensureValidDefaultPlanDelivery();
        loadSlider();
        changePlan(defaultPlanDeliveries);
      });
    }

    function getMonthConsumption() {
      return settings.getStatusPlanInfo()
          .then(function (result) {
            vm.extraEmailsSent = 0;
            vm.isAccountClosed = result.data.accountClosed;
            vm.accountEndDate = result.data.accountEndDate;
            vm.resetDate = result.data.endDate;
            vm.currentMonthlyCount = result.data.deliveriesCount;
            vm.planStatusInfoLoader = false;
            if (vm.currentPlanEmailsAmount < vm.currentMonthlyCount) {
              vm.extraEmailsSent = vm.currentMonthlyCount - vm.currentPlanEmailsAmount;
            }
          });
    }

    function changePlan(planDeliveries) {
      vm.emailsSuggestedAmount = planDeliveries;
      var selectedItems = planItems.filter(function(obj){
        return obj.included_deliveries == planDeliveries;
      });

      if (!selectedItems) {
        selectedItems = planItems[0];
        vm.hideDragMe = true;
      }

      var basic = selectedItems.find(function(plan){
        return plan.type != "pro";
      });
      var pro = selectedItems.find(function(plan){
        return plan.type == "pro";
      });

      vm.ipsPlanCount = pro ? pro.ips_count : 0;

      if (!basic) {
        vm.showPremiumPlanBox = true;

        vm.leftPlanName = pro.name;
        vm.leftPlanPrice = pro.fee + (pro.ips_count * pro.cost_by_ip || 0);
        vm.leftCostEmail = pro.extra_delivery_cost;
        vm.disableLeftPlan = isCurrentPlan(pro);

        vm.rightPlanName = 'Premium';
        vm.rightCostEmail = pro.extra_delivery_cost;
        vm.disableRightPlan = false;
      } else {
        vm.showPremiumPlanBox = false;

        vm.leftPlanName = basic.name;
        vm.leftPlanPrice = basic.fee;
        vm.leftCostEmail = basic.extra_delivery_cost;
        vm.disableLeftPlan = isCurrentPlan(basic);

        if (pro) {
          vm.rightPlanName = pro.name;
          vm.rightPlanPrice = pro.fee + (pro.ips_count * pro.cost_by_ip || 0);
          vm.rightCostEmail = pro.extra_delivery_cost;
          vm.disableRightPlan = isCurrentPlan(pro);
        }
      }
    }

    function loadSlider() {
      var items = planItems.map(function(plan) {
        return parseInt(plan.included_deliveries);
      });
      items = utils.removeDuplicates(items);
      items.sort(function(a, b) {
        return a - b;
      });

      vm.slider = {
        value: defaultPlanDeliveries,
        options: {
          showSelectionBar: true,
          showTicks: true,
          stepsArray: items,
          onChange: function (sliderId, modelValue) {
            vm.hideDragMe = true;
            changePlan(modelValue);
          }
        }
      };
    }

    function showPricingChart() {
        vm.pricingChartDisplayed = true;
    }

    function isCurrentPlan(plan){
      return plan.included_deliveries == vm.currentPlanEmailsAmount && vm.currentIpsPlanCount == plan.ips_count
    }

    function ensureValidDefaultPlanDelivery(){
      var selectedItems = planItems.filter(function(obj){
        return obj.included_deliveries == defaultPlanDeliveries;
      });

      if (selectedItems.length < 1) {
        var defaultPlanSuggested = planItems.reduce(function(prev, curr) {
          return (Math.abs(curr.included_deliveries - defaultPlanDeliveries) < Math.abs(prev.included_deliveries - defaultPlanDeliveries) ? curr : prev);
        });

        defaultPlanDeliveries = defaultPlanSuggested.included_deliveries.toString();
      }
    }

  }

})();
<commit_msg>[DR-1561] Get limit status info and store error validation status in a object<commit_after>(function() {
  'use strict';

  angular
    .module('dopplerRelay')
    .controller('PlanCtrl', PlanCtrl);

  PlanCtrl.$inject = [
    '$scope',
    '$location',
    '$rootScope',
    'auth',
    '$translate',
    '$timeout',
    'settings',
    '$filter',
    'reports',
    'moment',
    'utils'
  ];

  function PlanCtrl($scope, $location, $rootScope, auth, $translate, $timeout, settings, $filter, reports, moment, utils) {
    var vm = this;
    $rootScope.setSubmenues([
      { text: 'submenu_my_profile', url: 'settings/my-profile', active: false },
      { text: 'submenu_my_plan', url: 'settings/my-plan', active: true }
    ]);
    vm.hideDragMe = false;
    vm.activationPromise = activate();
    var defaultPlanDeliveries = '60000';
    var planItems;
    vm.langUsed = $translate.use();
    vm.showPricingChart = showPricingChart;
    vm.pricingChartDisplayed = false;
    vm.planInfoLoader = true;
    vm.planStatusInfoLoader = true;
    vm.ipsPlanCount = 0;
    vm.upgradePlanErrors = {'isDomainReady' : false};
    vm.isValidUpgradeablePlan = isValidUpgradeablePlan;

    function activate() {
      var getCurrentPlanInfo = settings.getCurrentPlanInfo().then(function(response) {
        vm.currentPlanPrice = response.data.fee + (response.data.ips_count * response.data.cost_by_ip || 0);
        vm.currentPlanEmailsAmount = response.data.includedDeliveries;
        vm.currentPlanEmailPrice = response.data.extraDeliveryCost;
        vm.currency = response.data.currency;
        vm.isFreeTrial = response.data.fee && response.data.includedDeliveries ? false : true;
        vm.currentIpsPlanCount = response.data.ips_count || 0;
        vm.hasScheduledPlan = !response.data.endDate;
        vm.hideDragMe = !vm.isFreeTrial;
        defaultPlanDeliveries = !vm.isFreeTrial ? response.data.includedDeliveries.toString() : defaultPlanDeliveries;
        if (!vm.hasScheduledPlan) {
          settings.getNextPlan().then(function(response){
            vm.nextPlanIncludedDeliveries = response.data.includedDeliveries;
          });
        }
      })
      .finally(function () {
        vm.planInfoLoader = false;
      });
      var getPlansAvailable = settings.getPlansAvailable().then(function(response) {
        planItems = response.data.items;
      });
      return Promise.all([getPlansAvailable, getCurrentPlanInfo, getMonthConsumption()]).then(function(){
        ensureValidDefaultPlanDelivery();
        loadSlider();
        changePlan(defaultPlanDeliveries);
      });
    }

    function getMonthConsumption() {
      return settings.getStatusPlanInfo()
          .then(function (result) {
            vm.extraEmailsSent = 0;
            vm.isAccountClosed = result.data.accountClosed;
            vm.accountEndDate = result.data.accountEndDate;
            vm.resetDate = result.data.endDate;
            vm.currentMonthlyCount = result.data.deliveriesCount;
            vm.planStatusInfoLoader = false;
            if (vm.currentPlanEmailsAmount < vm.currentMonthlyCount) {
              vm.extraEmailsSent = vm.currentMonthlyCount - vm.currentPlanEmailsAmount;
            }
          });
    }

    function changePlan(planDeliveries) {
      vm.emailsSuggestedAmount = planDeliveries;
      var selectedItems = planItems.filter(function(obj){
        return obj.included_deliveries == planDeliveries;
      });

      if (!selectedItems) {
        selectedItems = planItems[0];
        vm.hideDragMe = true;
      }

      var basic = selectedItems.find(function(plan){
        return plan.type != "pro";
      });
      var pro = selectedItems.find(function(plan){
        return plan.type == "pro";
      });

      vm.ipsPlanCount = pro ? pro.ips_count : 0;

      if (!basic) {
        vm.showPremiumPlanBox = true;

        vm.leftPlanName = pro.name;
        vm.leftPlanPrice = pro.fee + (pro.ips_count * pro.cost_by_ip || 0);
        vm.leftCostEmail = pro.extra_delivery_cost;
        vm.disableLeftPlan = isCurrentPlan(pro);

        vm.rightPlanName = 'Premium';
        vm.rightCostEmail = pro.extra_delivery_cost;
        vm.disableRightPlan = false;
      } else {
        vm.showPremiumPlanBox = false;

        vm.leftPlanName = basic.name;
        vm.leftPlanPrice = basic.fee;
        vm.leftCostEmail = basic.extra_delivery_cost;
        vm.disableLeftPlan = isCurrentPlan(basic);

        if (pro) {
          vm.rightPlanName = pro.name;
          vm.rightPlanPrice = pro.fee + (pro.ips_count * pro.cost_by_ip || 0);
          vm.rightCostEmail = pro.extra_delivery_cost;
          vm.disableRightPlan = isCurrentPlan(pro);
        }
      }
    }

    function loadSlider() {
      var items = planItems.map(function(plan) {
        return parseInt(plan.included_deliveries);
      });
      items = utils.removeDuplicates(items);
      items.sort(function(a, b) {
        return a - b;
      });

      vm.slider = {
        value: defaultPlanDeliveries,
        options: {
          showSelectionBar: true,
          showTicks: true,
          stepsArray: items,
          onChange: function (sliderId, modelValue) {
            vm.hideDragMe = true;
            changePlan(modelValue);
          }
        }
      };
    }

    function showPricingChart() {
        vm.pricingChartDisplayed = true;
    }

    function isCurrentPlan(plan){
      return plan.included_deliveries == vm.currentPlanEmailsAmount && vm.currentIpsPlanCount == plan.ips_count
    }

    function ensureValidDefaultPlanDelivery(){
      var selectedItems = planItems.filter(function(obj){
        return obj.included_deliveries == defaultPlanDeliveries;
      });

      if (selectedItems.length < 1) {
        var defaultPlanSuggested = planItems.reduce(function(prev, curr) {
          return (Math.abs(curr.included_deliveries - defaultPlanDeliveries) < Math.abs(prev.included_deliveries - defaultPlanDeliveries) ? curr : prev);
        });

        defaultPlanDeliveries = defaultPlanSuggested.included_deliveries.toString();
      }
    }

    function isValidUpgradeablePlan() {
      if($rootScope.accountLimits.requiresDomainConfiguration!=undefined)
        vm.upgradePlanErrors.isDomainReady = !$rootScope.accountLimits.requiresDomainConfiguration;
      var isValid=vm.upgradePlanErrors.isDomainReady;
      return isValid;
    }
  }

})();
<|endoftext|>