<commit_before>foam.CLASS({
  package: 'foam.u2',
  name: 'DeleteModal',
  extends: 'foam.u2.View',

  documentation: 'View for deleting any object',

  imports: [
    'notify'
  ],

  css: `
    ^ {
      border-radius: 3px;
      background-color: #fff;
      /* Don't let the modal exceed the screen size, minus some margin. */
      max-width: calc(100vw - 48px);
      max-height: calc(100vh - 116px);
      overflow-y: scroll;
      /* The line below accounts for the top nav bar. */
      margin-top: 60px;
    }
    ^main {
      padding: 24px;
    }
    ^main > h2:first-child {
      margin-top: 0;
    }
    ^ .buttons {
      padding: 24px;
      box-sizing: border-box;
      display: flex;
      justify-content: flex-end;
    }
  `,

  messages: [
    { name: 'TITLE', message: 'Delete ' },
    { name: 'CONFIRM_DELETE_1', message: 'Are you sure you want to delete' },
    { name: 'SUCCESS_MSG', message: ' deleted.' },
    { name: 'FAIL_MSG', message: 'Failed to delete' }
  ],

  properties: [
    {
      class: 'foam.dao.DAOProperty',
      name: 'dao'
    },
    {
      class: 'Function',
      name: 'onDelete'
    },
    {
      class: 'FObjectProperty',
      name: 'data'
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      this
        .addClass(this.myClass())
        .start()
          .addClass(this.myClass('main'))
          .start('h2')
            .add(this.TITLE).add(this.data.model_.label).add('?')
          .end()
          .start('p')
            .add(`${this.CONFIRM_DELETE_1} ${this.data.toSummary()}?`)
          .end()
        .end()
        .start()
          .addClass('buttons')
          .startContext({ data: this })
            .tag(this.CANCEL, { buttonStyle: 'SECONDARY' })
            .tag(this.DELETE, { isDestructive: true })
          .endContext()
        .end();
    }
  ],

  actions: [
    {
      name: 'delete',
      label: 'Delete',
      code: function(X) {
        this.dao.remove(this.data).then((o) => {
          if ( foam.comics.v2.userfeedback.UserFeedbackAware.isInstance(o) && o.userFeedback ){
            var currentFeedback = o.userFeedback;
            while ( currentFeedback ){

              this.notify(currentFeedback.message);

              currentFeedback = currentFeedback.next;
            }
          } else {
            this.notify(this.data.model_.label + this.SUCCESS_MSG);
          }

          this.onDelete();
        }).catch((err) => {

          // TODO: Uncomment once we turn UserFeedbackException in to a throwable
          // if ( foam.comics.v2.userfeedback.UserFeedbackException.isInstance(err) && err.userFeedback  ){
          //   var currentFeedback = err.userFeedback;
          //   while ( currentFeedback ){
          //     this.notify(currentFeedback.message);

          //     currentFeedback = currentFeedback.next;
          //   }
          // } else {
          //   this.notify(err.message || this.FAIL_MSG, 'error');
          // }

          if ( err.message === "An approval request has been sent out."  ){
            this.notify(err.message);

          } else {
            this.notify(err.message || this.FAIL_MSG, 'error');
          }

        });
        X.closeDialog();
      }
    },
    {
      name: 'cancel',
      label: 'Cancel',
      code: function(X) {
        X.closeDialog();
      }
    }
  ]
});
<commit_msg>Remove large margin on delete modal<commit_after>foam.CLASS({
  package: 'foam.u2',
  name: 'DeleteModal',
  extends: 'foam.u2.View',

  documentation: 'View for deleting any object',

  imports: [
    'notify'
  ],

  css: `
    ^ {
      border-radius: 3px;
      background-color: #fff;
      /* Don't let the modal exceed the screen size, minus some margin. */
      max-width: calc(100vw - 48px);
      max-height: calc(100vh - 116px);
      overflow-y: scroll;
      /* The line below accounts for the top nav bar. */
    }
    ^main {
      padding: 24px;
    }
    ^main > h2:first-child {
      margin-top: 0;
    }
    ^ .buttons {
      padding: 24px;
      box-sizing: border-box;
      display: flex;
      justify-content: flex-end;
    }
  `,

  messages: [
    { name: 'TITLE', message: 'Delete ' },
    { name: 'CONFIRM_DELETE_1', message: 'Are you sure you want to delete' },
    { name: 'SUCCESS_MSG', message: ' deleted.' },
    { name: 'FAIL_MSG', message: 'Failed to delete' }
  ],

  properties: [
    {
      class: 'foam.dao.DAOProperty',
      name: 'dao'
    },
    {
      class: 'Function',
      name: 'onDelete'
    },
    {
      class: 'FObjectProperty',
      name: 'data'
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      this
        .addClass(this.myClass())
        .start()
          .addClass(this.myClass('main'))
          .start('h2')
            .add(this.TITLE).add(this.data.model_.label).add('?')
          .end()
          .start('p')
            .add(`${this.CONFIRM_DELETE_1} ${this.data.toSummary()}?`)
          .end()
        .end()
        .start()
          .addClass('buttons')
          .startContext({ data: this })
            .tag(this.CANCEL, { buttonStyle: 'SECONDARY' })
            .tag(this.DELETE, { isDestructive: true })
          .endContext()
        .end();
    }
  ],

  actions: [
    {
      name: 'delete',
      label: 'Delete',
      code: function(X) {
        this.dao.remove(this.data).then((o) => {
          if ( foam.comics.v2.userfeedback.UserFeedbackAware.isInstance(o) && o.userFeedback ){
            var currentFeedback = o.userFeedback;
            while ( currentFeedback ){

              this.notify(currentFeedback.message);

              currentFeedback = currentFeedback.next;
            }
          } else {
            this.notify(this.data.model_.label + this.SUCCESS_MSG);
          }

          this.onDelete();
        }).catch((err) => {

          // TODO: Uncomment once we turn UserFeedbackException in to a throwable
          // if ( foam.comics.v2.userfeedback.UserFeedbackException.isInstance(err) && err.userFeedback  ){
          //   var currentFeedback = err.userFeedback;
          //   while ( currentFeedback ){
          //     this.notify(currentFeedback.message);

          //     currentFeedback = currentFeedback.next;
          //   }
          // } else {
          //   this.notify(err.message || this.FAIL_MSG, 'error');
          // }

          if ( err.message === "An approval request has been sent out."  ){
            this.notify(err.message);

          } else {
            this.notify(err.message || this.FAIL_MSG, 'error');
          }

        });
        X.closeDialog();
      }
    },
    {
      name: 'cancel',
      label: 'Cancel',
      code: function(X) {
        X.closeDialog();
      }
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.layout',
  name: 'Grid',
  extends: 'foam.u2.Element',
  documentation: `
    A grid of responsive elements
  `,

  imports: [
    'displayWidth?'
  ],

  requires: [
    'foam.u2.layout.GUnit'
  ],

  css: `
    ^ {
      display: grid;
    }
  `,

  properties: [
    {
      class: 'Int',
      name: 'currentWidth',
      value: 0
    },
    {
      class: 'String',
      name: 'columnGap',
      value: '16px'
    },
    {
      class: 'String',
      name: 'rowGap',
      value: '32px'
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      this.addClass(this.myClass());
      this.style(
        { 'grid-template-columns': this.displayWidth$.map(dw => {
            dw = dw || foam.u2.layout.DisplayWidth.XL;
            return `repeat(${dw.cols}, 1fr)`;
          }),
          'grid-row-gap': this.rowGap,
          'grid-column-gap': this.columnGap
        }
      )
    },

    function createChild_(spec, args){
      var ret = this.SUPER(spec, args);

      var width = this.GUnit.isInstance(ret)
        ? ret[`${this.displayWidth.name.toLowerCase()}Columns`]
        : this.displayWidth.cols;

      var startCol = this.currentWidth + 1;
      this.currentWidth += width;

      if ( this.currentWidth > this.displayWidth.cols ) {
        startCol = 1;
        this.currentWidth = width;
      }

      var endCol = startCol + width;

      ret.style({
        'grid-column-start': startCol,
        'grid-column-end': endCol,
      });

      return ret;
    }
  ]
});
<commit_msg>Hid the column and row gaps from user, they can just style it directly<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.layout',
  name: 'Grid',
  extends: 'foam.u2.Element',
  documentation: `
    A grid of responsive elements
  `,

  imports: [
    'displayWidth?'
  ],

  requires: [
    'foam.u2.layout.GUnit'
  ],

  css: `
    ^ {
      display: grid;
      grid-column-gap: 16px;
      grid-row-gap: 32px;
    }
  `,

  properties: [
    {
      class: 'Int',
      name: 'currentWidth',
      value: 0
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      this.addClass(this.myClass());
      this.style(
        { 'grid-template-columns': this.displayWidth$.map(dw => {
            dw = dw || foam.u2.layout.DisplayWidth.XL;
            return `repeat(${dw.cols}, 1fr)`;
          })
        }
      )
    },

    function createChild_(spec, args){
      var ret = this.SUPER(spec, args);

      var width = this.GUnit.isInstance(ret)
        ? ret[`${this.displayWidth.name.toLowerCase()}Columns`]
        : this.displayWidth.cols;

      var startCol = this.currentWidth + 1;
      this.currentWidth += width;

      if ( this.currentWidth > this.displayWidth.cols ) {
        startCol = 1;
        this.currentWidth = width;
      }

      var endCol = startCol + width;

      ret.style({
        'grid-column-start': startCol,
        'grid-column-end': endCol,
      });

      return ret;
    }
  ]
});
<|endoftext|>


<commit_before>(function() {
  var self = model

  var getSelectOption = function(event) {
    if (event.shiftKey)
    {
      if (event.ctrlKey)
        return 'remove';
      else
        return 'add';
    }
    else if (event.ctrlKey)
      return 'toggle';
    else
      return '';
  };

  self.holodeckModeMouseDown = {};

  self.holodeckModeMouseDown.fab = function (holodeck, mdevent) {
    console.log('my holodeckModeMouseDown.fab')
    if (mdevent.button === 0) {
      var queue = mdevent.shiftKey;
      model.fabCount(model.fabCount() + 1);
      if (queue && (model.fabCount() === 1)) {
        var shiftWatch = function (keyEvent) {
          if (!keyEvent.shiftKey) {
            $('body').off('keyup', shiftWatch);
            if (self.mode() === 'fab')
              self.endFabMode();
            else if (self.mode() === 'fab_rotate')
              self.mode('fab_end');
          }
        };
        $('body').on('keyup', shiftWatch);
      }
      var beginFabX = mdevent.offsetX;
      var beginFabY = mdevent.offsetY;
      var beginSnap = !mdevent.ctrlKey;
      holodeck.unitBeginFab(beginFabX, beginFabY, beginSnap);
      self.mode('fab_rotate');
      input.capture(holodeck.div, function (event) {
        if ((event.type === 'mouseup') && (event.button === 0)) {
          var snap = !event.ctrlKey;
          holodeck.unitEndFab(event.offsetX, event.offsetY, queue, snap).then(function (success) {
            holodeck.showCommandConfirmation("", event.offsetX, event.offsetY);
            if (success)
              api.audio.playSound("/SE/UI/UI_Building_place");
          });
          queue &= (self.mode() !== 'fab_end');
          self.mode('fab');
          input.release();
          if (!queue)
            self.endFabMode();
        }
        else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
          input.release();
          holodeck.unitCancelFab();
          self.endFabMode();
        }
      });
      return true;
    }
    else if (mdevent.button === 2) {
      self.endFabMode();
      return true;
    }
    return false;
  };

  var holodeckOnSelect = function (wasSelected, prevSelection, promise) {
    return promise.then(function (selection) {
      if (selection) {
        var jSelection = JSON.parse(selection);
        self.parseSelection(jSelection);
        self.playSelectionSound(wasSelected, prevSelection, self.hasSelection(), self.selection());
        return jSelection;
      }
      else
        return null;
    });
  };

  self.holodeckModeMouseDown['default'] = function (holodeck, mdevent) {
    console.log('my holodeckModeMouseDown.default')
    if (mdevent.button === 0) {
      if (model.celestialControlActive()) {
        if (model.celestialControlModel.findingTargetPlanet()) {
          model.celestialControlModel.mousedown(mdevent);

          input.capture($('body'), function (event) {
            if (event.type === 'mouseup' && event.button === 0) {
              model.celestialControlModel.mouseup(event);
              input.release();
            }
          });
        }
        return true;
      }

      var startx = mdevent.offsetX;
      var starty = mdevent.offsetY;
      var dragging = false;
      var now = new Date().getTime();
      if (holodeck.hasOwnProperty('doubleClickId') && (now < holodeck.doubleClickTime)) {
        holodeckOnSelect(self.hasSelection(), self.selection(),
                         holodeck.selectMatchingUnits(getSelectOption(mdevent), [holodeck.doubleClickId])
                        );
                        delete holodeck.doubleClickTime;
                        delete holodeck.doubleClickId;
      }
      else {
        self.mode('select');

        var wasSelected = model.hasSelection();
        var prevSelection = model.selection();

        holodeck.doubleClickTime = now + 250;
        delete holodeck.doubleClickId;
        input.capture(holodeck.div, function (event) {
          if (!dragging && (event.type === 'mousemove')) {
            dragging = true;
            holodeck.beginDragSelect(startx, starty);
            delete holodeck.doubleClickTime;
          }
          else if ((event.type === 'mouseup') && (event.button === 0)) {

            input.release();
            var option = getSelectOption(event);
            if (dragging)
              holodeckOnSelect(wasSelected, prevSelection,
                               holodeck.endDragSelect(option, { left: startx, top: starty, right: event.offsetX, bottom: event.offsetY })
                              );
            else {
              if (self.hasWorldHoverTarget())
                holodeck.doubleClickId = self.worldHoverTarget();
              var index = (holodeck.clickOffset || 0);
              holodeckOnSelect(wasSelected, prevSelection,
                               holodeck.selectAt(option, startx, starty, index)
                              ).then(function (selection) {
                  if (selection && selection.selectionResult) {
                    holodeck.doubleClickId = selection.selectionResult[0];
                    ++holodeck.clickOffset;
                    if (!selection.selectionResult.length)
                      api.camera.maybeSetFocusPlanet();

                  }
                });
            }
            self.mode('default');
            holodeck.showCommandConfirmation("", event.offsetX, event.offsetY);
          }
          else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
            input.release();
            holodeck.endDragSelect('cancel');
            self.mode('default');
          }
        });
      }

      return true;
    }
    else if ((mdevent.button === 2) && (!self.showTimeControls())) {
      if (model.celestialControlActive())
        return false;

      var startx = mdevent.offsetX;
      var starty = mdevent.offsetY;
      var dragCommand = "";
      // TODO: Consider changing this once we have event timestamps.
      // WLott is concerned that framerate dips will cause this to be wonky.
      var now = new Date().getTime();
      var dragTime = now + 75;
      var queue = mdevent.shiftKey;

      input.capture(holodeck.div, function (event) {
        var eventTime = new Date().getTime();
        if (self.showTimeControls())
          self.endCommandMode();

        if (dragCommand === "" && event.type === 'mousemove' && eventTime >= dragTime)
          {
            holodeck.unitBeginGo(startx, starty, model.allowCustomFormations()).then( function(ok) {
              dragCommand = ok;
              if (dragCommand)
                self.mode("command_" + dragCommand);
            } );
          }
          else if ((event.type === 'mouseup') && (event.button === 2)) {
            input.release();
            if (dragCommand === 'move') {
              holodeck.unitChangeCommandState(dragCommand, event.offsetX, event.offsetY, queue).then(function (success) {
                if (!success)
                  return;

                input.capture(holodeck.div, function (event) {
                  if ((event.type === 'mousedown') && (event.button === 2)) {
                    input.release();
                    holodeck.unitEndCommand(dragCommand, event.offsetX, event.offsetY, queue).then(function (success) {
                      holodeck.showCommandConfirmation(success ? dragCommand : "", event.offsetX, event.offsetY);
                    });
                    self.mode('default');
                  }
                  else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
                    input.release();
                    holodeck.unitCancelCommand();
                    self.mode('default');
                  }
                });
                return;
              });
            }
            else if (dragCommand !== "") {
              holodeck.unitEndCommand(dragCommand, event.offsetX, event.offsetY, queue).then(function (success) {
                holodeck.showCommandConfirmation(success ? dragCommand : "", event.offsetX, event.offsetY);
                if (!success)
                  return;
                var action = dragCommand.charAt(0).toUpperCase() + dragCommand.slice(1);
                api.audio.playSound("/SE/UI/UI_Command_" + action);
              });
            }
            else {
              holodeck.unitGo(startx, starty, queue).then(function (action) {
                holodeck.showCommandConfirmation(action, event.offsetX, event.offsetY);
                if (!action || (action === 'move')) {
                  // Note: move currently plays its own sound.
                  return;
                }
                var action = action.charAt(0).toUpperCase() + action.slice(1);
                api.audio.playSound("/SE/UI/UI_Command_" + action);
              });
              self.mode('default');
            }
          }
          else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
            input.release();
            holodeck.unitCancelCommand();
            self.mode('default');
          }
      });

      return true;
    }
    return false;
  };

  var holodeckCommandMouseDown = function (command, targetable) {
    return function (holodeck, mdevent) {
      console.log('my holodeckCommandMouseDown', command)
      if (mdevent.button === 0) {
        engine.call('camera.cameraMaybeSetFocusPlanet');
        var startx = mdevent.offsetX;
        var starty = mdevent.offsetY;
        var dragging = false;
        // TODO: Consider changing this once we have event timestamps.
        // WLott is concerned that framerate dips will cause this to be wonky.
        var now = new Date().getTime();
        var dragTime = now + 125;
        var queue = mdevent.shiftKey;
        model.cmdQueueCount(model.cmdQueueCount() + 1);
        if (queue && (model.cmdQueueCount() === 1)) {
          var shiftWatch = function (keyEvent) {
            if (!keyEvent.shiftKey) {
              $('body').off('keyup', shiftWatch);
              self.endCommandMode();
            }
          };
          $('body').on('keyup', shiftWatch);
        }

        input.capture(holodeck.div, function (event) {
          var playSound = function (success) {
            holodeck.showCommandConfirmation(success ? command : "", event.offsetX, event.offsetY);
            if (!success || (command === 'move')) {
              // Note: move currently plays its own sound.
              return;
            }
            var action = command.charAt(0).toUpperCase() + command.slice(1);
            api.audio.playSound("/SE/UI/UI_Command_" + action);
          };

          var eventTime = new Date().getTime();

          if (!model.allowCustomFormations() && (command === 'move' || command === 'unload')) {
            input.release();
            holodeck.unitCommand(command, mdevent.offsetX, mdevent.offsetY, queue).then(playSound);
            if (!queue)
              self.endCommandMode();
          }
          else if (!dragging && event.type === 'mousemove' && eventTime >= dragTime) {
            holodeck.unitBeginCommand(command, startx, starty).then(function (ok) { dragging = ok; });
          }
          else if ((event.type === 'mouseup') && (event.button === 0)) {
            input.release();
            if (dragging && (command === 'move' || command === 'unload')) {
              holodeck.unitChangeCommandState(command, event.offsetX, event.offsetY, queue).then(function (success) {
                if (!success)
                  return;
                // move and unload have extra input for their area command so recapture for it
                input.capture(holodeck.div, function (event) {
                  if ((event.type === 'mousedown') && (event.button === 0)) {
                    input.release();
                    holodeck.unitEndCommand(command, event.offsetX, event.offsetY, queue).then(playSound);
                    if (!queue)
                      self.endCommandMode();
                  }
                  else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
                    input.release();
                    holodeck.unitCancelCommand();
                    self.mode('command_' + command);
                  }
                });
              });
            }
            else if (dragging) {
              holodeck.unitEndCommand(command, event.offsetX, event.offsetY, queue).then(function (success) {
                holodeck.showCommandConfirmation(success ? command : "", event.offsetX, event.offsetY);
                if (!success)
                  return;
                var action = command.charAt(0).toUpperCase() + command.slice(1);
                api.audio.playSound("/SE/UI/UI_Command_" + action);
              });
              if (!queue)
                self.endCommandMode();
            }
            else {
              if (self.hasWorldHoverTarget() && targetable) {
                api.unit.targetCommand(command, self.worldHoverTarget(), queue).then(playSound);
              }
              else {
                holodeck.unitCommand(command, mdevent.offsetX, mdevent.offsetY, queue).then(playSound);
              }

              if (!queue)
                self.endCommandMode();
            }
          }
          else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
            input.release();
            holodeck.unitCancelCommand();
            self.mode('command_' + command);
          }
        });

        return true;
      }
    };
  };

  for (var i = 0; i < self.commands().length; ++i) {
    var command = self.commands()[i];
    var targetable = self.targetableCommands()[i];
    self.holodeckModeMouseDown['command_' + command] = holodeckCommandMouseDown(command, targetable);
  }

  //  :-( :-( :-(
  $('holodeck').off('mousedown')

  $('holodeck').mousedown(function (mdevent) {
    console.log('my $holodeck.mousedown')
    if (mdevent.target.nodeName !== 'HOLODECK')
      return;

    var holodeck = api.Holodeck.get(this);

    var handler = self.holodeckModeMouseDown[self.mode()];
    if (handler && handler(holodeck, mdevent)) {
      mdevent.preventDefault();
      mdevent.stopPropagation();
      return;
    }

    if (mdevent.button === 1) // middle click
    {
      var oldMode = self.mode();
      self.mode('camera');
      holodeck.beginControlCamera();
      input.capture(holodeck.div, function (event) {
        var mouseDone = ((event.type === 'mouseup') && (event.button === 1));
        var escKey = ((event.type === 'keydown') && (event.keyCode === keyboard.esc));
        if (mouseDone || escKey) {
          input.release();
          holodeck.endControlCamera();
          if (self.mode() === 'camera')
            self.mode(oldMode);
        }
      });
      mdevent.preventDefault();
      mdevent.stopPropagation();
      return;
    }

    if (mdevent.button === 2 && self.mode() !== 'default') // right click
    {
      self.endCommandMode()
    }
  });
})()
<commit_msg>end capture on matching button<commit_after>(function() {
  var self = model

  var getSelectOption = function(event) {
    if (event.shiftKey)
    {
      if (event.ctrlKey)
        return 'remove';
      else
        return 'add';
    }
    else if (event.ctrlKey)
      return 'toggle';
    else
      return '';
  };

  self.holodeckModeMouseDown = {};

  self.holodeckModeMouseDown.fab = function (holodeck, mdevent) {
    console.log('my holodeckModeMouseDown.fab')
    if (mdevent.button === 0) {
      var queue = mdevent.shiftKey;
      model.fabCount(model.fabCount() + 1);
      if (queue && (model.fabCount() === 1)) {
        var shiftWatch = function (keyEvent) {
          if (!keyEvent.shiftKey) {
            $('body').off('keyup', shiftWatch);
            if (self.mode() === 'fab')
              self.endFabMode();
            else if (self.mode() === 'fab_rotate')
              self.mode('fab_end');
          }
        };
        $('body').on('keyup', shiftWatch);
      }
      var beginFabX = mdevent.offsetX;
      var beginFabY = mdevent.offsetY;
      var beginSnap = !mdevent.ctrlKey;
      holodeck.unitBeginFab(beginFabX, beginFabY, beginSnap);
      self.mode('fab_rotate');
      input.capture(holodeck.div, function (event) {
        if ((event.type === 'mouseup') && (event.button === mdevent.button)) {
          var snap = !event.ctrlKey;
          holodeck.unitEndFab(event.offsetX, event.offsetY, queue, snap).then(function (success) {
            holodeck.showCommandConfirmation("", event.offsetX, event.offsetY);
            if (success)
              api.audio.playSound("/SE/UI/UI_Building_place");
          });
          queue &= (self.mode() !== 'fab_end');
          self.mode('fab');
          input.release();
          if (!queue)
            self.endFabMode();
        }
        else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
          input.release();
          holodeck.unitCancelFab();
          self.endFabMode();
        }
      });
      return true;
    }
    else if (mdevent.button === 2) {
      self.endFabMode();
      return true;
    }
    return false;
  };

  var holodeckOnSelect = function (wasSelected, prevSelection, promise) {
    return promise.then(function (selection) {
      if (selection) {
        var jSelection = JSON.parse(selection);
        self.parseSelection(jSelection);
        self.playSelectionSound(wasSelected, prevSelection, self.hasSelection(), self.selection());
        return jSelection;
      }
      else
        return null;
    });
  };

  self.holodeckModeMouseDown['default'] = function (holodeck, mdevent) {
    console.log('my holodeckModeMouseDown.default')
    if (mdevent.button === 0) {
      if (model.celestialControlActive()) {
        if (model.celestialControlModel.findingTargetPlanet()) {
          model.celestialControlModel.mousedown(mdevent);

          input.capture($('body'), function (event) {
            if (event.type === 'mouseup' && event.button === mdevent.button) {
              model.celestialControlModel.mouseup(event);
              input.release();
            }
          });
        }
        return true;
      }

      var startx = mdevent.offsetX;
      var starty = mdevent.offsetY;
      var dragging = false;
      var now = new Date().getTime();
      if (holodeck.hasOwnProperty('doubleClickId') && (now < holodeck.doubleClickTime)) {
        holodeckOnSelect(self.hasSelection(), self.selection(),
                         holodeck.selectMatchingUnits(getSelectOption(mdevent), [holodeck.doubleClickId])
                        );
                        delete holodeck.doubleClickTime;
                        delete holodeck.doubleClickId;
      }
      else {
        self.mode('select');

        var wasSelected = model.hasSelection();
        var prevSelection = model.selection();

        holodeck.doubleClickTime = now + 250;
        delete holodeck.doubleClickId;
        input.capture(holodeck.div, function (event) {
          if (!dragging && (event.type === 'mousemove')) {
            dragging = true;
            holodeck.beginDragSelect(startx, starty);
            delete holodeck.doubleClickTime;
          }
          else if ((event.type === 'mouseup') && (event.button === mdevent.button)) {

            input.release();
            var option = getSelectOption(event);
            if (dragging)
              holodeckOnSelect(wasSelected, prevSelection,
                               holodeck.endDragSelect(option, { left: startx, top: starty, right: event.offsetX, bottom: event.offsetY })
                              );
            else {
              if (self.hasWorldHoverTarget())
                holodeck.doubleClickId = self.worldHoverTarget();
              var index = (holodeck.clickOffset || 0);
              holodeckOnSelect(wasSelected, prevSelection,
                               holodeck.selectAt(option, startx, starty, index)
                              ).then(function (selection) {
                  if (selection && selection.selectionResult) {
                    holodeck.doubleClickId = selection.selectionResult[0];
                    ++holodeck.clickOffset;
                    if (!selection.selectionResult.length)
                      api.camera.maybeSetFocusPlanet();

                  }
                });
            }
            self.mode('default');
            holodeck.showCommandConfirmation("", event.offsetX, event.offsetY);
          }
          else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
            input.release();
            holodeck.endDragSelect('cancel');
            self.mode('default');
          }
        });
      }

      return true;
    }
    else if ((mdevent.button === 2) && (!self.showTimeControls())) {
      if (model.celestialControlActive())
        return false;

      var startx = mdevent.offsetX;
      var starty = mdevent.offsetY;
      var dragCommand = "";
      // TODO: Consider changing this once we have event timestamps.
      // WLott is concerned that framerate dips will cause this to be wonky.
      var now = new Date().getTime();
      var dragTime = now + 75;
      var queue = mdevent.shiftKey;

      input.capture(holodeck.div, function (event) {
        var eventTime = new Date().getTime();
        if (self.showTimeControls())
          self.endCommandMode();

        if (dragCommand === "" && event.type === 'mousemove' && eventTime >= dragTime)
          {
            holodeck.unitBeginGo(startx, starty, model.allowCustomFormations()).then( function(ok) {
              dragCommand = ok;
              if (dragCommand)
                self.mode("command_" + dragCommand);
            } );
          }
          else if ((event.type === 'mouseup') && (event.button === mdevent.button)) {
            input.release();
            if (dragCommand === 'move') {
              holodeck.unitChangeCommandState(dragCommand, event.offsetX, event.offsetY, queue).then(function (success) {
                if (!success)
                  return;

                input.capture(holodeck.div, function (event) {
                  if ((event.type === 'mousedown') && (event.button === mdevent.button)) {
                    input.release();
                    holodeck.unitEndCommand(dragCommand, event.offsetX, event.offsetY, queue).then(function (success) {
                      holodeck.showCommandConfirmation(success ? dragCommand : "", event.offsetX, event.offsetY);
                    });
                    self.mode('default');
                  }
                  else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
                    input.release();
                    holodeck.unitCancelCommand();
                    self.mode('default');
                  }
                });
                return;
              });
            }
            else if (dragCommand !== "") {
              holodeck.unitEndCommand(dragCommand, event.offsetX, event.offsetY, queue).then(function (success) {
                holodeck.showCommandConfirmation(success ? dragCommand : "", event.offsetX, event.offsetY);
                if (!success)
                  return;
                var action = dragCommand.charAt(0).toUpperCase() + dragCommand.slice(1);
                api.audio.playSound("/SE/UI/UI_Command_" + action);
              });
            }
            else {
              holodeck.unitGo(startx, starty, queue).then(function (action) {
                holodeck.showCommandConfirmation(action, event.offsetX, event.offsetY);
                if (!action || (action === 'move')) {
                  // Note: move currently plays its own sound.
                  return;
                }
                var action = action.charAt(0).toUpperCase() + action.slice(1);
                api.audio.playSound("/SE/UI/UI_Command_" + action);
              });
              self.mode('default');
            }
          }
          else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
            input.release();
            holodeck.unitCancelCommand();
            self.mode('default');
          }
      });

      return true;
    }
    return false;
  };

  var holodeckCommandMouseDown = function (command, targetable) {
    return function (holodeck, mdevent) {
      console.log('my holodeckCommandMouseDown', command)
      if (mdevent.button === 0) {
        engine.call('camera.cameraMaybeSetFocusPlanet');
        var startx = mdevent.offsetX;
        var starty = mdevent.offsetY;
        var dragging = false;
        // TODO: Consider changing this once we have event timestamps.
        // WLott is concerned that framerate dips will cause this to be wonky.
        var now = new Date().getTime();
        var dragTime = now + 125;
        var queue = mdevent.shiftKey;
        model.cmdQueueCount(model.cmdQueueCount() + 1);
        if (queue && (model.cmdQueueCount() === 1)) {
          var shiftWatch = function (keyEvent) {
            if (!keyEvent.shiftKey) {
              $('body').off('keyup', shiftWatch);
              self.endCommandMode();
            }
          };
          $('body').on('keyup', shiftWatch);
        }

        input.capture(holodeck.div, function (event) {
          var playSound = function (success) {
            holodeck.showCommandConfirmation(success ? command : "", event.offsetX, event.offsetY);
            if (!success || (command === 'move')) {
              // Note: move currently plays its own sound.
              return;
            }
            var action = command.charAt(0).toUpperCase() + command.slice(1);
            api.audio.playSound("/SE/UI/UI_Command_" + action);
          };

          var eventTime = new Date().getTime();

          if (!model.allowCustomFormations() && (command === 'move' || command === 'unload')) {
            input.release();
            holodeck.unitCommand(command, mdevent.offsetX, mdevent.offsetY, queue).then(playSound);
            if (!queue)
              self.endCommandMode();
          }
          else if (!dragging && event.type === 'mousemove' && eventTime >= dragTime) {
            holodeck.unitBeginCommand(command, startx, starty).then(function (ok) { dragging = ok; });
          }
          else if ((event.type === 'mouseup') && (event.button === mdevent.button)) {
            input.release();
            if (dragging && (command === 'move' || command === 'unload')) {
              holodeck.unitChangeCommandState(command, event.offsetX, event.offsetY, queue).then(function (success) {
                if (!success)
                  return;
                // move and unload have extra input for their area command so recapture for it
                input.capture(holodeck.div, function (event) {
                  if ((event.type === 'mousedown') && (event.button === mdevent.button)) {
                    input.release();
                    holodeck.unitEndCommand(command, event.offsetX, event.offsetY, queue).then(playSound);
                    if (!queue)
                      self.endCommandMode();
                  }
                  else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
                    input.release();
                    holodeck.unitCancelCommand();
                    self.mode('command_' + command);
                  }
                });
              });
            }
            else if (dragging) {
              holodeck.unitEndCommand(command, event.offsetX, event.offsetY, queue).then(function (success) {
                holodeck.showCommandConfirmation(success ? command : "", event.offsetX, event.offsetY);
                if (!success)
                  return;
                var action = command.charAt(0).toUpperCase() + command.slice(1);
                api.audio.playSound("/SE/UI/UI_Command_" + action);
              });
              if (!queue)
                self.endCommandMode();
            }
            else {
              if (self.hasWorldHoverTarget() && targetable) {
                api.unit.targetCommand(command, self.worldHoverTarget(), queue).then(playSound);
              }
              else {
                holodeck.unitCommand(command, mdevent.offsetX, mdevent.offsetY, queue).then(playSound);
              }

              if (!queue)
                self.endCommandMode();
            }
          }
          else if ((event.type === 'keydown') && (event.keyCode === keyboard.esc)) {
            input.release();
            holodeck.unitCancelCommand();
            self.mode('command_' + command);
          }
        });

        return true;
      }
    };
  };

  for (var i = 0; i < self.commands().length; ++i) {
    var command = self.commands()[i];
    var targetable = self.targetableCommands()[i];
    self.holodeckModeMouseDown['command_' + command] = holodeckCommandMouseDown(command, targetable);
  }

  //  :-( :-( :-(
  $('holodeck').off('mousedown')

  $('holodeck').mousedown(function (mdevent) {
    console.log('my $holodeck.mousedown')
    if (mdevent.target.nodeName !== 'HOLODECK')
      return;

    var holodeck = api.Holodeck.get(this);

    var handler = self.holodeckModeMouseDown[self.mode()];
    if (handler && handler(holodeck, mdevent)) {
      mdevent.preventDefault();
      mdevent.stopPropagation();
      return;
    }

    if (mdevent.button === 1) // middle click
    {
      var oldMode = self.mode();
      self.mode('camera');
      holodeck.beginControlCamera();
      input.capture(holodeck.div, function (event) {
        var mouseDone = ((event.type === 'mouseup') && (event.button === mdevent.button));
        var escKey = ((event.type === 'keydown') && (event.keyCode === keyboard.esc));
        if (mouseDone || escKey) {
          input.release();
          holodeck.endControlCamera();
          if (self.mode() === 'camera')
            self.mode(oldMode);
        }
      });
      mdevent.preventDefault();
      mdevent.stopPropagation();
      return;
    }

    if (mdevent.button === 2 && self.mode() !== 'default') // right click
    {
      self.endCommandMode()
    }
  });
})()
<|endoftext|>


<commit_before>// customInit() is called before any map initialization
function customInit() {

//     // I create a new control click event class
//     OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {
//         defaultHandlerOptions: {
//                 'single': true,
//                 'double': false,
//                 'pixelTolerance': 0,
//                 'stopSingle': false,
//                 'stopDouble': false
//         },
//         initialize: function(options) {
//                 this.handlerOptions = OpenLayers.Util.extend(
//                         {}, this.defaultHandlerOptions
//                 );
//                 OpenLayers.Control.prototype.initialize.apply(
//                         this, arguments
//                 );
//                 this.handler = new OpenLayers.Handler.Click(
//                         this, {
//                                 'click': this.trigger
//                         }, this.handlerOptions
//                 );
//         }
//     });
}

// called before map initialization
function customBeforeMapInit() {
}

// called after map initialization
function customAfterMapInit() {

//     // Create a new map control based on Control Click Event
//     openlayersClickEvent = new OpenLayers.Control.Click( {
//         trigger: function(e) {
//             var xy = geoExtMap.map.getLonLatFromViewPortPx(e.xy);
//             var x = xy.lon;
//             var y = xy.lat;
//
//             alert ( "You clicked on " + x + ", " + y );
//         }
//     });
//
//     geoExtMap.map.addControl(openlayersClickEvent);
}

// called when DOM is ready (Ext.onReady in WebgisInit.js)
function customPostLoading() {
//    Ext.get("panel_header").addClass('sogis-header').insertHtml('beforeEnd', '
hello world
');
}

// called when starting print
function customBeforePrint() {
    // do something. e.g. rearrange your layers
}

// new buttons for the toolbar
var customButtons = [

//    // Add a separator and a button
//    {
//      xtype: 'tbseparator'
//    }, {
//      xtype: 'button',
//      enableToggle: true,
//      allowDepress: true,
//      toggleGroup: 'mapTools',
//      scale: 'medium',
//      icon: 'gis_icons/test.gif',
//      tooltipType: 'qtip',
//      tooltip: "Test button - click on the map",
//      id: 'TESTBUTTON'
//    }
];

// code to add buttons in the toolbar
function customToolbarLoad() {
//     // Handle the button click
//     Ext.getCmp('TESTBUTTON').toggleHandler = mapToolbarHandler;
}

// called when an event on toolbar is invoked
function customMapToolbarHandler(btn, evt) {
//     // Check if the button is pressed or unpressed
//     if (btn.id == "TESTBUTTON") {
//         if (btn.pressed) {
//              alert ( "You clicked on Test Button!" );
//              openlayersClickEvent.activate();
//         }
//         else
//         {
//              alert ( "TEST button is toggled up!" );
//              openlayersClickEvent.deactivate();
//         }
//     }
}

// called when the user clicks on a check in layerTree.
// n is a Ext.TreeNode object
function customActionLayerTreeCheck(n) {
//    if (n.text == "test layer") {
//        alert ("test layer check state:" + n.attributes.checked);
//    }
}
<commit_msg>new customActionOnZoomEvent for Zoom Event<commit_after>// customInit() is called before any map initialization
function customInit() {

//     // I create a new control click event class
//     OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {
//         defaultHandlerOptions: {
//                 'single': true,
//                 'double': false,
//                 'pixelTolerance': 0,
//                 'stopSingle': false,
//                 'stopDouble': false
//         },
//         initialize: function(options) {
//                 this.handlerOptions = OpenLayers.Util.extend(
//                         {}, this.defaultHandlerOptions
//                 );
//                 OpenLayers.Control.prototype.initialize.apply(
//                         this, arguments
//                 );
//                 this.handler = new OpenLayers.Handler.Click(
//                         this, {
//                                 'click': this.trigger
//                         }, this.handlerOptions
//                 );
//         }
//     });
}

// called before map initialization
function customBeforeMapInit() {
}

// called after map initialization
function customAfterMapInit() {

//     // Create a new map control based on Control Click Event
//     openlayersClickEvent = new OpenLayers.Control.Click( {
//         trigger: function(e) {
//             var xy = geoExtMap.map.getLonLatFromViewPortPx(e.xy);
//             var x = xy.lon;
//             var y = xy.lat;
//
//             alert ( "You clicked on " + x + ", " + y );
//         }
//     });
//
//     geoExtMap.map.addControl(openlayersClickEvent);
}

// called when DOM is ready (Ext.onReady in WebgisInit.js)
function customPostLoading() {
//    Ext.get("panel_header").addClass('sogis-header').insertHtml('beforeEnd', '
hello world
');
}

// called when starting print
function customBeforePrint() {
    // do something. e.g. rearrange your layers
}

// new buttons for the toolbar
var customButtons = [

//    // Add a separator and a button
//    {
//      xtype: 'tbseparator'
//    }, {
//      xtype: 'button',
//      enableToggle: true,
//      allowDepress: true,
//      toggleGroup: 'mapTools',
//      scale: 'medium',
//      icon: 'gis_icons/test.gif',
//      tooltipType: 'qtip',
//      tooltip: "Test button - click on the map",
//      id: 'TESTBUTTON'
//    }
];

// code to add buttons in the toolbar
function customToolbarLoad() {
//     // Handle the button click
//     Ext.getCmp('TESTBUTTON').toggleHandler = mapToolbarHandler;
}

// called when an event on toolbar is invoked
function customMapToolbarHandler(btn, evt) {
//     // Check if the button is pressed or unpressed
//     if (btn.id == "TESTBUTTON") {
//         if (btn.pressed) {
//              alert ( "You clicked on Test Button!" );
//              openlayersClickEvent.activate();
//         }
//         else
//         {
//              alert ( "TEST button is toggled up!" );
//              openlayersClickEvent.deactivate();
//         }
//     }
}

// called when the user clicks on a check in layerTree.
// n is a Ext.TreeNode object
function customActionLayerTreeCheck(n) {
//    if (n.text == "test layer") {
//        alert ("test layer check state:" + n.attributes.checked);
//    }
}

// called when the user zooms. Note that this function is called
// when the map is drawn the first time, so we will skip only the
// first time the function is called.
firstTimeZoom = true;
function customActionOnZoomEvent() {
	if (firstTimeZoom) {
		firstTimeZoom = false;
		return;
	}

	// ... action to do on each zoom ...
}
<|endoftext|>


<commit_before>/* eslint-env node commonjs */
/** JavaScript Versions
 *  5 is minimum -> Last IE11
 *  6 = 2015 -> Node >8.10, iOS12+
 *  7 = 2016 -> FF78+,
 *  8 = 2017 -> Node 10.9+
 *  9 = 2018 -> Node 12.11+
 * 10 = 2019
 * 11 = 2020
 * 12 = 2021
 */
'use strict'
module.exports = {
    env: {
        browser: true,
        jquery: true,
        node: false,
        'shared-node-browser': false
    },
    parserOptions: {
        ecmaVersion: 2015,
        sourceType: 'script'
    },
    globals: {
        RED: true,
    },
    rules: {
        'jsdoc/multiline-blocks': 0,
        'jsdoc/newline-after-description': 0,
        'jsdoc/no-multi-asterisks': 0,

        //'sonarjs/no-duplicate-string': 0,
        //'sonarjs/cognitive-complexity': 0,

        //'es/no-object-entries': 0,
    }
}
<commit_msg>Add overrides for modules<commit_after>/* eslint-env node commonjs */
/** JavaScript Versions
 *  5 is minimum -> Last IE11
 *  6 = 2015 -> Node >8.10, iOS12+
 *  7 = 2016 -> FF78+,
 *  8 = 2017 -> Node 10.9+
 *  9 = 2018 -> Node 12.11+
 * 10 = 2019
 * 11 = 2020
 * 12 = 2021
 */
'use strict'
module.exports = {
    env: {
        browser: true,
        jquery: true,
        node: false,
        'shared-node-browser': false
    },
    parserOptions: {
        ecmaVersion: 2015,
        sourceType: 'script'
    },
    globals: {
        RED: true,
    },
    overrides: [
        {
            files: ['*.mod.js', '*.mjs'],
            parserOptions: { sourceType: 'module' },
        }
    ],
    rules: {
        'jsdoc/multiline-blocks': 0,
        'jsdoc/newline-after-description': 0,
        'jsdoc/no-multi-asterisks': 0,

        //'sonarjs/no-duplicate-string': 0,
        //'sonarjs/cognitive-complexity': 0,

        //'es/no-object-entries': 0,
    }
}
<|endoftext|>


<commit_before>'use strict';

module.exports = {
	db: 'mongodb://localhost/survey-app-dev',
	app: {
		title: 'Survey App - Development Environment'
	},
	facebook: {
		clientID: process.env.FACEBOOK_ID || 'APP_ID',
		clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
		callbackURL: '/auth/facebook/callback'
	},
	twitter: {
		clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
		clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
		callbackURL: '/auth/twitter/callback'
	},
	google: {
		clientID: process.env.GOOGLE_ID || 'APP_ID',
		clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
		callbackURL: '/auth/google/callback'
	},
	linkedin: {
		clientID: process.env.LINKEDIN_ID || 'APP_ID',
		clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
		callbackURL: '/auth/linkedin/callback'
	},
	github: {
		clientID: process.env.GITHUB_ID || 'APP_ID',
		clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
		callbackURL: '/auth/github/callback'
	},
	mailer: {
		from: process.env.MAILER_FROM || 'MAILER_FROM',
		options: {
			service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
			auth: {
				user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
				pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
			}
		}
	}
};
<commit_msg>just changed mongodb address<commit_after>'use strict';

module.exports = {
	//db: 'mongodb://localhost/survey-app-dev',
	db: 'mongodb://<dbuser>:<dbpassword>@ds049631.mongolab.com:49631/heroku_app35695970',
	app: {
		title: 'Survey App - Development Environment'
	},
	facebook: {
		clientID: process.env.FACEBOOK_ID || 'APP_ID',
		clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
		callbackURL: '/auth/facebook/callback'
	},
	twitter: {
		clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
		clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
		callbackURL: '/auth/twitter/callback'
	},
	google: {
		clientID: process.env.GOOGLE_ID || 'APP_ID',
		clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
		callbackURL: '/auth/google/callback'
	},
	linkedin: {
		clientID: process.env.LINKEDIN_ID || 'APP_ID',
		clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
		callbackURL: '/auth/linkedin/callback'
	},
	github: {
		clientID: process.env.GITHUB_ID || 'APP_ID',
		clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
		callbackURL: '/auth/github/callback'
	},
	mailer: {
		from: process.env.MAILER_FROM || 'MAILER_FROM',
		options: {
			service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
			auth: {
				user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
				pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
			}
		}
	}
};
<|endoftext|>


<commit_before>

function getLutDescriptor (dataSet, tag) {
  if (!dataSet.elements[tag] || dataSet.elements[tag].length !== 6) {
    return;
  }

  return [dataSet.uint16(tag, 0), dataSet.uint16(tag, 1), dataSet.uint16(tag, 2)];
}

function getLutData (lutDataSet, tag, lutDescriptor) {
  const lut = [];
  const lutData = lutDataSet.elements[tag];
  const numLutEntries = lutDescriptor[0];

  for (let i = 0; i < numLutEntries; i++) {
    // Output range is always unsigned
    if (lutDescriptor[2] === 16) {
      lut[i] = lutDataSet.uint16(tag, i);
    } else {
      lut[i] = lutDataSet.byteArray[i + lutData.dataOffset];
    }
  }

  return lut;
}

function populatePaletteColorLut (dataSet, imagePixelModule) {
  // return immediately if photometric interpretation is not PALETTE COLOR and no palette lut elements
  if (imagePixelModule.photometricInterpretation !== 'PALETTE COLOR' || !dataSet.elements.x00281101) {
    return;
  }
  imagePixelModule.redPaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281101');
  imagePixelModule.greenPaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281102');
  imagePixelModule.bluePaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281103');

  imagePixelModule.redPaletteColorLookupTableData = getLutData(dataSet, 'x00281201', imagePixelModule.redPaletteColorLookupTableDescriptor);
  imagePixelModule.greenPaletteColorLookupTableData = getLutData(dataSet, 'x00281202', imagePixelModule.greenPaletteColorLookupTableDescriptor);
  imagePixelModule.bluePaletteColorLookupTableData = getLutData(dataSet, 'x00281203', imagePixelModule.bluePaletteColorLookupTableDescriptor);
}

function populateSmallestLargestPixelValues (dataSet, imagePixelModule) {
  const pixelRepresentation = dataSet.uint16('x00280103');

  if (pixelRepresentation === 0) {
    imagePixelModule.smallestPixelValue = dataSet.uint16('x00280106');
    imagePixelModule.largestPixelValue = dataSet.uint16('x00280107');
  } else {
    imagePixelModule.smallestPixelValue = dataSet.int16('x00280106');
    imagePixelModule.largestPixelValue = dataSet.int16('x00280107');
  }
}

function getImagePixelModule (dataSet) {

  const imagePixelModule = {
    samplesPerPixel: dataSet.uint16('x00280002'),
    photometricInterpretation: dataSet.string('x00280004'),
    rows: dataSet.uint16('x00280010'),
    columns: dataSet.uint16('x00280011'),
    bitsAllocated: dataSet.uint16('x00280100'),
    bitsStored: dataSet.uint16('x00280101'),
    highBit: dataSet.uint16('x00280102'),
    pixelRepresentation: dataSet.uint16('x00280103'),
    planarConfiguration: dataSet.uint16('x00280006'),
    pixelAspectRatio: dataSet.string('x00280034')
  };

  populateSmallestLargestPixelValues(dataSet, imagePixelModule);
  populatePaletteColorLut(dataSet, imagePixelModule);

  return imagePixelModule;

}

export default getImagePixelModule;
<commit_msg>OHIF #89 Fix typo in the comment<commit_after>

function getLutDescriptor (dataSet, tag) {
  if (!dataSet.elements[tag] || dataSet.elements[tag].length !== 6) {
    return;
  }

  return [dataSet.uint16(tag, 0), dataSet.uint16(tag, 1), dataSet.uint16(tag, 2)];
}

function getLutData (lutDataSet, tag, lutDescriptor) {
  const lut = [];
  const lutData = lutDataSet.elements[tag];
  const numLutEntries = lutDescriptor[0];

  for (let i = 0; i < numLutEntries; i++) {
    // Output range is always unsigned
    if (lutDescriptor[2] === 16) {
      lut[i] = lutDataSet.uint16(tag, i);
    } else {
      lut[i] = lutDataSet.byteArray[i + lutData.dataOffset];
    }
  }

  return lut;
}

function populatePaletteColorLut (dataSet, imagePixelModule) {
  // return immediately if photometric interpretation is not PALETTE COLOR or no palette lut elements
  if (imagePixelModule.photometricInterpretation !== 'PALETTE COLOR' || !dataSet.elements.x00281101) {
    return;
  }
  imagePixelModule.redPaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281101');
  imagePixelModule.greenPaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281102');
  imagePixelModule.bluePaletteColorLookupTableDescriptor = getLutDescriptor(dataSet, 'x00281103');

  imagePixelModule.redPaletteColorLookupTableData = getLutData(dataSet, 'x00281201', imagePixelModule.redPaletteColorLookupTableDescriptor);
  imagePixelModule.greenPaletteColorLookupTableData = getLutData(dataSet, 'x00281202', imagePixelModule.greenPaletteColorLookupTableDescriptor);
  imagePixelModule.bluePaletteColorLookupTableData = getLutData(dataSet, 'x00281203', imagePixelModule.bluePaletteColorLookupTableDescriptor);
}

function populateSmallestLargestPixelValues (dataSet, imagePixelModule) {
  const pixelRepresentation = dataSet.uint16('x00280103');

  if (pixelRepresentation === 0) {
    imagePixelModule.smallestPixelValue = dataSet.uint16('x00280106');
    imagePixelModule.largestPixelValue = dataSet.uint16('x00280107');
  } else {
    imagePixelModule.smallestPixelValue = dataSet.int16('x00280106');
    imagePixelModule.largestPixelValue = dataSet.int16('x00280107');
  }
}

function getImagePixelModule (dataSet) {

  const imagePixelModule = {
    samplesPerPixel: dataSet.uint16('x00280002'),
    photometricInterpretation: dataSet.string('x00280004'),
    rows: dataSet.uint16('x00280010'),
    columns: dataSet.uint16('x00280011'),
    bitsAllocated: dataSet.uint16('x00280100'),
    bitsStored: dataSet.uint16('x00280101'),
    highBit: dataSet.uint16('x00280102'),
    pixelRepresentation: dataSet.uint16('x00280103'),
    planarConfiguration: dataSet.uint16('x00280006'),
    pixelAspectRatio: dataSet.string('x00280034')
  };

  populateSmallestLargestPixelValues(dataSet, imagePixelModule);
  populatePaletteColorLut(dataSet, imagePixelModule);

  return imagePixelModule;

}

export default getImagePixelModule;
<|endoftext|>


<commit_before>/*
 *
 *  Copyright 2014 Netflix, Inc.
 *
 *     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.
 *
 */
// PhantomJS script
// Takes screeshot of a given page. This correctly handles pages which
// dynamically load content making AJAX requests.

// Instead of waiting fixed amount of time before rendering, we give a short
// time for the page to make additional requests.

var _ = require('./lodash.js');
var fs = require('fs');

var defaultOpts = {
    // How long do we wait for additional requests
    //after all initial requests have got their response
    ajaxTimeout: 400,

    // How long do we wait at max
    maxTimeout: 800
};

var Page = (function(opts) {
    opts = _.extend(defaultOpts, opts);
    var requestCount = 0;
    var forceRenderTimeout;
    var ajaxRenderTimeout;

    var page = require('webpage').create();
    page.viewportSize = {
        width: opts.width,
        height: opts.height
    };

    page.settings.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36';
    page.customHeaders = {
      // Nullify Accept-Encoding header to disable compression (https://github.com/ariya/phantomjs/issues/10930)
      'Accept-Encoding': ' '
    };
    page.onInitialized = function() {
        page.customHeaders = {};
    };
    // Silence confirmation messages and errors
    page.onConfirm = page.onPrompt = page.onError = noop;

    page.onResourceRequested = function(request) {
        requestCount += 1;
        clearTimeout(ajaxRenderTimeout);
    };

    page.onResourceReceived = function(response) {
        if (!response.stage || response.stage === 'end') {
            requestCount -= 1;
            if (requestCount === 0) {
                ajaxRenderTimeout = setTimeout(renderAndExit, opts.ajaxTimeout);
            }
        }
    };

    var api = {};

    api.render = function(url, file) {
        opts.file = file;

        page.open(url, function(status) {
            if (status !== "success") {
                console.error('Unable to load url:', url);
                phantom.exit(1);
            } else {
                forceRenderTimeout = setTimeout(renderAndExit, opts.maxTimeout);
            }
        });
    };

    function renderAndExit() {
        page.render(opts.file + '.png');
        fs.write(opts.file + '.html', page.content)
        phantom.exit();
    }

    function noop() {}

    return api;
});

function die(error) {
    console.error(error);
    phantom.exit(1);
}

function main() {
    var args = require('system').args;

    var url = args[1];
    var file = args[2];
    var width = args[3] || 1280;
    var height = args[4] || 800;

    var isHelp = args[1] === '-h' || args[1] === '--help';
    if (args.length === 1 || isHelp) {
        var help = 'Usage: phantomjs capture.js <url> <output-file-without-extension> [width] [height]\n';
        help += 'Example: phantomjs capture.js http://google.com google 1200 800';
        die(help);
    }

    if (!url) die('Url parameter must be specified');
    if (!file) die('File parameter must be specified');

    var opts = {
        width: width,
        height: height
    };

    var page = Page(opts);
    page.render(url, file);
}

main();
<commit_msg>support for cookies for phantom via env<commit_after>/*
 *
 *  Copyright 2014 Netflix, Inc.
 *
 *     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.
 *
 */
// PhantomJS script
// Takes screeshot of a given page. This correctly handles pages which
// dynamically load content making AJAX requests.

// Instead of waiting fixed amount of time before rendering, we give a short
// time for the page to make additional requests.

var _ = require('./lodash.js');
var system = require('system');
var env = system.env;
var fs = require('fs');

var defaultOpts = {
    // How long do we wait for additional requests
    //after all initial requests have got their response
    ajaxTimeout: 400,

    // How long do we wait at max
    maxTimeout: 800
};

var Page = (function(opts) {
    opts = _.extend(defaultOpts, opts);
    var requestCount = 0;
    var forceRenderTimeout;
    var ajaxRenderTimeout;

    var page = require('webpage').create();
    page.viewportSize = {
        width: opts.width,
        height: opts.height
    };

    page.settings.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36';
    // You can set phantomjs_cookies env variable to send a cookie string
    var has_cookies = false;
    var cookie_values;
    Object.keys(env).forEach(function(key) {
        if (key === 'phantomjs_cookies'){
            has_cookies = true;
            cookie_values = env[key];
        };
    });

    if (has_cookies === true) {
          page.customHeaders = {'Accept-Encoding': ' ', 'Cookie': cookie_values };
    } else {
      page.customHeaders = {
        // Nullify Accept-Encoding header to disable compression (https://github.com/ariya/phantomjs/issues/10930)
        'Accept-Encoding': ' '
      };
    }
    page.onInitialized = function() {
        page.customHeaders = {};
    };
    // Silence confirmation messages and errors
    page.onConfirm = page.onPrompt = page.onError = noop;

    page.onResourceRequested = function(request) {
        requestCount += 1;
        clearTimeout(ajaxRenderTimeout);
    };

    page.onResourceReceived = function(response) {
        if (!response.stage || response.stage === 'end') {
            requestCount -= 1;
            if (requestCount === 0) {
                ajaxRenderTimeout = setTimeout(renderAndExit, opts.ajaxTimeout);
            }
        }
    };

    var api = {};

    api.render = function(url, file) {
        opts.file = file;

        page.open(url, function(status) {
            if (status !== "success") {
                console.error('Unable to load url:', url);
                phantom.exit(1);
            } else {
                forceRenderTimeout = setTimeout(renderAndExit, opts.maxTimeout);
            }
        });
    };

    function renderAndExit() {
        page.render(opts.file + '.png');
        fs.write(opts.file + '.html', page.content)
        phantom.exit();
    }

    function noop() {}

    return api;
});

function die(error) {
    console.error(error);
    phantom.exit(1);
}

function main() {
    var args = require('system').args;

    var url = args[1];
    var file = args[2];
    var width = args[3] || 1280;
    var height = args[4] || 800;

    var isHelp = args[1] === '-h' || args[1] === '--help';
    if (args.length === 1 || isHelp) {
        var help = 'Usage: phantomjs capture.js <url> <output-file-without-extension> [width] [height]\n';
        help += 'Example: phantomjs capture.js http://google.com google 1200 800';
        die(help);
    }

    if (!url) die('Url parameter must be specified');
    if (!file) die('File parameter must be specified');

    var opts = {
        width: width,
        height: height
    };

    var page = Page(opts);
    page.render(url, file);
}

main();
<|endoftext|>


<commit_before>const EventEmitter = require('events');
const { hexToArray } = require('./utils');

class FileReceiver extends EventEmitter {
  constructor() {
    super();
  }

  download() {
    return Promise.all([
      new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();

        xhr.onprogress = event => {
          if (event.lengthComputable && event.target.status !== 404) {
            const percentComplete = Math.floor(
              event.loaded / event.total * 100
            );
            this.emit('progress', percentComplete);
          }
        };

        xhr.onload = function(event) {
          if (xhr.status === 404) {
            reject(
              new Error('The file has expired, or has already been deleted.')
            );
            return;
          }

          const blob = new Blob([this.response]);
          const fileReader = new FileReader();
          fileReader.onload = function() {
            const meta = JSON.parse(xhr.getResponseHeader('X-File-Metadata'));
            resolve({
              data: this.result,
              aad: meta.aad,
              filename: meta.filename,
              iv: meta.id
            });
          };

          fileReader.readAsArrayBuffer(blob);
        };

        xhr.open('get', '/assets' + location.pathname.slice(0, -1), true);
        xhr.responseType = 'blob';
        xhr.send();
      }),
      window.crypto.subtle.importKey(
        'jwk',
        {
          kty: 'oct',
          k: location.hash.slice(1),
          alg: 'A128GCM',
          ext: true
        },
        {
          name: 'AES-GCM'
        },
        true,
        ['encrypt', 'decrypt']
      )
    ])
      .then(([fdata, key]) => {
        this.emit('decrypting', true);
        return Promise.all([
          window.crypto.subtle
            .decrypt(
              {
                name: 'AES-GCM',
                iv: hexToArray(fdata.iv),
                additionalData: hexToArray(fdata.aad)
              },
              key,
              fdata.data
            )
            .then(decrypted => {
              this.emit('decrypting', false);
              return new Promise((resolve, reject) => {
                resolve(decrypted);
              });
            }),
          new Promise((resolve, reject) => {
            resolve(fdata.filename);
          }),
          new Promise((resolve, reject) => {
            resolve(hexToArray(fdata.aad));
          })
        ]);
      })
      .then(([decrypted, fname, proposedHash]) => {
        this.emit('hashing', true);
        return window.crypto.subtle
          .digest('SHA-256', decrypted)
          .then(calculatedHash => {
            this.emit('hashing', false);
            const integrity =
              new Uint8Array(calculatedHash).toString() ===
              proposedHash.toString();
            if (!integrity) {
              return new Promise((resolve, reject) => {
                console.log('This file has been tampered with.');
                reject();
              });
            }

            return Promise.all([
              new Promise((resolve, reject) => {
                resolve(decrypted);
              }),
              new Promise((resolve, reject) => {
                resolve(fname);
              })
            ]);
          });
      });
  }
}

module.exports = FileReceiver;
<commit_msg>added tagLength to make app work in edge<commit_after>const EventEmitter = require('events');
const { hexToArray } = require('./utils');

class FileReceiver extends EventEmitter {
  constructor() {
    super();
  }

  download() {
    return Promise.all([
      new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();

        xhr.onprogress = event => {
          if (event.lengthComputable && event.target.status !== 404) {
            const percentComplete = Math.floor(
              event.loaded / event.total * 100
            );
            this.emit('progress', percentComplete);
          }
        };

        xhr.onload = function(event) {
          if (xhr.status === 404) {
            reject(
              new Error('The file has expired, or has already been deleted.')
            );
            return;
          }

          const blob = new Blob([this.response]);
          const fileReader = new FileReader();
          fileReader.onload = function() {
            const meta = JSON.parse(xhr.getResponseHeader('X-File-Metadata'));
            resolve({
              data: this.result,
              aad: meta.aad,
              filename: meta.filename,
              iv: meta.id
            });
          };

          fileReader.readAsArrayBuffer(blob);
        };

        xhr.open('get', '/assets' + location.pathname.slice(0, -1), true);
        xhr.responseType = 'blob';
        xhr.send();
      }),
      window.crypto.subtle.importKey(
        'jwk',
        {
          kty: 'oct',
          k: location.hash.slice(1),
          alg: 'A128GCM',
          ext: true
        },
        {
          name: 'AES-GCM'
        },
        true,
        ['encrypt', 'decrypt']
      )
    ])
      .then(([fdata, key]) => {
        this.emit('decrypting', true);
        return Promise.all([
          window.crypto.subtle
            .decrypt(
              {
                name: 'AES-GCM',
                iv: hexToArray(fdata.iv),
                additionalData: hexToArray(fdata.aad),
                tagLength: 128
              },
              key,
              fdata.data
            )
            .then(decrypted => {
              this.emit('decrypting', false);
              return new Promise((resolve, reject) => {
                resolve(decrypted);
              });
            }),
          new Promise((resolve, reject) => {
            resolve(fdata.filename);
          }),
          new Promise((resolve, reject) => {
            resolve(hexToArray(fdata.aad));
          })
        ]);
      })
      .then(([decrypted, fname, proposedHash]) => {
        this.emit('hashing', true);
        return window.crypto.subtle
          .digest('SHA-256', decrypted)
          .then(calculatedHash => {
            this.emit('hashing', false);
            const integrity =
              new Uint8Array(calculatedHash).toString() ===
              proposedHash.toString();
            if (!integrity) {
              return new Promise((resolve, reject) => {
                console.log('This file has been tampered with.');
                reject();
              });
            }

            return Promise.all([
              new Promise((resolve, reject) => {
                resolve(decrypted);
              }),
              new Promise((resolve, reject) => {
                resolve(fname);
              })
            ]);
          });
      });
  }
}

module.exports = FileReceiver;
<|endoftext|>


<commit_before>const path = require('path')

const resolve = dir => {
  return path.join(__dirname, dir)
}

// 项目部署基础
// 默认情况下，我们假设你的应用将被部署在域的根目录下,
// 例如：https://www.my-app.com/
// 默认：'/'
// 如果您的应用程序部署在子路径中，则需要在这指定子路径
// 例如：https://www.foobar.com/my-app/
// 需要将它改为'/my-app/'
// iview-admin线上演示打包路径： https://file.iviewui.com/admin-dist/
const BASE_URL = process.env.NODE_ENV === 'production'
  ? '/'
  : '/'

module.exports = {
  // Project deployment base
  // By default we assume your app will be deployed at the root of a domain,
  // e.g. https://www.my-app.com/
  // If your app is deployed at a sub-path, you will need to specify that
  // sub-path here. For example, if your app is deployed at
  // https://www.foobar.com/my-app/
  // then change this to '/my-app/'
  baseUrl: BASE_URL,
  // tweak internal webpack configuration.
  // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
  // 如果你不需要使用eslint，把lintOnSave设为false即可
  lintOnSave: true,
  chainWebpack: config => {
    config.resolve.alias
      .set('@', resolve('src')) // key,value自行定义，比如.set('@@', resolve('src/components'))
      .set('_c', resolve('src/components'))
  },
  // 打包时不生成.map文件
  productionSourceMap: false
  // 这里写你调用接口的基础路径，来解决跨域，如果设置了代理，那你本地开发环境的axios的baseUrl要写为 '' ，即空字符串
  // devServer: {
  //   proxy: 'localhost:3000'
  // }
}
<commit_msg>Update vue.config.js<commit_after>const path = require('path')

const resolve = dir => {
  return path.join(__dirname, dir)
}

// 项目部署基础
// 默认情况下，我们假设你的应用将被部署在域的根目录下,
// 例如：https://www.my-app.com/
// 默认：'/'
// 如果您的应用程序部署在子路径中，则需要在这指定子路径
// 例如：https://www.foobar.com/my-app/
// 需要将它改为'/my-app/'
// iview-admin线上演示打包路径： https://file.iviewui.com/admin-dist/
const BASE_URL = process.env.NODE_ENV === 'production'
  ? '/'
  : '/'

module.exports = {
  // Project deployment base
  // By default we assume your app will be deployed at the root of a domain,
  // e.g. https://www.my-app.com/
  // If your app is deployed at a sub-path, you will need to specify that
  // sub-path here. For example, if your app is deployed at
  // https://www.foobar.com/my-app/
  // then change this to '/my-app/'
  baseUrl: BASE_URL,
  // tweak internal webpack configuration.
  // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
  // 如果你不需要使用eslint，把lintOnSave设为false即可
  lintOnSave: true,
  chainWebpack: config => {
    config.resolve.alias
      .set('@', resolve('src')) // key,value自行定义，比如.set('@@', resolve('src/components'))
      .set('_c', resolve('src/components'))
  },
  // 设为false打包时不生成.map文件
  productionSourceMap: false
  // 这里写你调用接口的基础路径，来解决跨域，如果设置了代理，那你本地开发环境的axios的baseUrl要写为 '' ，即空字符串
  // devServer: {
  //   proxy: 'localhost:3000'
  // }
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google 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.
 */

/**
 * @fileoverview Defines the DATA message type used in the OTR protocol.
 *
 * @author rcc@google.com (Ryan Chan)
 */

goog.provide('e2e.otr.message.Data');

goog.require('e2e');
goog.require('e2e.cipher.DiffieHellman');
goog.require('e2e.hash.Sha1');
goog.require('e2e.otr');
goog.require('e2e.otr.Data');
goog.require('e2e.otr.Mpi');
goog.require('e2e.otr.constants');
goog.require('e2e.otr.constants.MessageType');
goog.require('e2e.otr.error.InvalidArgumentsError');
goog.require('e2e.otr.error.NotImplementedError');
goog.require('e2e.otr.message.Encoded');
goog.require('e2e.otr.message.handler');
goog.require('e2e.otr.util.Iterator');
goog.require('e2e.otr.util.Tee');
goog.require('e2e.otr.util.aes128ctr');
goog.require('goog.array');
goog.require('goog.crypt.Hmac');

goog.scope(function() {

var constants = e2e.otr.constants;
var MSGSTATE = constants.MSGSTATE;

/**
 * An OTRv3 DATA message.
 * @constructor
 * @extends {e2e.otr.message.Encoded}
 * @param {!e2e.otr.Session} session The enclosing session.
 * @param {string} plaintext The plaintext portion of the message.
 * @param {!Array.<e2e.otr.message.Tlv>=} opt_tlvs 0+ TLV records to include.
 * @param {number=} opt_flags The flags to set on the message.
 */
e2e.otr.message.Data = function(session, plaintext, opt_tlvs, opt_flags) {
  goog.base(this, session);

  if (plaintext.indexOf('\0') > -1) {
    throw new e2e.otr.error.InvalidArgumentsError(
        'Plaintext may not contain \\0');
  }

  this.plaintext_ = plaintext;
  this.tlvs_ = opt_tlvs || [];
  this.flags_ = new Uint8Array([0x00 | opt_flags]);
};
goog.inherits(e2e.otr.message.Data, e2e.otr.message.Encoded);

/**
 * Specifies the type of the message.
 * @type {!e2e.otr.Byte}
 */
e2e.otr.message.Data.MESSAGE_TYPE = constants.MessageType.DATA;

/**
 * Generates the keys used for AES and SHA1-HMAC of data messages.
 * @param {!e2e.cipher.DiffieHellman} localDh The DH of the local key component.
 * @param {!e2e.ByteArray} remoteKey The remote key component.
 * @return {!{sendingAes: !e2e.ByteArray, receivingAes: !e2e.ByteArray,
 *     sendingMac: !e2e.ByteArray, receivingMac: !e2e.ByteArray}}
 */
e2e.otr.message.Data.computeKeys = function(localDh, remoteKey) {
  var highEnd = e2e.otr.compareByteArray(localDh.generate(), remoteKey) > 0;
  var sendbyte = highEnd ? 0x01 : 0x02;
  var recvbyte = highEnd ? 0x02 : 0x01;
  var secret = localDh.generate(remoteKey);
  var secbytes = new e2e.otr.Mpi(new Uint8Array(secret)).serialize();

  /**
   * h1 function as defined in OTR spec.
   * @param {number} b The input byte.
   * @return {!e2e.ByteArray} The 160-bit output of the SHA-1 hash of the
   *     (5+len) bytes consisting of the byte b, followed by secbytes.
   */
  var h1 = function(b) {
    return new e2e.hash.Sha1().hash([b].concat(secbytes));
  };

  var sendingAes = h1(sendbyte).slice(0, 16);
  var receivingAes = h1(recvbyte).slice(0, 16);

  return {
    sendingAes: sendingAes,
    sendingMac: new e2e.hash.Sha1().hash(sendingAes),
    receivingAes: receivingAes,
    receivingMac: new e2e.hash.Sha1().hash(receivingAes)
  };
};

/**
 * Serialize the DATA message into a Uint8Array.
 * @return {!Uint8Array} The serialized DATA message.
 */
e2e.otr.message.Data.prototype.serializeMessageContent = function() {
  var ctrTop = e2e.incrementByteArray(this.session_.sendCtr);
  var remoteKey = this.session_.keymanager.getRemoteKey();
  var dh = this.session_.keymanager.getKey();

  var message = e2e.otr.serializeBytes([
    e2e.stringToByteArray(this.plaintext_),
    [0x00] // NULL BYTE
  ].concat(this.tlvs_));

  var keys = e2e.otr.message.Data.computeKeys(dh.key, remoteKey.key);

  var encryptedMessage = e2e.otr.util.aes128ctr.encrypt(keys.sendingAes,
      message, ctrTop.concat(goog.array.repeat(0, 8)));

  var nextDh = new e2e.cipher.DiffieHellman(constants.DH_MODULUS,
      [constants.DH_GENERATOR]);
  this.session_.keymanager.storeKey(nextDh);

  var partialData = e2e.otr.serializeBytes([
    this.flags_,
    dh.keyid,
    remoteKey.keyid,
    new e2e.otr.Mpi(new Uint8Array(nextDh.generate())),
    ctrTop,
    new e2e.otr.Data(new Uint8Array(encryptedMessage))
  ]);

  var mac = new goog.crypt.Hmac(new e2e.hash.Sha1(), keys.sendingMac).getHmac(
      this.addHeader(partialData));

  // TODO(rcc): old MAC keys to be revealed.
  var oldMacKeys = [];

  return e2e.otr.serializeBytes([partialData, mac, oldMacKeys]);
};

/**
 * Processes a DATA message message.
 * @param {!e2e.otr.Session} session The enclosing session.
 * @param {!Uint8Array} data The data to be processed.
 */
e2e.otr.message.Data.process = function(session, data) {
  switch (session.getMsgState()) {
    case MSGSTATE.ENCRYPTED:
      var tee = new e2e.otr.util.Tee();
      var iter = new e2e.otr.util.Iterator(data);

      var flags = e2e.otr.byteToNum(tee.tee(iter.next()));
      var ignoreUnreadable = flags & 0x01;
      var remoteKeyid = tee.tee(iter.next(4));
      var keyid = tee.tee(iter.next(4));
      var nextDh = e2e.otr.Mpi.parse(tee.tee(iter.nextEncoded())).serialize();
      var ctrTop = Array.apply([], tee.tee(iter.next(8)));
      var encryptedMessage = e2e.otr.Data.parse(tee.tee(iter.nextEncoded()))
          .deconstruct();
      var mac = iter.next(20);
      // TODO(rcc): handle revealing old MAC keys.

      var key = session.keymanager.getKey(keyid);
      var remoteKey = session.keymanager.getRemoteKey(remoteKeyid);
      var keys = e2e.otr.message.Data.computeKeys(key.key, remoteKey.key);

      var macData = e2e.otr.message.Encoded.addHeader(session,
          constants.MessageType.DATA, tee.dump(), true);
      var calculatedMac = new goog.crypt.Hmac(new e2e.hash.Sha1(),
          keys.receivingMac).getHmac(macData);

      if (e2e.otr.compareByteArray(mac, calculatedMac)) {
        if (!ignoreUnreadable) {
          // TODO(rcc): Log the error and/or warn the user. Remove extra return.
          return;
        }
        return;
      }

      session.keymanager.storeRemoteKey(e2e.incrementByteArray(Array.apply([],
          remoteKeyid)), Array.apply([], nextDh));

      var message = e2e.otr.util.aes128ctr.decrypt(keys.receivingAes,
          encryptedMessage, ctrTop.concat(goog.array.repeat(0, 8)));

      var split = Array.prototype.indexOf.call(message, '\0');
      session.display(e2e.byteArrayToString(message.subarray(0, split)));
      // TODO(rcc): Process TLVs.
      break;

    case MSGSTATE.PLAINTEXT:
    case MSGSTATE.FINISHED:
      // TODO(rcc): Inform the user that an unreadable encrypted message was
      // received, and reply with an Error Message.
      throw new e2e.otr.error.NotImplementedError('Not yet implemented.');
      break;

    default:
      e2e.otr.assertState(false, 'Invalid message state.');
  }
};

e2e.otr.message.handler.add(e2e.otr.message.Data);
});  // goog.scope
<commit_msg>Properly store next DH from OTR data message<commit_after>/**
 * @license
 * Copyright 2014 Google 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.
 */

/**
 * @fileoverview Defines the DATA message type used in the OTR protocol.
 *
 * @author rcc@google.com (Ryan Chan)
 */

goog.provide('e2e.otr.message.Data');

goog.require('e2e');
goog.require('e2e.cipher.DiffieHellman');
goog.require('e2e.hash.Sha1');
goog.require('e2e.otr');
goog.require('e2e.otr.Data');
goog.require('e2e.otr.Mpi');
goog.require('e2e.otr.constants');
goog.require('e2e.otr.constants.MessageType');
goog.require('e2e.otr.error.InvalidArgumentsError');
goog.require('e2e.otr.error.NotImplementedError');
goog.require('e2e.otr.message.Encoded');
goog.require('e2e.otr.message.handler');
goog.require('e2e.otr.util.Iterator');
goog.require('e2e.otr.util.Tee');
goog.require('e2e.otr.util.aes128ctr');
goog.require('goog.array');
goog.require('goog.crypt.Hmac');

goog.scope(function() {

var constants = e2e.otr.constants;
var MSGSTATE = constants.MSGSTATE;

/**
 * An OTRv3 DATA message.
 * @constructor
 * @extends {e2e.otr.message.Encoded}
 * @param {!e2e.otr.Session} session The enclosing session.
 * @param {string} plaintext The plaintext portion of the message.
 * @param {!Array.<e2e.otr.message.Tlv>=} opt_tlvs 0+ TLV records to include.
 * @param {number=} opt_flags The flags to set on the message.
 */
e2e.otr.message.Data = function(session, plaintext, opt_tlvs, opt_flags) {
  goog.base(this, session);

  if (plaintext.indexOf('\0') > -1) {
    throw new e2e.otr.error.InvalidArgumentsError(
        'Plaintext may not contain \\0');
  }

  this.plaintext_ = plaintext;
  this.tlvs_ = opt_tlvs || [];
  this.flags_ = new Uint8Array([0x00 | opt_flags]);
};
goog.inherits(e2e.otr.message.Data, e2e.otr.message.Encoded);

/**
 * Specifies the type of the message.
 * @type {!e2e.otr.Byte}
 */
e2e.otr.message.Data.MESSAGE_TYPE = constants.MessageType.DATA;

/**
 * Generates the keys used for AES and SHA1-HMAC of data messages.
 * @param {!e2e.cipher.DiffieHellman} localDh The DH of the local key component.
 * @param {!e2e.ByteArray} remoteKey The remote key component.
 * @return {!{sendingAes: !e2e.ByteArray, receivingAes: !e2e.ByteArray,
 *     sendingMac: !e2e.ByteArray, receivingMac: !e2e.ByteArray}}
 */
e2e.otr.message.Data.computeKeys = function(localDh, remoteKey) {
  var highEnd = e2e.otr.compareByteArray(localDh.generate(), remoteKey) > 0;
  var sendbyte = highEnd ? 0x01 : 0x02;
  var recvbyte = highEnd ? 0x02 : 0x01;
  var secret = localDh.generate(remoteKey);
  var secbytes = new e2e.otr.Mpi(new Uint8Array(secret)).serialize();

  /**
   * h1 function as defined in OTR spec.
   * @param {number} b The input byte.
   * @return {!e2e.ByteArray} The 160-bit output of the SHA-1 hash of the
   *     (5+len) bytes consisting of the byte b, followed by secbytes.
   */
  var h1 = function(b) {
    return new e2e.hash.Sha1().hash([b].concat(secbytes));
  };

  var sendingAes = h1(sendbyte).slice(0, 16);
  var receivingAes = h1(recvbyte).slice(0, 16);

  return {
    sendingAes: sendingAes,
    sendingMac: new e2e.hash.Sha1().hash(sendingAes),
    receivingAes: receivingAes,
    receivingMac: new e2e.hash.Sha1().hash(receivingAes)
  };
};

/**
 * Serialize the DATA message into a Uint8Array.
 * @return {!Uint8Array} The serialized DATA message.
 */
e2e.otr.message.Data.prototype.serializeMessageContent = function() {
  var ctrTop = e2e.incrementByteArray(this.session_.sendCtr);
  var remoteKey = this.session_.keymanager.getRemoteKey();
  var dh = this.session_.keymanager.getKey();

  var message = e2e.otr.serializeBytes([
    e2e.stringToByteArray(this.plaintext_),
    [0x00] // NULL BYTE
  ].concat(this.tlvs_));

  var keys = e2e.otr.message.Data.computeKeys(dh.key, remoteKey.key);

  var encryptedMessage = e2e.otr.util.aes128ctr.encrypt(keys.sendingAes,
      message, ctrTop.concat(goog.array.repeat(0, 8)));

  var nextDh = new e2e.cipher.DiffieHellman(constants.DH_MODULUS,
      [constants.DH_GENERATOR]);
  this.session_.keymanager.storeKey(nextDh);

  var partialData = e2e.otr.serializeBytes([
    this.flags_,
    dh.keyid,
    remoteKey.keyid,
    new e2e.otr.Mpi(new Uint8Array(nextDh.generate())),
    ctrTop,
    new e2e.otr.Data(new Uint8Array(encryptedMessage))
  ]);

  var mac = new goog.crypt.Hmac(new e2e.hash.Sha1(), keys.sendingMac).getHmac(
      this.addHeader(partialData));

  // TODO(rcc): old MAC keys to be revealed.
  var oldMacKeys = [];

  return e2e.otr.serializeBytes([partialData, mac, oldMacKeys]);
};

/**
 * Processes a DATA message message.
 * @param {!e2e.otr.Session} session The enclosing session.
 * @param {!Uint8Array} data The data to be processed.
 */
e2e.otr.message.Data.process = function(session, data) {
  switch (session.getMsgState()) {
    case MSGSTATE.ENCRYPTED:
      var tee = new e2e.otr.util.Tee();
      var iter = new e2e.otr.util.Iterator(data);

      var flags = e2e.otr.byteToNum(tee.tee(iter.next()));
      var ignoreUnreadable = flags & 0x01;
      var remoteKeyid = tee.tee(iter.next(4));
      var keyid = tee.tee(iter.next(4));
      var nextDh = e2e.otr.Mpi.parse(tee.tee(iter.nextEncoded())).deconstruct();
      var ctrTop = Array.apply([], tee.tee(iter.next(8)));
      var encryptedMessage = e2e.otr.Data.parse(tee.tee(iter.nextEncoded()))
          .deconstruct();
      var mac = iter.next(20);
      // TODO(rcc): handle revealing old MAC keys.

      var key = session.keymanager.getKey(keyid);
      var remoteKey = session.keymanager.getRemoteKey(remoteKeyid);
      var keys = e2e.otr.message.Data.computeKeys(key.key, remoteKey.key);

      var macData = e2e.otr.message.Encoded.addHeader(session,
          constants.MessageType.DATA, tee.dump(), true);
      var calculatedMac = new goog.crypt.Hmac(new e2e.hash.Sha1(),
          keys.receivingMac).getHmac(macData);

      if (e2e.otr.compareByteArray(mac, calculatedMac)) {
        if (!ignoreUnreadable) {
          // TODO(rcc): Log the error and/or warn the user. Remove extra return.
          return;
        }
        return;
      }

      session.keymanager.storeRemoteKey(e2e.incrementByteArray(Array.apply([],
          remoteKeyid)), Array.apply([], nextDh));

      var message = e2e.otr.util.aes128ctr.decrypt(keys.receivingAes,
          encryptedMessage, ctrTop.concat(goog.array.repeat(0, 8)));

      var split = Array.prototype.indexOf.call(message, '\0');
      session.display(e2e.byteArrayToString(message.subarray(0, split)));
      // TODO(rcc): Process TLVs.
      break;

    case MSGSTATE.PLAINTEXT:
    case MSGSTATE.FINISHED:
      // TODO(rcc): Inform the user that an unreadable encrypted message was
      // received, and reply with an Error Message.
      throw new e2e.otr.error.NotImplementedError('Not yet implemented.');
      break;

    default:
      e2e.otr.assertState(false, 'Invalid message state.');
  }
};

e2e.otr.message.handler.add(e2e.otr.message.Data);
});  // goog.scope
<|endoftext|>