Viewing File: /home/ubuntu/code-backup/code_review/phabricator/webroot/rsrc/js/core/behavior-line-linker.js

/**
 * @provides javelin-behavior-phabricator-line-linker
 * @requires javelin-behavior
 *           javelin-stratcom
 *           javelin-dom
 *           javelin-history
 *           javelin-external-editor-link-engine
 */

JX.behavior('phabricator-line-linker', function() {
  var origin = null;
  var target = null;
  var root = null;
  var highlighted = null;

  var editor_link = null;
  try {
    editor_link = JX.$('editor_link');
  } catch (ex) {
    // Ignore.
  }

  function getRowNumber(th) {
    // If the "<th />" tag contains an "<a />" with "data-n" that we're using
    // to prevent copy/paste of line numbers, use that.
    if (th.firstChild) {
      var line = th.firstChild.getAttribute('data-n');
      if (line) {
        return line;
      }
    }

    return null;
  }

  JX.Stratcom.listen(
    ['click', 'mousedown'],
    ['phabricator-source', 'tag:th', 'tag:a'],
    function(e) {
      if (!e.isNormalMouseEvent()) {
        return;
      }

      // Make sure the link we clicked is actually a line number in a source
      // table, not some kind of link in some element embedded inside the
      // table. The row's immediate ancestor table needs to be the table with
      // the "phabricator-source" sigil.

      var cell = e.getNode('tag:th');
      var table = e.getNode('phabricator-source');
      if (JX.DOM.findAbove(cell, 'table') !== table) {
        return;
      }

      var number = getRowNumber(cell);
      if (!number) {
        return;
      }

      e.kill();

      // If this is a click event, kill it. We handle mousedown and mouseup
      // instead.
      if (e.getType() === 'click') {
        return;
      }

      origin = cell;
      target = origin;

      root = table;
    });

  var highlight = function(e) {
    if (!origin) {
      return;
    }

    if (e.getNode('phabricator-source') !== root) {
      return;
    }
    target = e.getNode('tag:th');

    var min;
    var max;

    // NOTE: We're using position to figure out which order these rows are in,
    // not row numbers. We do this because Harbormaster build logs may have
    // multiple rows with the same row number.

    if (JX.$V(origin).y <= JX.$V(target).y) {
      min = origin;
      max = target;
    } else {
      min = target;
      max = origin;
    }

    // If we haven't changed highlighting, we don't have a list of highlighted
    // nodes yet. Assume every row is highlighted.
    var ii;
    if (highlighted === null) {
      highlighted = [];
      var rows = JX.DOM.scry(root, 'tr');
      for (ii = 0; ii < rows.length; ii++) {
        highlighted.push(rows[ii]);
      }
    }

    // Unhighlight any existing highlighted rows.
    for (ii = 0; ii < highlighted.length; ii++) {
      JX.DOM.alterClass(highlighted[ii], 'phabricator-source-highlight', false);
    }
    highlighted = [];

    // Highlight the newly selected rows.
    min = JX.DOM.findAbove(min, 'tr');
    max = JX.DOM.findAbove(max, 'tr');

    var cursor = min;
    while (true) {
      JX.DOM.alterClass(cursor, 'phabricator-source-highlight', true);
      highlighted.push(cursor);

      if (cursor === max) {
        break;
      }

      cursor = cursor.nextSibling;
    }
  };

  JX.Stratcom.listen('mouseover', 'phabricator-source', highlight);

  JX.Stratcom.listen(
    'mouseup',
    null,
    function(e) {
      if (!origin) {
        return;
      }

      highlight(e);
      e.kill();

      var o = getRowNumber(origin);
      var t = getRowNumber(target);
      var uri = JX.Stratcom.getData(root).uri;
      var path;

      if (!uri) {
        uri = JX.$U(window.location);
        path = uri.getPath();
        path = path.replace(/\$[\d-]+$/, '');
        uri.setPath(path);
        uri = uri.toString();
      }

      origin = null;
      target = null;
      root = null;

      var lines = (o == t ? o : Math.min(o, t) + '-' + Math.max(o, t));

      uri = JX.$U(uri);
      path = uri.getPath();
      path = path + '$' + lines;
      uri = uri.setPath(path).toString();

      JX.History.replace(uri);

      if (editor_link) {
        var data = JX.Stratcom.getData(editor_link);

        var variables = {
          l: parseInt(Math.min(o, t), 10),
        };

        var template = data.template;

        var editor_uri = new JX.ExternalEditorLinkEngine()
          .setTemplate(template)
          .setVariables(variables)
          .newURI();

        editor_link.href = editor_uri;
      }
    });


  // Try to jump to the highlighted lines if we don't have an explicit anchor
  // in the URI.
  if (!window.location.hash.length) {
    try {
      var anchor = JX.$('phabricator-line-linker-anchor');
      JX.DOM.scrollToPosition(0, JX.$V(anchor).y - 60);
    } catch (ex) {
      // If we didn't hit an element on the page, just move on.
    }
  }

  if (editor_link) {
    // TODO: This should be pht()'d, but this behavior is weird enough to
    // make that a bit tricky.

    new JX.KeyboardShortcut('\\', 'Open File in External Editor')
        .setGroup('diff-nav')
        .setHandler(function() {
          JX.$U(editor_link.href).go();
        })
        .register();
  }

});
Back to Directory File Manager