<commit_before>import {TextBuffer} from 'atom';

export default class MultiFilePatch {
  constructor(buffer = null, layers = {}, filePatches = []) {
    this.buffer = buffer;

    this.patchLayer = layers.patch;
    this.hunkLayer = layers.hunk;
    this.unchangedLayer = layers.unchanged;
    this.additionLayer = layers.addition;
    this.deletionLayer = layers.deletion;
    this.noNewlineLayer = layers.noNewline;

    this.filePatches = filePatches;

    this.filePatchesByMarker = new Map();
    this.hunksByMarker = new Map();

    for (const filePatch of this.filePatches) {
      this.filePatchesByMarker.set(filePatch.getMarker(), filePatch);
      for (const hunk of filePatch.getHunks()) {
        this.hunksByMarker.set(hunk.getMarker(), hunk);
      }
    }
  }

  getBuffer() {
    return this.buffer;
  }

  getPatchLayer() {
    return this.patchLayer;
  }

  getHunkLayer() {
    return this.hunkLayer;
  }

  getUnchangedLayer() {
    return this.unchangedLayer;
  }

  getAdditionLayer() {
    return this.additionLayer;
  }

  getDeletionLayer() {
    return this.deletionLayer;
  }

  getNoNewlineLayer() {
    return this.noNewlineLayer;
  }

  getFilePatches() {
    return this.filePatches;
  }

  getPathSet() {
    return this.getFilePatches().reduce((pathSet, filePatch) => {
      for (const file of [filePatch.getOldFile(), filePatch.getNewFile()]) {
        if (file.isPresent()) {
          pathSet.add(file.getPath());
        }
      }
      return pathSet;
    }, new Set());
  }

  getFilePatchAt(bufferRow) {
    const [marker] = this.patchLayer.findMarkers({intersectsRow: bufferRow});
    return this.filePatchesByMarker.get(marker);
  }

  getHunkAt(bufferRow) {
    const [marker] = this.hunkLayer.findMarkers({intersectsRow: bufferRow});
    return this.hunksByMarker.get(marker);
  }

  getStagePatchForLines(selectedLineSet) {
    const nextLayeredBuffer = this.buildLayeredBuffer();
    const nextFilePatches = this.getFilePatchesContaining(selectedLineSet).map(fp => {
      return fp.buildStagePatchForLines(this.getBuffer(), nextLayeredBuffer, selectedLineSet);
    });

    return new MultiFilePatch(
      nextLayeredBuffer.buffer,
      nextLayeredBuffer.layers,
      nextFilePatches,
    );
  }

  getStagePatchForHunk(hunk) {
    return this.getStagePatchForLines(new Set(hunk.getBufferRows()));
  }

  getUnstagePatchForLines(selectedLineSet) {
    const nextLayeredBuffer = this.buildLayeredBuffer();
    const nextFilePatches = this.getFilePatchesContaining(selectedLineSet).map(fp => {
      return fp.buildUnstagePatchForLines(this.getBuffer(), nextLayeredBuffer, selectedLineSet);
    });

    return new MultiFilePatch(
      nextLayeredBuffer.buffer,
      nextLayeredBuffer.layers,
      nextFilePatches,
    );
  }

  getUnstagePatchForHunk(hunk) {
    return this.getUnstagePatchForLines(new Set(hunk.getBufferRows()));
  }

  getNextSelectionRange(lastMultiFilePatch, lastSelectedRows) {
    if (lastSelectedRows.size === 0) {
      const [firstPatch] = this.getFilePatches();
      if (!firstPatch) {
        return [[0, 0], [0, 0]];
      }

      return firstPatch.getFirstChangeRange();
    }

    const lastMax = Math.max(...lastSelectedRows);

    let lastSelectionIndex = 0;
    for (const lastFilePatch of lastMultiFilePatch.getFilePatches()) {
      for (const hunk of lastFilePatch.getHunks()) {
        let includesMax = false;
        let hunkSelectionOffset = 0;

        changeLoop: for (const change of hunk.getChanges()) {
          for (const {intersection, gap} of change.intersectRows(lastSelectedRows, true)) {
          // Only include a partial range if this intersection includes the last selected buffer row.
            includesMax = intersection.intersectsRow(lastMax);
            const delta = includesMax ? lastMax - intersection.start.row + 1 : intersection.getRowCount();

            if (gap) {
            // Range of unselected changes.
              hunkSelectionOffset += delta;
            }

            if (includesMax) {
              break changeLoop;
            }
          }
        }

        lastSelectionIndex += hunkSelectionOffset;

        if (includesMax) {
          break;
        }
      }
    }

    let newSelectionRow = 0;
    patchLoop: for (const filePatch of this.getFilePatches()) {
      for (const hunk of filePatch.getHunks()) {
        for (const change of hunk.getChanges()) {
          if (lastSelectionIndex < change.bufferRowCount()) {
            newSelectionRow = change.getStartBufferRow() + lastSelectionIndex;
            break patchLoop;
          } else {
            lastSelectionIndex -= change.bufferRowCount();
          }
        }
      }
    }

    return [[newSelectionRow, 0], [newSelectionRow, Infinity]];
  }

  adoptBufferFrom(lastMultiFilePatch) {
    lastMultiFilePatch.getPatchLayer().clear();
    lastMultiFilePatch.getHunkLayer().clear();
    lastMultiFilePatch.getUnchangedLayer().clear();
    lastMultiFilePatch.getAdditionLayer().clear();
    lastMultiFilePatch.getDeletionLayer().clear();
    lastMultiFilePatch.getNoNewlineLayer().clear();

    this.filePatchesByMarker.clear();
    this.hunksByMarker.clear();

    const nextBuffer = lastMultiFilePatch.getBuffer();
    nextBuffer.setText(this.getBuffer().getText());

    for (const filePatch of this.getFilePatches()) {
      filePatch.getPatch().reMarkOn(lastMultiFilePatch.getPatchLayer());
      this.filePatchesByMarker.set(filePatch.getMarker(), filePatch);

      for (const hunk of filePatch.getHunks()) {
        hunk.reMarkOn(lastMultiFilePatch.getHunkLayer());
        this.hunksByMarker.set(hunk.getMarker(), hunk);

        for (const region of hunk.getRegions()) {
          const target = region.when({
            unchanged: () => lastMultiFilePatch.getUnchangedLayer(),
            addition: () => lastMultiFilePatch.getAdditionLayer(),
            deletion: () => lastMultiFilePatch.getDeletionLayer(),
            nonewline: () => lastMultiFilePatch.getNoNewlineLayer(),
          });
          region.reMarkOn(target);
        }
      }
    }

    this.patchLayer = lastMultiFilePatch.getPatchLayer();
    this.hunkLayer = lastMultiFilePatch.getHunkLayer();
    this.unchangedLayer = lastMultiFilePatch.getUnchangedLayer();
    this.additionLayer = lastMultiFilePatch.getAdditionLayer();
    this.deletionLayer = lastMultiFilePatch.getDeletionLayer();
    this.noNewlineLayer = lastMultiFilePatch.getNoNewlineLayer();

    this.buffer = nextBuffer;
  }

  buildLayeredBuffer() {
    const buffer = new TextBuffer();
    buffer.retain();

    return {
      buffer,
      layers: {
        patch: buffer.addMarkerLayer(),
        hunk: buffer.addMarkerLayer(),
        unchanged: buffer.addMarkerLayer(),
        addition: buffer.addMarkerLayer(),
        deletion: buffer.addMarkerLayer(),
        noNewline: buffer.addMarkerLayer(),
      },
    };
  }

  /*
   * Efficiently locate the FilePatch instances that contain at least one row from a Set.
   */
  getFilePatchesContaining(rowSet) {
    const sortedRowSet = Array.from(rowSet);
    sortedRowSet.sort((a, b) => a - b);

    const filePatches = [];
    let lastFilePatch = null;
    for (const row of sortedRowSet) {
      // Because the rows are sorted, consecutive rows will almost certainly belong to the same patch, so we can save
      // many avoidable marker index lookups by comparing with the last.
      if (lastFilePatch && lastFilePatch.containsRow(row)) {
        continue;
      }

      lastFilePatch = this.getFilePatchAt(row);
      filePatches.push(lastFilePatch);
    }

    return filePatches;
  }

  anyPresent() {
    return this.buffer !== null && this.filePatches.some(fp => fp.isPresent());
  }

  didAnyChangeExecutableMode() {
    for (const filePatch of this.getFilePatches()) {
      if (filePatch.didChangeExecutableMode()) {
        return true;
      }
    }
    return false;
  }

  anyHaveTypechange() {
    return this.getFilePatches().some(fp => fp.hasTypechange());
  }

  getMaxLineNumberWidth() {
    return this.getFilePatches().reduce((maxWidth, filePatch) => {
      const width = filePatch.getMaxLineNumberWidth();
      return maxWidth >= width ? maxWidth : width;
    }, 0);
  }

  /*
   * Construct an apply-able patch String.
   */
  toString() {
    return this.filePatches.map(fp => fp.toStringIn(this.buffer)).join('');
  }
}
<commit_msg>new clone method for MFP<commit_after>import {TextBuffer} from 'atom';

export default class MultiFilePatch {
  constructor(buffer = null, layers = {}, filePatches = []) {
    this.buffer = buffer;

    this.patchLayer = layers.patch;
    this.hunkLayer = layers.hunk;
    this.unchangedLayer = layers.unchanged;
    this.additionLayer = layers.addition;
    this.deletionLayer = layers.deletion;
    this.noNewlineLayer = layers.noNewline;

    this.filePatches = filePatches;

    this.filePatchesByMarker = new Map();
    this.hunksByMarker = new Map();

    for (const filePatch of this.filePatches) {
      this.filePatchesByMarker.set(filePatch.getMarker(), filePatch);
      for (const hunk of filePatch.getHunks()) {
        this.hunksByMarker.set(hunk.getMarker(), hunk);
      }
    }
  }

  clone(opts = {}) {
    return new this.constructor({
      buffer: opts.buffer !== undefined ? opts.buffer : this.getBuffer(),
      layers: opts.layers !== undefined ? opts.layers : {
        patch: this.getPatchLayer(),
        hunk: this.getHunkLayer(),
        unchanged: this.getUnchangedLayer(),
        addition: this.getAdditionLayer(),
        deletion: this.getDeletionLayer(),
        noNewline: this.getNoNewlineLayer(),
      },
      filePatches: opts.filePatches !== undefined ? opts.filePatches : this.getFilePatches(),
    });
  }

  getBuffer() {
    return this.buffer;
  }

  getPatchLayer() {
    return this.patchLayer;
  }

  getHunkLayer() {
    return this.hunkLayer;
  }

  getUnchangedLayer() {
    return this.unchangedLayer;
  }

  getAdditionLayer() {
    return this.additionLayer;
  }

  getDeletionLayer() {
    return this.deletionLayer;
  }

  getNoNewlineLayer() {
    return this.noNewlineLayer;
  }

  getFilePatches() {
    return this.filePatches;
  }

  getPathSet() {
    return this.getFilePatches().reduce((pathSet, filePatch) => {
      for (const file of [filePatch.getOldFile(), filePatch.getNewFile()]) {
        if (file.isPresent()) {
          pathSet.add(file.getPath());
        }
      }
      return pathSet;
    }, new Set());
  }

  getFilePatchAt(bufferRow) {
    const [marker] = this.patchLayer.findMarkers({intersectsRow: bufferRow});
    return this.filePatchesByMarker.get(marker);
  }

  getHunkAt(bufferRow) {
    const [marker] = this.hunkLayer.findMarkers({intersectsRow: bufferRow});
    return this.hunksByMarker.get(marker);
  }

  getStagePatchForLines(selectedLineSet) {
    const nextLayeredBuffer = this.buildLayeredBuffer();
    const nextFilePatches = this.getFilePatchesContaining(selectedLineSet).map(fp => {
      return fp.buildStagePatchForLines(this.getBuffer(), nextLayeredBuffer, selectedLineSet);
    });

    return new MultiFilePatch(
      nextLayeredBuffer.buffer,
      nextLayeredBuffer.layers,
      nextFilePatches,
    );
  }

  getStagePatchForHunk(hunk) {
    return this.getStagePatchForLines(new Set(hunk.getBufferRows()));
  }

  getUnstagePatchForLines(selectedLineSet) {
    const nextLayeredBuffer = this.buildLayeredBuffer();
    const nextFilePatches = this.getFilePatchesContaining(selectedLineSet).map(fp => {
      return fp.buildUnstagePatchForLines(this.getBuffer(), nextLayeredBuffer, selectedLineSet);
    });

    return new MultiFilePatch(
      nextLayeredBuffer.buffer,
      nextLayeredBuffer.layers,
      nextFilePatches,
    );
  }

  getUnstagePatchForHunk(hunk) {
    return this.getUnstagePatchForLines(new Set(hunk.getBufferRows()));
  }

  getNextSelectionRange(lastMultiFilePatch, lastSelectedRows) {
    if (lastSelectedRows.size === 0) {
      const [firstPatch] = this.getFilePatches();
      if (!firstPatch) {
        return [[0, 0], [0, 0]];
      }

      return firstPatch.getFirstChangeRange();
    }

    const lastMax = Math.max(...lastSelectedRows);

    let lastSelectionIndex = 0;
    for (const lastFilePatch of lastMultiFilePatch.getFilePatches()) {
      for (const hunk of lastFilePatch.getHunks()) {
        let includesMax = false;
        let hunkSelectionOffset = 0;

        changeLoop: for (const change of hunk.getChanges()) {
          for (const {intersection, gap} of change.intersectRows(lastSelectedRows, true)) {
          // Only include a partial range if this intersection includes the last selected buffer row.
            includesMax = intersection.intersectsRow(lastMax);
            const delta = includesMax ? lastMax - intersection.start.row + 1 : intersection.getRowCount();

            if (gap) {
            // Range of unselected changes.
              hunkSelectionOffset += delta;
            }

            if (includesMax) {
              break changeLoop;
            }
          }
        }

        lastSelectionIndex += hunkSelectionOffset;

        if (includesMax) {
          break;
        }
      }
    }

    let newSelectionRow = 0;
    patchLoop: for (const filePatch of this.getFilePatches()) {
      for (const hunk of filePatch.getHunks()) {
        for (const change of hunk.getChanges()) {
          if (lastSelectionIndex < change.bufferRowCount()) {
            newSelectionRow = change.getStartBufferRow() + lastSelectionIndex;
            break patchLoop;
          } else {
            lastSelectionIndex -= change.bufferRowCount();
          }
        }
      }
    }

    return [[newSelectionRow, 0], [newSelectionRow, Infinity]];
  }

  adoptBufferFrom(lastMultiFilePatch) {
    lastMultiFilePatch.getPatchLayer().clear();
    lastMultiFilePatch.getHunkLayer().clear();
    lastMultiFilePatch.getUnchangedLayer().clear();
    lastMultiFilePatch.getAdditionLayer().clear();
    lastMultiFilePatch.getDeletionLayer().clear();
    lastMultiFilePatch.getNoNewlineLayer().clear();

    this.filePatchesByMarker.clear();
    this.hunksByMarker.clear();

    const nextBuffer = lastMultiFilePatch.getBuffer();
    nextBuffer.setText(this.getBuffer().getText());

    for (const filePatch of this.getFilePatches()) {
      filePatch.getPatch().reMarkOn(lastMultiFilePatch.getPatchLayer());
      this.filePatchesByMarker.set(filePatch.getMarker(), filePatch);

      for (const hunk of filePatch.getHunks()) {
        hunk.reMarkOn(lastMultiFilePatch.getHunkLayer());
        this.hunksByMarker.set(hunk.getMarker(), hunk);

        for (const region of hunk.getRegions()) {
          const target = region.when({
            unchanged: () => lastMultiFilePatch.getUnchangedLayer(),
            addition: () => lastMultiFilePatch.getAdditionLayer(),
            deletion: () => lastMultiFilePatch.getDeletionLayer(),
            nonewline: () => lastMultiFilePatch.getNoNewlineLayer(),
          });
          region.reMarkOn(target);
        }
      }
    }

    this.patchLayer = lastMultiFilePatch.getPatchLayer();
    this.hunkLayer = lastMultiFilePatch.getHunkLayer();
    this.unchangedLayer = lastMultiFilePatch.getUnchangedLayer();
    this.additionLayer = lastMultiFilePatch.getAdditionLayer();
    this.deletionLayer = lastMultiFilePatch.getDeletionLayer();
    this.noNewlineLayer = lastMultiFilePatch.getNoNewlineLayer();

    this.buffer = nextBuffer;
  }

  buildLayeredBuffer() {
    const buffer = new TextBuffer();
    buffer.retain();

    return {
      buffer,
      layers: {
        patch: buffer.addMarkerLayer(),
        hunk: buffer.addMarkerLayer(),
        unchanged: buffer.addMarkerLayer(),
        addition: buffer.addMarkerLayer(),
        deletion: buffer.addMarkerLayer(),
        noNewline: buffer.addMarkerLayer(),
      },
    };
  }

  /*
   * Efficiently locate the FilePatch instances that contain at least one row from a Set.
   */
  getFilePatchesContaining(rowSet) {
    const sortedRowSet = Array.from(rowSet);
    sortedRowSet.sort((a, b) => a - b);

    const filePatches = [];
    let lastFilePatch = null;
    for (const row of sortedRowSet) {
      // Because the rows are sorted, consecutive rows will almost certainly belong to the same patch, so we can save
      // many avoidable marker index lookups by comparing with the last.
      if (lastFilePatch && lastFilePatch.containsRow(row)) {
        continue;
      }

      lastFilePatch = this.getFilePatchAt(row);
      filePatches.push(lastFilePatch);
    }

    return filePatches;
  }

  anyPresent() {
    return this.buffer !== null && this.filePatches.some(fp => fp.isPresent());
  }

  didAnyChangeExecutableMode() {
    for (const filePatch of this.getFilePatches()) {
      if (filePatch.didChangeExecutableMode()) {
        return true;
      }
    }
    return false;
  }

  anyHaveTypechange() {
    return this.getFilePatches().some(fp => fp.hasTypechange());
  }

  getMaxLineNumberWidth() {
    return this.getFilePatches().reduce((maxWidth, filePatch) => {
      const width = filePatch.getMaxLineNumberWidth();
      return maxWidth >= width ? maxWidth : width;
    }, 0);
  }

  /*
   * Construct an apply-able patch String.
   */
  toString() {
    return this.filePatches.map(fp => fp.toStringIn(this.buffer)).join('');
  }
}
<|endoftext|>


<commit_before>
var Proteus = require("proteus"),
    async   = require("async"),
    Task    = require("sake/task"),
    options = require("sake/options"),
    log     = require("sake/logger"),
    Runner,
    ActionRunner,
    TaskRunner
;

/**
 * Base class for Task and Actions runners
 */
Runner = Proteus.Class.derive({
    init: function (callback) {
        this.callback = callback;
    },

    run: function () {
        this.postRun.apply(this, arguments);
    },

    postRun: function () {
        this.complete.apply(this, arguments);
    },

    complete: function (err) {
        var nil = this.callback && this.callback(err);
        this.destroy();
    },

    destroy: function () {
        delete this.callback;
    }
});

/**
 * Responsible for running a tasks actions, one by one.
 */
ActionRunner = Runner.derive({

    init: function (task, callback) {
        this.task = task;
        ActionRunner.__super__.init.call(this, callback);
    },

    run: function () {
        var task        = this.task,
            actions     = task.actions,
            noActions   = !actions || !actions.length,
            callback    = ActionRunner.__super__.run.bind(this)
        ;

        if (noActions) {
            log.debug("No actions for " + task.fqn);
            callback();
            return;
        }

        async.series(actions.map(this.makeAsyncCallback, this), callback);
    },

    destroy: function () {
        delete this.task;
        ActionRunner.__super__.destroy.call(this);
    },

    makeActionCallback: function (idx, action, fn, callback, abort, t) {
        var isDryRun    = options.dryrun,
            dryRunSfx   = isDryRun ? " (dry-run)" : ""
        ;

        if (action !== fn.action) {
            return;
        }

        t.removeListener("actionDone", arguments.callee);
        t.removeListener("abort", arguments.callee);

        log.debug(
            t.fqn + " action " + idx +
            (abort ? " aborted." : " complete" + dryRunSfx + ".")
        );

        callback(abort);
    },

    makeAsyncCallback: function (action, idx) {
        var self        = this,
            task        = this.task,
            isDryRun    = options.dryrun,
            dryRunSfx   = isDryRun ? " (dry-run)" : "",
            actionCb    = this.makeActionCallback,
            asyncFn
        ;

        asyncFn = function (asyncCb) {
            var doneCb, abortCb, nil;

            doneCb  = actionCb.bind(self, idx, action, asyncFn, asyncCb, false);
            abortCb = actionCb.bind(self, idx, action, asyncFn, asyncCb);

            task.on("actionDone", doneCb);
            task.on("abort", abortCb);

            log.debug("Execute " + task.fqn + " action " + idx + dryRunSfx);

            nil = isDryRun ?
                asyncFn.action = doneCb && doneCb(task) :
                action.apply(task, [task].concat(task.arguments));
        };

        asyncFn.action = action;
        return asyncFn;
    },
});

/**
 * Responsible for executing a task's prerequisites one by one
 */
TaskRunner = Runner.derive({

    init: function (task, callback) {
        this.task = task;
        TaskRunner.__super__.init.call(this, callback);
    },

    run: function () {
        var task        = this.task,
            running     = task.isRunning,
            prereqs     = task.prerequisites,
            hasPreqs    = prereqs && prereqs.length,
            callback    = TaskRunner.__super__.run.bind(this)
        ;

        if (running) {
            log.warn(
                "Possible dependency recursion: " + task.fqn +
                " is already running."
            );
            return;
        }

        if (!hasPreqs) {
            log.debug("No prerequisites for " + task.fqn);
            return callback();
        }

        task.isRunning = true;
        async.series(prereqs.map(this.makeAsyncCallback, this), callback);
    },

    postRun: function (err) {
        var callback = TaskRunner.__super__.postRun.bind(this);
        (this.actionRunner = new ActionRunner(this.task, callback)).run();
    },

    makeAsyncCallback: function (taskname) {
        var self = this,
            task = Task.get(taskname, self.task.namespace)
        ;

        return function (asyncCb) {
            function taskCb () {
                task.removeListener("complete", taskCb);
                asyncCb();
            }

            task.on("complete", taskCb);
            task.invoke();
        };
    },

    destroy: function () {
        delete this.task.isRunning;
        delete this.actionRunner;
        TaskRunner.__super__.destroy.call(this);
    }

});

module.exports = {
    run: function (task, callback) {
        (new TaskRunner(task, callback)).run();
    }
};
<commit_msg>Use one callback listener for actionDone and abort.<commit_after>
var Proteus = require("proteus"),
    async   = require("async"),
    Task    = require("sake/task"),
    options = require("sake/options"),
    log     = require("sake/logger"),
    Runner,
    ActionRunner,
    TaskRunner
;

/**
 * Base class for Task and Actions runners
 */
Runner = Proteus.Class.derive({
    init: function (callback) {
        this.callback = callback;
    },

    run: function () {
        this.postRun.apply(this, arguments);
    },

    postRun: function () {
        this.complete.apply(this, arguments);
    },

    complete: function (err) {
        var nil = this.callback && this.callback(err);
        this.destroy();
    },

    destroy: function () {
        delete this.callback;
    }
});

/**
 * Responsible for running a tasks actions, one by one.
 */
ActionRunner = Runner.derive({

    init: function (task, callback) {
        this.task = task;
        ActionRunner.__super__.init.call(this, callback);
    },

    run: function () {
        var task        = this.task,
            actions     = task.actions,
            noActions   = !actions || !actions.length,
            callback    = ActionRunner.__super__.run.bind(this)
        ;

        if (noActions) {
            log.debug("No actions for " + task.fqn);
            callback();
            return;
        }

        async.series(actions.map(this.makeAsyncCallback, this), callback);
    },

    destroy: function () {
        delete this.task;
        ActionRunner.__super__.destroy.call(this);
    },

    makeActionCallback: function (idx, action, fn, callback, abort) {
        var task        = this.task,
            isDryRun    = options.dryrun,
            dryRunSfx   = isDryRun ? " (dry-run)" : ""
        ;

        if (task !== this.task || action !== fn.action) {
            log.debug(task.fqn + " action callback called out of context.");
            return;
        }

        task.removeListener("actionDone", arguments.callee);
        task.removeListener("abort", arguments.callee);

        log.debug(
            task.fqn + " action " + idx +
            (abort ? " aborted." : " complete" + dryRunSfx + ".")
        );

        callback(abort);
    },

    makeAsyncCallback: function (action, idx) {
        var self        = this,
            task        = this.task,
            isDryRun    = options.dryrun,
            dryRunSfx   = isDryRun ? " (dry-run)" : "",
            actionCb    = this.makeActionCallback,
            asyncFn
        ;

        asyncFn = function (asyncCb) {
            var cb, nil;

            cb = actionCb.bind(self, idx, action, asyncFn, asyncCb);

            task.on("actionDone", cb);
            task.on("abort", cb);

            log.debug("Execute " + task.fqn + " action " + idx + dryRunSfx);

            nil = isDryRun ?
                asyncFn.action = doneCb && doneCb(task) :
                action.apply(task, [task].concat(task.arguments));
        };

        asyncFn.action = action;
        return asyncFn;
    },
});

/**
 * Responsible for executing a task's prerequisites one by one
 */
TaskRunner = Runner.derive({

    init: function (task, callback) {
        this.task = task;
        TaskRunner.__super__.init.call(this, callback);
    },

    run: function () {
        var task        = this.task,
            running     = task.isRunning,
            prereqs     = task.prerequisites,
            hasPreqs    = prereqs && prereqs.length,
            callback    = TaskRunner.__super__.run.bind(this)
        ;

        if (running) {
            log.warn(
                "Possible dependency recursion: " + task.fqn +
                " is already running."
            );
            return;
        }

        if (!hasPreqs) {
            log.debug("No prerequisites for " + task.fqn);
            return callback();
        }

        task.isRunning = true;
        async.series(prereqs.map(this.makeAsyncCallback, this), callback);
    },

    postRun: function (err) {
        var callback = TaskRunner.__super__.postRun.bind(this);
        (this.actionRunner = new ActionRunner(this.task, callback)).run();
    },

    makeAsyncCallback: function (taskname) {
        var self = this,
            task = Task.get(taskname, self.task.namespace)
        ;

        return function (asyncCb) {
            function taskCb () {
                task.removeListener("complete", taskCb);
                asyncCb();
            }

            task.on("complete", taskCb);
            task.invoke();
        };
    },

    destroy: function () {
        delete this.task.isRunning;
        delete this.actionRunner;
        TaskRunner.__super__.destroy.call(this);
    }

});

module.exports = {
    run: function (task, callback) {
        (new TaskRunner(task, callback)).run();
    }
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var debug = require('debug')('passport-open-humans');
var util = require('util');

var OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
var InternalOAuthError = require('passport-oauth').InternalOAuthError;

/**
 * `Strategy` constructor.
 *
 * The Open Humans authentication strategy authenticates requests by delegating
 * to Open Humans using the OAuth 2.0 protocol.
 *
 * Applications must supply a `verify` callback which accepts an `accessToken`,
 * `refreshToken` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `clientID`      your Open Humans application's App ID
 *   - `clientSecret`  your Open Humans application's App Secret
 *   - `callbackURL`   URL to which Open Humans will redirect the user after
 *                     granting authorization
 *
 * Examples:
 *
 *     passport.use(new OpenHumansStrategy({
 *         clientID: '123-456-789',
 *         clientSecret: 'shhh-its-a-secret'
 *         callbackURL: 'https://www.example.net/auth/open-humans/callback'
 *       },
 *       function(accessToken, refreshToken, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function Strategy(options, verify) {
  options = options || {};

  options.hostURL = options.hostURL || 'https://www.openhumans.org';

  options.authorizationURL = options.authorizationURL || options.hostURL +
    '/oauth2/authorize/';
  options.tokenURL = options.tokenURL || options.hostURL +
    '/oauth2/token/';

  options.scopeSeparator = options.scopeSeparator || ' ';

  OAuth2Strategy.call(this, options, verify);

  this.name = 'open-humans';
  this.hostURL = options.hostURL;
}

/**
 * Inherit from `OAuth2Strategy`.
 */
util.inherits(Strategy, OAuth2Strategy);

function getParams(options) {
  var params = {};

  if (options.origin) {
    if (options.origin === 'open-humans') {
      params.origin = 'open-humans';
    } else {
      params.origin = 'external';
    }
  }

  return params;
}

/**
 * Support the Open Humans origin parameter.
 */
Strategy.prototype.authorizationParams = getParams;
Strategy.prototype.tokenParams = getParams;

/**
 * Retrieve user profile from Open Humans.
 *
 * This function constructs a normalized profile, with the following properties:
 *
 *   - `provider`         always set to `open-humans`
 *   - `id`               the user's Open Humans ID
 *   - `username`         the user's Open Humans username (same as their ID)
 *   - `displayName`      the user's full name
 *   - `profileUrl`       the URL of the profile for the user on Open Humans
 *   - `emails`           the proxied or contact email address
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api protected
 */
Strategy.prototype.userProfile = function (accessToken, done) {
  this._oauth2.getProtectedResource(this.hostURL + '/api/member/',
    accessToken, function (err, body) {
      debug('userProfile() with accessToken "%s"', accessToken);

      if (err) {
        debug('failed to fetch profile: "%j"', err);

        return done(new InternalOAuthError('failed to fetch user profile', err));
      }

      var profile = {};

      try {
        profile = JSON.parse(body);
      } catch (e) {
        debug('failed to parse body: "%s"', body);

        // pass
      }

      profile.provider = 'open-humans';

      done(null, profile);
    });
};

/**
 * Expose `Strategy`.
 */
module.exports = Strategy;
<commit_msg>more debug logging<commit_after>'use strict';

/**
 * Module dependencies.
 */
var debug = require('debug')('passport-open-humans');
var util = require('util');

var OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
var InternalOAuthError = require('passport-oauth').InternalOAuthError;

/**
 * `Strategy` constructor.
 *
 * The Open Humans authentication strategy authenticates requests by delegating
 * to Open Humans using the OAuth 2.0 protocol.
 *
 * Applications must supply a `verify` callback which accepts an `accessToken`,
 * `refreshToken` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `clientID`      your Open Humans application's App ID
 *   - `clientSecret`  your Open Humans application's App Secret
 *   - `callbackURL`   URL to which Open Humans will redirect the user after
 *                     granting authorization
 *
 * Examples:
 *
 *     passport.use(new OpenHumansStrategy({
 *         clientID: '123-456-789',
 *         clientSecret: 'shhh-its-a-secret'
 *         callbackURL: 'https://www.example.net/auth/open-humans/callback'
 *       },
 *       function(accessToken, refreshToken, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function Strategy(options, verify) {
  options = options || {};

  options.hostURL = options.hostURL || 'https://www.openhumans.org';

  options.authorizationURL = options.authorizationURL || options.hostURL +
    '/oauth2/authorize/';
  options.tokenURL = options.tokenURL || options.hostURL +
    '/oauth2/token/';

  options.scopeSeparator = options.scopeSeparator || ' ';

  OAuth2Strategy.call(this, options, verify);

  this.name = 'open-humans';
  this.hostURL = options.hostURL;
}

/**
 * Inherit from `OAuth2Strategy`.
 */
util.inherits(Strategy, OAuth2Strategy);

function getParams(options) {
  debug('getParams() with options "%j"', options);

  var params = {};

  if (options.origin) {
    if (options.origin === 'open-humans') {
      params.origin = 'open-humans';
    } else {
      params.origin = 'external';
    }
  }

  return params;
}

/**
 * Support the Open Humans origin parameter.
 */
Strategy.prototype.authorizationParams = getParams;
Strategy.prototype.tokenParams = getParams;

/**
 * Retrieve user profile from Open Humans.
 *
 * This function constructs a normalized profile, with the following properties:
 *
 *   - `provider`         always set to `open-humans`
 *   - `id`               the user's Open Humans ID
 *   - `username`         the user's Open Humans username (same as their ID)
 *   - `displayName`      the user's full name
 *   - `profileUrl`       the URL of the profile for the user on Open Humans
 *   - `emails`           the proxied or contact email address
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api protected
 */
Strategy.prototype.userProfile = function (accessToken, done) {
  this._oauth2.getProtectedResource(this.hostURL + '/api/member/',
    accessToken, function (err, body) {
      debug('userProfile() with accessToken "%s"', accessToken);

      if (err) {
        debug('failed to fetch profile: "%j"', err);

        return done(new InternalOAuthError('failed to fetch user profile', err));
      }

      var profile = {};

      try {
        profile = JSON.parse(body);
      } catch (e) {
        debug('failed to parse body: "%s"', body);

        // pass
      }

      profile.provider = 'open-humans';

      done(null, profile);
    });
};

/**
 * Expose `Strategy`.
 */
module.exports = Strategy;
<|endoftext|>


<commit_before>var Plugin = require('./Plugin');
var path = require('path');
var HOOKS = require('../hooks');
var find = require('../utils/find');
var format = require('util').format;
var Promise = require('bluebird');
var getAdditionalAssets = require('../utils/getAdditionalAssets');

var interpolateName = require('../utils/interpolateName');
var Page = require('../data/Page');
var ExampleFileCompiler = require('../modules/ExampleFileCompiler');
var ExampleFile = require('../data/ExampleFile');
var Asset = require('../data/Asset');

// TODO: add [example-index] or [file-index] placeholders
var defaultOptions = {
  outputFilename: 'docs/[path][name].[ext]/examples/[hash].js',
  filenamePrefix: '',
  filenameSuffix: ''
};

var ExampleCompilerPlugin = Plugin.create(defaultOptions);

ExampleCompilerPlugin.prototype.apply = function (compiler) {
  var plugin = this;

  compiler.plugin('compilation', function (compilation) {

    // Skip child compilations
    if (compilation.compiler.hasOwnProperty('parentCompilation'))
      return;

    compilation.plugin(HOOKS.POSTPROCESS_EXTRACTED_RESULTS, function (context, done) {
      var results = context.results;
      var promises = [];
      var files = [];

      var compilerOptions = {
        name: 'Example files compiler'
      };

      var compiler = new ExampleFileCompiler(
        compilation,
        compilerOptions,
        plugin.options.filenamePrefix,
        plugin.options.filenameSuffix
      );

      results.filter(function (result) {
        return result instanceof Page;
      })
      .forEach(function (page, i) {
        var source = page.source;

        find(page, function (item) {
          return item instanceof ExampleFile;
        })
        .forEach(function(file, j) {
          file.path = interpolateName(plugin.options.outputFilename, {
            path: source.absolutePath,
            context: compilation.compiler.context,
            content: file.source
          });
          files.push(file);

          compiler.addFile(file, source.absolutePath, plugin.options.outputFilename);
        });
      });

      compiler.run()
        .then(function(compilation) {
          var getAssets = getAdditionalAssets;
          var assetsByName = Object.keys(compilation.assets);
          var additionalAssets = getAdditionalAssets(compilation);
          var additionalAssetsNames = Object.keys(additionalAssets);

          files.forEach(function(file) {
            var fileAssets = file.assets;

            assetsByName.filter(function(assetName) {
              return file.path === assetName;
            })
            .forEach(function(assetPath) {
              var asset = new Asset({
                type: path.extname(assetPath).substr(1),
                source: compilation.assets[assetPath].source(),
                path: assetPath
              });

              fileAssets.push(asset);

              Object.keys(additionalAssets).forEach(function(chunkName) {
                if (assetPath.indexOf(chunkName) == 0) {
                  additionalAssets[chunkName].forEach(function(assetPath) {
                    var asset = new Asset({
                      type: path.extname(assetPath).substr(1),
                      source: compilation.assets[assetPath].source(),
                      path: assetPath
                    });

                    fileAssets.push(asset);
                  });
                }
              })
            });
          });

        })
        .finally(function() {
          done();
        });

    });
  });
};

module.exports = ExampleCompilerPlugin;<commit_msg>Add getAdditionalAssets util<commit_after>var Plugin = require('./Plugin');
var path = require('path');
var HOOKS = require('../hooks');
var find = require('../utils/find');
var format = require('util').format;
var Promise = require('bluebird');
var getAdditionalAssets = require('../utils/getAdditionalAssets');

var interpolateName = require('../utils/interpolateName');
var Page = require('../data/Page');
var ExampleFileCompiler = require('../modules/ExampleFileCompiler');
var ExampleFile = require('../data/ExampleFile');
var Asset = require('../data/Asset');

// TODO: add [example-index] or [file-index] placeholders
var defaultOptions = {
  outputFilename: 'docs/[path][name].[ext]/examples/[hash].js',
  filenamePrefix: '',
  filenameSuffix: ''
};

var ExampleCompilerPlugin = Plugin.create(defaultOptions);

ExampleCompilerPlugin.prototype.apply = function (compiler) {
  var plugin = this;

  compiler.plugin('compilation', function (compilation) {

    // Skip child compilations
    if (compilation.compiler.hasOwnProperty('parentCompilation'))
      return;

    compilation.plugin(HOOKS.POSTPROCESS_EXTRACTED_RESULTS, function (context, done) {
      var results = context.results;
      var promises = [];
      var files = [];

      var compilerOptions = {
        name: 'Example files compiler'
      };

      var compiler = new ExampleFileCompiler(
        compilation,
        compilerOptions,
        plugin.options.filenamePrefix,
        plugin.options.filenameSuffix
      );

      results.filter(function (result) {
        return result instanceof Page;
      })
      .forEach(function (page, i) {
        var source = page.source;

        find(page, function (item) {
          return item instanceof ExampleFile;
        })
        .forEach(function(file, j) {
          file.path = interpolateName(plugin.options.outputFilename, {
            path: source.absolutePath,
            context: compilation.compiler.context,
            content: file.source
          });
          files.push(file);

          compiler.addFile(file, source.absolutePath, plugin.options.outputFilename);
        });
      });

      compiler.run()
        .then(function(compilation) {
          var assetsByChunkName = compilation.getStats().toJson().assetsByChunkName;
          var additionalAssets = getAdditionalAssets(compilation);

          files.forEach(function(file) {
            var chunkName = file.path.substr(0, file.path.lastIndexOf('.'));
            var assets = typeof assetsByChunkName[chunkName] == 'string' ? [assetsByChunkName[chunkName]] : assetsByChunkName[chunkName];

            // Additional assets
            if (chunkName in additionalAssets) {
              additionalAssets[chunkName].forEach(function(assetPath) {
                assets.push(assetPath);
              });
            }

            assets.forEach(function(assetPath) {
              var asset = new Asset({
                type: path.extname(assetPath).substr(1),
                source: compilation.assets[assetPath].source(),
                path: assetPath
              });

              file.assets.push(asset);
            });
          });

          debugger;
        })
        .finally(function() {
          done();
        });

    });
  });
};

module.exports = ExampleCompilerPlugin;<|endoftext|>


<commit_before>/** @scratch /panels/5
 *
 * include::panels/pcap.asciidoc[]
 */

/** @scratch /panels/pcap/0
 * == pcap
 * Status: *Stable*
 *
 * The pcap panel is used for displaying static pcap formated as markdown, sanitized html or as plain
 * pcap.
 *
 */

define([
  'angular',
  'app',
  'lodash',
  'require',
  'jquery',
  'kbn'
],
function (angular, app, _, require, kbn) {
  'use strict';

  var module = angular.module('kibana.panels.pcap', []);
  app.useModule(module);

  module.controller('pcap', function($filter, $scope, $http) {
    $scope.user = window.user;

$scope.panelMeta = {
      modals : [
        {
          description: "Inspect",
          icon: "icon-info-sign",
          partial: "app/partials/inspector.html",
          show: $scope.panel.spyable
        }
      ],
      status  : 'Stable',
      description : 'OpenSoc PCAP viewer'
    };

    $scope.durations = [
      {desc: '1d', sec: 24 * 60 * 60},
      {desc: '6h', sec: 6 * 60 * 60},
      {desc: '1h', sec: 60 * 60},
      {desc: '30m', sec: 30 * 60},
      {desc: '15m', sec: 15 * 60},
      {desc: '5m', sec: 5 * 60},
      {desc: '1m', sec: 60},
      {desc: '30s', sec: 10},
      {desc: '10s', sec: 10},
      {desc: '5s', sec: 5},
      {desc: '1s', sec: 1}
    ];

    $scope.streamPage = 0;
    $scope.duration = $scope.durations[$scope.durations.length - 1];

    function serialize(obj) {
      var str = [];
      for(var p in obj)
        if (obj.hasOwnProperty(p)) {
          str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
        }
      return str.join("&");
    }

    function makeTimeFilter(filter) {
      if($scope.ts) {
        var ts = Date.parse($scope.ts);
        ts = ts > 0 ? ts : parseInt($scope.ts);
        if (ts <= 999999999999) {
          ts *= 1000;
        }

        filter = (filter || ejs.BoolFilter())
          .must(ejs.RangeFilter('ts_micro')
            .gte((ts - $scope.duration.sec * 1000) * 1000)
            .lte((ts + $scope.duration.sec * 1000) * 1000))
      }
      return filter;
    }

    function makeFilter() {
      var fields = [
        'ip_src_addr', 'ip_dst_addr',
        'ip_src_port', 'ip_dst_port',
        'ip_protocol'
      ];

      var filter;
      $.each(fields, function(i, f) {
        if ($scope[f]) {
          filter = (filter || ejs.BoolFilter()).must(ejs.TermFilter(f, $scope[f]))
        }
      });

      return makeTimeFilter(filter);
    }

    function makePcapQuery(keys) {
      var params = {
        keys: keys,
        includeReverseTraffic: $scope.includeReverseTraffic || false,
      };

      if ($scope.ts) {
        var ts = Date.parse($scope.ts);
        ts = ts > 0 ? ts : parseInt($scope.ts);
        if (ts <= 9999999999999) {
          ts *= 1000;
        }

        $.extend(params, {
          startTime: ts - $scope.duration.sec * 1000,
          endTime: ts + $scope.duration.sec + 1000
        });
      }
      return params;
    }

    function reversePcapId(pcapId) {
      var identifiers = pcapId.split('-');
      return [
        identifiers[1], identifiers[0], identifiers[2],
        identifiers[4], identifiers[3]
      ].join('-');
    }

    $scope.setStream = function(idx, pcapId, page) {
      $scope.stream = null;
      $scope.selectedStream = idx;
      $scope.selectedPacket = null;
      $scope.packetData = [];
      $scope.packet = null;
      $scope.streamPage = page;

      var filter = makeTimeFilter() || ejs.BoolFilter();
      filter.must(ejs.OrFilter([
        ejs.TermFilter('pcap_id', pcapId),
        ejs.TermFilter('pcap_id', reversePcapId(pcapId))
      ]));

      var size = 500;
      $scope.streamFrom = (page || 0) * size;
      var request = ejs.Request()
        .indices('pcap_all')
        .types('pcap_doc')
        .filter(filter)
        .sort('ts_micro')
        .from($scope.streamFrom)
        .size(size);

      $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
      request.doSearch(function(stream) {
        $scope.stream = stream;
      });
    };

    $scope.downloadStream = function(doc) {
      var response = true;
      if (doc.count > 500) {
        var message = [
          'Are you sure you want to download', doc.count, 'packets?'
        ].join(' ');
        response = window.confirm(message);
      }

      if (response) {
        var params = makePcapQuery(doc.term);
        params.raw = true;
        window.location = '/pcap/getPcapsByKeys?' + serialize(params);
      }
    };

    $scope.setPacket = function(idx, message) {
      $scope.selectedPacket = idx;
      $scope.packetData = [];
      $scope.packet = null;
      var params = makePcapQuery([
        message.pcap_id, message.ip_id, message.frag_offset
      ].join('-'));

      $scope.evtSource && $scope.evtSource.close();
      $scope.evtSource = new EventSource('/pcap/getPcapsByKeys?' + serialize(params));

      $scope.evtSource.onmessage = function(e) {
        $scope.packetData.push(JSON.parse(e.data));
        $scope.$apply();
      };

      $scope.evtSource.onerror = function () {
        $scope.evtSource.close();
        $scope.evtSource = null;
      };
    };

    $scope.setPcapPacket = function(p) {
      $scope.packet = p;
    };

    $scope.parsePcapId = function(pcapId) {
      var identifiers = pcapId.split('-');
      return {
        src_ip: $filter('hexToIp')(identifiers[0]),
        dst_ip: $filter('hexToIp')(identifiers[1]),
        protocol: parseInt(identifiers[2], 10),
        src_port: parseInt(identifiers[3], 10),
        dst_port: parseInt(identifiers[4], 10)
      };
    };

    $scope.search = function() {
      $scope.selectedStream = null;
      $scope.selectedPacket = null;
      $scope.packetData = null;
      $scope.packet = null;
      $scope.results = null;
      $scope.stream = null;
      $scope.streamPage = 0;
      $scope.streamFrom = 0;

      var filter = makeFilter();
      var facet = ejs.TermsFacet('packets').field('pcap_id');

      if (filter) {
        facet = facet.facetFilter(filter)
      }

      var size = 100;
      var request = ejs.Request()
        .indices('pcap_all')
        .types('pcap_doc')
        .facet(facet.size(size))

      $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
      request.doSearch(function(results) {
        $scope.results = results;
      });
    };
  });

  // From: http://stackoverflow.com/questions/14430655/recursion-in-angular-directives
  module.factory('RecursionHelper', ['$compile', function($compile) {
    /* The MIT License (MIT)

    Copyright (c) 2014 Mark Lagendijk

    Permission is hereby granted, free of charge, to any person obtaining a copy of
    this software and associated documentation files (the "Software"), to deal in
    the Software without restriction, including without limitation the rights to
    use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    the Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    This license only applies to this angular factory.
    */

    return {
      /**
       * Manually compiles the element, fixing the recursion loop.
       * @param element
       * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
       * @returns An object containing the linking functions.
       */
      compile: function(element, link) {
        // Normalize the link parameter
        if(angular.isFunction(link)){
          link = { post: link };
        }

        // Break the recursion loop by removing the contents
        var contents = element.contents().remove();
        var compiledContents;
        return {
          pre: (link && link.pre) ? link.pre : null,
          /**
           * Compiles and re-adds the contents
           */
          post: function(scope, element){
            // Compile the contents
            if(!compiledContents){
              compiledContents = $compile(contents);
            }
            // Re-add the compiled contents to the element
            compiledContents(scope, function(clone) {
              element.append(clone);
            });

            // Call the post-linking function, if any
            if(link && link.post){
              link.post.apply(null, arguments);
            }
          }
        };
      }
    };
  }]);

  module.directive('packet', function() {
    return {
      templateUrl: 'packet.html',
      scope: {
        packet: '='
      },
      controller: function($scope) {
        return {
          toggle: function(proto, value) {
            proto.expanded = value;
            if (proto.parent) {
              this.toggle(proto.parent.$, value);
            }
          },
          select: function(proto) {
            if ($scope.selected == proto.$) {
              $scope.selected.expanded = !$scope.selected.expanded;
              return;
            }

            $scope.selected && this.toggle($scope.selected, false);
            this.toggle(proto.$, true);
            $scope.selected = proto.$;
          },
          uid: function(obj) {
            return [
              String(obj.showname),
              (Math.random() + 1).toString(36).substring(7)
            ].join('_');
          },
          buildHighlightMap: function(proto) {
            var self = this;
            $.each(proto.field || [], function(i, field) {
              self.processField(proto, field);
            });
          },
          processField: function(proto, field) {
            var start = parseInt(field.$.pos);
            var end = start + parseInt(field.$.size) - 1;
            field.$.uid = this.uid(field.$);
            field.$.parent = proto;

            if (proto.$.name == 'geninfo') {
              field.$.showname += ': ' + field.$.value;
              return;
            }

            if (end >= start) {
              this.highlightMap.push({
                $: field.$,
                uid: field.$.uid,
                start: start,
                end: end
              });

              field.field && this.buildHighlightMap(field);
            }
          }
        };
      },
      link: function(scope, elm, attr, controller) {
        scope.$watch('packet', function(value, oldValue) {
          console.log(value);
          controller.highlightMap = [];
          scope.selected = {};
          if (value) {
            $.each(value.proto, function(i, proto) {
              proto.$.uid = controller.uid(proto.$);
              controller.buildHighlightMap(proto);
            });
          }
        });
      }
    };
  });

  module.directive('proto', function(RecursionHelper) {
    return {
      templateUrl: 'proto.html',
      scope: {
        proto: '=',
        selected: '=',
        padding: '='
      },
      require: '^packet',
      compile: function(element) {
        return RecursionHelper.compile(element, function(scope, elm, attr, packet) {

          scope.select = $.proxy(packet.select, packet);
          scope.$watch('padding', function(value) {
            scope.nextPadding = parseInt(value || '0') + 30;
          })

        });
      }
    };
  });

  module.directive('hexBytes', function() {
    return {
      templateUrl: 'hexBytes.html',
      require: '^packet',
      scope: {
        selected: '=',
        dump: '='
      },
      link: function(scope, elm, attr, packet) {
        scope.$watch('dump', function(value, oldValue) {
          scope.bytes = [];
          for (var b = 0; b < scope.dump.length; b += 2) {
            scope.bytes.push(scope.dump.slice(b, b + 2));
          }
        });

        scope.selectByte = function(offset) {
          var selected = $.grep(packet.highlightMap, function(obj) {
            return obj.start <= offset && obj.end >= offset;
          })[0];

          selected && packet.select(selected);
        };
      }
    };
  });

  module.filter('sourcePort', function() {
    return function(sourcePort) {
      if (sourcePort) {
        var parts = sourcePort.split(': ');
        return parts[parts.length - 1];
      }
    }
  });

  module.filter('hexToIp', function() {
    return function(hex) {
      var parts = [];
      for (var p = 0; p < 4; p++) {
        var hexPart = hex.slice(p * 2, p * 2 + 2)
        parts.push(parseInt(hexPart, 16));
      }
      return parts.join('.');
    }
  });

  module.filter('num', function() {
    return function(input) {
      return parseInt(input, 10);
    }
  });

  module.filter('toAscii', function() {
    return function (text) {
      var code = parseInt(text, 16);
      var c = code <= 32 || code > 127 ? '.' : String.fromCharCode(code);
      return c + ' ';
    };
  });
});
<commit_msg>Only fetch reverse traffic if box is checked.<commit_after>/** @scratch /panels/5
 *
 * include::panels/pcap.asciidoc[]
 */

/** @scratch /panels/pcap/0
 * == pcap
 * Status: *Stable*
 *
 * The pcap panel is used for displaying static pcap formated as markdown, sanitized html or as plain
 * pcap.
 *
 */

define([
  'angular',
  'app',
  'lodash',
  'require',
  'jquery',
  'kbn'
],
function (angular, app, _, require, kbn) {
  'use strict';

  var module = angular.module('kibana.panels.pcap', []);
  app.useModule(module);

  module.controller('pcap', function($filter, $scope, $http) {
    $scope.user = window.user;

$scope.panelMeta = {
      modals : [
        {
          description: "Inspect",
          icon: "icon-info-sign",
          partial: "app/partials/inspector.html",
          show: $scope.panel.spyable
        }
      ],
      status  : 'Stable',
      description : 'OpenSoc PCAP viewer'
    };

    $scope.durations = [
      {desc: '1d', sec: 24 * 60 * 60},
      {desc: '6h', sec: 6 * 60 * 60},
      {desc: '1h', sec: 60 * 60},
      {desc: '30m', sec: 30 * 60},
      {desc: '15m', sec: 15 * 60},
      {desc: '5m', sec: 5 * 60},
      {desc: '1m', sec: 60},
      {desc: '30s', sec: 10},
      {desc: '10s', sec: 10},
      {desc: '5s', sec: 5},
      {desc: '1s', sec: 1}
    ];

    $scope.streamPage = 0;
    $scope.duration = $scope.durations[$scope.durations.length - 1];

    function serialize(obj) {
      var str = [];
      for(var p in obj)
        if (obj.hasOwnProperty(p)) {
          str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
        }
      return str.join("&");
    }

    function makeTimeFilter(filter) {
      if($scope.ts) {
        var ts = Date.parse($scope.ts);
        ts = ts > 0 ? ts : parseInt($scope.ts);
        if (ts <= 999999999999) {
          ts *= 1000;
        }

        filter = (filter || ejs.BoolFilter())
          .must(ejs.RangeFilter('ts_micro')
            .gte((ts - $scope.duration.sec * 1000) * 1000)
            .lte((ts + $scope.duration.sec * 1000) * 1000))
      }
      return filter;
    }

    function makeFilter() {
      var fields = [
        'ip_src_addr', 'ip_dst_addr',
        'ip_src_port', 'ip_dst_port',
        'ip_protocol'
      ];

      var filter;
      $.each(fields, function(i, f) {
        if ($scope[f]) {
          filter = (filter || ejs.BoolFilter()).must(ejs.TermFilter(f, $scope[f]))
        }
      });

      return makeTimeFilter(filter);
    }

    function makePcapQuery(keys) {
      var params = {
        keys: keys,
        includeReverseTraffic: $scope.includeReverseTraffic || false,
      };

      if ($scope.ts) {
        var ts = Date.parse($scope.ts);
        ts = ts > 0 ? ts : parseInt($scope.ts);
        if (ts <= 9999999999999) {
          ts *= 1000;
        }

        $.extend(params, {
          startTime: ts - $scope.duration.sec * 1000,
          endTime: ts + $scope.duration.sec + 1000
        });
      }
      return params;
    }

    function reversePcapId(pcapId) {
      var identifiers = pcapId.split('-');
      return [
        identifiers[1], identifiers[0], identifiers[2],
        identifiers[4], identifiers[3]
      ].join('-');
    }

    $scope.setStream = function(idx, pcapId, page) {
      $scope.stream = null;
      $scope.selectedStream = idx;
      $scope.selectedPacket = null;
      $scope.packetData = [];
      $scope.packet = null;
      $scope.streamPage = page;

      var filter = makeTimeFilter() || ejs.BoolFilter();
      var termFilters = [ejs.TermFilter('pcap_id', pcapId)];
      if ($scope.includeReverseTraffic) {
        termFilters.push(
          ejs.TermFilter('pcap_id', reversePcapId(pcapId)));
      }

      filter = filter.must(ejs.OrFilter(termFilters))

      var size = 500;
      $scope.streamFrom = (page || 0) * size;
      var request = ejs.Request()
        .indices('pcap_all')
        .types('pcap_doc')
        .filter(filter)
        .sort('ts_micro')
        .from($scope.streamFrom)
        .size(size);

      $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
      request.doSearch(function(stream) {
        $scope.stream = stream;
      });
    };

    $scope.downloadStream = function(doc) {
      var response = true;
      if (doc.count > 500) {
        var message = [
          'Are you sure you want to download', doc.count, 'packets?'
        ].join(' ');
        response = window.confirm(message);
      }

      if (response) {
        var params = makePcapQuery(doc.term);
        params.raw = true;
        window.location = '/pcap/getPcapsByKeys?' + serialize(params);
      }
    };

    $scope.setPacket = function(idx, message) {
      $scope.selectedPacket = idx;
      $scope.packetData = [];
      $scope.packet = null;
      var params = makePcapQuery([
        message.pcap_id, message.ip_id, message.frag_offset
      ].join('-'));

      $scope.evtSource && $scope.evtSource.close();
      $scope.evtSource = new EventSource('/pcap/getPcapsByKeys?' + serialize(params));

      $scope.evtSource.onmessage = function(e) {
        $scope.packetData.push(JSON.parse(e.data));
        $scope.$apply();
      };

      $scope.evtSource.onerror = function () {
        $scope.evtSource.close();
        $scope.evtSource = null;
      };
    };

    $scope.setPcapPacket = function(p) {
      $scope.packet = p;
    };

    $scope.parsePcapId = function(pcapId) {
      var identifiers = pcapId.split('-');
      return {
        src_ip: $filter('hexToIp')(identifiers[0]),
        dst_ip: $filter('hexToIp')(identifiers[1]),
        protocol: parseInt(identifiers[2], 10),
        src_port: parseInt(identifiers[3], 10),
        dst_port: parseInt(identifiers[4], 10)
      };
    };

    $scope.search = function() {
      $scope.selectedStream = null;
      $scope.selectedPacket = null;
      $scope.packetData = null;
      $scope.packet = null;
      $scope.results = null;
      $scope.stream = null;
      $scope.streamPage = 0;
      $scope.streamFrom = 0;

      var filter = makeFilter();
      var facet = ejs.TermsFacet('packets').field('pcap_id');

      if (filter) {
        facet = facet.facetFilter(filter)
      }

      var size = 100;
      var request = ejs.Request()
        .indices('pcap_all')
        .types('pcap_doc')
        .facet(facet.size(size))

      $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
      request.doSearch(function(results) {
        $scope.results = results;
      });
    };
  });

  // From: http://stackoverflow.com/questions/14430655/recursion-in-angular-directives
  module.factory('RecursionHelper', ['$compile', function($compile) {
    /* The MIT License (MIT)

    Copyright (c) 2014 Mark Lagendijk

    Permission is hereby granted, free of charge, to any person obtaining a copy of
    this software and associated documentation files (the "Software"), to deal in
    the Software without restriction, including without limitation the rights to
    use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    the Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    This license only applies to this angular factory.
    */

    return {
      /**
       * Manually compiles the element, fixing the recursion loop.
       * @param element
       * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
       * @returns An object containing the linking functions.
       */
      compile: function(element, link) {
        // Normalize the link parameter
        if(angular.isFunction(link)){
          link = { post: link };
        }

        // Break the recursion loop by removing the contents
        var contents = element.contents().remove();
        var compiledContents;
        return {
          pre: (link && link.pre) ? link.pre : null,
          /**
           * Compiles and re-adds the contents
           */
          post: function(scope, element){
            // Compile the contents
            if(!compiledContents){
              compiledContents = $compile(contents);
            }
            // Re-add the compiled contents to the element
            compiledContents(scope, function(clone) {
              element.append(clone);
            });

            // Call the post-linking function, if any
            if(link && link.post){
              link.post.apply(null, arguments);
            }
          }
        };
      }
    };
  }]);

  module.directive('packet', function() {
    return {
      templateUrl: 'packet.html',
      scope: {
        packet: '='
      },
      controller: function($scope) {
        return {
          toggle: function(proto, value) {
            proto.expanded = value;
            if (proto.parent) {
              this.toggle(proto.parent.$, value);
            }
          },
          select: function(proto) {
            if ($scope.selected == proto.$) {
              $scope.selected.expanded = !$scope.selected.expanded;
              return;
            }

            $scope.selected && this.toggle($scope.selected, false);
            this.toggle(proto.$, true);
            $scope.selected = proto.$;
          },
          uid: function(obj) {
            return [
              String(obj.showname),
              (Math.random() + 1).toString(36).substring(7)
            ].join('_');
          },
          buildHighlightMap: function(proto) {
            var self = this;
            $.each(proto.field || [], function(i, field) {
              self.processField(proto, field);
            });
          },
          processField: function(proto, field) {
            var start = parseInt(field.$.pos);
            var end = start + parseInt(field.$.size) - 1;
            field.$.uid = this.uid(field.$);
            field.$.parent = proto;

            if (proto.$.name == 'geninfo') {
              field.$.showname += ': ' + field.$.value;
              return;
            }

            if (end >= start) {
              this.highlightMap.push({
                $: field.$,
                uid: field.$.uid,
                start: start,
                end: end
              });

              field.field && this.buildHighlightMap(field);
            }
          }
        };
      },
      link: function(scope, elm, attr, controller) {
        scope.$watch('packet', function(value, oldValue) {
          console.log(value);
          controller.highlightMap = [];
          scope.selected = {};
          if (value) {
            $.each(value.proto, function(i, proto) {
              proto.$.uid = controller.uid(proto.$);
              controller.buildHighlightMap(proto);
            });
          }
        });
      }
    };
  });

  module.directive('proto', function(RecursionHelper) {
    return {
      templateUrl: 'proto.html',
      scope: {
        proto: '=',
        selected: '=',
        padding: '='
      },
      require: '^packet',
      compile: function(element) {
        return RecursionHelper.compile(element, function(scope, elm, attr, packet) {

          scope.select = $.proxy(packet.select, packet);
          scope.$watch('padding', function(value) {
            scope.nextPadding = parseInt(value || '0') + 30;
          })

        });
      }
    };
  });

  module.directive('hexBytes', function() {
    return {
      templateUrl: 'hexBytes.html',
      require: '^packet',
      scope: {
        selected: '=',
        dump: '='
      },
      link: function(scope, elm, attr, packet) {
        scope.$watch('dump', function(value, oldValue) {
          scope.bytes = [];
          for (var b = 0; b < scope.dump.length; b += 2) {
            scope.bytes.push(scope.dump.slice(b, b + 2));
          }
        });

        scope.selectByte = function(offset) {
          var selected = $.grep(packet.highlightMap, function(obj) {
            return obj.start <= offset && obj.end >= offset;
          })[0];

          selected && packet.select(selected);
        };
      }
    };
  });

  module.filter('sourcePort', function() {
    return function(sourcePort) {
      if (sourcePort) {
        var parts = sourcePort.split(': ');
        return parts[parts.length - 1];
      }
    }
  });

  module.filter('hexToIp', function() {
    return function(hex) {
      var parts = [];
      for (var p = 0; p < 4; p++) {
        var hexPart = hex.slice(p * 2, p * 2 + 2)
        parts.push(parseInt(hexPart, 16));
      }
      return parts.join('.');
    }
  });

  module.filter('num', function() {
    return function(input) {
      return parseInt(input, 10);
    }
  });

  module.filter('toAscii', function() {
    return function (text) {
      var code = parseInt(text, 16);
      var c = code <= 32 || code > 127 ? '.' : String.fromCharCode(code);
      return c + ' ';
    };
  });
});
<|endoftext|>


<commit_before>// Defines some globally useful helper functions
'use strict';
var $ = require('jquery');
var _ = require('underscore');
var markerlessRE = /p\d+/;

// indexOf polyfill
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
// TODO this may make sense to move elsewhere
if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function (searchElement, fromIndex) {
    if ( this === undefined || this === null ) {
      throw new TypeError( '"this" is null or not defined' );
    }

    var length = this.length >>> 0; // Hack to convert object.length to a UInt32

    fromIndex = +fromIndex || 0;

    if (Math.abs(fromIndex) === Infinity) {
      fromIndex = 0;
    }

    if (fromIndex < 0) {
      fromIndex += length;
      if (fromIndex < 0) {
        fromIndex = 0;
      }
    }

    for (;fromIndex < length; fromIndex++) {
      if (this[fromIndex] === searchElement) {
        return fromIndex;
      }
    }

    return -1;
  };
}

module.exports = {
    isIterable: function(obj) {
        if (typeof obj === 'array' || typeof obj === 'object') {
            return true;
        }
        return false;
    },

    // **Params**
    // ```interpParts```: Array of Strings or Numbers, entity that is
    // interpreted
    //
    // **Returns** human-readable representation of the reg section
    interpId: function(interpParts) {
        if (interpParts.length === 1) {
            return 'Supplement I to Part ';
        }
        else if (isNaN(interpParts[1])) {
            return 'Supplement I to Appendix ';
        }
        else {
            return 'Supplement I to ';
        }
    },

    // **Params**
    // ```p0```: String or Number, section ID
    // ```p1```: String or Number, reg ID
    //
    // **Returns** human-readable representation of the reg section
    appendixId: function(part, letter) {
        return 'Appendix ' + letter  + ' to Part ' + part;
    },

    // **Param** dash-delimited string representation of reg entity ID
    //
    // **Returns** Reg entity marker formatted for human readability
    idToRef: function(id) {
        var ref = '',
            isAppendix = this.isAppendix(id);
        var parts, i, len, dividers, item, interpIndex, interpParts, subpartIndex, markerlessIndex;
        parts = id.split('-');
        len = parts.length - 1;
        subpartIndex = parts.indexOf('Subpart');
        dividers = ['§ .', '', '( )', '( )', '( )', '( )'];

        // if we've got only the reg part number
        if (len === 0) {
            ref = parts[0];
            return ref;
        }

        // if there is a subpart
        if (subpartIndex !== -1) {
            parts.splice(1, subpartIndex);

            // subpartIndex is now the next part after "Subpart"
            // removes subpart letter
            if (isNaN(parts[subpartIndex]) && parts[subpartIndex] !== 'Interp') {
                parts.splice(1, subpartIndex);
            }

            len = parts.length - 1;
        }

        // if we have a supplement
        interpIndex = parts.indexOf('Interp');
        if (interpIndex >= 0) {
            interpParts = parts.slice(0, interpIndex);
            ref += this.interpId(interpParts);
        }
        // if we have an appendix
        else if (isAppendix) {
            return this.appendixId(parts[0], parts[1]);
        }

        if (interpParts) {
            parts = _.compact(interpParts);
            len = parts.length -1;
        }

        // we have a subpart interpretation to appendices
        if (parts.indexOf('Appendices') !== -1) {
            return 'Supplement I to Appendices';
        }

        // the second part of a supplement to an appendix
        if (len === 1 && isAppendix) {
            return ref += parts[1];
        // we have a paragraph
        } else {
            // trim anything after a markerless paragraph
            markerlessIndex = _.findIndex(parts, markerlessRE.test.bind(markerlessRE));
            if (markerlessIndex !== -1) {
              parts = parts.slice(0, markerlessIndex);
              len = parts.length - 1;
            }
            for (i = 0; i <= len; i++) {
                // return part number alone
                if (len < 1) {
                    return ref += parts[i];
                }

                // top paragraph has no punctuation
                if (i === 1) {
                    ref += parts[i];
                } else {
                    item = dividers[i].split(' ');
                    ref += item[0] + parts[i] + item[1];
                }
            }
        }

        return ref;

    },

    // Finds parent-most reg paragraph
    findBaseSection: function(id) {
        var parts, interpIndex;

        if (id.indexOf('-') !== -1) {
            parts = id.split('-');
        } else {
            return id;
        }

        // if what has been passed in is a base section already
        // catches:
        // 123
        // 123-1
        // 123-A
        // 123-Interp
        if (parts.length <= 2) {
            return id;
        }

        interpIndex = parts.indexOf('Interp');

        if (interpIndex !== -1) {
            // catches 123-Interp-h1
            if (parts[1] === 'Interp') {
                return id;
            }
            // catches:
            // 123-4-Interp
            // 123-4-Interp-5
            // 123-Subpart-Interp
            // 123-Subpart-A-Interp
            // 123-Subpart-Interp-4
            // 123-Subpart-A-Interp-4
            // 123-Appendices-Interp
            // 123-Appendices-Interp-4
            else {
                return parts.slice(0, interpIndex + 1).join('-');
            }
        }

        // catches:
        // 123-4-*
        // 123-A-*
        return parts[0] + '-' + parts[1];
    },

    // Unpaired this function from the DOM to make
    // it more testable and flexible. Look at resources.js
    // to add places to look for version elements.
    // To call: `findVersion(Resources.versionElements)`

    // -- old message --
    // these next two are a little desperate and heavy handed
    // the next step, if the app were going to do more
    // interesting things, is to introduce the concept of reg
    // version and maybe effective dates to the architecture
    // at that time, this should be removed
    findVersion: function(versionElements) {
      return $(versionElements.toc).attr('data-toc-version') ||
                  $(versionElements.regLandingPage).attr('data-base-version')||
                  $(versionElements.timelineList).find('.stop-button').attr('data-version');
                    // includes .stop-button to be sure its not
                    // the comparison version in diff mode
    },

    // returns newer version. findVersion will return base version
    findDiffVersion: function(versionElements, currentVersion) {
        var version;
        currentVersion = currentVersion || this.findVersion(versionElements);
        version = $(versionElements.diffToc).attr('data-from-version');
        if (!version || version === currentVersion) {
            if ($(versionElements.timelineList).find('.version-link').attr('data-version') !== currentVersion) {
                version = $(versionElements.timelineList).find('.version-link').attr('data-version');
            }
        }

        return version;
    },

    isSupplement: function(id) {
        var parts;

        if (typeof id !== 'undefined') {
            parts = _.compact(id.split('-'));
            if (parts.length < 2) {
                return false;
            }

            if (parts[1].toLowerCase() === 'interp') {
                return true;
            }
        }

        return false;
    },

    isAppendix: function(id) {
        var parts;

        if (typeof id !== 'undefined') {
            parts = _.compact(id.split('-'));
            if (parts.length < 2) {
                return false;
            }

            if (isNaN(parts[1].substr(0, 1)) && parts[1].toLowerCase() !== 'interp') {
                return true;
            }
        }

        return false;
    },

    formatSubpartLabel: function(id) {
        // accepts 123-Subpart-C
        var parts = id.split('-'),
            label = 'Subpart ';
        if (isNaN(parts[0]) === false && parts[1] === 'Subpart') {
            label += parts[2];
        }

        return label;
    },

    // accepts to params:
    // element to be expanded
    // animation duration
    toggleExpandable: function($expandable, dur) {
        $expandable.toggleClass('open')
          .next('.chunk').slideToggle(dur);
    },

    /**
     * Parse a preamble section ID.
     * @param {string} id Preamble section ID
     * @example
     * parsePreambleId('2016_02749-preamble-2016_02749-I-A')
     * {
     *   docId: '2016_02749',
     *   path: ['preamble', '2016_02749', 'I'],
     *   hash: '2016_02749-I-A'
     * }
     * @example
     * parsePreambleId('2016_02749-cfr-478-32-a-1')
     * {
     *    docId: '2016_02749',
     *    path: ['preamble', '2016_02749', 'cfr_changes', '478-32-a-1'],
     *    hash: '478-32-a-1'
     * }
     */
    parsePreambleId: function(id) {
      var parts = id.split('-');
      var docId = parts.shift();
      var type = parts.shift();
      var path = ['preamble', docId];
      var section;
      if (type === 'preamble') {
        // Note: Document ID appears twice in preamble IDs
        // TODO: Standardize IDs and drop the `slice`
        path = path.concat(parts.slice(1, 2));
        section = [docId].concat(parts.slice(1, 2));
      } else if (type === 'cfr') {
        path = path.concat(['cfr_changes', parts.join('-')]);
        section = parts.slice(0, 2);
      }
      return {
        path: path,
        type: type,
        hash: parts.join('-'),
        docId: docId,
        section: section
      };
    }
};
<commit_msg>Fix ajax navigation to cfr changes from index view.<commit_after>// Defines some globally useful helper functions
'use strict';
var $ = require('jquery');
var _ = require('underscore');
var markerlessRE = /p\d+/;

// indexOf polyfill
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
// TODO this may make sense to move elsewhere
if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function (searchElement, fromIndex) {
    if ( this === undefined || this === null ) {
      throw new TypeError( '"this" is null or not defined' );
    }

    var length = this.length >>> 0; // Hack to convert object.length to a UInt32

    fromIndex = +fromIndex || 0;

    if (Math.abs(fromIndex) === Infinity) {
      fromIndex = 0;
    }

    if (fromIndex < 0) {
      fromIndex += length;
      if (fromIndex < 0) {
        fromIndex = 0;
      }
    }

    for (;fromIndex < length; fromIndex++) {
      if (this[fromIndex] === searchElement) {
        return fromIndex;
      }
    }

    return -1;
  };
}

module.exports = {
    isIterable: function(obj) {
        if (typeof obj === 'array' || typeof obj === 'object') {
            return true;
        }
        return false;
    },

    // **Params**
    // ```interpParts```: Array of Strings or Numbers, entity that is
    // interpreted
    //
    // **Returns** human-readable representation of the reg section
    interpId: function(interpParts) {
        if (interpParts.length === 1) {
            return 'Supplement I to Part ';
        }
        else if (isNaN(interpParts[1])) {
            return 'Supplement I to Appendix ';
        }
        else {
            return 'Supplement I to ';
        }
    },

    // **Params**
    // ```p0```: String or Number, section ID
    // ```p1```: String or Number, reg ID
    //
    // **Returns** human-readable representation of the reg section
    appendixId: function(part, letter) {
        return 'Appendix ' + letter  + ' to Part ' + part;
    },

    // **Param** dash-delimited string representation of reg entity ID
    //
    // **Returns** Reg entity marker formatted for human readability
    idToRef: function(id) {
        var ref = '',
            isAppendix = this.isAppendix(id);
        var parts, i, len, dividers, item, interpIndex, interpParts, subpartIndex, markerlessIndex;
        parts = id.split('-');
        len = parts.length - 1;
        subpartIndex = parts.indexOf('Subpart');
        dividers = ['§ .', '', '( )', '( )', '( )', '( )'];

        // if we've got only the reg part number
        if (len === 0) {
            ref = parts[0];
            return ref;
        }

        // if there is a subpart
        if (subpartIndex !== -1) {
            parts.splice(1, subpartIndex);

            // subpartIndex is now the next part after "Subpart"
            // removes subpart letter
            if (isNaN(parts[subpartIndex]) && parts[subpartIndex] !== 'Interp') {
                parts.splice(1, subpartIndex);
            }

            len = parts.length - 1;
        }

        // if we have a supplement
        interpIndex = parts.indexOf('Interp');
        if (interpIndex >= 0) {
            interpParts = parts.slice(0, interpIndex);
            ref += this.interpId(interpParts);
        }
        // if we have an appendix
        else if (isAppendix) {
            return this.appendixId(parts[0], parts[1]);
        }

        if (interpParts) {
            parts = _.compact(interpParts);
            len = parts.length -1;
        }

        // we have a subpart interpretation to appendices
        if (parts.indexOf('Appendices') !== -1) {
            return 'Supplement I to Appendices';
        }

        // the second part of a supplement to an appendix
        if (len === 1 && isAppendix) {
            return ref += parts[1];
        // we have a paragraph
        } else {
            // trim anything after a markerless paragraph
            markerlessIndex = _.findIndex(parts, markerlessRE.test.bind(markerlessRE));
            if (markerlessIndex !== -1) {
              parts = parts.slice(0, markerlessIndex);
              len = parts.length - 1;
            }
            for (i = 0; i <= len; i++) {
                // return part number alone
                if (len < 1) {
                    return ref += parts[i];
                }

                // top paragraph has no punctuation
                if (i === 1) {
                    ref += parts[i];
                } else {
                    item = dividers[i].split(' ');
                    ref += item[0] + parts[i] + item[1];
                }
            }
        }

        return ref;

    },

    // Finds parent-most reg paragraph
    findBaseSection: function(id) {
        var parts, interpIndex;

        if (id.indexOf('-') !== -1) {
            parts = id.split('-');
        } else {
            return id;
        }

        // if what has been passed in is a base section already
        // catches:
        // 123
        // 123-1
        // 123-A
        // 123-Interp
        if (parts.length <= 2) {
            return id;
        }

        interpIndex = parts.indexOf('Interp');

        if (interpIndex !== -1) {
            // catches 123-Interp-h1
            if (parts[1] === 'Interp') {
                return id;
            }
            // catches:
            // 123-4-Interp
            // 123-4-Interp-5
            // 123-Subpart-Interp
            // 123-Subpart-A-Interp
            // 123-Subpart-Interp-4
            // 123-Subpart-A-Interp-4
            // 123-Appendices-Interp
            // 123-Appendices-Interp-4
            else {
                return parts.slice(0, interpIndex + 1).join('-');
            }
        }

        // catches:
        // 123-4-*
        // 123-A-*
        return parts[0] + '-' + parts[1];
    },

    // Unpaired this function from the DOM to make
    // it more testable and flexible. Look at resources.js
    // to add places to look for version elements.
    // To call: `findVersion(Resources.versionElements)`

    // -- old message --
    // these next two are a little desperate and heavy handed
    // the next step, if the app were going to do more
    // interesting things, is to introduce the concept of reg
    // version and maybe effective dates to the architecture
    // at that time, this should be removed
    findVersion: function(versionElements) {
      return $(versionElements.toc).attr('data-toc-version') ||
                  $(versionElements.regLandingPage).attr('data-base-version')||
                  $(versionElements.timelineList).find('.stop-button').attr('data-version');
                    // includes .stop-button to be sure its not
                    // the comparison version in diff mode
    },

    // returns newer version. findVersion will return base version
    findDiffVersion: function(versionElements, currentVersion) {
        var version;
        currentVersion = currentVersion || this.findVersion(versionElements);
        version = $(versionElements.diffToc).attr('data-from-version');
        if (!version || version === currentVersion) {
            if ($(versionElements.timelineList).find('.version-link').attr('data-version') !== currentVersion) {
                version = $(versionElements.timelineList).find('.version-link').attr('data-version');
            }
        }

        return version;
    },

    isSupplement: function(id) {
        var parts;

        if (typeof id !== 'undefined') {
            parts = _.compact(id.split('-'));
            if (parts.length < 2) {
                return false;
            }

            if (parts[1].toLowerCase() === 'interp') {
                return true;
            }
        }

        return false;
    },

    isAppendix: function(id) {
        var parts;

        if (typeof id !== 'undefined') {
            parts = _.compact(id.split('-'));
            if (parts.length < 2) {
                return false;
            }

            if (isNaN(parts[1].substr(0, 1)) && parts[1].toLowerCase() !== 'interp') {
                return true;
            }
        }

        return false;
    },

    formatSubpartLabel: function(id) {
        // accepts 123-Subpart-C
        var parts = id.split('-'),
            label = 'Subpart ';
        if (isNaN(parts[0]) === false && parts[1] === 'Subpart') {
            label += parts[2];
        }

        return label;
    },

    // accepts to params:
    // element to be expanded
    // animation duration
    toggleExpandable: function($expandable, dur) {
        $expandable.toggleClass('open')
          .next('.chunk').slideToggle(dur);
    },

    /**
     * Parse a preamble section ID.
     * @param {string} id Preamble section ID
     * @example
     * parsePreambleId('2016_02749-preamble-2016_02749-I-A')
     * {
     *   docId: '2016_02749',
     *   path: ['preamble', '2016_02749', 'I'],
     *   hash: '2016_02749-I-A'
     * }
     * @example
     * parsePreambleId('2016_02749-cfr-478-32-a-1')
     * {
     *    docId: '2016_02749',
     *    path: ['preamble', '2016_02749', 'cfr_changes', '478-32-a-1'],
     *    hash: '478-32-a-1'
     * }
     */
    parsePreambleId: function(id) {
      var parts = id.split('-');
      var docId = parts.shift();
      var type = parts.shift();
      var path = ['preamble', docId];
      var section;
      if (type === 'preamble') {
        // Note: Document ID appears twice in preamble IDs
        // TODO: Standardize IDs and drop the `slice`
        path = path.concat(parts.slice(1, 2));
        section = [docId].concat(parts.slice(1, 2));
      } else if (type === 'cfr') {
        // Sections for CFR changes include the two first parts; the ID
        // 478-32-p243 maps to the section 478-32
        path = path.concat(['cfr_changes', parts.slice(0, 2).join('-')]);
        section = parts.slice(0, 2);
      }
      return {
        path: path,
        type: type,
        hash: parts.join('-'),
        docId: docId,
        section: section
      };
    }
};
<|endoftext|>


<commit_before>'use babel';

/* @flow*/

import { Observable } from 'rxjs';
import { TRANSFORM_MESSAGES, errorAction, updateMessagesAction } from '../actions';
import type { Store } from '../../types';

export default function transformMessages(action$: Observable, store: Store) {
  return action$.ofType(TRANSFORM_MESSAGES)
    .switchMap((action) => {
      console.log(action);
      const currentState = store.getState();
      const filePath = currentState.editor ? currentState.editor.getPath() : '';
      const sortKey = currentState.sorter.key;
      const desc = currentState.sorter.desc;
      const rawMessages = action.payload && action.payload.rawMessages ? action.payload.rawMessages : currentState.rawMessages;
      return Observable.from(rawMessages)
        .filter(message => currentState.currentFileOnly && message.filePath === filePath)
        .toArray()
        .map(messages => messages.sort((current, next) => {
          const currentValue = (sortKey === 'error') ? (current.error ? current.error.message : '') : current[sortKey];
          const nextValue = (sortKey === 'error') ? (next.error ? next.error.message : '') : next[sortKey];

          if (currentValue < nextValue) {
            return desc ? 1 : -1;
          }
          if (currentValue > nextValue) {
            return desc ? -1 : 1;
          }
          return 0;
        }))
        .switchMap((transformedMessages) => {
          console.log('transformMessages', transformMessages);
          if (currentState.messages === transformedMessages) {
            return Observable.empty();
          }
          return Observable.of(updateMessagesAction(transformedMessages, rawMessages));
        });
    })
    .catch(err => Observable.of(errorAction(err)));
}
<commit_msg>fix filter<commit_after>'use babel';

/* @flow*/

import { Observable } from 'rxjs';
import { TRANSFORM_MESSAGES, errorAction, updateMessagesAction } from '../actions';
import type { Store } from '../../types';

export default function transformMessages(action$: Observable, store: Store) {
  return action$.ofType(TRANSFORM_MESSAGES)
    .switchMap((action) => {
      console.log(action);
      const currentState = store.getState();
      const filePath = currentState.editor ? currentState.editor.getPath() : '';
      const sortKey = currentState.sorter.key;
      const desc = currentState.sorter.desc;
      const rawMessages = action.payload && action.payload.rawMessages ? action.payload.rawMessages : currentState.rawMessages;
      return Observable.from(rawMessages)
        .filter((message) => {
          if (!currentState.currentFileOnly) {
            return true;
          }
          return message.filePath === filePath;
        })
        .toArray()
        .map(messages => messages.sort((current, next) => {
          const currentValue = (sortKey === 'error') ? (current.error ? current.error.message : '') : current[sortKey];
          const nextValue = (sortKey === 'error') ? (next.error ? next.error.message : '') : next[sortKey];

          if (currentValue < nextValue) {
            return desc ? 1 : -1;
          }
          if (currentValue > nextValue) {
            return desc ? -1 : 1;
          }
          return 0;
        }))
        .switchMap((transformedMessages) => {
          console.log('transformMessages', transformMessages);
          if (currentState.messages === transformedMessages) {
            return Observable.empty();
          }
          return Observable.of(updateMessagesAction(transformedMessages, rawMessages));
        });
    })
    .catch(err => Observable.of(errorAction(err)));
}
<|endoftext|>


<commit_before>"use strict";

var connect = require("connect");

//middlewares
var parseURL = require('./middleware/parseURL.js'),
    setAjaxFlag = require('./middleware/setAjaxFlag.js'),
    serveInitPageShortcut = require('./middleware/serveInitPageShortcut.js');

var middleware = [
        parseURL,
        setAjaxFlag,
        connect.bodyParser(),
        serveInitPageShortcut
    ];

function onRequest() {
    return middleware;
}

module.exports = onRequest();<commit_msg>- removed connect body parser. Parsing only json-parser and multipart parser now.<commit_after>"use strict";

var connect = require("connect");

//middlewares
var parseURL = require('./middleware/parseURL.js'),
    setAjaxFlag = require('./middleware/setAjaxFlag.js'),
    serveInitPageShortcut = require('./middleware/serveInitPageShortcut.js');

var middleware = [
        parseURL,
        setAjaxFlag,
        //Maybe add the add another point?
        connect.json(),
        connect.urlencoded(),
        serveInitPageShortcut
    ];

function onRequest() {
    return middleware;
}

module.exports = onRequest();<|endoftext|>


<commit_before>define("amber/helpers", ["amber/boot", "require"], function (boot, require) {
    var globals = boot.globals,
        exports = {},
        api = boot.api,
        nil = boot.nil;

    // API

    exports.popupHelios = function () {
        require(['helios/index'], function (helios) {
            helios.popup();
        }, function (err) {
            window.alert("Error loading helios.\nIf not present, you can install it with 'bower install helios --save-dev'.\nThe error follows:\n" + err);
        });
    };
    Object.defineProperty(exports, "api", {
        value: api,
        enumerable: true, configurable: true, writable: false
    });
    Object.defineProperty(exports, "globals", {
        value: globals,
        enumerable: true, configurable: true, writable: false
    });
    Object.defineProperty(exports, "nil", {
        value: nil,
        enumerable: true, configurable: true, writable: false
    });

    function mixinToSettings(source) {
        var settings = globals.SmalltalkSettings;
        Object.keys(source).forEach(function (key) {
            settings[key] = source[key];
        });
    }

    function settingsInLocalStorage() {
        //jshint evil:true
        var global = new Function('return this')(),
            storage = 'localStorage' in global && global.localStorage;

        if (storage) {
            var fromStorage;
            try {
                fromStorage = JSON.parse(storage.getItem('amber.SmalltalkSettings'));
            } catch (ex) {
                // pass
            }
            mixinToSettings(fromStorage || {});
            if (typeof window !== "undefined") {
                requirejs(['jquery'], function ($) {
                    $(window).on('beforeunload', function () {
                        storage.setItem('amber.SmalltalkSettings', JSON.stringify(globals.SmalltalkSettings));
                    });
                });
            }
        }
    }

    exports.initialize = function (options) {
        globals.SmalltalkSettings['transport.defaultAmdNamespace'] = api.defaultAmdNamespace;
        settingsInLocalStorage();
        mixinToSettings(options || {});
        return api.initialize();
    };

    // Backward compatibility, deprecated

    Object.defineProperty(exports, "smalltalk", {
        value: api,
        enumerable: true, configurable: true, writable: false
    });
    Object.defineProperty(exports, "vm", {
        value: api,
        enumerable: true, configurable: true, writable: false
    });

    // Exports

    return  exports;
});
<commit_msg>deprecation off: amber.{smalltalk,vm}<commit_after>define("amber/helpers", ["amber/boot", "require"], function (boot, require) {
    var globals = boot.globals,
        exports = {},
        api = boot.api,
        nil = boot.nil;

    // API

    exports.popupHelios = function () {
        require(['helios/index'], function (helios) {
            helios.popup();
        }, function (err) {
            window.alert("Error loading helios.\nIf not present, you can install it with 'bower install helios --save-dev'.\nThe error follows:\n" + err);
        });
    };
    Object.defineProperty(exports, "api", {
        value: api,
        enumerable: true, configurable: true, writable: false
    });
    Object.defineProperty(exports, "globals", {
        value: globals,
        enumerable: true, configurable: true, writable: false
    });
    Object.defineProperty(exports, "nil", {
        value: nil,
        enumerable: true, configurable: true, writable: false
    });

    function mixinToSettings(source) {
        var settings = globals.SmalltalkSettings;
        Object.keys(source).forEach(function (key) {
            settings[key] = source[key];
        });
    }

    function settingsInLocalStorage() {
        //jshint evil:true
        var global = new Function('return this')(),
            storage = 'localStorage' in global && global.localStorage;

        if (storage) {
            var fromStorage;
            try {
                fromStorage = JSON.parse(storage.getItem('amber.SmalltalkSettings'));
            } catch (ex) {
                // pass
            }
            mixinToSettings(fromStorage || {});
            if (typeof window !== "undefined") {
                requirejs(['jquery'], function ($) {
                    $(window).on('beforeunload', function () {
                        storage.setItem('amber.SmalltalkSettings', JSON.stringify(globals.SmalltalkSettings));
                    });
                });
            }
        }
    }

    exports.initialize = function (options) {
        globals.SmalltalkSettings['transport.defaultAmdNamespace'] = api.defaultAmdNamespace;
        settingsInLocalStorage();
        mixinToSettings(options || {});
        return api.initialize();
    };

    // Exports

    return  exports;
});
<|endoftext|>