<commit_before>// ==========================================================================
//                          DG.GameController
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  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.
// ==========================================================================

/* globals iframePhone */
sc_require('controllers/component_controller');

/** @class
 *
 * The GameController manages a Component presumably containing a Data
 * Interactive.
 *
 * From CODAP's perspective a Data Interactive is a data source. It provides
 * data to an associated Data Context.
 *
 * It receives events from the interactive and acts upon them. Chiefly these
 * are events that create or update case data in collections managed by its
 * data context.
 *
 * @extends DG.ComponentController
*/
DG.GameController = DG.ComponentController.extend(
/** @scope DG.GameController.prototype */ {

      confirmCloseDescription: 'DG.GameController.confirmCloseDescription',

      shouldDestroyOnComponentDestroy: true,

      gameIsReady: true,  // Will be set to false at the point we discover we're
                          // going to have a default game loaded

      /**
       * Every game manages directly a default GameContext. A game context knows
       * basic information about the game and has collections and the namespace
       * for the game to store its data into.
       *
       * @property {DG.GameContext}
       */
      contextBinding: '.model.context',

      /**
       The total number of document-dirtying changes.
       @property   {Number}
       */
      changeCount: 0,

      /**
       The number of document-dirtying changes that have been saved.
       If this is less than the total change count, then the document is dirty.
       @property   {Number}
       */
      savedChangeCount: 0,

      /**
       Whether or not the document contains unsaved changes such that the user
       should be prompted to confirm when closing the document, for instance.
       @property   {Boolean}
       */
      hasUnsavedChanges: function() {
        return this.get('changeCount') > this.get('savedChangeCount');
      }.property(),

      /**
       Synchronize the saved change count with the full change count.
       This method should be called when a save occurs, for instance.
       */
      updateSavedChangeCount: function() {
        this.set('savedChangeCount', this.get('changeCount'));
      },

      /**
       * If false, permit the data interactive to participate in normal component
       * selection, including coming to the front.
       * @type {boolean}
       */
      preventBringToFront: true,

      /**
       * If true, prevent collection reorganization for this data interactive's
       * data context.
       *
       */
      preventDataContextReorg: true,

      init: function () {
        sc_super();

        this._cmdQueue = [];

        this.gamePhoneHandler = DG.GamePhoneHandler.create({
          controller: this
        });
        this.dataInteractivePhoneHandler = DG.DataInteractivePhoneHandler.create({
          controller: this,
        });
        this.gamePhoneHandler.addObserver('connected', this, 'connectionDidChange');
        this.dataInteractivePhoneHandler.addObserver('connected', this, 'connectionDidChange');
      },

      /**
       * Break loops
       */
      destroy: function() {
        this.setPath('model.content', null);
        this.removeObserver('gamePhoneHandler.connected', this, 'connectionDidChange');
        this.removeObserver('dataInteractivePhoneHandler.connected', this, 'connectionDidChange');
        if (this.gamePhoneHandler) {
          this.gamePhoneHandler.destroy();
          this.gamePhoneHandler = null;
        }
        if (this.dataInteractivePhoneHandler) {
          this.dataInteractivePhoneHandler.destroy();
          this.dataInteractivePhoneHandler = null;
        }
        if (this.iframePhoneEndpoint) {
          this.iframePhoneEndpoint.disconnect();
        }
        sc_super();
      },

      /**
       * The Parent Endpoint for the iFramePhone connection to the data interactive.
       * @property {i
       */
      iframePhoneEndpoint: null,

      /**
       * Handles the old-style 'game' API using async iframePhone post-messaging
       * @property {DG.GamePhoneHandler}
       */
      gamePhoneHandler: null,

      /**
       * Handles the new-style 'data interactive' API using async iframePhone post-messaging
       * Brought into existence in March, 2016
       * @property {DG.DataInteractivePhoneHandler}
       */
      dataInteractivePhoneHandler: null,

      /**
       * Return the phone handler that is connected. (Only one should be connected.)
       * @property { DG.DataInteractivePhoneHandler | DG.GamePhoneHandler }
       */
      activeChannel: function () {
        if (this.dataInteractivePhoneHandler.get('connected')) {
          return this.dataInteractivePhoneHandler;
        } else if (this.gamePhoneHandler.get('connected')) {
          return this.gamePhoneHandler;
        }
      }.property(),

      /**
       * @property {Boolean}
       */
      isUsingDataInteractiveChannel: function() {
        return this.get('activeChannel') === this.dataInteractivePhoneHandler;
      }.property('activeChannel'),

      connectionDidChange: function () {
        this.sendQueuedCommands();
        this.notifyPropertyChange('activeChannel');
      },

      setUpChannels: function (iFrame, iUrl) {
        var setupHandler = function (iHandler, iKey) {
          var wrapper = function (command, callback) {
            iHandler.set('isPhoneInUse', true);
            iHandler.doCommand(command, function (ret) {
              DG.doCommandResponseHandler(ret, callback);
            });
          };

          //First discontinue listening to old interactive.
          if (iHandler.rpcEndpoint) {
            iHandler.rpcEndpoint.disconnect();
          }

          // Global flag used to indicate whether calls to application should be made via phone, or not.
          iHandler.set('isPhoneInUse', false);

          iHandler.rpcEndpoint = new iframePhone.IframePhoneRpcEndpoint(wrapper.bind(this),
              iKey, iFrame, this.extractOrigin(iUrl), this.iframePhoneEndpoint);
          // Let games/interactives know that they are talking to CODAP, specifically (rather than any
          // old iframePhone supporting page) and can use its API.
          iHandler.rpcEndpoint.call({message: "codap-present"}, function (reply) {
            DG.log('Got codap-present reply on channel: "' + iKey + '": ' + JSON.stringify(reply));
            // success or failure, getting a reply indicates we are connected
          });
        }.bind( this);

        // We create a parent endpoint. The rpc endpoints will live within
        // the raw parent endpoint.
        this.iframePhoneEndpoint = new iframePhone.ParentEndpoint(iFrame,
            this.extractOrigin(iUrl), function () {DG.log('connected');});
        setupHandler(this.get('gamePhoneHandler'), 'codap-game');
        setupHandler(this.get('dataInteractivePhoneHandler'), 'data-interactive');
      },

      queueCommand: function(iCmd, iResponseHandler) {
        this._cmdQueue.push({ cmd: iCmd, responseHandler: iResponseHandler });
      },

      sendQueuedCommands: function() {
        var channel = this.get('activeChannel');
        if (channel) {
          while (this._cmdQueue.length) {
            var entry = this._cmdQueue.shift();
            channel.rpcEndpoint.call(entry.cmd, entry.responseHandler);
          }
        }
      },

      sendCommand: function(iCmd, iResponseHandler) {
        this.queueCommand(iCmd, iResponseHandler);
        this.sendQueuedCommands();
      },

      /**
       * If the URL is a web URL return the origin.
       *
       * The origin is scheme://domain_name.port
       */
      extractOrigin: function (url) {
        var re = /([^:]*:\/\/[^\/]*)/;
        if (/^http.*/i.test(url)) {
          return re.exec(url)[1];
        }
      },

      gameViewWillClose: function() {
      },

      /**
       * Saves the current state of the DataInteractive (if the DataInteractive supports this).
       *
       * This should be assumed to be asynchronous, as it will generally use iFramePhone,
       * although it's possible for it to return synchronously.
       *
       * @param {Function} callback     A function which will be called with a results object:
       * {success: bool, state: state}
       */
      saveGameState: function (callback) {
        var model, modelStorage, handler;
        // State can be stored in one of two places. Older, game API plugins
        // store state in their associated data context. There was a one-one
        // correspondence between a plugin and a data context assumed by
        // the Game API. Modern plugins have state stored in the model.
        if (this.get('isUsingDataInteractiveChannel')) {
          model = this.get('model');
          modelStorage = model && model.get('componentStorage');
          if (!modelStorage && model) {
            model.set('componentStorage', {});
          }
          handler = this.get('dataInteractivePhoneHandler');
          handler.requestDataInteractiveState(function(result) {
            // Documented property for plugin state is result.values
            // Some plugins use result.status for historical reasons.
            // We continue to support, for now.
            modelStorage.savedGameState = result.values || result.state;
            callback(result);
          });
        } else {
          modelStorage = this.get('context');
          handler = this.get('gamePhoneHandler');
          handler.saveGameState(function (result) {
            modelStorage.savedGameState = result.state;
            callback(result);
          });
        }
      },

      /**
       *  Returns an object that should be stored with the document when the document is saved.
       *  @returns  {Object}  An object whose properties should be stored with the document.
       */
      createComponentStorage: function () {
        var tStorage = this.getPath('model.componentStorage');

        if (!SC.none(this.context)) {
          // Save information about the current game
          tStorage.currentGameName = tStorage.currentGameName || this.getPath('context.gameName');
          tStorage.currentGameUrl = tStorage.currentGameUrl || this.getPath('context.gameUrl');
        }

        var dataContext = this.get('context');

        if (dataContext && !this.getLinkID(tStorage, 'context')) {
          this.addLink(tStorage, 'context', dataContext);
        }

        // Save any user-created formulas from DG.FormulaObjects.
        // Currently, only formulas from the current game are saved.
        // Eventually, the formulas should be stored with the DG.GameContext
        // and they should be saved for any game in which formulas are defined.
        tStorage.currentGameFormulas = SC.clone(this.getPath('context.formulas'));

        return tStorage;
      },

      /**
       *  Restores the properties of the DG.GamePhoneHandler from the specified Object.
       *  @param  {Object}  iComponentStorage -- The object whose properties should be restored.
       *  @param  {String}  iDocumentID -- The ID of the document being restored.
       */
      restoreComponentStorage: function (iComponentStorage, iDocumentID) {
        var gameName = iComponentStorage.currentGameName,
            // 'di' URL param can override stored URL
            storedGameUrl = iComponentStorage && iComponentStorage.currentGameUrl,
            gameUrl = DG.finalGameUrl(storedGameUrl),
            contextID,
            dataContext;

        // We try to hook up the appropriate restored context.
        // Try to restore the data context for the current game.
        // First, see if it was written out with the document.
        // (Writing out the link began in build 0175.)
        contextID = this.getLinkID(iComponentStorage, 'context');
        dataContext = contextID && DG.currDocumentController().getContextByID(contextID);
        if (!dataContext) {
          // If it wasn't written out with the document, look for one
          // associated with a game of the correct name, or for the
          // first context as a last resort.
          DG.currDocumentController().contexts.forEach(function (iContext) {
            // Look for a context with matching game name.
            // Note that at the moment, 'gameName' is a computed
            // property which relies on the 'gameSpec' property
            // having been set, which is unlikely to have occurred
            // given that the gameSpec doesn't know about the context.
            // This could change down the road, however, so we leave it.
            if (!dataContext && iContext) {
              var contextGameName = iContext.get('gameName');
              if (contextGameName === gameName) {
                dataContext = iContext;
              }
            }
          });
        }
        if (dataContext) {
          this.set('context', dataContext);
          this.setPath('context.gameName', gameName);
          this.setPath('context.gameUrl', gameUrl);
        }

        // If there are user-created formulas to restore, set them in the
        // gameSpec.
        if (dataContext && iComponentStorage.currentGameFormulas)
          dataContext.set('formulas', iComponentStorage.currentGameFormulas);

        this.set('gameIsReady', true);
      },
      restoreGameState: function (iState) {
        this.gamePhoneHandler.restoreGameState(iState);
      }
    }) ;

/**
 * This entry point allows other parts of the code to send arbitrary notifications
 * to all DataInteractives currently running. The notification should be as defined
 * in https://github.com/concord-consortium/codap/wiki/CODAP-Data-Interactive-API#codap-initiated-actions.
 * If supported for the Game API (https://github.com/concord-consortium/codap/wiki/CODAP-Game-API-(Deprecated))
 * and the Data Interactive has connected through that channel, it will convert the
 * notification and issue it. Otherwise it will not send the command.
 *
 * No monitoring of the actual receipt of the notification is possible through
 * this method. It is possible that the notifications will be made and fail on the
 * DI side.
 *
 * @param iCmd       An object representing the command to be send
 * @return the number of DataInteractives that the event was issued to.
 */
DG.sendCommandToDI = function( iCmd) {
  function translateNotification(notification) {
    if (notification && notification.resource === 'undoChangeNotice') {
      return {operation: notification.values.operation};
    }
  }
  var controllers = DG.currDocumentController().get('dataInteractives');
  var gameNotification = null;
  controllers.forEach(function (controller) {
    var name = controller.getPath('view.title');
    var activeChannel = controller.get('activeChannel');
    var gamePhoneHandler = controller.get('gamePhoneHandler');
    function handleResponse(response) {
      //if (!response || !response.success) {
        //DG.log('Data Interactive Notification to %@ failed: %@'.loc(name, JSON.stringify(iCmd)));
      //}
    }
    if (activeChannel === gamePhoneHandler) {
      if (!gameNotification) {
        gameNotification = translateNotification(iCmd);
      }
      activeChannel.rpcEndpoint.call(gameNotification, handleResponse);
      DG.log('sendCommandToDI: sent game notification to %@: %@'.loc(name, JSON.stringify(gameNotification)));
    } else if (activeChannel) {
      activeChannel.rpcEndpoint.call(iCmd, handleResponse);
      DG.log('sendCommandToDI: sent notification to %@: %@'.loc(name, JSON.stringify(iCmd)));
    }
  });
};

<commit_msg>[#153864692] Bug fix: Closing web page component brings up error dialog box.<commit_after>// ==========================================================================
//                          DG.GameController
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  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.
// ==========================================================================

/* globals iframePhone */
sc_require('controllers/component_controller');

/** @class
 *
 * The GameController manages a Component presumably containing a Data
 * Interactive.
 *
 * From CODAP's perspective a Data Interactive is a data source. It provides
 * data to an associated Data Context.
 *
 * It receives events from the interactive and acts upon them. Chiefly these
 * are events that create or update case data in collections managed by its
 * data context.
 *
 * @extends DG.ComponentController
*/
DG.GameController = DG.ComponentController.extend(
/** @scope DG.GameController.prototype */ {

      confirmCloseDescription: 'DG.GameController.confirmCloseDescription',

      shouldDestroyOnComponentDestroy: true,

      gameIsReady: true,  // Will be set to false at the point we discover we're
                          // going to have a default game loaded

      /**
       * Every game manages directly a default GameContext. A game context knows
       * basic information about the game and has collections and the namespace
       * for the game to store its data into.
       *
       * @property {DG.GameContext}
       */
      contextBinding: '.model.context',

      /**
       The total number of document-dirtying changes.
       @property   {Number}
       */
      changeCount: 0,

      /**
       The number of document-dirtying changes that have been saved.
       If this is less than the total change count, then the document is dirty.
       @property   {Number}
       */
      savedChangeCount: 0,

      /**
       Whether or not the document contains unsaved changes such that the user
       should be prompted to confirm when closing the document, for instance.
       @property   {Boolean}
       */
      hasUnsavedChanges: function() {
        return this.get('changeCount') > this.get('savedChangeCount');
      }.property(),

      /**
       Synchronize the saved change count with the full change count.
       This method should be called when a save occurs, for instance.
       */
      updateSavedChangeCount: function() {
        this.set('savedChangeCount', this.get('changeCount'));
      },

      /**
       * If false, permit the data interactive to participate in normal component
       * selection, including coming to the front.
       * @type {boolean}
       */
      preventBringToFront: true,

      /**
       * If true, prevent collection reorganization for this data interactive's
       * data context.
       *
       */
      preventDataContextReorg: true,

      init: function () {
        sc_super();

        this._cmdQueue = [];

        this.gamePhoneHandler = DG.GamePhoneHandler.create({
          controller: this
        });
        this.dataInteractivePhoneHandler = DG.DataInteractivePhoneHandler.create({
          controller: this,
        });
        this.gamePhoneHandler.addObserver('connected', this, 'connectionDidChange');
        this.dataInteractivePhoneHandler.addObserver('connected', this, 'connectionDidChange');
      },

      /**
       * Break loops
       */
      destroy: function() {
        this.setPath('model.content', null);
        this.removeObserver('gamePhoneHandler.connected', this, 'connectionDidChange');
        this.removeObserver('dataInteractivePhoneHandler.connected', this, 'connectionDidChange');
        if (this.gamePhoneHandler) {
          this.gamePhoneHandler.destroy();
          this.gamePhoneHandler = null;
        }
        if (this.dataInteractivePhoneHandler) {
          this.dataInteractivePhoneHandler.destroy();
          this.dataInteractivePhoneHandler = null;
        }
        if (this.iframePhoneEndpoint) {
          this.iframePhoneEndpoint.disconnect();
        }
        sc_super();
      },

      /**
       * The Parent Endpoint for the iFramePhone connection to the data interactive.
       * @property {i
       */
      iframePhoneEndpoint: null,

      /**
       * Handles the old-style 'game' API using async iframePhone post-messaging
       * @property {DG.GamePhoneHandler}
       */
      gamePhoneHandler: null,

      /**
       * Handles the new-style 'data interactive' API using async iframePhone post-messaging
       * Brought into existence in March, 2016
       * @property {DG.DataInteractivePhoneHandler}
       */
      dataInteractivePhoneHandler: null,

      /**
       * Return the phone handler that is connected. (Only one should be connected.)
       * @property { DG.DataInteractivePhoneHandler | DG.GamePhoneHandler }
       */
      activeChannel: function () {
        if (this.dataInteractivePhoneHandler.get('connected')) {
          return this.dataInteractivePhoneHandler;
        } else if (this.gamePhoneHandler.get('connected')) {
          return this.gamePhoneHandler;
        }
      }.property(),

      /**
       * @property {Boolean}
       */
      isUsingDataInteractiveChannel: function() {
        return this.get('activeChannel') === this.dataInteractivePhoneHandler;
      }.property('activeChannel'),

      connectionDidChange: function () {
        this.sendQueuedCommands();
        this.notifyPropertyChange('activeChannel');
      },

      setUpChannels: function (iFrame, iUrl) {
        var setupHandler = function (iHandler, iKey) {
          var wrapper = function (command, callback) {
            iHandler.set('isPhoneInUse', true);
            iHandler.doCommand(command, function (ret) {
              DG.doCommandResponseHandler(ret, callback);
            });
          };

          //First discontinue listening to old interactive.
          if (iHandler.rpcEndpoint) {
            iHandler.rpcEndpoint.disconnect();
          }

          // Global flag used to indicate whether calls to application should be made via phone, or not.
          iHandler.set('isPhoneInUse', false);

          iHandler.rpcEndpoint = new iframePhone.IframePhoneRpcEndpoint(wrapper.bind(this),
              iKey, iFrame, this.extractOrigin(iUrl), this.iframePhoneEndpoint);
          // Let games/interactives know that they are talking to CODAP, specifically (rather than any
          // old iframePhone supporting page) and can use its API.
          iHandler.rpcEndpoint.call({message: "codap-present"}, function (reply) {
            DG.log('Got codap-present reply on channel: "' + iKey + '": ' + JSON.stringify(reply));
            // success or failure, getting a reply indicates we are connected
          });
        }.bind( this);

        // We create a parent endpoint. The rpc endpoints will live within
        // the raw parent endpoint.
        this.iframePhoneEndpoint = new iframePhone.ParentEndpoint(iFrame,
            this.extractOrigin(iUrl), function () {DG.log('connected');});
        setupHandler(this.get('gamePhoneHandler'), 'codap-game');
        setupHandler(this.get('dataInteractivePhoneHandler'), 'data-interactive');
      },

      queueCommand: function(iCmd, iResponseHandler) {
        this._cmdQueue.push({ cmd: iCmd, responseHandler: iResponseHandler });
      },

      sendQueuedCommands: function() {
        var channel = this.get('activeChannel');
        if (channel) {
          while (this._cmdQueue.length) {
            var entry = this._cmdQueue.shift();
            channel.rpcEndpoint.call(entry.cmd, entry.responseHandler);
          }
        }
      },

      sendCommand: function(iCmd, iResponseHandler) {
        this.queueCommand(iCmd, iResponseHandler);
        this.sendQueuedCommands();
      },

      /**
       * If the URL is a web URL return the origin.
       *
       * The origin is scheme://domain_name.port
       */
      extractOrigin: function (url) {
        var re = /([^:]*:\/\/[^\/]*)/;
        if (/^http.*/i.test(url)) {
          return re.exec(url)[1];
        }
      },

      gameViewWillClose: function() {
      },

      /**
       * Saves the current state of the DataInteractive (if the DataInteractive supports this).
       *
       * This should be assumed to be asynchronous, as it will generally use iFramePhone,
       * although it's possible for it to return synchronously.
       *
       * @param {Function} callback     A function which will be called with a results object:
       * {success: bool, state: state}
       */
      saveGameState: function (callback) {
        var model, modelStorage, handler;
        // State can be stored in one of two places. Older, game API plugins
        // store state in their associated data context. There was a one-one
        // correspondence between a plugin and a data context assumed by
        // the Game API. Modern plugins have state stored in the model.
        if (this.get('isUsingDataInteractiveChannel')) {
          model = this.get('model');
          modelStorage = model && model.get('componentStorage');
          if (!modelStorage && model) {
            model.set('componentStorage', {});
          }
          handler = this.get('dataInteractivePhoneHandler');
          handler.requestDataInteractiveState(function(result) {
            // Documented property for plugin state is result.values
            // Some plugins use result.status for historical reasons.
            // We continue to support, for now.
            modelStorage.savedGameState = result.values || result.state;
            callback(result);
          });
        } else {
          modelStorage = this.get('context');
          handler = this.get('gamePhoneHandler');
          handler.saveGameState(function (result) {
            if(modelStorage)
              modelStorage.savedGameState = result.state;
            callback(result);
          });
        }
      },

      /**
       *  Returns an object that should be stored with the document when the document is saved.
       *  @returns  {Object}  An object whose properties should be stored with the document.
       */
      createComponentStorage: function () {
        var tStorage = this.getPath('model.componentStorage');

        if (!SC.none(this.context)) {
          // Save information about the current game
          tStorage.currentGameName = tStorage.currentGameName || this.getPath('context.gameName');
          tStorage.currentGameUrl = tStorage.currentGameUrl || this.getPath('context.gameUrl');
        }

        var dataContext = this.get('context');

        if (dataContext && !this.getLinkID(tStorage, 'context')) {
          this.addLink(tStorage, 'context', dataContext);
        }

        // Save any user-created formulas from DG.FormulaObjects.
        // Currently, only formulas from the current game are saved.
        // Eventually, the formulas should be stored with the DG.GameContext
        // and they should be saved for any game in which formulas are defined.
        tStorage.currentGameFormulas = SC.clone(this.getPath('context.formulas'));

        return tStorage;
      },

      /**
       *  Restores the properties of the DG.GamePhoneHandler from the specified Object.
       *  @param  {Object}  iComponentStorage -- The object whose properties should be restored.
       *  @param  {String}  iDocumentID -- The ID of the document being restored.
       */
      restoreComponentStorage: function (iComponentStorage, iDocumentID) {
        var gameName = iComponentStorage.currentGameName,
            // 'di' URL param can override stored URL
            storedGameUrl = iComponentStorage && iComponentStorage.currentGameUrl,
            gameUrl = DG.finalGameUrl(storedGameUrl),
            contextID,
            dataContext;

        // We try to hook up the appropriate restored context.
        // Try to restore the data context for the current game.
        // First, see if it was written out with the document.
        // (Writing out the link began in build 0175.)
        contextID = this.getLinkID(iComponentStorage, 'context');
        dataContext = contextID && DG.currDocumentController().getContextByID(contextID);
        if (!dataContext) {
          // If it wasn't written out with the document, look for one
          // associated with a game of the correct name, or for the
          // first context as a last resort.
          DG.currDocumentController().contexts.forEach(function (iContext) {
            // Look for a context with matching game name.
            // Note that at the moment, 'gameName' is a computed
            // property which relies on the 'gameSpec' property
            // having been set, which is unlikely to have occurred
            // given that the gameSpec doesn't know about the context.
            // This could change down the road, however, so we leave it.
            if (!dataContext && iContext) {
              var contextGameName = iContext.get('gameName');
              if (contextGameName === gameName) {
                dataContext = iContext;
              }
            }
          });
        }
        if (dataContext) {
          this.set('context', dataContext);
          this.setPath('context.gameName', gameName);
          this.setPath('context.gameUrl', gameUrl);
        }

        // If there are user-created formulas to restore, set them in the
        // gameSpec.
        if (dataContext && iComponentStorage.currentGameFormulas)
          dataContext.set('formulas', iComponentStorage.currentGameFormulas);

        this.set('gameIsReady', true);
      },
      restoreGameState: function (iState) {
        this.gamePhoneHandler.restoreGameState(iState);
      }
    }) ;

/**
 * This entry point allows other parts of the code to send arbitrary notifications
 * to all DataInteractives currently running. The notification should be as defined
 * in https://github.com/concord-consortium/codap/wiki/CODAP-Data-Interactive-API#codap-initiated-actions.
 * If supported for the Game API (https://github.com/concord-consortium/codap/wiki/CODAP-Game-API-(Deprecated))
 * and the Data Interactive has connected through that channel, it will convert the
 * notification and issue it. Otherwise it will not send the command.
 *
 * No monitoring of the actual receipt of the notification is possible through
 * this method. It is possible that the notifications will be made and fail on the
 * DI side.
 *
 * @param iCmd       An object representing the command to be send
 * @return the number of DataInteractives that the event was issued to.
 */
DG.sendCommandToDI = function( iCmd) {
  function translateNotification(notification) {
    if (notification && notification.resource === 'undoChangeNotice') {
      return {operation: notification.values.operation};
    }
  }
  var controllers = DG.currDocumentController().get('dataInteractives');
  var gameNotification = null;
  controllers.forEach(function (controller) {
    var name = controller.getPath('view.title');
    var activeChannel = controller.get('activeChannel');
    var gamePhoneHandler = controller.get('gamePhoneHandler');
    function handleResponse(response) {
      //if (!response || !response.success) {
        //DG.log('Data Interactive Notification to %@ failed: %@'.loc(name, JSON.stringify(iCmd)));
      //}
    }
    if (activeChannel === gamePhoneHandler) {
      if (!gameNotification) {
        gameNotification = translateNotification(iCmd);
      }
      activeChannel.rpcEndpoint.call(gameNotification, handleResponse);
      DG.log('sendCommandToDI: sent game notification to %@: %@'.loc(name, JSON.stringify(gameNotification)));
    } else if (activeChannel) {
      activeChannel.rpcEndpoint.call(iCmd, handleResponse);
      DG.log('sendCommandToDI: sent notification to %@: %@'.loc(name, JSON.stringify(iCmd)));
    }
  });
};

<|endoftext|>


<commit_before>import { FormattedMessage as T } from "react-intl";
import { Balance, Tooltip } from "shared";

const AccountsList = ({
  account,
  showRenameAccount,
  hidden,
  hideAccount,
  showAccount,
}) => (

              <T id="accounts.balances" m="Balances" />

              <T id="accounts.total" m="Total" />

<Balance amount={account.total} />

              <T id="accounts.details.spendable" m="Spendable" />

<Balance amount={account.spendable} />

              <T id="accounts.immatureRewards" m="Immature Rewards" />

<Balance amount={account.immatureReward} />

              <T id="accounts.lockedByTickets" m="Locked By Tickets" />

<Balance amount={account.lockedByTickets} />

              <T id="accounts.votingAuthority" m="Voting Authority" />

<Balance amount={account.votingAuthority} />

              <T id="accounts.immatureStake" m="Immature Stake Generation" />

<Balance amount={account.immatureStakeGeneration} />

<T id="accounts.properties" m="Properties" />

<T id="accounts.number" m="Account number" />

{account.accountNumber}

<T id="accounts.hdPath" m="HD Path" />

{account.HDPath}

<T id="accounts.keys" m="Keys" />

              <T id="accounts.keys.external" m="{keys} external" values={{ keys: account.externalKeys }} />
              <T id="accounts.keys.internal" m="{keys} internal" values={{ keys: account.internalKeys }} />
              <T id="accounts.keys.imported" m="{keys} imported" values={{ keys: account.importedKeys }} />

        {account.accountName !== "imported" ?
          <Tooltip text={<T id="accounts.rename.tip" m="Rename Account" />}>

          </Tooltip> :

        }
        {account.accountName !== "imported" && account.accountName !== "default" && account.total == 0 && !hidden ?
          <Tooltip text={<T id="accounts.show.tip" m="Show" />}>

          </Tooltip> :
          account.accountName !== "imported" && account.accountName !== "default" && hidden ?
            <Tooltip text={<T id="accounts.hide.tip" m="Hide" />}>

            </Tooltip> :

        }

  );

AccountsList.propTypes = {
  account: PropTypes.object.isRequired,
  showRenameAccount: PropTypes.func.isRequired,
  hidden: PropTypes.bool.isRequired,
  hideAccount: PropTypes.func.isRequired,
  showAccount: PropTypes.func.isRequired,
};

export default AccountsList;
<commit_msg>Switch account keys to a single translated message (#1004)<commit_after>import { FormattedMessage as T } from "react-intl";
import { Balance, Tooltip } from "shared";

const AccountsList = ({
  account,
  showRenameAccount,
  hidden,
  hideAccount,
  showAccount,
}) => (

              <T id="accounts.balances" m="Balances" />

              <T id="accounts.total" m="Total" />

<Balance amount={account.total} />

              <T id="accounts.details.spendable" m="Spendable" />

<Balance amount={account.spendable} />

              <T id="accounts.immatureRewards" m="Immature Rewards" />

<Balance amount={account.immatureReward} />

              <T id="accounts.lockedByTickets" m="Locked By Tickets" />

<Balance amount={account.lockedByTickets} />

              <T id="accounts.votingAuthority" m="Voting Authority" />

<Balance amount={account.votingAuthority} />

              <T id="accounts.immatureStake" m="Immature Stake Generation" />

<Balance amount={account.immatureStakeGeneration} />

<T id="accounts.properties" m="Properties" />

<T id="accounts.number" m="Account number" />

{account.accountNumber}

<T id="accounts.hdPath" m="HD Path" />

{account.HDPath}

<T id="accounts.keys" m="Keys" />

              <T id="accounts.keys.counts" m="{external} external, {internal} internal, {imported} imported"
                values={{
                  external: account.externalKeys,
                  internal: account.internalKeys,
                  imported: account.importedKeys }} />

        {account.accountName !== "imported" ?
          <Tooltip text={<T id="accounts.rename.tip" m="Rename Account" />}>

          </Tooltip> :

        }
        {account.accountName !== "imported" && account.accountName !== "default" && account.total == 0 && !hidden ?
          <Tooltip text={<T id="accounts.show.tip" m="Show" />}>

          </Tooltip> :
          account.accountName !== "imported" && account.accountName !== "default" && hidden ?
            <Tooltip text={<T id="accounts.hide.tip" m="Hide" />}>

            </Tooltip> :

        }

  );

AccountsList.propTypes = {
  account: PropTypes.object.isRequired,
  showRenameAccount: PropTypes.func.isRequired,
  hidden: PropTypes.bool.isRequired,
  hideAccount: PropTypes.func.isRequired,
  showAccount: PropTypes.func.isRequired,
};

export default AccountsList;
<|endoftext|>


<commit_before>/*!
 *  mindmaps - a HTML5 powered mind mapping application
 *  Copyright (C) 2011  David Richard
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Make sure this is the first file to be referenced in index.hml.
 */

// Use ECMA5 strict mode. see:
// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
"use strict";

/**
 * @namespace Application wide namespace for mindmaps.
 */
var mindmaps = mindmaps || {};

/**
 * Start up. This function is executed when the DOM is loaded.
 */
$(function() {
  removeEventLayerXY();

  // take car of old browsers
  createECMA5Shims();
  createHTML5Shims();

  setupConsole();
  trackErrors();

  if (!mindmaps.DEBUG) {
    addUnloadHook();
  }

  // create a new app controller and go
  var appController = new mindmaps.ApplicationController();
  appController.go();
});

/**
 * Remove layerX and layerY from the jQuery event object, it causes heaps of deprecated
 * warnings in WebKit browsers.
 * See: http://stackoverflow.com/questions/7825448/webkit-issues-with-event-layerx-and-event-layery
 *
 * Can be removed when upgrading to jQuery 1.7+.
 */
function removeEventLayerXY() {
  // remove layerX and layerY
  var all = $.event.props,
  len = all.length,
  res = [];

  while (len--) {
    var el = all[len];
    if (el != 'layerX' && el != 'layerY') res.push(el);
  }
  $.event.props = res;
}

/**
* Adds a confirmation dialog when the user navigates away from the app.
*/
function addUnloadHook () {
  window.onbeforeunload = function (e) {
    var msg = "Are you sure? Any unsaved progress will be lost."
    e = e || window.event;

    // For IE and Firefox prior to version 4
    if (e) {
      e.returnValue = msg;
    }

    // For Safari
    return msg;
  };
}

function trackErrors() {
  window.onerror = function(msg, url, line) {
    if (!window._gaq) {
      return;
    }

    // Track JS errors in GA.
    _gaq.push([ '_trackEvent', 'Error Log', msg, url + '_' + line ]);

    return false; // false prevents default error handling.
  };
}

// TODO make non global
/**
* Initialize the console object.
*/
function setupConsole() {
  var noOp = function() {
  };

  // provide console object and dummy functions if not built-in
  var console = window.console || {};
  console.log = console.log || noOp;
  console.info = console.info || noOp;
  console.debug = console.debug || noOp;
  console.warn = console.warn || noOp;
  console.error = console.error || noOp;

  // turn all console.xx calls into no-ops when in production mode except
  // for errors, do an alert.
  if (!mindmaps.DEBUG) {
    console.debug = noOp;
    console.info = noOp;
    console.log = noOp;
    console.warn = noOp;
    console.error = function(s) {
      window.alert("Error: " + s);
    };
  }
}

/**
* Creates ECMA5 shims if the browser does not implement them.
*/
function createECMA5Shims() {
  // from: https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js

  // ES-5 15.3.4.5
  // http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf
  if (!Function.prototype.bind) {
    var slice = Array.prototype.slice;
    Function.prototype.bind = function bind(that) { // .length is 1
      var target = this;
      if (typeof target.apply !== "function"
      || typeof target.call !== "function")
      return new TypeError();
      var args = slice.call(arguments);

      function bound() {

        if (this instanceof bound) {

          var self = Object.create(target.prototype);
          target.apply(self, args.concat(slice.call(arguments)));
          return self;
        } else {
          return target.call.apply(target, args.concat(slice
            .call(arguments)));
        }

      }
      bound.length = (typeof target === "function" ? Math.max(
      target.length - args.length, 0) : 0);
      return bound;
    };
  }

  // ES5 15.4.3.2
  if (!Array.isArray) {
    Array.isArray = function isArray(obj) {
      return Object.prototype.toString.call(obj) === "[object Array]";
    };
  }

  // ES5 15.4.4.18
  if (!Array.prototype.forEach) {
    Array.prototype.forEach = function forEach(block, thisObject) {
      var len = +this.length;
      for ( var i = 0; i < len; i++) {
        if (i in this) {
          block.call(thisObject, this[i], i, this);
        }
      }
    };
  }

  // ES5 15.4.4.19
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
  if (!Array.prototype.map) {
    Array.prototype.map = function map(fun /* , thisp */) {
      var len = +this.length;
      if (typeof fun !== "function")
        throw new TypeError();

      var res = new Array(len);
      var thisp = arguments[1];
      for ( var i = 0; i < len; i++) {
        if (i in this)
          res[i] = fun.call(thisp, this[i], i, this);
      }

      return res;
    };
  }

  // ES5 15.4.4.20
  if (!Array.prototype.filter) {
    Array.prototype.filter = function filter(block /* , thisp */) {
      var values = [];
      var thisp = arguments[1];
      for ( var i = 0; i < this.length; i++)
        if (block.call(thisp, this[i]))
          values.push(this[i]);
        return values;
    };
  }

  // ES5 15.4.4.16
  if (!Array.prototype.every) {
    Array.prototype.every = function every(block /* , thisp */) {
      var thisp = arguments[1];
      for ( var i = 0; i < this.length; i++)
        if (!block.call(thisp, this[i]))
          return false;
        return true;
    };
  }

  // ES5 15.4.4.17
  if (!Array.prototype.some) {
    Array.prototype.some = function some(block /* , thisp */) {
      var thisp = arguments[1];
      for ( var i = 0; i < this.length; i++)
        if (block.call(thisp, this[i]))
          return true;
        return false;
    };
  }

  // ES5 15.4.4.21
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
  if (!Array.prototype.reduce) {
    Array.prototype.reduce = function reduce(fun /* , initial */) {
      var len = +this.length;
      if (typeof fun !== "function")
        throw new TypeError();

      // no value to return if no initial value and an empty array
      if (len === 0 && arguments.length === 1)
        throw new TypeError();

      var i = 0;
      if (arguments.length >= 2) {
        var rv = arguments[1];
      } else {
        do {
          if (i in this) {
            rv = this[i++];
            break;
          }

          // if array contains no values, no initial value to return
          if (++i >= len)
            throw new TypeError();
        } while (true);
      }

      for (; i < len; i++) {
        if (i in this)
          rv = fun.call(null, rv, this[i], i, this);
      }

      return rv;
    };
  }

  // ES5 15.4.4.22
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
  if (!Array.prototype.reduceRight) {
    Array.prototype.reduceRight = function reduceRight(fun /* , initial */) {
      var len = +this.length;
      if (typeof fun !== "function")
        throw new TypeError();

      // no value to return if no initial value, empty array
      if (len === 0 && arguments.length === 1)
        throw new TypeError();

      var rv, i = len - 1;
      if (arguments.length >= 2) {
        rv = arguments[1];
      } else {
        do {
          if (i in this) {
            rv = this[i--];
            break;
          }

          // if array contains no values, no initial value to return
          if (--i < 0)
            throw new TypeError();
        } while (true);
      }

      for (; i >= 0; i--) {
        if (i in this)
          rv = fun.call(null, rv, this[i], i, this);
      }

      return rv;
    };
  }

  // ES5 15.4.4.14
  if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function indexOf(value /* , fromIndex */) {
      var length = this.length;
      if (!length)
        return -1;
      var i = arguments[1] || 0;
      if (i >= length)
        return -1;
      if (i < 0)
        i += length;
      for (; i < length; i++) {
        if (!(i in this))
          continue;
        if (value === this[i])
          return i;
      }
      return -1;
    };
  }

  // ES5 15.4.4.15
  if (!Array.prototype.lastIndexOf) {
    Array.prototype.lastIndexOf = function lastIndexOf(value) {
      var length = this.length;
      if (!length)
        return -1;
      var i = arguments[1] || length;
      if (i < 0)
        i += length;
      i = Math.min(i, length - 1);
      for (; i >= 0; i--) {
        if (!(i in this))
          continue;
        if (value === this[i])
          return i;
      }
      return -1;
    };
  }

  // ES5 15.9.4.4
  if (!Date.now) {
    Date.now = function now() {
      return new Date().getTime();
    };
  }
}

/**
* Create shims for HTML5 functionality if not supported by browser.
*/
function createHTML5Shims() {
  // localstorage dummy (does nothing)
  if (typeof window.localStorage == 'undefined') {
    window.localStorage = {
      getItem : function() {
        return null;
      },
      setItem : function() {
      },
      clear : function() {
      },
      removeItem : function() {
      },
      length : 0,
      key : function() {
        return null;
      }
    };
  }
}
<commit_msg>try appcache invalidator again<commit_after>/*!
 *  mindmaps - a HTML5 powered mind mapping application
 *  Copyright (C) 2011  David Richard
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Make sure this is the first file to be referenced in index.hml.
 */

// Use ECMA5 strict mode. see:
// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
"use strict";

/**
 * @namespace Application wide namespace for mindmaps.
 */
var mindmaps = mindmaps || {};

// experimental app cache invalidator. from:
// http://www.html5rocks.com/en/tutorials/appcache/beginner/#toc-updating-cache/
// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {
  window.applicationCache.addEventListener('updateready', function(e) {
    if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
      // Browser downloaded a new app cache.
      window.applicationCache.swapCache();
      window.onbeforeunload = null;
      if (confirm('A new version of the app is available. Load it?')) {
        window.location.reload();
      }
    } else {
      // Manifest didn't changed. Nothing new to server.
    }
  }, false);

}, false)

/**
 * Start up. This function is executed when the DOM is loaded.
 */
$(function() {
  removeEventLayerXY();

  // take car of old browsers
  createECMA5Shims();
  createHTML5Shims();

  setupConsole();
  trackErrors();

  if (!mindmaps.DEBUG) {
    addUnloadHook();
  }

  // create a new app controller and go
  var appController = new mindmaps.ApplicationController();
  appController.go();
});

/**
 * Remove layerX and layerY from the jQuery event object, it causes heaps of deprecated
 * warnings in WebKit browsers.
 * See: http://stackoverflow.com/questions/7825448/webkit-issues-with-event-layerx-and-event-layery
 *
 * Can be removed when upgrading to jQuery 1.7+.
 */
function removeEventLayerXY() {
  // remove layerX and layerY
  var all = $.event.props,
  len = all.length,
  res = [];

  while (len--) {
    var el = all[len];
    if (el != 'layerX' && el != 'layerY') res.push(el);
  }
  $.event.props = res;
}

/**
* Adds a confirmation dialog when the user navigates away from the app.
*/
function addUnloadHook () {
  window.onbeforeunload = function (e) {
    var msg = "Are you sure? Any unsaved progress will be lost."
    e = e || window.event;

    // For IE and Firefox prior to version 4
    if (e) {
      e.returnValue = msg;
    }

    // For Safari
    return msg;
  };
}

function trackErrors() {
  window.onerror = function(msg, url, line) {
    if (!window._gaq) {
      return;
    }

    // Track JS errors in GA.
    _gaq.push([ '_trackEvent', 'Error Log', msg, url + '_' + line ]);

    return false; // false prevents default error handling.
  };
}

// TODO make non global
/**
* Initialize the console object.
*/
function setupConsole() {
  var noOp = function() {
  };

  // provide console object and dummy functions if not built-in
  var console = window.console || {};
  console.log = console.log || noOp;
  console.info = console.info || noOp;
  console.debug = console.debug || noOp;
  console.warn = console.warn || noOp;
  console.error = console.error || noOp;

  // turn all console.xx calls into no-ops when in production mode except
  // for errors, do an alert.
  if (!mindmaps.DEBUG) {
    console.debug = noOp;
    console.info = noOp;
    console.log = noOp;
    console.warn = noOp;
    console.error = function(s) {
      window.alert("Error: " + s);
    };
  }
}

/**
* Creates ECMA5 shims if the browser does not implement them.
*/
function createECMA5Shims() {
  // from: https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js

  // ES-5 15.3.4.5
  // http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf
  if (!Function.prototype.bind) {
    var slice = Array.prototype.slice;
    Function.prototype.bind = function bind(that) { // .length is 1
      var target = this;
      if (typeof target.apply !== "function"
      || typeof target.call !== "function")
      return new TypeError();
      var args = slice.call(arguments);

      function bound() {

        if (this instanceof bound) {

          var self = Object.create(target.prototype);
          target.apply(self, args.concat(slice.call(arguments)));
          return self;
        } else {
          return target.call.apply(target, args.concat(slice
            .call(arguments)));
        }

      }
      bound.length = (typeof target === "function" ? Math.max(
      target.length - args.length, 0) : 0);
      return bound;
    };
  }

  // ES5 15.4.3.2
  if (!Array.isArray) {
    Array.isArray = function isArray(obj) {
      return Object.prototype.toString.call(obj) === "[object Array]";
    };
  }

  // ES5 15.4.4.18
  if (!Array.prototype.forEach) {
    Array.prototype.forEach = function forEach(block, thisObject) {
      var len = +this.length;
      for ( var i = 0; i < len; i++) {
        if (i in this) {
          block.call(thisObject, this[i], i, this);
        }
      }
    };
  }

  // ES5 15.4.4.19
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
  if (!Array.prototype.map) {
    Array.prototype.map = function map(fun /* , thisp */) {
      var len = +this.length;
      if (typeof fun !== "function")
        throw new TypeError();

      var res = new Array(len);
      var thisp = arguments[1];
      for ( var i = 0; i < len; i++) {
        if (i in this)
          res[i] = fun.call(thisp, this[i], i, this);
      }

      return res;
    };
  }

  // ES5 15.4.4.20
  if (!Array.prototype.filter) {
    Array.prototype.filter = function filter(block /* , thisp */) {
      var values = [];
      var thisp = arguments[1];
      for ( var i = 0; i < this.length; i++)
        if (block.call(thisp, this[i]))
          values.push(this[i]);
        return values;
    };
  }

  // ES5 15.4.4.16
  if (!Array.prototype.every) {
    Array.prototype.every = function every(block /* , thisp */) {
      var thisp = arguments[1];
      for ( var i = 0; i < this.length; i++)
        if (!block.call(thisp, this[i]))
          return false;
        return true;
    };
  }

  // ES5 15.4.4.17
  if (!Array.prototype.some) {
    Array.prototype.some = function some(block /* , thisp */) {
      var thisp = arguments[1];
      for ( var i = 0; i < this.length; i++)
        if (block.call(thisp, this[i]))
          return true;
        return false;
    };
  }

  // ES5 15.4.4.21
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
  if (!Array.prototype.reduce) {
    Array.prototype.reduce = function reduce(fun /* , initial */) {
      var len = +this.length;
      if (typeof fun !== "function")
        throw new TypeError();

      // no value to return if no initial value and an empty array
      if (len === 0 && arguments.length === 1)
        throw new TypeError();

      var i = 0;
      if (arguments.length >= 2) {
        var rv = arguments[1];
      } else {
        do {
          if (i in this) {
            rv = this[i++];
            break;
          }

          // if array contains no values, no initial value to return
          if (++i >= len)
            throw new TypeError();
        } while (true);
      }

      for (; i < len; i++) {
        if (i in this)
          rv = fun.call(null, rv, this[i], i, this);
      }

      return rv;
    };
  }

  // ES5 15.4.4.22
  // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
  if (!Array.prototype.reduceRight) {
    Array.prototype.reduceRight = function reduceRight(fun /* , initial */) {
      var len = +this.length;
      if (typeof fun !== "function")
        throw new TypeError();

      // no value to return if no initial value, empty array
      if (len === 0 && arguments.length === 1)
        throw new TypeError();

      var rv, i = len - 1;
      if (arguments.length >= 2) {
        rv = arguments[1];
      } else {
        do {
          if (i in this) {
            rv = this[i--];
            break;
          }

          // if array contains no values, no initial value to return
          if (--i < 0)
            throw new TypeError();
        } while (true);
      }

      for (; i >= 0; i--) {
        if (i in this)
          rv = fun.call(null, rv, this[i], i, this);
      }

      return rv;
    };
  }

  // ES5 15.4.4.14
  if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function indexOf(value /* , fromIndex */) {
      var length = this.length;
      if (!length)
        return -1;
      var i = arguments[1] || 0;
      if (i >= length)
        return -1;
      if (i < 0)
        i += length;
      for (; i < length; i++) {
        if (!(i in this))
          continue;
        if (value === this[i])
          return i;
      }
      return -1;
    };
  }

  // ES5 15.4.4.15
  if (!Array.prototype.lastIndexOf) {
    Array.prototype.lastIndexOf = function lastIndexOf(value) {
      var length = this.length;
      if (!length)
        return -1;
      var i = arguments[1] || length;
      if (i < 0)
        i += length;
      i = Math.min(i, length - 1);
      for (; i >= 0; i--) {
        if (!(i in this))
          continue;
        if (value === this[i])
          return i;
      }
      return -1;
    };
  }

  // ES5 15.9.4.4
  if (!Date.now) {
    Date.now = function now() {
      return new Date().getTime();
    };
  }
}

/**
* Create shims for HTML5 functionality if not supported by browser.
*/
function createHTML5Shims() {
  // localstorage dummy (does nothing)
  if (typeof window.localStorage == 'undefined') {
    window.localStorage = {
      getItem : function() {
        return null;
      },
      setItem : function() {
      },
      clear : function() {
      },
      removeItem : function() {
      },
      length : 0,
      key : function() {
        return null;
      }
    };
  }
}
<|endoftext|>


<commit_before>vde.App.directive('vdeProperty', function($rootScope, logger, timeline) {
  return {
    restrict: 'E',
    scope: {
      label: '@',
      type: '@',
      max: '@',
      min: '@',
      step: '@',
      item: '=',
      property: '@',
      ngModel: '=',
      scale: '=',
      field: '=',
      options: '=',
      nodrop: '@',
      nochange: '@',
      hint: '@'
    },
    transclude: true,
    templateUrl: 'tmpl/inspectors/property.html',
    controller: function($scope, $element, $attrs, $timeout) {
      $scope.onchange = function(prop) {
        if($attrs.nochange) return;
        if('checkExtents' in $scope.item)
          $scope.item.checkExtents(prop || $scope.property);

        $timeout(function() {
          if($scope.item.update) $scope.item.update(prop || $attrs.property);
          else vde.Vis.parse();

          vde.iVis.show('selected');

          timeline.save();
        }, 1);

        logger.log('onchange', {
          item: $scope.item.name,
          group: $scope.item.groupName,
          pipeline: $scope.item.pipelineName,
          property: $attrs.property,
          ngModel: $attrs.ngModel,
          value: $scope.ngModel
        });
      };

      $scope.unbind = function(property) {
        $scope.item.unbindProperty(property);
        vde.Vis.parse();

        logger.log('unbind', {
          item: $scope.item.name,
          group: $scope.item.groupName,
          pipeline: $scope.item.pipelineName,
          property: $attrs.property,
          ngModel: $attrs.ngModel
        }, true, true);

        timeline.save();
      };

      $scope.showHelper = function(target, e, helperClass) {
        if($scope.item instanceof vde.Vis.Mark) $scope.item.helper($attrs.property);

        target.addClass(helperClass);
      };

      $scope.hideHelper = function(target, e, helperClass) {
        target.removeClass(helperClass);
        if(target.hasClass('helper') || target.hasClass('drophover')) return;

        if(!vde.iVis.dragging) vde.iVis.show('selected');
        else if($rootScope.activeVisual instanceof vde.Vis.Mark)
          $rootScope.activeVisual.propertyTargets();
      };
    },
    link: function(scope, element, attrs) {
      if(attrs.nodrop) return;

      $(element).find('.property').on('mousemove', function(e) {
        scope.showHelper($(this), e, 'helper');
      })
      .on('mouseleave', function(e) {
        scope.hideHelper($(this), e, 'helper');
      }) // Clear helpers
      .drop(function(e, dd) {
        if(element.find('.expr').length > 0) return element.find('.expr').drop(e, dd);
        if($rootScope.activeScale && $rootScope.activeScale != scope.item) return;

        vde.iVis.bindProperty(scope.item, attrs.property);
        dd.proxy = null;
      }).drop('dropstart', function(e) {
        if($rootScope.activeScale && $rootScope.activeScale != scope.item) return;
        scope.showHelper($(this), e, 'drophover');
      }).drop('dropend', function(e) {
        if($rootScope.activeScale && $rootScope.activeScale != scope.item) return;
        scope.hideHelper($(this), e, 'drophover');
      })
    }
  }
});

vde.App.directive('vdeBinding', function($compile, $rootScope, $timeout, logger) {
  return {
    restrict: 'E',
    scope: {
      scale: '=',
      field: '=',
      draggable: '@'
    },
    templateUrl: 'tmpl/inspectors/binding.html',
    controller: function($scope, $element, $attrs) {
      // if($attrs.draggable) {
        var el = $compile("

")($scope);
        $element.append(el);
      // }

      $rootScope.aggregate = function(stat) {
        var field = $rootScope.activeField;
        field.pipeline().aggregate(field, stat);
        $timeout(function() { vde.Vis.parse(); }, 1);
        $('#aggregate-inspector').hide();

        timeline.save();
      };

      $scope.editBinding = function(evt, part) {
        var inspector = null;
        if(part == 'scale') {
          inspector = $('#binding-inspector');
          $rootScope.activeScale = inspector.is(':visible') ? null : $scope.scale;
          vde.iVis.parse($rootScope.activeScale); // Visualize scale
        } else {
          inspector = $('#aggregate-inspector');
          $rootScope.activeField = inspector.is(':visible') ? null : $scope.field;
        }

        $timeout(function() {
          var winHeight = $(window).height(), winWidth = $(window).width(),
              pageX = evt.pageX, pageY = evt.pageY;

          inspector.css('left', (pageX-15) + 'px');
          $('.bubble', inspector).removeClass('top-left top-right bottom-left bottom-right');
          var className = '';
          if(pageY > winHeight / 2) { // If below half-way, position top
            inspector.css('top', (pageY - inspector.height() - 25) + 'px');
            className = 'bottom';
          } else {
            inspector.css('top', (pageY + 25) + 'px');
            className = 'top';
          }

          if(pageX > winWidth/2) {
            inspector.css('left', (pageX - inspector.width()) + 'px');
            className += '-right';
          } else {
            inspector.css('left', (pageX - 10) + 'px');
            className += '-left';
          }

          $('.bubble', inspector).addClass(className);
          inspector.toggle();
        }, 100);
      };
    },
    link: function(scope, element, attrs) {
      // if(attrs.draggable) {
        var binding = element.find('.binding');
        element.find('.binding-draggable').append(binding);
      // }
      $timeout(function() {
        if(scope.field instanceof vde.Vis.Field)
          element.find('.schema').data('field', scope.field);
      }, 100)
    }
  }
});

vde.App.directive('vdeExpr', function($rootScope, $compile, $timeout, logger) {
  return {
    restrict: 'A',
    scope: {
      item: '=',
      property: '=',
      ngModel: '=',
      vdeExpr: '@'
    },
    template: '

',
    link: function(scope, element, attrs) {
      var parse = function() {
        var elem = $(element).find('.expr');
        var html  = elem.html().replace('
','');
        var value = $('
' + html + '
');
        var strConcat = (attrs.vdeExpr == 'str');
        var digesting = (scope.$$phase || scope.$root.$$phase);

        // When we add a transform containing an expr to the pipeline, the references
        // change and this function is called before .expr.html() is rendered correctly.
        if(digesting && html == "") return;

        value.find('.schema').each(function(i, e) {
          if(strConcat) $(e).text('" + d.' + $(e).attr('field-spec') + ' + "');
          else          $(e).text('d.' + $(e).attr('field-spec'));
        });

        var applyProperties = function() {
          scope.item.properties[scope.property] = strConcat ? '"' + value.text() + '"' : value.text();
          scope.item.properties[scope.property + 'Html'] = html;

          scope.$parent.onchange();
          vde.Vis.parse();
        };

        // Safe apply in case parse is called from within a watch.
        digesting ? applyProperties() : scope.$apply(applyProperties);
      };

      $(element).find('.expr')
        // .html(scope.$parent.ngModel)
        .drop(function(e, dd) {
          var proxy = vde.iVis.dragging, expr = this;
          var field = $(proxy).data('field') || $(proxy).find('.schema').data('field') || $(proxy).find('.schema').attr('field');
          if(!field) return;

          if(scope.item instanceof vde.Vis.Transform &&
            !scope.item.requiresFork && field instanceof vde.Vis.Field)
              scope.item.requiresFork = ($rootScope.activePipeline.name != field.pipelineName);

          var bindingScope = $rootScope.$new();
          bindingScope.field = new vde.Vis.Field(field);
          scope.item.exprFields.push(bindingScope.field);
          var binding = $compile('<vde-binding style="display: none" field="field"></vde-binding>')(bindingScope);
          scope.$apply();

          $(this).append(binding.find('.schema').attr('contenteditable', 'false'));

          if(dd) dd.proxy = null;
          $('.proxy').remove();
//          parse();
          $(this).focus();
        }).drop('dropstart', function() {
          $(this).parent().css('borderColor', '#333');
        }).drop('dropend', function() {
          $(this).parent().css('borderColor', '#aaa');
        })
        .bind('keyup', function(e) { parse(); })
        .bind('click', function() { $(this).focus(); });

      // This captures any aggregation changes made to the fields used. We need to set it on
      // a timeout because parse requires the html of element to have been completely rendered.
      scope.$watch('item.exprFields', function() { $timeout(function() { parse() }, 100) }, true);

      // NgModel is registered on the top-level directive. We need this to move the value of
      // the model into our editable div.
      scope.$watch(function($scope) { return $scope.ngModel },
        function() {
          var expr = $(element).find('.expr'), html = scope.ngModel;
          if(expr.html() != html) expr.html(html)
        }, true);
    }
  }
});

vde.App.directive('vdeScaleValues', function() {
  return {
    restrict: 'E',
    scope: {
      type: '@',
      scale: '=',
      property: '@',
      options: '=',
      ngModel: '='
    },
    templateUrl: 'tmpl/inspectors/scale-values.html',
    controller: function($scope, $element, $attrs) {
      $scope.values = (($scope.scale || {})[$scope.property] || []).map(function(v) { return {value: v} });

      $scope.update = function() {
        $scope.scale[$scope.property] = vg.keys($scope.values).map(function(k) { return $scope.values[k].value; });
        vde.Vis.parse();
      }

      $scope.add = function(evt, button) {
        if((evt && evt.keyCode != 13) && !button) return;
        $scope.values.push({ value: $scope.newValue });
        $scope.update();
        $scope.newValue = '';
      }

      $scope.delete = function($index) {
        if($scope.values[$index].value == '') $scope.values.splice($index, 1);
        $scope.update();
      }

      $scope.foo = function() { console.log('foo'); }
    }
  }
})
<commit_msg>Bug fix, inject timeline<commit_after>vde.App.directive('vdeProperty', function($rootScope, logger, timeline) {
  return {
    restrict: 'E',
    scope: {
      label: '@',
      type: '@',
      max: '@',
      min: '@',
      step: '@',
      item: '=',
      property: '@',
      ngModel: '=',
      scale: '=',
      field: '=',
      options: '=',
      nodrop: '@',
      nochange: '@',
      hint: '@'
    },
    transclude: true,
    templateUrl: 'tmpl/inspectors/property.html',
    controller: function($scope, $element, $attrs, $timeout) {
      $scope.onchange = function(prop) {
        if($attrs.nochange) return;
        if('checkExtents' in $scope.item)
          $scope.item.checkExtents(prop || $scope.property);

        $timeout(function() {
          if($scope.item.update) $scope.item.update(prop || $attrs.property);
          else vde.Vis.parse();

          vde.iVis.show('selected');

          timeline.save();
        }, 1);

        logger.log('onchange', {
          item: $scope.item.name,
          group: $scope.item.groupName,
          pipeline: $scope.item.pipelineName,
          property: $attrs.property,
          ngModel: $attrs.ngModel,
          value: $scope.ngModel
        });
      };

      $scope.unbind = function(property) {
        $scope.item.unbindProperty(property);
        vde.Vis.parse();

        logger.log('unbind', {
          item: $scope.item.name,
          group: $scope.item.groupName,
          pipeline: $scope.item.pipelineName,
          property: $attrs.property,
          ngModel: $attrs.ngModel
        }, true, true);

        timeline.save();
      };

      $scope.showHelper = function(target, e, helperClass) {
        if($scope.item instanceof vde.Vis.Mark) $scope.item.helper($attrs.property);

        target.addClass(helperClass);
      };

      $scope.hideHelper = function(target, e, helperClass) {
        target.removeClass(helperClass);
        if(target.hasClass('helper') || target.hasClass('drophover')) return;

        if(!vde.iVis.dragging) vde.iVis.show('selected');
        else if($rootScope.activeVisual instanceof vde.Vis.Mark)
          $rootScope.activeVisual.propertyTargets();
      };
    },
    link: function(scope, element, attrs) {
      if(attrs.nodrop) return;

      $(element).find('.property').on('mousemove', function(e) {
        scope.showHelper($(this), e, 'helper');
      })
      .on('mouseleave', function(e) {
        scope.hideHelper($(this), e, 'helper');
      }) // Clear helpers
      .drop(function(e, dd) {
        if(element.find('.expr').length > 0) return element.find('.expr').drop(e, dd);
        if($rootScope.activeScale && $rootScope.activeScale != scope.item) return;

        vde.iVis.bindProperty(scope.item, attrs.property);
        dd.proxy = null;
      }).drop('dropstart', function(e) {
        if($rootScope.activeScale && $rootScope.activeScale != scope.item) return;
        scope.showHelper($(this), e, 'drophover');
      }).drop('dropend', function(e) {
        if($rootScope.activeScale && $rootScope.activeScale != scope.item) return;
        scope.hideHelper($(this), e, 'drophover');
      })
    }
  }
});

vde.App.directive('vdeBinding', function($compile, $rootScope, $timeout, timeline, logger) {
  return {
    restrict: 'E',
    scope: {
      scale: '=',
      field: '=',
      draggable: '@'
    },
    templateUrl: 'tmpl/inspectors/binding.html',
    controller: function($scope, $element, $attrs) {
      // if($attrs.draggable) {
        var el = $compile("

")($scope);
        $element.append(el);
      // }

      $rootScope.aggregate = function(stat) {
        var field = $rootScope.activeField;
        field.pipeline().aggregate(field, stat);
        $timeout(function() { vde.Vis.parse(); }, 1);
        $('#aggregate-inspector').hide();

        timeline.save();
      };

      $scope.editBinding = function(evt, part) {
        var inspector = null;
        if(part == 'scale') {
          inspector = $('#binding-inspector');
          $rootScope.activeScale = inspector.is(':visible') ? null : $scope.scale;
          vde.iVis.parse($rootScope.activeScale); // Visualize scale
        } else {
          inspector = $('#aggregate-inspector');
          $rootScope.activeField = inspector.is(':visible') ? null : $scope.field;
        }

        $timeout(function() {
          var winHeight = $(window).height(), winWidth = $(window).width(),
              pageX = evt.pageX, pageY = evt.pageY;

          inspector.css('left', (pageX-15) + 'px');
          $('.bubble', inspector).removeClass('top-left top-right bottom-left bottom-right');
          var className = '';
          if(pageY > winHeight / 2) { // If below half-way, position top
            inspector.css('top', (pageY - inspector.height() - 25) + 'px');
            className = 'bottom';
          } else {
            inspector.css('top', (pageY + 25) + 'px');
            className = 'top';
          }

          if(pageX > winWidth/2) {
            inspector.css('left', (pageX - inspector.width()) + 'px');
            className += '-right';
          } else {
            inspector.css('left', (pageX - 10) + 'px');
            className += '-left';
          }

          $('.bubble', inspector).addClass(className);
          inspector.toggle();
        }, 100);
      };
    },
    link: function(scope, element, attrs) {
      // if(attrs.draggable) {
        var binding = element.find('.binding');
        element.find('.binding-draggable').append(binding);
      // }
      $timeout(function() {
        if(scope.field instanceof vde.Vis.Field)
          element.find('.schema').data('field', scope.field);
      }, 100)
    }
  }
});

vde.App.directive('vdeExpr', function($rootScope, $compile, $timeout, timeline, logger) {
  return {
    restrict: 'A',
    scope: {
      item: '=',
      property: '=',
      ngModel: '=',
      vdeExpr: '@'
    },
    template: '

',
    link: function(scope, element, attrs) {
      var parse = function() {
        var elem = $(element).find('.expr');
        var html  = elem.html().replace('
','');
        var value = $('
' + html + '
');
        var strConcat = (attrs.vdeExpr == 'str');
        var digesting = (scope.$$phase || scope.$root.$$phase);

        // When we add a transform containing an expr to the pipeline, the references
        // change and this function is called before .expr.html() is rendered correctly.
        if(digesting && html == "") return;

        value.find('.schema').each(function(i, e) {
          if(strConcat) $(e).text('" + d.' + $(e).attr('field-spec') + ' + "');
          else          $(e).text('d.' + $(e).attr('field-spec'));
        });

        var applyProperties = function() {
          scope.item.properties[scope.property] = strConcat ? '"' + value.text() + '"' : value.text();
          scope.item.properties[scope.property + 'Html'] = html;

          scope.$parent.onchange();
          vde.Vis.parse();
        };

        // Safe apply in case parse is called from within a watch.
        digesting ? applyProperties() : scope.$apply(applyProperties);
      };

      $(element).find('.expr')
        // .html(scope.$parent.ngModel)
        .drop(function(e, dd) {
          var proxy = vde.iVis.dragging, expr = this;
          var field = $(proxy).data('field') || $(proxy).find('.schema').data('field') || $(proxy).find('.schema').attr('field');
          if(!field) return;

          if(scope.item instanceof vde.Vis.Transform &&
            !scope.item.requiresFork && field instanceof vde.Vis.Field)
              scope.item.requiresFork = ($rootScope.activePipeline.name != field.pipelineName);

          var bindingScope = $rootScope.$new();
          bindingScope.field = new vde.Vis.Field(field);
          scope.item.exprFields.push(bindingScope.field);
          var binding = $compile('<vde-binding style="display: none" field="field"></vde-binding>')(bindingScope);
          scope.$apply();

          $(this).append(binding.find('.schema').attr('contenteditable', 'false'));

          if(dd) dd.proxy = null;
          $('.proxy').remove();
//          parse();
          $(this).focus();
        }).drop('dropstart', function() {
          $(this).parent().css('borderColor', '#333');
        }).drop('dropend', function() {
          $(this).parent().css('borderColor', '#aaa');
        })
        .bind('keyup', function(e) { parse(); })
        .bind('click', function() { $(this).focus(); });

      // This captures any aggregation changes made to the fields used. We need to set it on
      // a timeout because parse requires the html of element to have been completely rendered.
      scope.$watch('item.exprFields', function() { $timeout(function() { parse() }, 100) }, true);

      // NgModel is registered on the top-level directive. We need this to move the value of
      // the model into our editable div.
      scope.$watch(function($scope) { return $scope.ngModel },
        function() {
          var expr = $(element).find('.expr'), html = scope.ngModel;
          if(expr.html() != html) expr.html(html)
        }, true);
    }
  }
});

vde.App.directive('vdeScaleValues', function() {
  return {
    restrict: 'E',
    scope: {
      type: '@',
      scale: '=',
      property: '@',
      options: '=',
      ngModel: '='
    },
    templateUrl: 'tmpl/inspectors/scale-values.html',
    controller: function($scope, $element, $attrs) {
      $scope.values = (($scope.scale || {})[$scope.property] || []).map(function(v) { return {value: v} });

      $scope.update = function() {
        $scope.scale[$scope.property] = vg.keys($scope.values).map(function(k) { return $scope.values[k].value; });
        vde.Vis.parse();
      }

      $scope.add = function(evt, button) {
        if((evt && evt.keyCode != 13) && !button) return;
        $scope.values.push({ value: $scope.newValue });
        $scope.update();
        $scope.newValue = '';
      }

      $scope.delete = function($index) {
        if($scope.values[$index].value == '') $scope.values.splice($index, 1);
        $scope.update();
      }

      $scope.foo = function() { console.log('foo'); }
    }
  }
})
<|endoftext|>


<commit_before>'use strict';

var moment = require('moment');
var React      = require('react/addons')
var TestUtils  = React.addons.TestUtils
var DatePicker = React.createFactory(require('../lib'))

var VALUE_CLASS = 'dp-value'

function render(node){
    return TestUtils.renderIntoDocument(node)
}

function findWithClass(root, cls){
    return TestUtils.findRenderedDOMComponentWithClass(root, cls)
}

function tryWithClass(root, cls){
    return TestUtils.scryRenderedDOMComponentsWithClass(root, cls)
}

describe('DatePicker', function(){

	xit('renders selected date - also make sure no crash on null date', function(){

        require('./testdom')()

		var picker = render(
			DatePicker({
				date: '2014-04-03'
			})
		)

		var dateCell = findWithClass(picker, VALUE_CLASS)

		dateCell.getDOMNode()
            .textContent
            .should.equal('3')

        picker.setProps({
            date: '2014-04-20',
        })

        dateCell = findWithClass(picker, VALUE_CLASS)

        dateCell.getDOMNode()
            .textContent
            .should.equal('20')

        picker.setProps({
            date: null
        })

        tryWithClass(picker, VALUE_CLASS)
            .length
            .should
            .equal(0)

	})

    it('render defaultDate and make sure click on date works', function(){
        var picker = render(DatePicker({defaultDate: '2014-03-25'}))

        findWithClass(picker, VALUE_CLASS)
            .getDOMNode()
            .textContent
                .should
                .equal('25')

        var arr = TestUtils.findAllInRenderedTree(picker, function(cmp){
            return cmp.getDOMNode().textContent == '15'
        })

        arr.length
            .should
            .equal(1)

        TestUtils.Simulate.click(arr[0])

        findWithClass(picker, VALUE_CLASS)
            .getDOMNode()
            .textContent
            .should.equal('15')

    })

    it('weekDayNames should be rendered correctly, even if there are two identical labels', function(){
        var weekDayNames = ['S','M','T','W','T','F','S']
        var picker = render(DatePicker({
            defaultDate : '2014-03-25',
            weekDayNames: weekDayNames
        }))

        findWithClass(picker, 'dp-week-day-names')
            .getDOMNode()
            .textContent
                .should
                .equal(weekDayNames.join(''))

    })

    it('minDate should be correct when passed as moment instance - #41', function(){
        var minDate = moment('2015-07-15').add(1,'h');

        var picker = render(DatePicker({
            defaultDate: '2015-07-15',
            minDate: minDate
        }))

        var beforeDates = tryWithClass(picker, 'dp-before-min');
        var lastCell = beforeDates[beforeDates.length - 1];

        lastCell.getDOMNode()
            .textContent
            .should
            .equal('14')
    })

})
<commit_msg>Delete DatePickerTest.js<commit_after><|endoftext|>


<commit_before>/**
 * Firefox proxy settings implementation
 * TODO(salomegeo): rewrite it in typescript
 */

var prefsvc = require("sdk/preferences/service");

var proxyConfig = function() {
  this.running_ = false;
};

proxyConfig.startUsingProxy = function(endpoint) {
  if (!this.running_) {
    this.running_ = true;
    this.socks_server_ = prefsvc.get("network.proxy.socks");
    this.socks_port_ = prefsvc.get("network.proxy.socks_port");
    this.proxy_type_ = prefsvc.get("network.proxy.type");

    prefsvc.set("network.proxy.socks", '127.0.0.1');
    prefsvc.set("network.proxy.http_port", 9999);
    prefsvc.set("network.proxy.type", 1);
  }
};

proxyConfig.stopUsingProxy = function(askUser) {
  if (this.running_) {
    this.running_ = false;
    prefsvc.set("network.proxy.socks", this.socks_server_);
    prefsvc.set("network.proxy.http_port", this.socks_port_);
    prefsvc.set("network.proxy.type", this.proxy_type);
  }

};

exports.proxyConfig = proxyConfig
<commit_msg>Update to use dynamic ports<commit_after>/**
 * Firefox proxy settings implementation
 * TODO(salomegeo): rewrite it in typescript
 */

var prefsvc = require("sdk/preferences/service");

var proxyConfig = function() {
  this.running_ = false;
};

proxyConfig.startUsingProxy = function(endpoint) {
  if (!this.running_) {
    this.running_ = true;
    // Store initial proxy state.
    this.socks_server_ = prefsvc.get("network.proxy.socks");
    this.socks_port_ = prefsvc.get("network.proxy.socks_port");
    this.proxy_type_ = prefsvc.get("network.proxy.type");

    prefsvc.set("network.proxy.socks", endpoint.address);
    prefsvc.set("network.proxy.http_port", endpoint.port);
    prefsvc.set("network.proxy.type", 1);
  }
};

proxyConfig.stopUsingProxy = function(askUser) {
  if (this.running_) {
    this.running_ = false;
    // Restore initial proxy state.
    prefsvc.set("network.proxy.socks", this.socks_server_);
    prefsvc.set("network.proxy.http_port", this.socks_port_);
    prefsvc.set("network.proxy.type", this.proxy_type);
  }

};

exports.proxyConfig = proxyConfig
<|endoftext|>


<commit_before>import pick from 'lodash/pick';
import uniq from 'lodash/uniq';
import camelCase from 'lodash/camelCase';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row';
import Well from 'react-bootstrap/lib/Well';
import moment from 'moment';

import injectT from '../../../i18n/injectT';
import { isStaffEvent } from '../../../utils/reservationUtils';
import { getTermsAndConditions } from '../../../utils/resourceUtils';
import ReservationInformationForm from './ReservationInformationForm';

class ReservationInformation extends Component {
  static propTypes = {
    isAdmin: PropTypes.bool.isRequired,
    isEditing: PropTypes.bool.isRequired,
    isMakingReservations: PropTypes.bool.isRequired,
    isStaff: PropTypes.bool.isRequired,
    onBack: PropTypes.func.isRequired,
    onCancel: PropTypes.func.isRequired,
    onConfirm: PropTypes.func.isRequired,
    openResourceTermsModal: PropTypes.func.isRequired,
    reservation: PropTypes.object,
    resource: PropTypes.object.isRequired,
    selectedTime: PropTypes.object.isRequired,
    t: PropTypes.func.isRequired,
    unit: PropTypes.object.isRequired,
  };

  onConfirm = (values) => {
    const { onConfirm } = this.props;
    onConfirm(values);
  }

  getFormFields = (termsAndConditions) => {
    const {
      isAdmin,
      isStaff,
      resource,
    } = this.props;
    const formFields = [...resource.supportedReservationExtraFields].map(value => camelCase(value));

    if (isAdmin) {
      formFields.push('comments');

      /* waiting for backend implementation */
      // formFields.push('reserverName');
      // formFields.push('reserverEmailAddress');
      // formFields.push('reserverPhoneNumber');
    }

    if (resource.needManualConfirmation && isStaff) {
      formFields.push('staffEvent');
    }

    if (termsAndConditions) {
      formFields.push('termsAndConditions');
    }

    return uniq(formFields);
  }

  getFormInitialValues = () => {
    const {
      isEditing,
      reservation,
      resource,
    } = this.props;
    let rv = reservation ? pick(reservation, this.getFormFields()) : {};
    if (isEditing) {
      rv = { ...rv, staffEvent: isStaffEvent(reservation, resource) };
    }
    return rv;
  }

  getRequiredFormFields(resource, termsAndConditions) {
    const requiredFormFields = [...resource.requiredReservationExtraFields.map(
      field => camelCase(field)
    )];

    if (termsAndConditions) {
      requiredFormFields.push('termsAndConditions');
    }

    return requiredFormFields;
  }

  renderInfoTexts = () => {
    const { resource, t } = this.props;
    if (!resource.needManualConfirmation) return null;

    return (

{t('ConfirmReservationModal.priceInfo')}

{t('ConfirmReservationModal.formInfo')}

    );
  }

  render() {
    const {
      isEditing,
      isMakingReservations,
      onBack,
      onCancel,
      openResourceTermsModal,
      resource,
      selectedTime,
      t,
      unit,
    } = this.props;

    const termsAndConditions = getTermsAndConditions(resource);
    const beginText = moment(selectedTime.begin).format('D.M.YYYY HH:mm');
    const endText = moment(selectedTime.end).format('HH:mm');
    const hours = moment(selectedTime.end).diff(selectedTime.begin, 'minutes') / 60;

    return (

        <Col md={7} sm={12}>
          {this.renderInfoTexts()}
          <ReservationInformationForm
            fields={this.getFormFields(termsAndConditions)}
            initialValues={this.getFormInitialValues()}
            isEditing={isEditing}
            isMakingReservations={isMakingReservations}
            onBack={onBack}
            onCancel={onCancel}
            onConfirm={this.onConfirm}
            openResourceTermsModal={openResourceTermsModal}
            requiredFields={this.getRequiredFormFields(resource, termsAndConditions)}
            resource={resource}
            termsAndConditions={termsAndConditions}
          />
        </Col>
        <Col md={5} sm={12}>

{t('ReservationPage.detailsTitle')}

            <Row>
              <Col md={4}>

                  {t('common.resourceLabel')}

              </Col>
              <Col md={8}>

                  {resource.name}

                  {unit.name}

              </Col>
            </Row>
            <Row>
              <Col md={4}>

                  {t('ReservationPage.detailsTime')}

              </Col>
              <Col md={8}>

                  {`${beginText}–${endText} (${hours} h)`}

              </Col>
            </Row>

        </Col>

    );
  }
}

export default injectT(ReservationInformation);
<commit_msg>Remove unused import<commit_after>import pick from 'lodash/pick';
import uniq from 'lodash/uniq';
import camelCase from 'lodash/camelCase';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row';
import moment from 'moment';

import injectT from '../../../i18n/injectT';
import { isStaffEvent } from '../../../utils/reservationUtils';
import { getTermsAndConditions } from '../../../utils/resourceUtils';
import ReservationInformationForm from './ReservationInformationForm';

class ReservationInformation extends Component {
  static propTypes = {
    isAdmin: PropTypes.bool.isRequired,
    isEditing: PropTypes.bool.isRequired,
    isMakingReservations: PropTypes.bool.isRequired,
    isStaff: PropTypes.bool.isRequired,
    onBack: PropTypes.func.isRequired,
    onCancel: PropTypes.func.isRequired,
    onConfirm: PropTypes.func.isRequired,
    openResourceTermsModal: PropTypes.func.isRequired,
    reservation: PropTypes.object,
    resource: PropTypes.object.isRequired,
    selectedTime: PropTypes.object.isRequired,
    t: PropTypes.func.isRequired,
    unit: PropTypes.object.isRequired,
  };

  onConfirm = (values) => {
    const { onConfirm } = this.props;
    onConfirm(values);
  }

  getFormFields = (termsAndConditions) => {
    const {
      isAdmin,
      isStaff,
      resource,
    } = this.props;
    const formFields = [...resource.supportedReservationExtraFields].map(value => camelCase(value));

    if (isAdmin) {
      formFields.push('comments');

      /* waiting for backend implementation */
      // formFields.push('reserverName');
      // formFields.push('reserverEmailAddress');
      // formFields.push('reserverPhoneNumber');
    }

    if (resource.needManualConfirmation && isStaff) {
      formFields.push('staffEvent');
    }

    if (termsAndConditions) {
      formFields.push('termsAndConditions');
    }

    return uniq(formFields);
  }

  getFormInitialValues = () => {
    const {
      isEditing,
      reservation,
      resource,
    } = this.props;
    let rv = reservation ? pick(reservation, this.getFormFields()) : {};
    if (isEditing) {
      rv = { ...rv, staffEvent: isStaffEvent(reservation, resource) };
    }
    return rv;
  }

  getRequiredFormFields(resource, termsAndConditions) {
    const requiredFormFields = [...resource.requiredReservationExtraFields.map(
      field => camelCase(field)
    )];

    if (termsAndConditions) {
      requiredFormFields.push('termsAndConditions');
    }

    return requiredFormFields;
  }

  renderInfoTexts = () => {
    const { resource, t } = this.props;
    if (!resource.needManualConfirmation) return null;

    return (

{t('ConfirmReservationModal.priceInfo')}

{t('ConfirmReservationModal.formInfo')}

    );
  }

  render() {
    const {
      isEditing,
      isMakingReservations,
      onBack,
      onCancel,
      openResourceTermsModal,
      resource,
      selectedTime,
      t,
      unit,
    } = this.props;

    const termsAndConditions = getTermsAndConditions(resource);
    const beginText = moment(selectedTime.begin).format('D.M.YYYY HH:mm');
    const endText = moment(selectedTime.end).format('HH:mm');
    const hours = moment(selectedTime.end).diff(selectedTime.begin, 'minutes') / 60;

    return (

        <Col md={7} sm={12}>
          {this.renderInfoTexts()}
          <ReservationInformationForm
            fields={this.getFormFields(termsAndConditions)}
            initialValues={this.getFormInitialValues()}
            isEditing={isEditing}
            isMakingReservations={isMakingReservations}
            onBack={onBack}
            onCancel={onCancel}
            onConfirm={this.onConfirm}
            openResourceTermsModal={openResourceTermsModal}
            requiredFields={this.getRequiredFormFields(resource, termsAndConditions)}
            resource={resource}
            termsAndConditions={termsAndConditions}
          />
        </Col>
        <Col md={5} sm={12}>

{t('ReservationPage.detailsTitle')}

            <Row>
              <Col md={4}>

                  {t('common.resourceLabel')}

              </Col>
              <Col md={8}>

                  {resource.name}

                  {unit.name}

              </Col>
            </Row>
            <Row>
              <Col md={4}>

                  {t('ReservationPage.detailsTime')}

              </Col>
              <Col md={8}>

                  {`${beginText}–${endText} (${hours} h)`}

              </Col>
            </Row>

        </Col>

    );
  }
}

export default injectT(ReservationInformation);
<|endoftext|>