<commit_before>/**
 * Copyright 2012 Google, Inc. All Rights Reserved.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/**
 * @fileoverview 'Tracks' panel.
 *
 * @author benvanik@google.com (Ben Vanik)
 */

goog.provide('wtf.app.ui.tracks.TracksPanel');

goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.math.Rect');
goog.require('goog.soy');
goog.require('goog.style');
goog.require('wtf.analysis.db.EventDatabase');
goog.require('wtf.analysis.db.Granularity');
goog.require('wtf.app.ui.FramePainter');
goog.require('wtf.app.ui.MarkPainter');
goog.require('wtf.app.ui.SelectionPainter');
goog.require('wtf.app.ui.TabPanel');
goog.require('wtf.app.ui.tracks.TimeRangePainter');
goog.require('wtf.app.ui.tracks.TrackInfoBar');
goog.require('wtf.app.ui.tracks.ZonePainter');
goog.require('wtf.app.ui.tracks.trackspanel');
goog.require('wtf.events');
goog.require('wtf.events.EventType');
goog.require('wtf.events.KeyboardScope');
goog.require('wtf.timing');
goog.require('wtf.ui.GridPainter');
goog.require('wtf.ui.LayoutMode');
goog.require('wtf.ui.Painter');
goog.require('wtf.ui.ResizableControl');
goog.require('wtf.ui.RulerPainter');
goog.require('wtf.ui.Tooltip');
goog.require('wtf.ui.zoom.TransitionMode');
goog.require('wtf.ui.zoom.Viewport');

/**
 * Tracks panel, showing a list of tracks on a time graph.
 * @param {!wtf.app.ui.DocumentView} documentView Parent document view.
 * @constructor
 * @extends {wtf.app.ui.TabPanel}
 */
wtf.app.ui.tracks.TracksPanel = function(documentView) {
  goog.base(this, documentView, 'tracks', 'Tracks');
  var dom = this.getDom();

  var doc = documentView.getDocument();
  var db = doc.getDatabase();

  /**
   * Database.
   * @type {!wtf.analysis.db.EventDatabase}
   * @private
   */
  this.db_ = db;

  /**
   * Infobar control.
   * @type {!wtf.app.ui.tracks.TrackInfoBar}
   * @private
   */
  this.infobar_ = new wtf.app.ui.tracks.TrackInfoBar(this,
      this.getChildElement(goog.getCssName('infoControl')));
  this.registerDisposable(this.infobar_);
  this.infobar_.addListener(
      wtf.ui.ResizableControl.EventType.SIZE_CHANGED,
      this.layout, this);

  /**
   * Zooming viewport.
   * @type {!wtf.ui.zoom.Viewport}
   * @private
   */
  this.viewport_ = new wtf.ui.zoom.Viewport();
  this.registerDisposable(this.viewport_);
  this.viewport_.setAllowedScales(
      1000 / wtf.app.ui.tracks.TracksPanel.MIN_GRANULARITY_,
      1000 / wtf.app.ui.tracks.TracksPanel.MAX_GRANULARITY_);
  var reentry = 0;
  this.viewport_.addListener(
      wtf.events.EventType.INVALIDATED,
      function() {
        if (reentry) {
          return;
        }
        reentry++;

        var firstEventTime = db.getFirstEventTime();

        // Update from viewport.
        var width = this.viewport_.getScreenWidth();
        var timeLeft = this.viewport_.screenToScene(0, 0).x;
        var timeRight = this.viewport_.screenToScene(width, 0).x;
        timeLeft += firstEventTime;
        timeRight += firstEventTime;

        // Update the main view.
        // This will be ignored if our invalidation came from the view.
        var localView = documentView.getLocalView();
        localView.setVisibleRange(timeLeft, timeRight);

        for (var n = 0; n < this.timePainters_.length; n++) {
          var painter = this.timePainters_[n];
          painter.setTimeRange(timeLeft, timeRight);
        }

        this.requestRepaint();
        reentry--;
      }, this);
  // TODO(benvanik): set to something larger to get more precision.
  this.viewport_.setSceneSize(1, 1);

  // Watch for view changes and update.
  var localView = documentView.getLocalView();
  localView.addListener(wtf.events.EventType.INVALIDATED, function(immediate) {
    if (reentry) {
      return;
    }

    var firstEventTime = db.getFirstEventTime();
    var startTime = localView.getVisibleTimeStart() - firstEventTime;
    var endTime = localView.getVisibleTimeEnd() - firstEventTime - startTime;
    this.viewport_.zoomToBounds(
        startTime, 0, endTime, 0.001,
        immediate ? wtf.ui.zoom.TransitionMode.IMMEDIATE : undefined);
  }, this);

  // Setup keyboard hooks. These are only valid when the panel is active.
  var keyboard = wtf.events.getWindowKeyboard(dom);
  /**
   * Keyboard scope.
   * @type {!wtf.events.KeyboardScope}
   * @private
   */
  this.keyboardScope_ = new wtf.events.KeyboardScope(keyboard);
  this.registerDisposable(this.keyboardScope_);
  this.setupKeyboardShortcuts_();

  /**
   * Track canvas.
   * @type {!HTMLCanvasElement}
   * @private
   */
  this.trackCanvas_ = /** @type {!HTMLCanvasElement} */ (
      this.getChildElement(goog.getCssName('canvas')));

  /**
   * Tooltip.
   * @type {!wtf.ui.Tooltip}
   * @private
   */
  this.tooltip_ = new wtf.ui.Tooltip(this.getDom());
  this.registerDisposable(this.tooltip_);
  this.setTooltip(this.tooltip_);

  var paintContext = new wtf.ui.Painter(this.trackCanvas_);
  this.setPaintContext(paintContext);

  /**
   * A list of all paint contexts that extend {@see wtf.ui.TimePainter}.
   * This is used to update all of the painters when the current time range
   * changes.
   * @type {!Array.<!wtf.ui.TimePainter>}
   * @private
   */
  this.timePainters_ = [];

  var gridPainter = new wtf.ui.GridPainter(this.trackCanvas_);
  paintContext.addChildPainter(gridPainter);
  gridPainter.setGranularities(
      wtf.app.ui.tracks.TracksPanel.MIN_GRANULARITY_,
      wtf.app.ui.tracks.TracksPanel.MAX_GRANULARITY_);
  this.timePainters_.push(gridPainter);

  /**
   * Selection painter.
   * @type {!wtf.app.ui.SelectionPainter}
   * @private
   */
  this.selectionPainter_ = new wtf.app.ui.SelectionPainter(
      this.trackCanvas_, documentView.getSelection(), this.viewport_);
  paintContext.addChildPainter(this.selectionPainter_);
  this.timePainters_.push(this.selectionPainter_);

  /**
   * Vertical stack of painters that make up the main view.
   * @type {!wtf.ui.Painter}
   * @private
   */
  this.painterStack_ = new wtf.ui.Painter(this.trackCanvas_);
  paintContext.addChildPainter(this.painterStack_);
  this.painterStack_.setLayoutMode(wtf.ui.LayoutMode.VERTICAL);

  /**
   * Ruler painter.
   * @type {!wtf.ui.RulerPainter}
   * @private
   */
  this.rulerPainter_ = new wtf.ui.RulerPainter(this.trackCanvas_);
  this.painterStack_.addChildPainter(this.rulerPainter_);
  this.rulerPainter_.setGranularities(
      wtf.app.ui.tracks.TracksPanel.MIN_GRANULARITY_,
      wtf.app.ui.tracks.TracksPanel.MAX_GRANULARITY_);
  this.timePainters_.push(this.rulerPainter_);

  var markPainter = new wtf.app.ui.MarkPainter(this.trackCanvas_, db);
  this.painterStack_.addChildPainter(markPainter);
  this.timePainters_.push(markPainter);

  // Watch for zones and add as needed.
  db.addListener(wtf.analysis.db.EventDatabase.EventType.ZONES_ADDED,
      function(zoneIndices) {
        goog.array.forEach(zoneIndices, this.addZoneTrack_, this);
      }, this);
  var zoneIndices = db.getZoneIndices();
  goog.array.forEach(zoneIndices, this.addZoneTrack_, this);

  // Done last so any other handlers are properly registered.
  this.viewport_.registerElement(this.trackCanvas_);

  wtf.timing.setImmediate(this.layout, this);
  this.requestRepaint();
};
goog.inherits(wtf.app.ui.tracks.TracksPanel, wtf.app.ui.TabPanel);

/**
 * @override
 */
wtf.app.ui.tracks.TracksPanel.prototype.createDom = function(dom) {
  return /** @type {!Element} */ (goog.soy.renderAsFragment(
      wtf.app.ui.tracks.trackspanel.control, undefined, undefined, dom));
};

/**
 * Sets up some simple keyboard shortcuts.
 * @private
 */
wtf.app.ui.tracks.TracksPanel.prototype.setupKeyboardShortcuts_ = function() {
  var db = this.db_;
  var viewport = this.viewport_;

  var commandManager = wtf.events.getCommandManager();
  var keyboardScope = this.keyboardScope_;

  keyboardScope.addShortcut('space', function() {
    var width = viewport.getScreenWidth();
    viewport.panDelta((width * 0.8) / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('shift+space', function() {
    var width = viewport.getScreenWidth();
    viewport.panDelta(-(width * 0.8) / viewport.getScale(), 0);
  }, this);

  function moveFrames(delta, framesOnly) {
    // Find a frame index.
    var frameIndex = db.getFirstFrameIndex();
    if (!frameIndex) {
      return;
    }

    // Find center time.
    var time = viewport.screenToScene(viewport.getScreenWidth() / 2, 0).x;
    time += db.getFirstEventTime();

    // Find the frame at the center of the viewport.
    var hit = frameIndex.getFrameAtTime(time);
    if (hit) {
      // Frame, move to adjacent intra-frame space or frame.
      if (framesOnly) {
        var newFrame;
        if (delta < 0) {
          newFrame = frameIndex.getPreviousFrame(hit);
        } else {
          newFrame = frameIndex.getNextFrame(hit);
        }
        commandManager.execute('goto_frame', this, null, newFrame);
      } else {
        var startTime;
        var endTime;
        if (delta < 0) {
          var otherFrame = frameIndex.getPreviousFrame(hit);
          startTime = otherFrame ?
              otherFrame.getEndTime() : db.getFirstEventTime();
          endTime = hit.getStartTime();
        } else {
          var otherFrame = frameIndex.getNextFrame(hit);
          startTime = hit.getEndTime();
          endTime = otherFrame ?
              otherFrame.getStartTime() : db.getLastEventTime();
        }
        commandManager.execute('goto_range', this, null, startTime, endTime);
      }
    } else {
      // If in a intra-frame space, move to a frame.
      hit = frameIndex.getIntraFrameAtTime(time);
      if (hit) {
        var newFrame = delta < 0 ? hit[0] : hit[1];
        commandManager.execute('goto_frame', this, null, newFrame);
      }
    }
  };
  keyboardScope.addShortcut('z', function() {
    moveFrames(-1, true);
  }, this);
  keyboardScope.addShortcut('x', function() {
    moveFrames(1, true);
  }, this);
  keyboardScope.addShortcut('shift+z', function() {
    moveFrames(-1, false);
  }, this);
  keyboardScope.addShortcut('shift+x', function() {
    moveFrames(1, false);
  }, this);

  keyboardScope.addShortcut('left|a', function() {
    viewport.panDelta(-160 / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('right|d', function() {
    viewport.panDelta(160 / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('shift+left|shift+a', function() {
    viewport.panDelta(-160 * 3 / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('shift+right|shift+d', function() {
    viewport.panDelta(160 * 3 / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('up|w', function() {
    viewport.zoomDelta(2.5);
  }, this);
  keyboardScope.addShortcut('down|s', function() {
    viewport.zoomDelta(1 / 2.5);
  }, this);

  keyboardScope.addShortcut('home', function() {
    var firstEventTime = db.getFirstEventTime();
    var lastEventTime = db.getLastEventTime();
    commandManager.execute('goto_range', this, null,
        firstEventTime, lastEventTime);
  }, this);
};

/**
 * Minimum granularity, in ms.
 * @const
 * @type {number}
 * @private
 */
wtf.app.ui.tracks.TracksPanel.MIN_GRANULARITY_ =
    100 * wtf.analysis.db.Granularity.SECOND;

/**
 * Maximum granularity, in ms.
 * @const
 * @type {number}
 * @private
 */
wtf.app.ui.tracks.TracksPanel.MAX_GRANULARITY_ =
    0.001;

/**
 * @override
 */
wtf.app.ui.tracks.TracksPanel.prototype.setVisible = function(value) {
  goog.base(this, 'setVisible', value);
  this.keyboardScope_.setEnabled(value);
};

/**
 * @override
 */
wtf.app.ui.tracks.TracksPanel.prototype.navigate = function(pathParts) {
  // TODO(benvanik): support navigation
};

/**
 * @override
 */
wtf.app.ui.tracks.TracksPanel.prototype.layoutInternal = function() {
  var canvas = this.trackCanvas_;
  var canvasOuter = goog.dom.getParentElement(canvas);

  var infobarWidth = this.infobar_.getSplitterSize();
  goog.style.setStyle(canvasOuter, 'margin-right', (infobarWidth + 1) + 'px');

  var currentSize = goog.style.getSize(canvasOuter);
  this.viewport_.setScreenSize(currentSize.width, currentSize.height);
};

/**
 * Adds a new zone track for the given zone index.
 * @param {!wtf.analysis.db.ZoneIndex} zoneIndex Zone index to add the track
 *     for.
 * @private
 */
wtf.app.ui.tracks.TracksPanel.prototype.addZoneTrack_ = function(zoneIndex) {
  var zonePainterStack = new wtf.ui.Painter(this.trackCanvas_);
  this.painterStack_.addChildPainter(zonePainterStack);
  zonePainterStack.setLayoutMode(wtf.ui.LayoutMode.VERTICAL);
  zonePainterStack.setPadding(new goog.math.Rect(0, 5, 0, 5));

  var framePainter = new wtf.app.ui.FramePainter(
      this.trackCanvas_, this.db_, zoneIndex.getFrameIndex());
  zonePainterStack.addChildPainter(framePainter);
  this.timePainters_.push(framePainter);
  framePainter.setPadding(new goog.math.Rect(0, 0, 0, 5));

  var timeRangePainter = new wtf.app.ui.tracks.TimeRangePainter(
      this.trackCanvas_, this.db_, zoneIndex.getTimeRangeIndex());
  zonePainterStack.addChildPainter(timeRangePainter);
  this.timePainters_.push(timeRangePainter);
  timeRangePainter.setPadding(new goog.math.Rect(0, 0, 0, 5));

  var docView = this.getDocumentView();
  var zonePainter = new wtf.app.ui.tracks.ZonePainter(
      this.trackCanvas_, this.db_, zoneIndex, docView.getSelection());
  zonePainterStack.addChildPainter(zonePainter);
  this.timePainters_.push(zonePainter);
};
<commit_msg>Fixing click-to-clear-filter.<commit_after>/**
 * Copyright 2012 Google, Inc. All Rights Reserved.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/**
 * @fileoverview 'Tracks' panel.
 *
 * @author benvanik@google.com (Ben Vanik)
 */

goog.provide('wtf.app.ui.tracks.TracksPanel');

goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.math.Rect');
goog.require('goog.soy');
goog.require('goog.style');
goog.require('wtf.analysis.db.EventDatabase');
goog.require('wtf.analysis.db.Granularity');
goog.require('wtf.app.ui.FramePainter');
goog.require('wtf.app.ui.MarkPainter');
goog.require('wtf.app.ui.SelectionPainter');
goog.require('wtf.app.ui.TabPanel');
goog.require('wtf.app.ui.tracks.TimeRangePainter');
goog.require('wtf.app.ui.tracks.TrackInfoBar');
goog.require('wtf.app.ui.tracks.ZonePainter');
goog.require('wtf.app.ui.tracks.trackspanel');
goog.require('wtf.events');
goog.require('wtf.events.EventType');
goog.require('wtf.events.KeyboardScope');
goog.require('wtf.timing');
goog.require('wtf.ui.GridPainter');
goog.require('wtf.ui.LayoutMode');
goog.require('wtf.ui.Painter');
goog.require('wtf.ui.ResizableControl');
goog.require('wtf.ui.RulerPainter');
goog.require('wtf.ui.Tooltip');
goog.require('wtf.ui.zoom.TransitionMode');
goog.require('wtf.ui.zoom.Viewport');

/**
 * Tracks panel, showing a list of tracks on a time graph.
 * @param {!wtf.app.ui.DocumentView} documentView Parent document view.
 * @constructor
 * @extends {wtf.app.ui.TabPanel}
 */
wtf.app.ui.tracks.TracksPanel = function(documentView) {
  goog.base(this, documentView, 'tracks', 'Tracks');
  var dom = this.getDom();

  var doc = documentView.getDocument();
  var db = doc.getDatabase();

  /**
   * Database.
   * @type {!wtf.analysis.db.EventDatabase}
   * @private
   */
  this.db_ = db;

  /**
   * Infobar control.
   * @type {!wtf.app.ui.tracks.TrackInfoBar}
   * @private
   */
  this.infobar_ = new wtf.app.ui.tracks.TrackInfoBar(this,
      this.getChildElement(goog.getCssName('infoControl')));
  this.registerDisposable(this.infobar_);
  this.infobar_.addListener(
      wtf.ui.ResizableControl.EventType.SIZE_CHANGED,
      this.layout, this);

  /**
   * Zooming viewport.
   * @type {!wtf.ui.zoom.Viewport}
   * @private
   */
  this.viewport_ = new wtf.ui.zoom.Viewport();
  this.registerDisposable(this.viewport_);
  this.viewport_.setAllowedScales(
      1000 / wtf.app.ui.tracks.TracksPanel.MIN_GRANULARITY_,
      1000 / wtf.app.ui.tracks.TracksPanel.MAX_GRANULARITY_);
  var reentry = 0;
  this.viewport_.addListener(
      wtf.events.EventType.INVALIDATED,
      function() {
        if (reentry) {
          return;
        }
        reentry++;

        var firstEventTime = db.getFirstEventTime();

        // Update from viewport.
        var width = this.viewport_.getScreenWidth();
        var timeLeft = this.viewport_.screenToScene(0, 0).x;
        var timeRight = this.viewport_.screenToScene(width, 0).x;
        timeLeft += firstEventTime;
        timeRight += firstEventTime;

        // Update the main view.
        // This will be ignored if our invalidation came from the view.
        var localView = documentView.getLocalView();
        localView.setVisibleRange(timeLeft, timeRight);

        for (var n = 0; n < this.timePainters_.length; n++) {
          var painter = this.timePainters_[n];
          painter.setTimeRange(timeLeft, timeRight);
        }

        this.requestRepaint();
        reentry--;
      }, this);
  // TODO(benvanik): set to something larger to get more precision.
  this.viewport_.setSceneSize(1, 1);

  // Watch for view changes and update.
  var localView = documentView.getLocalView();
  localView.addListener(wtf.events.EventType.INVALIDATED, function(immediate) {
    if (reentry) {
      return;
    }

    var firstEventTime = db.getFirstEventTime();
    var startTime = localView.getVisibleTimeStart() - firstEventTime;
    var endTime = localView.getVisibleTimeEnd() - firstEventTime - startTime;
    this.viewport_.zoomToBounds(
        startTime, 0, endTime, 0.001,
        immediate ? wtf.ui.zoom.TransitionMode.IMMEDIATE : undefined);
  }, this);

  // Setup keyboard hooks. These are only valid when the panel is active.
  var keyboard = wtf.events.getWindowKeyboard(dom);
  /**
   * Keyboard scope.
   * @type {!wtf.events.KeyboardScope}
   * @private
   */
  this.keyboardScope_ = new wtf.events.KeyboardScope(keyboard);
  this.registerDisposable(this.keyboardScope_);
  this.setupKeyboardShortcuts_();

  /**
   * Track canvas.
   * @type {!HTMLCanvasElement}
   * @private
   */
  this.trackCanvas_ = /** @type {!HTMLCanvasElement} */ (
      this.getChildElement(goog.getCssName('canvas')));

  /**
   * Tooltip.
   * @type {!wtf.ui.Tooltip}
   * @private
   */
  this.tooltip_ = new wtf.ui.Tooltip(this.getDom());
  this.registerDisposable(this.tooltip_);
  this.setTooltip(this.tooltip_);

  var paintContext = new wtf.ui.Painter(this.trackCanvas_);
  this.setPaintContext(paintContext);

  // Clicking on non-handled space will clear the filter.
  var commandManager = wtf.events.getCommandManager();
  paintContext.onClickInternal = goog.bind(function(x, y, modifiers, bounds) {
    commandManager.execute('filter_events', this, null, '');
  }, this);

  /**
   * A list of all paint contexts that extend {@see wtf.ui.TimePainter}.
   * This is used to update all of the painters when the current time range
   * changes.
   * @type {!Array.<!wtf.ui.TimePainter>}
   * @private
   */
  this.timePainters_ = [];

  var gridPainter = new wtf.ui.GridPainter(this.trackCanvas_);
  paintContext.addChildPainter(gridPainter);
  gridPainter.setGranularities(
      wtf.app.ui.tracks.TracksPanel.MIN_GRANULARITY_,
      wtf.app.ui.tracks.TracksPanel.MAX_GRANULARITY_);
  this.timePainters_.push(gridPainter);

  /**
   * Selection painter.
   * @type {!wtf.app.ui.SelectionPainter}
   * @private
   */
  this.selectionPainter_ = new wtf.app.ui.SelectionPainter(
      this.trackCanvas_, documentView.getSelection(), this.viewport_);
  paintContext.addChildPainter(this.selectionPainter_);
  this.timePainters_.push(this.selectionPainter_);

  /**
   * Vertical stack of painters that make up the main view.
   * @type {!wtf.ui.Painter}
   * @private
   */
  this.painterStack_ = new wtf.ui.Painter(this.trackCanvas_);
  paintContext.addChildPainter(this.painterStack_);
  this.painterStack_.setLayoutMode(wtf.ui.LayoutMode.VERTICAL);

  /**
   * Ruler painter.
   * @type {!wtf.ui.RulerPainter}
   * @private
   */
  this.rulerPainter_ = new wtf.ui.RulerPainter(this.trackCanvas_);
  this.painterStack_.addChildPainter(this.rulerPainter_);
  this.rulerPainter_.setGranularities(
      wtf.app.ui.tracks.TracksPanel.MIN_GRANULARITY_,
      wtf.app.ui.tracks.TracksPanel.MAX_GRANULARITY_);
  this.timePainters_.push(this.rulerPainter_);

  var markPainter = new wtf.app.ui.MarkPainter(this.trackCanvas_, db);
  this.painterStack_.addChildPainter(markPainter);
  this.timePainters_.push(markPainter);

  // Watch for zones and add as needed.
  db.addListener(wtf.analysis.db.EventDatabase.EventType.ZONES_ADDED,
      function(zoneIndices) {
        goog.array.forEach(zoneIndices, this.addZoneTrack_, this);
      }, this);
  var zoneIndices = db.getZoneIndices();
  goog.array.forEach(zoneIndices, this.addZoneTrack_, this);

  // Done last so any other handlers are properly registered.
  this.viewport_.registerElement(this.trackCanvas_);

  wtf.timing.setImmediate(this.layout, this);
  this.requestRepaint();
};
goog.inherits(wtf.app.ui.tracks.TracksPanel, wtf.app.ui.TabPanel);

/**
 * @override
 */
wtf.app.ui.tracks.TracksPanel.prototype.createDom = function(dom) {
  return /** @type {!Element} */ (goog.soy.renderAsFragment(
      wtf.app.ui.tracks.trackspanel.control, undefined, undefined, dom));
};

/**
 * Sets up some simple keyboard shortcuts.
 * @private
 */
wtf.app.ui.tracks.TracksPanel.prototype.setupKeyboardShortcuts_ = function() {
  var db = this.db_;
  var viewport = this.viewport_;

  var commandManager = wtf.events.getCommandManager();
  var keyboardScope = this.keyboardScope_;

  keyboardScope.addShortcut('space', function() {
    var width = viewport.getScreenWidth();
    viewport.panDelta((width * 0.8) / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('shift+space', function() {
    var width = viewport.getScreenWidth();
    viewport.panDelta(-(width * 0.8) / viewport.getScale(), 0);
  }, this);

  function moveFrames(delta, framesOnly) {
    // Find a frame index.
    var frameIndex = db.getFirstFrameIndex();
    if (!frameIndex) {
      return;
    }

    // Find center time.
    var time = viewport.screenToScene(viewport.getScreenWidth() / 2, 0).x;
    time += db.getFirstEventTime();

    // Find the frame at the center of the viewport.
    var hit = frameIndex.getFrameAtTime(time);
    if (hit) {
      // Frame, move to adjacent intra-frame space or frame.
      if (framesOnly) {
        var newFrame;
        if (delta < 0) {
          newFrame = frameIndex.getPreviousFrame(hit);
        } else {
          newFrame = frameIndex.getNextFrame(hit);
        }
        commandManager.execute('goto_frame', this, null, newFrame);
      } else {
        var startTime;
        var endTime;
        if (delta < 0) {
          var otherFrame = frameIndex.getPreviousFrame(hit);
          startTime = otherFrame ?
              otherFrame.getEndTime() : db.getFirstEventTime();
          endTime = hit.getStartTime();
        } else {
          var otherFrame = frameIndex.getNextFrame(hit);
          startTime = hit.getEndTime();
          endTime = otherFrame ?
              otherFrame.getStartTime() : db.getLastEventTime();
        }
        commandManager.execute('goto_range', this, null, startTime, endTime);
      }
    } else {
      // If in a intra-frame space, move to a frame.
      hit = frameIndex.getIntraFrameAtTime(time);
      if (hit) {
        var newFrame = delta < 0 ? hit[0] : hit[1];
        commandManager.execute('goto_frame', this, null, newFrame);
      }
    }
  };
  keyboardScope.addShortcut('z', function() {
    moveFrames(-1, true);
  }, this);
  keyboardScope.addShortcut('x', function() {
    moveFrames(1, true);
  }, this);
  keyboardScope.addShortcut('shift+z', function() {
    moveFrames(-1, false);
  }, this);
  keyboardScope.addShortcut('shift+x', function() {
    moveFrames(1, false);
  }, this);

  keyboardScope.addShortcut('left|a', function() {
    viewport.panDelta(-160 / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('right|d', function() {
    viewport.panDelta(160 / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('shift+left|shift+a', function() {
    viewport.panDelta(-160 * 3 / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('shift+right|shift+d', function() {
    viewport.panDelta(160 * 3 / viewport.getScale(), 0);
  }, this);
  keyboardScope.addShortcut('up|w', function() {
    viewport.zoomDelta(2.5);
  }, this);
  keyboardScope.addShortcut('down|s', function() {
    viewport.zoomDelta(1 / 2.5);
  }, this);

  keyboardScope.addShortcut('home', function() {
    var firstEventTime = db.getFirstEventTime();
    var lastEventTime = db.getLastEventTime();
    commandManager.execute('goto_range', this, null,
        firstEventTime, lastEventTime);
  }, this);
};

/**
 * Minimum granularity, in ms.
 * @const
 * @type {number}
 * @private
 */
wtf.app.ui.tracks.TracksPanel.MIN_GRANULARITY_ =
    100 * wtf.analysis.db.Granularity.SECOND;

/**
 * Maximum granularity, in ms.
 * @const
 * @type {number}
 * @private
 */
wtf.app.ui.tracks.TracksPanel.MAX_GRANULARITY_ =
    0.001;

/**
 * @override
 */
wtf.app.ui.tracks.TracksPanel.prototype.setVisible = function(value) {
  goog.base(this, 'setVisible', value);
  this.keyboardScope_.setEnabled(value);
};

/**
 * @override
 */
wtf.app.ui.tracks.TracksPanel.prototype.navigate = function(pathParts) {
  // TODO(benvanik): support navigation
};

/**
 * @override
 */
wtf.app.ui.tracks.TracksPanel.prototype.layoutInternal = function() {
  var canvas = this.trackCanvas_;
  var canvasOuter = goog.dom.getParentElement(canvas);

  var infobarWidth = this.infobar_.getSplitterSize();
  goog.style.setStyle(canvasOuter, 'margin-right', (infobarWidth + 1) + 'px');

  var currentSize = goog.style.getSize(canvasOuter);
  this.viewport_.setScreenSize(currentSize.width, currentSize.height);
};

/**
 * Adds a new zone track for the given zone index.
 * @param {!wtf.analysis.db.ZoneIndex} zoneIndex Zone index to add the track
 *     for.
 * @private
 */
wtf.app.ui.tracks.TracksPanel.prototype.addZoneTrack_ = function(zoneIndex) {
  var zonePainterStack = new wtf.ui.Painter(this.trackCanvas_);
  this.painterStack_.addChildPainter(zonePainterStack);
  zonePainterStack.setLayoutMode(wtf.ui.LayoutMode.VERTICAL);
  zonePainterStack.setPadding(new goog.math.Rect(0, 5, 0, 5));

  var framePainter = new wtf.app.ui.FramePainter(
      this.trackCanvas_, this.db_, zoneIndex.getFrameIndex());
  zonePainterStack.addChildPainter(framePainter);
  this.timePainters_.push(framePainter);
  framePainter.setPadding(new goog.math.Rect(0, 0, 0, 5));

  var timeRangePainter = new wtf.app.ui.tracks.TimeRangePainter(
      this.trackCanvas_, this.db_, zoneIndex.getTimeRangeIndex());
  zonePainterStack.addChildPainter(timeRangePainter);
  this.timePainters_.push(timeRangePainter);
  timeRangePainter.setPadding(new goog.math.Rect(0, 0, 0, 5));

  var docView = this.getDocumentView();
  var zonePainter = new wtf.app.ui.tracks.ZonePainter(
      this.trackCanvas_, this.db_, zoneIndex, docView.getSelection());
  zonePainterStack.addChildPainter(zonePainter);
  this.timePainters_.push(zonePainter);
};
<|endoftext|>


<commit_before>const nock = require('nock')
const test = require('tape')

let request = require('supertest')
let assert = require('assert')
let req = require('request')
let bitcoin = require('bitcoinjs-lib')
let fs = require('fs')
const Readable = require('stream').Readable

let StorageAuth = require('../lib/server/StorageAuthentication.js').StorageAuthentication
let ProofChecker = require('../lib/server/ProofChecker.js').ProofChecker
let HubServer = require('../lib/server/server.js').HubServer
let config = require('../lib/server/config.js')

const makeHttpServer = require('../lib/server/http.js').makeHttpServer
const AzDriver = require('../lib/server/drivers/AzDriver.js')
const S3Driver = require('../lib/server/drivers/S3Driver.js')
const DiskDriver = require('../lib/server/drivers/diskDriver.js')
const errors = require('../lib/server/errors')

let azConfigPath = process.env.AZ_CONFIG_PATH
let awsConfigPath = process.env.AWS_CONFIG_PATH
let diskConfigPath = process.env.DISK_CONFIG_PATH

const testWIFs = [
  'L4kMoaVivcd1FMPPwRU9XT2PdKHPob3oo6YmgTBHrnBHMmo7GcCf',
  'L3W7EzxYNdG3kBjtQmhKEq2iiZAwpiKEwMobXdaY9xueSUFPkQeH',
  'KwzzsbVzMekdj9myzxojsgT6DQ6yRQKbWqSXQgo1YKsJcvFJhtRr',
  'KxYYiJg9mJpCDHcyYMMvSfY4SWGwMofqavxG2ZyDNcXuY7ShBknK']
const testPairs = testWIFs.map(x => bitcoin.ECPair.fromWIF(x))
const testAddrs = testPairs.map(x => x.getAddress())

function setupAwsNocks() {
}

class MockDriver {
  constructor() {
    this.lastWrite = null
  }
  performWrite(write) {
    this.lastWrite = write
    return Promise.resolve(`http://test.com/${write.storageTopLevel}/${write.path}`)
  }
}

class MockProofs {
  checkProofs() {
    return Promise.resolve()
  }
}

function testAuth() {
  test('storage validation', (t) => {
    t.plan(5)
    const authorization = StorageAuth.makeWithKey(testPairs[0]).toAuthHeader()
    const authenticator = StorageAuth.fromAuthHeader(authorization)
    t.throws(() => authenticator.isAuthenticationValid(testAddrs[1], true),
             errors.ValidationError, 'Wrong address must throw')
    t.ok(!authenticator.isAuthenticationValid(testAddrs[1], false), 'Wrong address must fail')
    t.ok(authenticator.isAuthenticationValid(testAddrs[0], true), 'Good signature must pass')

    const pkBad = bitcoin.ECPair.fromPublicKeyBuffer(testPairs[1].getPublicKeyBuffer())
    const authBad = new StorageAuth(pkBad, authenticator.signature)

    t.throws(() => authenticator.isAuthenticationValid(testAddrs[1], true, 'Bad signature must throw'))
    t.ok(!authenticator.isAuthenticationValid(testAddrs[1], false), 'Bad signature must fail')
  })
}

function testAzDriver() {
  if (!azConfigPath) {
    return
  }
  const config = JSON.parse(fs.readFileSync(azConfigPath))

  test('azDriver', (t) => {
    t.plan(3)
    const driver = new AzDriver(config)
    const prefix = driver.getReadURLPrefix()
    const s = new Readable()
    s._read = function noop() {}
    s.push('hello world')
    s.push(null)

    driver.performWrite(
      { path: '../foo.js'})
      .then(() => t.ok(false, 'Should have thrown'))
      .catch((err) => t.equal(err.message, 'Invalid Path', 'Should throw bad path'))
      .then(() => driver.performWrite(
        { path: 'foo.txt',
          storageTopLevel: '12345',
          stream: s,
          contentType: 'application/octet-stream',
          contentLength: 12 }))
      .then((readUrl) => {
        t.ok(readUrl.startsWith(prefix), `${readUrl} must start with readUrlPrefix ${prefix}`)
        return fetch(readUrl)
      })
      .then((resp) => resp.text())
      .then((resptxt) => t.equal(resptxt, 'hello world', `Must get back hello world: got back: ${resptxt}`))
  })
}

function testS3Driver() {
  if (!awsConfigPath) {
    return
  }
  const config = JSON.parse(fs.readFileSync(awsConfigPath))

  test('awsDriver', (t) => {
    t.plan(3)
    const driver = new S3Driver(config)
    const prefix = driver.getReadURLPrefix()
    const s = new Readable()
    s._read = function noop() {}
    s.push('hello world')
    s.push(null)

    driver.performWrite(
      { path: '../foo.js'})
      .then(() => t.ok(false, 'Should have thrown'))
      .catch((err) => t.equal(err.message, 'Invalid Path', 'Should throw bad path'))
      .then(() => driver.performWrite(
        { path: 'foo.txt',
          storageTopLevel: '12345',
          stream: s,
          contentType: 'application/octet-stream',
          contentLength: 12 }))
      .then((readUrl) => {
        t.ok(readUrl.startsWith(prefix + '12345'), `${readUrl} must start with readUrlPrefix ${prefix}12345`)
        return fetch(readUrl)
      })
      .then((resp) => resp.text())
      .then((resptxt) => t.equal(resptxt, 'hello world', `Must get back hello world: got back: ${resptxt}`))
  })
}

function testDiskDriver() {
  const config = JSON.parse(fs.readFileSync(diskConfigPath))

  test('diskDriver', (t) => {
    t.plan(3)
    const driver = new DiskDriver(config)
    const prefix = driver.getReadURLPrefix()
    const s = new Readable()
    s._read = function noop() {}
    s.push('hello world')
    s.push(null)

    driver.performWrite(
      { path: '../foo.js'})
      .then(() => t.ok(false, 'Should have thrown'))
      .catch((err) => t.equal(err.message, 'Invalid Path', 'Should throw bad path'))
      .then(() => driver.performWrite(
        { path: 'foo.txt',
          storageTopLevel: '12345',
          stream: s,
          contentType: 'application/octet-stream',
          contentLength: 12 }))
      .then((readUrl) => {
        t.ok(readUrl.startsWith(prefix + '12345'), `${readUrl} must start with readUrlPrefix ${prefix}12345`)
        return fetch(readUrl)
      })
      .then((resp) => resp.text())
      .then((resptxt) => t.equal(resptxt, 'hello world', `Must get back hello world: got back: ${resptxt}`))
  })
}

function testServer() {
  test('validation tests', (t) => {
    t.plan(3)
    const server = new HubServer(new MockDriver(), new MockProofs(),
                                 { whitelist: [testAddrs[0]] })
    t.throws(() => server.validate(testAddrs[1], {}),
             errors.ValidationError, 'Non-whitelisted address should fail validation')
    t.throws(() => server.validate(testAddrs[0], {}),
             errors.ValidationError, 'Bad request headers should fail validation')

    const authorization = StorageAuth.makeWithKey(testPairs[0]).toAuthHeader()
    try {
      server.validate(testAddrs[0], { authorization })
      t.pass('White-listed address with good auth header should pass')
    } catch (err) {
      t.fail('White-listed address with good auth header should pass')
    }
  })

  test('handle request', (t) => {
    t.plan(8)
    const mockDriver = new MockDriver()
    const server = new HubServer(mockDriver, new MockProofs(),
                                 { whitelist: [testAddrs[0]] })
    const authorization = StorageAuth.makeWithKey(testPairs[0]).toAuthHeader()

    const s = new Readable()
    s._read = function noop() {}
    s.push('hello world')
    s.push(null)
    const s2 = new Readable()
    s2._read = function noop() {}
    s2.push('hello world')
    s2.push(null)

    server.handleRequest(testAddrs[0], 'foo.txt',
                         { 'content-type' : 'text/text',
                           'content-length': 4,
                           authorization }, s)
      .then(path => {
        t.equal(path, `http://test.com/${testAddrs[0]}/foo.txt`)
        t.equal(mockDriver.lastWrite.path, 'foo.txt')
        t.equal(mockDriver.lastWrite.storageTopLevel, testAddrs[0])
        t.equal(mockDriver.lastWrite.contentType, 'text/text')
      })
      .then(() => server.handleRequest(testAddrs[0], 'foo.txt',
                         { 'content-length': 4,
                           authorization }, s2))
      .then(path => {
        t.equal(path, `http://test.com/${testAddrs[0]}/foo.txt`)
        t.equal(mockDriver.lastWrite.path, 'foo.txt')
        t.equal(mockDriver.lastWrite.storageTopLevel, testAddrs[0])
        t.equal(mockDriver.lastWrite.contentType, 'application/octet-stream')
      })
  })
}

function testHttpPost() {
  if (!azConfigPath) {
    console.log('eliding test, you must set AZ_CONFIG_PATH to run this test.')
    return
  }

  test('handle request', (t) => {
    t.plan(2)
    const config = JSON.parse(fs.readFileSync(azConfigPath))
    Object.assign(config, { driver: 'azure' })

    let app = makeHttpServer(config)
    let sk = bitcoin.ECPair.makeRandom()
    let fileContents = sk.toWIF()
    let blob = Buffer(fileContents)
    let authHeader = StorageAuth
        .makeWithKey(sk)
        .toAuthHeader()
    let address = sk.getAddress()
    let path = `/store/${address}/helloWorld`
    request(app).post(path)
      .set('Content-Type', 'application/octet-stream')
      .set('Authorization', authHeader)
      .send(blob)
      .expect(202)
      .then((response) => {
        let url = JSON.parse(response.text).publicURL
        t.ok(url, 'Must return URL')
        console.log(url)
        fetch(url)
          .then(resp => resp.text())
          .then(text => t.equal(text, fileContents))
      })
  })
}

function testBadSig(done, configObj) {
  configObj = Object.assign({}, {proofsConfig : {proofsRequired : 0}}, configObj)
  const conf = config(configObj)

  let app = new HubServer({}, {}, conf)
  let sk = bitcoin.ECPair.makeRandom()
  let fileContents = sk.toWIF()
  let blob = Buffer(fileContents)
  let authHeader = StorageAuth
      .makeWithKey(sk)
      .toAuthHeader()
  let address = bitcoin.ECPair.makeRandom().getAddress()
  let path = `/store/${address}/helloWorld`
  request(app).post(path)
    .set('Content-Type', 'application/octet-stream')
    .set('Authorization', authHeader)
    .send(blob)
    .expect(401)
    .then((response) => {
      done()
    })
}

testServer()
testAuth()
testAzDriver()
testS3Driver()
testDiskDriver()
testHttpPost()
<commit_msg>don't run the disk test if there is no disk config environment variable set; also add a comment on how to test disk<commit_after>const nock = require('nock')
const test = require('tape')

let request = require('supertest')
let assert = require('assert')
let req = require('request')
let bitcoin = require('bitcoinjs-lib')
let fs = require('fs')
const Readable = require('stream').Readable

let StorageAuth = require('../lib/server/StorageAuthentication.js').StorageAuthentication
let ProofChecker = require('../lib/server/ProofChecker.js').ProofChecker
let HubServer = require('../lib/server/server.js').HubServer
let config = require('../lib/server/config.js')

const makeHttpServer = require('../lib/server/http.js').makeHttpServer
const AzDriver = require('../lib/server/drivers/AzDriver.js')
const S3Driver = require('../lib/server/drivers/S3Driver.js')
const DiskDriver = require('../lib/server/drivers/diskDriver.js')
const errors = require('../lib/server/errors')

let azConfigPath = process.env.AZ_CONFIG_PATH
let awsConfigPath = process.env.AWS_CONFIG_PATH
let diskConfigPath = process.env.DISK_CONFIG_PATH

const testWIFs = [
  'L4kMoaVivcd1FMPPwRU9XT2PdKHPob3oo6YmgTBHrnBHMmo7GcCf',
  'L3W7EzxYNdG3kBjtQmhKEq2iiZAwpiKEwMobXdaY9xueSUFPkQeH',
  'KwzzsbVzMekdj9myzxojsgT6DQ6yRQKbWqSXQgo1YKsJcvFJhtRr',
  'KxYYiJg9mJpCDHcyYMMvSfY4SWGwMofqavxG2ZyDNcXuY7ShBknK']
const testPairs = testWIFs.map(x => bitcoin.ECPair.fromWIF(x))
const testAddrs = testPairs.map(x => x.getAddress())

function setupAwsNocks() {
}

class MockDriver {
  constructor() {
    this.lastWrite = null
  }
  performWrite(write) {
    this.lastWrite = write
    return Promise.resolve(`http://test.com/${write.storageTopLevel}/${write.path}`)
  }
}

class MockProofs {
  checkProofs() {
    return Promise.resolve()
  }
}

function testAuth() {
  test('storage validation', (t) => {
    t.plan(5)
    const authorization = StorageAuth.makeWithKey(testPairs[0]).toAuthHeader()
    const authenticator = StorageAuth.fromAuthHeader(authorization)
    t.throws(() => authenticator.isAuthenticationValid(testAddrs[1], true),
             errors.ValidationError, 'Wrong address must throw')
    t.ok(!authenticator.isAuthenticationValid(testAddrs[1], false), 'Wrong address must fail')
    t.ok(authenticator.isAuthenticationValid(testAddrs[0], true), 'Good signature must pass')

    const pkBad = bitcoin.ECPair.fromPublicKeyBuffer(testPairs[1].getPublicKeyBuffer())
    const authBad = new StorageAuth(pkBad, authenticator.signature)

    t.throws(() => authenticator.isAuthenticationValid(testAddrs[1], true, 'Bad signature must throw'))
    t.ok(!authenticator.isAuthenticationValid(testAddrs[1], false), 'Bad signature must fail')
  })
}

function testAzDriver() {
  if (!azConfigPath) {
    return
  }
  const config = JSON.parse(fs.readFileSync(azConfigPath))

  test('azDriver', (t) => {
    t.plan(3)
    const driver = new AzDriver(config)
    const prefix = driver.getReadURLPrefix()
    const s = new Readable()
    s._read = function noop() {}
    s.push('hello world')
    s.push(null)

    driver.performWrite(
      { path: '../foo.js'})
      .then(() => t.ok(false, 'Should have thrown'))
      .catch((err) => t.equal(err.message, 'Invalid Path', 'Should throw bad path'))
      .then(() => driver.performWrite(
        { path: 'foo.txt',
          storageTopLevel: '12345',
          stream: s,
          contentType: 'application/octet-stream',
          contentLength: 12 }))
      .then((readUrl) => {
        t.ok(readUrl.startsWith(prefix), `${readUrl} must start with readUrlPrefix ${prefix}`)
        return fetch(readUrl)
      })
      .then((resp) => resp.text())
      .then((resptxt) => t.equal(resptxt, 'hello world', `Must get back hello world: got back: ${resptxt}`))
  })
}

function testS3Driver() {
  if (!awsConfigPath) {
    return
  }
  const config = JSON.parse(fs.readFileSync(awsConfigPath))

  test('awsDriver', (t) => {
    t.plan(3)
    const driver = new S3Driver(config)
    const prefix = driver.getReadURLPrefix()
    const s = new Readable()
    s._read = function noop() {}
    s.push('hello world')
    s.push(null)

    driver.performWrite(
      { path: '../foo.js'})
      .then(() => t.ok(false, 'Should have thrown'))
      .catch((err) => t.equal(err.message, 'Invalid Path', 'Should throw bad path'))
      .then(() => driver.performWrite(
        { path: 'foo.txt',
          storageTopLevel: '12345',
          stream: s,
          contentType: 'application/octet-stream',
          contentLength: 12 }))
      .then((readUrl) => {
        t.ok(readUrl.startsWith(prefix + '12345'), `${readUrl} must start with readUrlPrefix ${prefix}12345`)
        return fetch(readUrl)
      })
      .then((resp) => resp.text())
      .then((resptxt) => t.equal(resptxt, 'hello world', `Must get back hello world: got back: ${resptxt}`))
  })
}

/*
 * To run this test, you should run an HTTP server on localhost:4000
 * and use the ../config.sample.disk.json config file.
 */
function testDiskDriver() {
  if (!diskConfigPath) {
    return
  }
  const config = JSON.parse(fs.readFileSync(diskConfigPath))

  test('diskDriver', (t) => {
    t.plan(3)
    const driver = new DiskDriver(config)
    const prefix = driver.getReadURLPrefix()
    const s = new Readable()
    s._read = function noop() {}
    s.push('hello world')
    s.push(null)

    driver.performWrite(
      { path: '../foo.js'})
      .then(() => t.ok(false, 'Should have thrown'))
      .catch((err) => t.equal(err.message, 'Invalid Path', 'Should throw bad path'))
      .then(() => driver.performWrite(
        { path: 'foo.txt',
          storageTopLevel: '12345',
          stream: s,
          contentType: 'application/octet-stream',
          contentLength: 12 }))
      .then((readUrl) => {
        t.ok(readUrl.startsWith(prefix + '12345'), `${readUrl} must start with readUrlPrefix ${prefix}12345`)
        return fetch(readUrl)
      })
      .then((resp) => resp.text())
      .then((resptxt) => t.equal(resptxt, 'hello world', `Must get back hello world: got back: ${resptxt}`))
  })
}

function testServer() {
  test('validation tests', (t) => {
    t.plan(3)
    const server = new HubServer(new MockDriver(), new MockProofs(),
                                 { whitelist: [testAddrs[0]] })
    t.throws(() => server.validate(testAddrs[1], {}),
             errors.ValidationError, 'Non-whitelisted address should fail validation')
    t.throws(() => server.validate(testAddrs[0], {}),
             errors.ValidationError, 'Bad request headers should fail validation')

    const authorization = StorageAuth.makeWithKey(testPairs[0]).toAuthHeader()
    try {
      server.validate(testAddrs[0], { authorization })
      t.pass('White-listed address with good auth header should pass')
    } catch (err) {
      t.fail('White-listed address with good auth header should pass')
    }
  })

  test('handle request', (t) => {
    t.plan(8)
    const mockDriver = new MockDriver()
    const server = new HubServer(mockDriver, new MockProofs(),
                                 { whitelist: [testAddrs[0]] })
    const authorization = StorageAuth.makeWithKey(testPairs[0]).toAuthHeader()

    const s = new Readable()
    s._read = function noop() {}
    s.push('hello world')
    s.push(null)
    const s2 = new Readable()
    s2._read = function noop() {}
    s2.push('hello world')
    s2.push(null)

    server.handleRequest(testAddrs[0], 'foo.txt',
                         { 'content-type' : 'text/text',
                           'content-length': 4,
                           authorization }, s)
      .then(path => {
        t.equal(path, `http://test.com/${testAddrs[0]}/foo.txt`)
        t.equal(mockDriver.lastWrite.path, 'foo.txt')
        t.equal(mockDriver.lastWrite.storageTopLevel, testAddrs[0])
        t.equal(mockDriver.lastWrite.contentType, 'text/text')
      })
      .then(() => server.handleRequest(testAddrs[0], 'foo.txt',
                         { 'content-length': 4,
                           authorization }, s2))
      .then(path => {
        t.equal(path, `http://test.com/${testAddrs[0]}/foo.txt`)
        t.equal(mockDriver.lastWrite.path, 'foo.txt')
        t.equal(mockDriver.lastWrite.storageTopLevel, testAddrs[0])
        t.equal(mockDriver.lastWrite.contentType, 'application/octet-stream')
      })
  })
}

function testHttpPost() {
  if (!azConfigPath) {
    console.log('eliding test, you must set AZ_CONFIG_PATH to run this test.')
    return
  }

  test('handle request', (t) => {
    t.plan(2)
    const config = JSON.parse(fs.readFileSync(azConfigPath))
    Object.assign(config, { driver: 'azure' })

    let app = makeHttpServer(config)
    let sk = bitcoin.ECPair.makeRandom()
    let fileContents = sk.toWIF()
    let blob = Buffer(fileContents)
    let authHeader = StorageAuth
        .makeWithKey(sk)
        .toAuthHeader()
    let address = sk.getAddress()
    let path = `/store/${address}/helloWorld`
    request(app).post(path)
      .set('Content-Type', 'application/octet-stream')
      .set('Authorization', authHeader)
      .send(blob)
      .expect(202)
      .then((response) => {
        let url = JSON.parse(response.text).publicURL
        t.ok(url, 'Must return URL')
        console.log(url)
        fetch(url)
          .then(resp => resp.text())
          .then(text => t.equal(text, fileContents))
      })
  })
}

function testBadSig(done, configObj) {
  configObj = Object.assign({}, {proofsConfig : {proofsRequired : 0}}, configObj)
  const conf = config(configObj)

  let app = new HubServer({}, {}, conf)
  let sk = bitcoin.ECPair.makeRandom()
  let fileContents = sk.toWIF()
  let blob = Buffer(fileContents)
  let authHeader = StorageAuth
      .makeWithKey(sk)
      .toAuthHeader()
  let address = bitcoin.ECPair.makeRandom().getAddress()
  let path = `/store/${address}/helloWorld`
  request(app).post(path)
    .set('Content-Type', 'application/octet-stream')
    .set('Authorization', authHeader)
    .send(blob)
    .expect(401)
    .then((response) => {
      done()
    })
}

testServer()
testAuth()
testAzDriver()
testS3Driver()
testDiskDriver()
testHttpPost()
<|endoftext|>


<commit_before>const http = require("http");
const util = require("util");
const fileToMime = require("./modules/fileToMime");

// PARAMS
const PORT = 8080;

/*
 * TODO:
 *  1. Получить параметры запроса
 *  2. Подготовить базовые заголовки
 *  3. Базовый файловый сервер (MIME type)
 *  4. Работа с веб сокетами (socket.io игровой сервер)
*/

http.createServer((req, res) => {
    var path = req.url;

    // базовая обработка req
    // обработка ошибок
    // доступ только к публичным файлам
    // обработка http методов

    // end res
    var end = fileToMime(path);
    console.log(end);
    res.end(end);
    console.log("Got %s request. Url:%s", req.method, req.url);
}).listen(PORT);

console.log("SERVER: OK!\nPORT: %d\n", PORT);<commit_msg>main app file<commit_after>const http = require("http");
const util = require("util");
const fileToMime = require("./modules/fileToMime");
const url = require("url");

// PARAMS
const PORT = 8080;

/*
 * TODO:
 *  1. Получить параметры запроса
 *  2. Подготовить базовые заголовки
 *  3. Базовый файловый сервер (MIME type)
 *  4. Работа с веб сокетами (socket.io игровой сервер)
*/

var server = http.createServer();

server.on("request", (req, res) => {
    // пропарсим url
    var urlParsed = url.parse(req.url);

    // базовая обработка req
    // обработка ошибок
    // доступ только к публичным файлам
    // обработка http методов

    // headers
    // отмена кеширования
    res.setHeader("Cache-control", "no-cache, no-store, must-revalidate");

    // end res
    var end = fileToMime(urlParsed.pathname);
    console.log(end);
    res.end(end);
    //console.log("Got %s request. Url:%s", req.method, urlParsed.pathname);
    //console.log(urlParsed);
});

server.listen(PORT, () => {
    console.log("SERVER: OK!\nPORT: %d\n", PORT);
});

<|endoftext|>


<commit_before>import upload from './upload';

import express from 'express';
import logger from 'winston';

const app = express();
export default app;

app.set('port', process.env.PORT || 3000);

logger.remove(logger.transports.Console);
logger.add(logger.transports.Console, {'timestamp':true,});

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'angeleandgus.com');
  res.header('Access-Control-Allow-Headers', 'Content-Type');

  next();
});

app.use('/upload', upload);

app.use((err, req, res, next) => {
  const status = err.statusCode || err.status || 500;

  res.status(status);
  const ip = req.headers['x-forwarded-for'] ||
    req.connection.remoteAddress ||
    req.socket.remoteAddress ||
    req.connection.socket.remoteAddress;
  logger.error(`ip: ${ip} err: ${err}`);

  res.end(`Error ${status}: ${err.shortMessage}`);
  next(err);
});

app.listen(app.get('port'), () => {
  logger.info(`Server started: http://localhost:${app.get('port')}/`);
});
<commit_msg>Fix cross-origin<commit_after>import upload from './upload';

import express from 'express';
import logger from 'winston';

const app = express();
export default app;

app.set('port', process.env.PORT || 3000);

logger.remove(logger.transports.Console);
logger.add(logger.transports.Console, {'timestamp':true,});

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://angeleandgus.com');
  res.header('Access-Control-Allow-Headers', 'Content-Type');

  next();
});

app.use('/upload', upload);

app.use((err, req, res, next) => {
  const status = err.statusCode || err.status || 500;

  res.status(status);
  const ip = req.headers['x-forwarded-for'] ||
    req.connection.remoteAddress ||
    req.socket.remoteAddress ||
    req.connection.socket.remoteAddress;
  logger.error(`ip: ${ip} err: ${err}`);

  res.end(`Error ${status}: ${err.shortMessage}`);
  next(err);
});

app.listen(app.get('port'), () => {
  logger.info(`Server started: http://localhost:${app.get('port')}/`);
});
<|endoftext|>


<commit_before>'use strict';

const Matrix = require('ml-matrix');
const SparseMatrix = require('ml-sparse-matrix');
const binarySearch = require('ml-binary-search');
const newArray = require('new-array');

const getPauli = require('./pauli');

function simulate1d(spinSystem, options = {}) {
    var i;
    const frequencyMHz = (options.frequency || 400);
    const from = (options.from || 0) * frequencyMHz;
    const to = (options.to || 10) * frequencyMHz;
    const lineWidth = options.lineWidth || 1;
    const nbPoints = options.nbPoints || 1024;
    const maxClusterSize = options.maxClusterSize || 10;

    const chemicalShifts = spinSystem.chemicalShifts.slice();
    for (i = 0; i < chemicalShifts.length; i++) {
        chemicalShifts[i] = chemicalShifts[i] * frequencyMHz;
    }

    let lineWidthPoints = (nbPoints * lineWidth / Math.abs(to - from));
    let lnPoints = lineWidthPoints * 50;
    lineWidthPoints = Math.pow(lineWidthPoints / 2, 2);

    const lorentzianLength = lnPoints | 0;
    const lorentzian = new Array(lorentzianLength);
    for (i = 0; i < lorentzianLength; i++) {
        lorentzian[i] = 1e12 * lineWidthPoints / (Math.pow(i - lnPoints / 2, 2) + lineWidthPoints);
    }

    const result = new newArray(nbPoints, 0);

    const multiplicity = spinSystem.multiplicity;
    for (var h = 0; h < spinSystem.clusters.length; h++) {
        const cluster = spinSystem.clusters[h];

        if (cluster.length > maxClusterSize) {
            throw new Error('too big cluster: ' + cluster.length);
        }

        var weight = 1;
        var sumI = 0;
        const frequencies = [];
        const intensities = [];
        if (false) {
            // if(tnonZeros.contains(2)){
        } else {
            const hamiltonian = getHamiltonian(
                chemicalShifts,
                spinSystem.couplingConstants,
                multiplicity,
                spinSystem.connectivity,
                cluster
            );

            const hamSize = hamiltonian.rows;
            const evd = new Matrix.DC.EVD(hamiltonian);
            const V = evd.eigenvectorMatrix;
            const diagB = evd.realEigenvalues;
            const assignmentMatrix = new SparseMatrix(hamSize, hamSize);
            const multLen = cluster.length;
            weight = 0;
            for (var n = 0; n < multLen; n++) {
                const L = getPauli(multiplicity[cluster[n]]);

                let temp = 1;
                for (var j = 0; j < n; j++) {
                    temp *= multiplicity[cluster[j]];
                }
                const A = SparseMatrix.eye(temp);

                temp = 1;
                for (j = n + 1; j < multLen; j++) {
                    temp *= multiplicity[cluster[j]];
                }
                const B = SparseMatrix.eye(temp);
                const tempMat = A.kroneckerProduct(L.m).kroneckerProduct(B);
                if (cluster[n] >= 0) {
                    assignmentMatrix.add(tempMat.mul(cluster[n] + 1));
                    weight++;
                } else {
                    assignmentMatrix.add(tempMat.mul(0 - (cluster[n] + 1)));

                }
            }

            let rhoip = Matrix.zeros(hamSize, hamSize);
            assignmentMatrix.forEachNonZero((i, j, v) => {
                if (v > 0) {
                    const row = V[j];
                    for (var k = 0; k < row.length; k++) {
                        if (row[k] !== 0) {
                            rhoip.set(i, k, rhoip.get(i, k) + row[k]);
                        }
                    }
                }
                return v;
            });

            let rhoip2 = rhoip.clone();
            assignmentMatrix.forEachNonZero((i, j, v) => {
                if (v < 0) {
                    const row = V[j];
                    for (var k = 0; k < row.length; k++) {
                        if (row[k] !== 0) {
                            rhoip2.set(i, k, rhoip2.get(i, k) + row[k]);
                        }
                    }
                }
                return v;
            });

            const tV = V.transpose();
            rhoip = tV.mmul(rhoip);
            rhoip = new SparseMatrix(rhoip, {threshold: 1e-1});
            triuTimesAbs(rhoip, 1e-1);
            rhoip2 = tV.mmul(rhoip2);
            rhoip2 = new SparseMatrix(rhoip2, {threshold: 1e-1});
            triuTimesAbs(rhoip2, 1e-1);

            rhoip2.forEachNonZero((i, j, v) => {
                var val = rhoip.get(i, j);
                val = Math.min(Math.abs(val), Math.abs(v));
                val *= val;

                sumI += val;
                var valFreq = diagB[i] - diagB[j];
                var insertIn = binarySearch(frequencies, valFreq);
                if (insertIn < 0) {
                    frequencies.splice(-1 - insertIn, 0, valFreq);
                    intensities.splice(-1 - insertIn, 0, val);
                } else {
                    intensities[insertIn] += val;
                }
            });
        }

        const numFreq = frequencies.length;
        if (numFreq > 0) {
            weight = weight / sumI;
            const diff = lineWidth / 16;
            let valFreq = frequencies[0];
            let inte = intensities[0];
            let count = 1;
            for (i = 0; i < numFreq; i++) {
                if (Math.abs(frequencies[i] - valFreq / count) < diff) {
                    inte += intensities[i];
                    valFreq += frequencies[i];
                    count++;
                } else {
                    addPeak(result, valFreq / count, inte * weight, from, to, nbPoints, lorentzian);
                    valFreq = frequencies[i];
                    inte = intensities[i];
                    count = 1;
                }
            }
            addPeak(result, valFreq / count, inte * weight, from, to, nbPoints, lorentzian);
        }
    }

    return result;
}

function addPeak(result, freq, height, from, to, nbPoints, lorentzian) {
    const center = (nbPoints * (-freq-from) / (to - from)) | 0;
    const lnPoints = lorentzian.length;
    var index = 0;
    var indexLorentz = 0;
    for (var i = center - lnPoints / 2; i < center + lnPoints / 2; i++) {
        index = i | 0;
        if (i >= 0 && i < nbPoints) {
            result[index] = result[index] + lorentzian[indexLorentz] * height;
        }
        indexLorentz++;
    }
}

function triuTimesAbs(A, val) {
    A.forEachNonZero((i, j, v) => {
        if (i > j) return 0;
        if (Math.abs(v) <= val) return 0;
        return v;
    });
}

function getHamiltonian(chemicalShifts, couplingConstants, multiplicity, conMatrix, cluster) {
    let hamSize = 1;
    for (var i = 0; i < cluster.length; i++) {
        hamSize *= multiplicity[cluster[i]];
    }

    const clusterHam = new SparseMatrix(hamSize, hamSize);

    for (var pos = 0; pos < cluster.length; pos++) {
        const n = cluster[pos];
        const L = getPauli(multiplicity[n]);

        let A1, B1;
        let temp = 1;
        for (let i = 0; i < pos; i++) {
            temp *= multiplicity[cluster[i]];
        }
        A1 = SparseMatrix.eye(temp);

        temp = 1;
        for (let i = pos + 1; i < cluster.length; i++) {
            temp *= multiplicity[cluster[i]];
        }
        B1 = SparseMatrix.eye(temp);

        const alpha = chemicalShifts[n];
        const kronProd = A1.kroneckerProduct(L.z).kroneckerProduct(B1);
        clusterHam.add(kronProd.mul(alpha));

        for (var pos2 = 0; pos2 < cluster.length; pos2++) {
            const k = cluster[pos2];
            if (conMatrix[n][k] === 1) {
                const S = getPauli(multiplicity[k]);

                let A2, B2;
                let temp = 1;
                for (let i = 0; i < pos2; i++) {
                    temp *= multiplicity[cluster[i]];
                }
                A2 = SparseMatrix.eye(temp);

                temp = 1;
                for (let i = pos2 + 1; i < cluster.length; i++) {
                    temp *= multiplicity[cluster[i]];
                }
                B2 = SparseMatrix.eye(temp);

                const kron1 = A1.kroneckerProduct(L.x).kroneckerProduct(B1).mmul(A2.kroneckerProduct(S.x).kroneckerProduct(B2));
                kron1.add(A1.kroneckerProduct(L.y).kroneckerProduct(B1).mul(-1).mmul(A2.kroneckerProduct(S.y).kroneckerProduct(B2)));
                kron1.add(A1.kroneckerProduct(L.z).kroneckerProduct(B1).mmul(A2.kroneckerProduct(S.z).kroneckerProduct(B2)));

                clusterHam.add(kron1.mul(couplingConstants[n][k]));
            }
        }
    }

    return clusterHam;
}

module.exports = simulate1d;
<commit_msg>fix 1D simulation<commit_after>'use strict';

const Matrix = require('ml-matrix');
const SparseMatrix = require('ml-sparse-matrix');
const binarySearch = require('ml-binary-search');
const newArray = require('new-array');

const getPauli = require('./pauli');

function simulate1d(spinSystem, options = {}) {
    var i;
    const frequencyMHz = (options.frequency || 400);
    const from = (options.from || 0) * frequencyMHz;
    const to = (options.to || 10) * frequencyMHz;
    const lineWidth = options.lineWidth || 1;
    const nbPoints = options.nbPoints || 1024;
    const maxClusterSize = options.maxClusterSize || 10;

    const chemicalShifts = spinSystem.chemicalShifts.slice();
    for (i = 0; i < chemicalShifts.length; i++) {
        chemicalShifts[i] = chemicalShifts[i] * frequencyMHz;
    }

    let lineWidthPoints = (nbPoints * lineWidth / Math.abs(to - from));
    let lnPoints = lineWidthPoints * 50;
    lineWidthPoints = Math.pow(lineWidthPoints / 2, 2);

    const lorentzianLength = lnPoints | 0;
    const lorentzian = new Array(lorentzianLength);
    for (i = 0; i < lorentzianLength; i++) {
        lorentzian[i] = 1e12 * lineWidthPoints / (Math.pow(i - lnPoints / 2, 2) + lineWidthPoints);
    }

    const result = new newArray(nbPoints, 0);

    const multiplicity = spinSystem.multiplicity;
    for (var h = 0; h < spinSystem.clusters.length; h++) {
        const cluster = spinSystem.clusters[h];

        if (cluster.length > maxClusterSize) {
            throw new Error('too big cluster: ' + cluster.length);
        }

        var weight = 1;
        var sumI = 0;
        const frequencies = [];
        const intensities = [];
        if (false) {
            // if(tnonZeros.contains(2)){
        } else {
            const hamiltonian = getHamiltonian(
                chemicalShifts,
                spinSystem.couplingConstants,
                multiplicity,
                spinSystem.connectivity,
                cluster
            );

            const hamSize = hamiltonian.rows;
            const evd = new Matrix.DC.EVD(hamiltonian);
            const V = evd.eigenvectorMatrix;
            const diagB = evd.realEigenvalues;
            const assignmentMatrix = new SparseMatrix(hamSize, hamSize);
            const multLen = cluster.length;
            weight = 0;
            for (var n = 0; n < multLen; n++) {
                const L = getPauli(multiplicity[cluster[n]]);

                let temp = 1;
                for (var j = 0; j < n; j++) {
                    temp *= multiplicity[cluster[j]];
                }
                const A = SparseMatrix.eye(temp);

                temp = 1;
                for (j = n + 1; j < multLen; j++) {
                    temp *= multiplicity[cluster[j]];
                }
                const B = SparseMatrix.eye(temp);
                const tempMat = A.kroneckerProduct(L.m).kroneckerProduct(B);
                if (cluster[n] >= 0) {
                    assignmentMatrix.add(tempMat.mul(cluster[n] + 1));
                    weight++;
                } else {
                    assignmentMatrix.add(tempMat.mul(0 - (cluster[n] + 1)));

                }
            }

            let rhoip = Matrix.zeros(hamSize, hamSize);
            assignmentMatrix.forEachNonZero((i, j, v) => {
                if (v > 0) {
                    const row = V[j];
                    for (var k = 0; k < row.length; k++) {
                        if (row[k] !== 0) {
                            rhoip.set(i, k, rhoip.get(i, k) + row[k]);
                        }
                    }
                }
                return v;
            });

            let rhoip2 = rhoip.clone();
            assignmentMatrix.forEachNonZero((i, j, v) => {
                if (v < 0) {
                    const row = V[j];
                    for (var k = 0; k < row.length; k++) {
                        if (row[k] !== 0) {
                            rhoip2.set(i, k, rhoip2.get(i, k) + row[k]);
                        }
                    }
                }
                return v;
            });

            const tV = V.transpose();
            rhoip = tV.mmul(rhoip);
            rhoip = new SparseMatrix(rhoip, {threshold: 1e-1});
            triuTimesAbs(rhoip, 1e-1);
            rhoip2 = tV.mmul(rhoip2);
            rhoip2 = new SparseMatrix(rhoip2, {threshold: 1e-1});
            triuTimesAbs(rhoip2, 1e-1);

            rhoip2.forEachNonZero((i, j, v) => {
                var val = rhoip.get(i, j);
                val = Math.min(Math.abs(val), Math.abs(v));
                val *= val;

                sumI += val;
                var valFreq = diagB[i] - diagB[j];
                var insertIn = binarySearch(frequencies, valFreq);
                if (insertIn < 0) {
                    frequencies.splice(-1 - insertIn, 0, valFreq);
                    intensities.splice(-1 - insertIn, 0, val);
                } else {
                    intensities[insertIn] += val;
                }
            });
        }

        const numFreq = frequencies.length;
        if (numFreq > 0) {
            weight = weight / sumI;
            const diff = lineWidth / 16;
            let valFreq = frequencies[0];
            let inte = intensities[0];
            let count = 1;
            for (i = 1; i < numFreq; i++) {
                if (Math.abs(frequencies[i] - valFreq / count) < diff) {
                    inte += intensities[i];
                    valFreq += frequencies[i];
                    count++;
                } else {
                    addPeak(result, valFreq / count, inte * weight, from, to, nbPoints, lorentzian);
                    valFreq = frequencies[i];
                    inte = intensities[i];
                    count = 1;
                }
            }
            addPeak(result, valFreq / count, inte * weight, from, to, nbPoints, lorentzian);
        }
    }

    return result;
}

function addPeak(result, freq, height, from, to, nbPoints, lorentzian) {
    const center = (nbPoints * (-freq-from) / (to - from)) | 0;
    const lnPoints = lorentzian.length;
    var index = 0;
    var indexLorentz = 0;
    for (var i = center - lnPoints / 2; i < center + lnPoints / 2; i++) {
        index = i | 0;
        if (i >= 0 && i < nbPoints) {
            result[index] = result[index] + lorentzian[indexLorentz] * height;
        }
        indexLorentz++;
    }
}

function triuTimesAbs(A, val) {
    A.forEachNonZero((i, j, v) => {
        if (i > j) return 0;
        if (Math.abs(v) <= val) return 0;
        return v;
    });
}

function getHamiltonian(chemicalShifts, couplingConstants, multiplicity, conMatrix, cluster) {
    let hamSize = 1;
    for (var i = 0; i < cluster.length; i++) {
        hamSize *= multiplicity[cluster[i]];
    }

    const clusterHam = new SparseMatrix(hamSize, hamSize);

    for (var pos = 0; pos < cluster.length; pos++) {
        const n = cluster[pos];
        const L = getPauli(multiplicity[n]);

        let A1, B1;
        let temp = 1;
        for (let i = 0; i < pos; i++) {
            temp *= multiplicity[cluster[i]];
        }
        A1 = SparseMatrix.eye(temp);

        temp = 1;
        for (let i = pos + 1; i < cluster.length; i++) {
            temp *= multiplicity[cluster[i]];
        }
        B1 = SparseMatrix.eye(temp);

        const alpha = chemicalShifts[n];
        const kronProd = A1.kroneckerProduct(L.z).kroneckerProduct(B1);
        clusterHam.add(kronProd.mul(alpha));

        for (var pos2 = 0; pos2 < cluster.length; pos2++) {
            const k = cluster[pos2];
            if (conMatrix[n][k] === 1) {
                const S = getPauli(multiplicity[k]);

                let A2, B2;
                let temp = 1;
                for (let i = 0; i < pos2; i++) {
                    temp *= multiplicity[cluster[i]];
                }
                A2 = SparseMatrix.eye(temp);

                temp = 1;
                for (let i = pos2 + 1; i < cluster.length; i++) {
                    temp *= multiplicity[cluster[i]];
                }
                B2 = SparseMatrix.eye(temp);

                const kron1 = A1.kroneckerProduct(L.x).kroneckerProduct(B1).mmul(A2.kroneckerProduct(S.x).kroneckerProduct(B2));
                kron1.add(A1.kroneckerProduct(L.y).kroneckerProduct(B1).mul(-1).mmul(A2.kroneckerProduct(S.y).kroneckerProduct(B2)));
                kron1.add(A1.kroneckerProduct(L.z).kroneckerProduct(B1).mmul(A2.kroneckerProduct(S.z).kroneckerProduct(B2)));

                clusterHam.add(kron1.mul(couplingConstants[n][k]));
            }
        }
    }

    return clusterHam;
}

module.exports = simulate1d;
<|endoftext|>


<commit_before>(function($) {

  $.fn.tableOperations = function(options) {
    var $table = $(this),
        $form = {},
        $select = {},
        defaults = {
          defaultSelect: 'Select',
          selectClass: 'form-control',
          buttonClass: 'btn btn-default',
          formClass: 'form-inline',
          buttonText: 'Go',
          operations: {
            test: { label: 'Test Me', callback: function(selected) { console.log(selected); }  }
          }
        };

    options = $.extend({}, defaults, options);

    //Add the checkbox tds
    if ($table.find('thead').length <= 0) { $table.prepend('

'); }
    $table.find('tbody tr').prepend('<input type="checkbox"/>	');
    $table.find('thead tr').prepend('<input type="checkbox"/>	');

    //Add the operations selector box
    $table.before('<form id="batch-select-operation-form" class="'+options.formClass+'" role="form">
<select class="'+options.selectClass+'"><option>'+options.defaultSelect+'</option></select>

<button type="submit" class="'+options.buttonClass+'">'+options.buttonText+'</button>
');
    $form = $table.prev('form#batch-select-operation-form');
    $select = $form.find('select');
    $.each(options.operations, function(index, item) { $select.append('<option value="'+index+'">'+item.label+'</option>'); });

    //check all
    $table.find('.batch-select-check-all input').on('click', function() {
      $table.find('td.batch-select-check input').prop('checked', $(this).prop('checked') );
    });

    //Form submition actions
    $form.on('submit', function(e) {
      e.preventDefault();
      var val = $(this).find('select').val(),
          $selected = $table.find('.batch-select-check input:checked').closest('tr');

      if (val != options.defaultSelect && $selected.length > 0) { options.operations[val].callback($selected); }
    });

  };

})(jQuery);
<commit_msg>Check to see if checkboxes exist before creating them<commit_after>(function($) {

  $.fn.tableOperations = function(options) {
    var $table = $(this),
        $form,
        $select,
        defaults = {
          defaultSelect: 'Select',
          selectClass: 'form-control',
          buttonClass: 'btn btn-default',
          formClass: 'form-inline',
          buttonText: 'Go',
          operations: {
            test: { label: 'Test Me', callback: function(selected) { console.log(selected); }  }
          }
        };

    options = $.extend({}, defaults, options);

    //Add the checkbox tds
    if ($table.find('thead').length <= 0) { $table.prepend('

'); }

    if ($table.find('tbody tr .batch-select-check').length <= 0) {
      $table.find('tbody tr').prepend('<input type="checkbox"/>	');
    }

    if ($table.find('thead tr .batch-select-check-all').length <= 0) {
      $table.find('thead tr').prepend('<input type="checkbox"/>	');
    }

    //Add the operations selector box
    $table.before('<form id="batch-select-operation-form" class="'+options.formClass+'" role="form">
<select class="'+options.selectClass+'"><option>'+options.defaultSelect+'</option></select>

<button type="submit" class="'+options.buttonClass+'">'+options.buttonText+'</button>
');
    $form = $table.prev('form#batch-select-operation-form');
    $select = $form.find('select');
    $.each(options.operations, function(index, item) { $select.append('<option value="'+index+'">'+item.label+'</option>'); });

    //check all
    $table.find('.batch-select-check-all input').on('click', function() {
      $table.find('td.batch-select-check input').prop('checked', $(this).prop('checked') );
    });

    //Form submition actions
    $form.on('submit', function(e) {
      e.preventDefault();
      var val = $(this).find('select').val(),
          $selected = $table.find('.batch-select-check input:checked').closest('tr');

      if (val != options.defaultSelect && $selected.length > 0) { options.operations[val].callback($selected); }
    });

  };

})(jQuery);
<|endoftext|>


<commit_before>/**
 * Given a DOM subtree, wraps it in a list, and puts each line into its own
 * list item.
 *
 * @param {Node} node modified in place.  Its content is pulled into an
 *     HTMLOListElement, and each line is moved into a separate list item.
 *     This requires cloning elements, so the input might not have unique
 *     IDs after numbering.
 */
function numberLines(node, opt_startLineNum) {
  var nocode = /(?:^|\s)nocode(?:\s|$)/;
  var lineBreak = /\r\n?|\n/;

  var document = node.ownerDocument;

  var whitespace;
  if (node.currentStyle) {
    whitespace = node.currentStyle.whiteSpace;
  } else if (window.getComputedStyle) {
    whitespace = document.defaultView.getComputedStyle(node, null)
        .getPropertyValue('white-space');
  }
  // If it's preformatted, then we need to split lines on line breaks
  // in addition to
s.
  var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);

  var li = document.createElement('LI');
  while (node.firstChild) {
    li.appendChild(node.firstChild);
  }
  // An array of lines.  We split below, so this is initialized to one
  // un-split line.
  var listItems = [li];

  function walk(node) {
    switch (node.nodeType) {
      case 1:  // Element
        if (nocode.test(node.className)) { break; }
        if ('BR' === node.nodeName) {
          breakAfter(node);
          // Discard the
 since it is now flush against a
.
          if (node.parentNode) {
            node.parentNode.removeChild(node);
          }
        } else {
          for (var child = node.firstChild; child; child = child.nextSibling) {
            walk(child);
          }
        }
        break;
      case 3: case 4:  // Text
        if (isPreformatted) {
          var text = node.nodeValue;
          var match = text.match(lineBreak);
          if (match) {
            var firstLine = text.substring(0, match.index);
            node.nodeValue = firstLine;
            var tail = text.substring(match.index + match[0].length);
            if (tail) {
              var parent = node.parentNode;
              parent.insertBefore(
                  document.createTextNode(tail), node.nextSibling);
            }
            breakAfter(node);
            if (!firstLine) {
              // Don't leave blank text nodes in the DOM.
              node.parentNode.removeChild(node);
            }
          }
        }
        break;
    }
  }

  // Split a line after the given node.
  function breakAfter(lineEndNode) {
    // If there's nothing to the right, then we can skip ending the line
    // here, and move root-wards since splitting just before an end-tag
    // would require us to create a bunch of empty copies.
    while (!lineEndNode.nextSibling) {
      lineEndNode = lineEndNode.parentNode;
      if (!lineEndNode) { return; }
    }

    function breakLeftOf(limit, copy) {
      // Clone shallowly if this node needs to be on both sides of the break.
      var rightSide = copy ? limit.cloneNode(false) : limit;
      var parent = limit.parentNode;
      if (parent) {
        // We clone the parent chain.
        // This helps us resurrect important styling elements that cross lines.
        // E.g. in Foo
Bar
        // should be rewritten to
Foo

Bar
.
        var parentClone = breakLeftOf(parent, 1);
        // Move the clone and everything to the right of the original
        // onto the cloned parent.
        var next = limit.nextSibling;
        parentClone.appendChild(rightSide);
        for (var sibling = next; sibling; sibling = next) {
          next = sibling.nextSibling;
          parentClone.appendChild(sibling);
        }
      }
      return rightSide;
    }

    var split = breakLeftOf(lineEndNode.nextSibling, 0);

    // Walk the parent chain until we reach an unattached LI.
    for (var parent; (parent = split.parentNode);) { split = parent; }
    // Put it on the list of lines for later processing.
    listItems.push(split);
console.log('pushing listItem length=' + listItems.length);
  }

  // Split lines while there are lines left to split.
  for (var i = 0;  // Number of lines that have been split so far.
       i < listItems.length;  // length updated by breakAfter calls.
       ++i) {
console.log('walking list item ' + i);
    walk(listItems[i]);
  }

  // Make sure numeric indices show correctly.
  if (opt_startLineNum === (opt_startLineNum|0)) {
    listItems[0].setAttribute('value', opt_startLineNum);
  }

  var ol = document.createElement('OL');
  ol.className = 'linenums';
  var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
  for (var i = 0, n = listItems.length; i < n; ++i) {
    li = listItems[i];
    // Stick a class on the LIs so that stylesheets can
    // color odd/even rows, or any other row pattern that
    // is co-prime with 10.
    li.className = 'L' + ((i + offset) % 10);
    if (!li.firstChild) {
      li.appendChild(document.createTextNode('\xA0'));
    }
    ol.appendChild(li);
  }

  node.appendChild(ol);
}<commit_msg>logging for IE<commit_after>/**
 * Given a DOM subtree, wraps it in a list, and puts each line into its own
 * list item.
 *
 * @param {Node} node modified in place.  Its content is pulled into an
 *     HTMLOListElement, and each line is moved into a separate list item.
 *     This requires cloning elements, so the input might not have unique
 *     IDs after numbering.
 */
function numberLines(node, opt_startLineNum) {
  var nocode = /(?:^|\s)nocode(?:\s|$)/;
  var lineBreak = /\r\n?|\n/;

  var document = node.ownerDocument;

  var whitespace;
  if (node.currentStyle) {
    whitespace = node.currentStyle.whiteSpace;
  } else if (window.getComputedStyle) {
    whitespace = document.defaultView.getComputedStyle(node, null)
        .getPropertyValue('white-space');
  }
  // If it's preformatted, then we need to split lines on line breaks
  // in addition to
s.
  var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);

  var li = document.createElement('LI');
  while (node.firstChild) {
    li.appendChild(node.firstChild);
  }
  // An array of lines.  We split below, so this is initialized to one
  // un-split line.
  var listItems = [li];

  function walk(node) {
    switch (node.nodeType) {
      case 1:  // Element
        if (nocode.test(node.className)) { break; }
        if ('BR' === node.nodeName) {
          breakAfter(node);
          // Discard the
 since it is now flush against a
.
          if (node.parentNode) {
            node.parentNode.removeChild(node);
          }
        } else {
          for (var child = node.firstChild; child; child = child.nextSibling) {
            walk(child);
          }
        }
        break;
      case 3: case 4:  // Text
        if (isPreformatted) {
          var text = node.nodeValue;
          var match = text.match(lineBreak);
          if (match) {
            var firstLine = text.substring(0, match.index);
            node.nodeValue = firstLine;
            var tail = text.substring(match.index + match[0].length);
console.log('split "' + text.replace(/\r\n?|\n/g, '\\n') + '" into "' + firstLine + '" and "' + tail.replace(/\r\n?|\n/g, '\\n') + '"');
            if (tail) {
              var parent = node.parentNode;
              parent.insertBefore(
                  document.createTextNode(tail), node.nextSibling);
            }
            breakAfter(node);
            if (!firstLine) {
              // Don't leave blank text nodes in the DOM.
              node.parentNode.removeChild(node);
            }
          }
        }
        break;
    }
  }

  // Split a line after the given node.
  function breakAfter(lineEndNode) {
    // If there's nothing to the right, then we can skip ending the line
    // here, and move root-wards since splitting just before an end-tag
    // would require us to create a bunch of empty copies.
    while (!lineEndNode.nextSibling) {
      lineEndNode = lineEndNode.parentNode;
      if (!lineEndNode) { return; }
    }

    function breakLeftOf(limit, copy) {
      // Clone shallowly if this node needs to be on both sides of the break.
      var rightSide = copy ? limit.cloneNode(false) : limit;
      var parent = limit.parentNode;
      if (parent) {
        // We clone the parent chain.
        // This helps us resurrect important styling elements that cross lines.
        // E.g. in Foo
Bar
        // should be rewritten to
Foo

Bar
.
        var parentClone = breakLeftOf(parent, 1);
        // Move the clone and everything to the right of the original
        // onto the cloned parent.
        var next = limit.nextSibling;
        parentClone.appendChild(rightSide);
        for (var sibling = next; sibling; sibling = next) {
          next = sibling.nextSibling;
          parentClone.appendChild(sibling);
        }
      }
      return rightSide;
    }

    var split = breakLeftOf(lineEndNode.nextSibling, 0);

    // Walk the parent chain until we reach an unattached LI.
    for (var parent; (parent = split.parentNode);) { split = parent; }
    // Put it on the list of lines for later processing.
    listItems.push(split);
console.log('pushing listItem length=' + listItems.length);
  }

  // Split lines while there are lines left to split.
  for (var i = 0;  // Number of lines that have been split so far.
       i < listItems.length;  // length updated by breakAfter calls.
       ++i) {
console.log('walking list item ' + i);
    walk(listItems[i]);
  }

  // Make sure numeric indices show correctly.
  if (opt_startLineNum === (opt_startLineNum|0)) {
    listItems[0].setAttribute('value', opt_startLineNum);
  }

  var ol = document.createElement('OL');
  ol.className = 'linenums';
  var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
  for (var i = 0, n = listItems.length; i < n; ++i) {
    li = listItems[i];
    // Stick a class on the LIs so that stylesheets can
    // color odd/even rows, or any other row pattern that
    // is co-prime with 10.
    li.className = 'L' + ((i + offset) % 10);
    if (!li.firstChild) {
      li.appendChild(document.createTextNode('\xA0'));
    }
    ol.appendChild(li);
  }

  node.appendChild(ol);
}<|endoftext|>


<commit_before>// Copyright 2017, University of Colorado Boulder

/**
 * QUnit tests for NumberProperty
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var NumberProperty = require( 'AXON/NumberProperty' );

  QUnit.module( 'NumberProperty' );

  QUnit.test( 'NumberProperty', function( assert ) {
    var p = new NumberProperty( 1 );
    p.value = 0;

    // default validation
    window.assert && assert.throws( function() {
      p = new NumberProperty( 'foo' );
    }, 'should throw Assertion failed: invalid initial value: foo' );
    p = new NumberProperty( 0 );
    window.assert && assert.throws( function() {
      p.value = 'bar';
    }, 'should throw Assertion failed: invalid initial value: bar' );

    // range
    p = new NumberProperty( 0, {
      range: { min: 0, max: 10 }
    } );
    p.value = 5;
    window.assert && assert.throws( function() {
      p.value = 11;
    }, 'should throw Assertion failed: invalid value: 11' );
    window.assert && assert.throws( function() {
      p.value = -1;
    }, 'should throw Assertion failed: invalid value: -1' );

    // valueType
    p = new NumberProperty( 0 );
    assert.equal( p.valueType, 'FloatingPoint', 'default valueType should be FloatingPoint' );

    p = new NumberProperty( 0, { valueType: 'Integer' } );
    assert.equal( p.valueType, 'Integer', 'valueType should be integer when set as such.' );

    window.assert && assert.throws( function() {
      p = new NumberProperty( 0, { valueType: 'GarbaldyGOOK' } );
    }, 'should throw Assertion failed: invalid type: GarbaldyGOOK' );

    p = new NumberProperty( 0, { valueType: 'Integer' } );

    window.assert && assert.throws( function() {
      p.value = 3.4;
    }, 'should throw Assertion failed: invalid value: 3.4' );

    p.value = 3;
    assert.equal( p.value, 3 );

    p = new NumberProperty( 0, { range: { min: 0, max: 5 }, valueType: 'Integer' } );
    window.assert && assert.throws( function() {
      p.value = 3.4;
    }, 'should throw Assertion failed: invalid value: 3.4' );

    p = new NumberProperty( 3.4, { range: { min: 0, max: 5 }, valueType: 'FloatingPoint' } );
    window.assert && assert.throws( function() {
      p = new NumberProperty( 3.4, { range: { min: 0, max: 5 }, valueType: 'Integer' } );
    }, 'should throw Assertion failed: initial value 3.4 must be of type: Integer' );

    p = new NumberProperty( 0, { range: { min: 0, max: 5 }, valueType: 'FloatingPoint' } );
    p.value = 3.4;
    assert.equal( p.value, 3.4 );

    p = new NumberProperty( 0, { validValues: [ 0, 1, 2, 3, 4, 5 ], valueType: 'Integer' } );
    window.assert && assert.throws( function() {
      p = new NumberProperty( 0, { validValues: [ 0, 1, 2, 3.4, 5 ], valueType: 'Integer' } );
    }, 'should throw Assertion failed: validValues must contain numbers of the right valueType' );

    p = new NumberProperty( 0, { range: { min: 0, max: 5 }, valueType: 'FloatingPoint' } );
    p.value = 3.4;
    assert.equal( p.value, 3.4 );

    // validValues
    p = new NumberProperty( 0, {
      validValues: [ 0, 1, 2 ]
    } );
    p.value = 1;
    p.value = 2;
    window.assert && assert.throws( function() {
      p.value = 3;
    }, 'should throw Assertion failed: invalid value: 3' );

    // isValidValue
    p = new NumberProperty( 0, {
      isValidValue: function( value ) { return value >= 0; }
    } );
    p.value = 1;
    p.value = 0;
    window.assert && assert.throws( function() {
      p.value = -1;
    }, 'should throw Assertion failed: invalid value: -1' );
    window.assert && assert.throws( function() {
      p.value = 'foo';
    }, 'should throw Assertion failed: invalid value: foo' );

    // mutually-exclusive options
    window.assert && assert.throws( function() {
      p = new NumberProperty( 0, {
        range: { min: 0, max: 10 },
        isValidValue: function( value ) { return value >= 0; },
        validValues: [ 0, 1, 2 ]
      }, 'should throw Assertion failed: validValues, isValidValue and range are mutually-exclusive options' );
    } );

    if ( !window.assert ) {
      assert.expect( 5 ); // TODO: this is a hack to suppress the "expected 0 tests but 5 were run" error.
    }
  } );

} );<commit_msg>remove unnecessary assert.expect, https://github.com/phetsims/aqua/issues/31<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * QUnit tests for NumberProperty
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var NumberProperty = require( 'AXON/NumberProperty' );

  QUnit.module( 'NumberProperty' );

  QUnit.test( 'NumberProperty', function( assert ) {
    var p = new NumberProperty( 1 );
    p.value = 0;

    // default validation
    window.assert && assert.throws( function() {
      p = new NumberProperty( 'foo' );
    }, 'should throw Assertion failed: invalid initial value: foo' );
    p = new NumberProperty( 0 );
    window.assert && assert.throws( function() {
      p.value = 'bar';
    }, 'should throw Assertion failed: invalid initial value: bar' );

    // range
    p = new NumberProperty( 0, {
      range: { min: 0, max: 10 }
    } );
    p.value = 5;
    window.assert && assert.throws( function() {
      p.value = 11;
    }, 'should throw Assertion failed: invalid value: 11' );
    window.assert && assert.throws( function() {
      p.value = -1;
    }, 'should throw Assertion failed: invalid value: -1' );

    // valueType
    p = new NumberProperty( 0 );
    assert.equal( p.valueType, 'FloatingPoint', 'default valueType should be FloatingPoint' );

    p = new NumberProperty( 0, { valueType: 'Integer' } );
    assert.equal( p.valueType, 'Integer', 'valueType should be integer when set as such.' );

    window.assert && assert.throws( function() {
      p = new NumberProperty( 0, { valueType: 'GarbaldyGOOK' } );
    }, 'should throw Assertion failed: invalid type: GarbaldyGOOK' );

    p = new NumberProperty( 0, { valueType: 'Integer' } );

    window.assert && assert.throws( function() {
      p.value = 3.4;
    }, 'should throw Assertion failed: invalid value: 3.4' );

    p.value = 3;
    assert.equal( p.value, 3 );

    p = new NumberProperty( 0, { range: { min: 0, max: 5 }, valueType: 'Integer' } );
    window.assert && assert.throws( function() {
      p.value = 3.4;
    }, 'should throw Assertion failed: invalid value: 3.4' );

    p = new NumberProperty( 3.4, { range: { min: 0, max: 5 }, valueType: 'FloatingPoint' } );
    window.assert && assert.throws( function() {
      p = new NumberProperty( 3.4, { range: { min: 0, max: 5 }, valueType: 'Integer' } );
    }, 'should throw Assertion failed: initial value 3.4 must be of type: Integer' );

    p = new NumberProperty( 0, { range: { min: 0, max: 5 }, valueType: 'FloatingPoint' } );
    p.value = 3.4;
    assert.equal( p.value, 3.4 );

    p = new NumberProperty( 0, { validValues: [ 0, 1, 2, 3, 4, 5 ], valueType: 'Integer' } );
    window.assert && assert.throws( function() {
      p = new NumberProperty( 0, { validValues: [ 0, 1, 2, 3.4, 5 ], valueType: 'Integer' } );
    }, 'should throw Assertion failed: validValues must contain numbers of the right valueType' );

    p = new NumberProperty( 0, { range: { min: 0, max: 5 }, valueType: 'FloatingPoint' } );
    p.value = 3.4;
    assert.equal( p.value, 3.4 );

    // validValues
    p = new NumberProperty( 0, {
      validValues: [ 0, 1, 2 ]
    } );
    p.value = 1;
    p.value = 2;
    window.assert && assert.throws( function() {
      p.value = 3;
    }, 'should throw Assertion failed: invalid value: 3' );

    // isValidValue
    p = new NumberProperty( 0, {
      isValidValue: function( value ) { return value >= 0; }
    } );
    p.value = 1;
    p.value = 0;
    window.assert && assert.throws( function() {
      p.value = -1;
    }, 'should throw Assertion failed: invalid value: -1' );
    window.assert && assert.throws( function() {
      p.value = 'foo';
    }, 'should throw Assertion failed: invalid value: foo' );

    // mutually-exclusive options
    window.assert && assert.throws( function() {
      p = new NumberProperty( 0, {
        range: { min: 0, max: 10 },
        isValidValue: function( value ) { return value >= 0; },
        validValues: [ 0, 1, 2 ]
      }, 'should throw Assertion failed: validValues, isValidValue and range are mutually-exclusive options' );
    } );
  } );

} );<|endoftext|>