<commit_before>// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Controllers for the editor's state graph.
 *
 * @author <EMAIL> (<NAME>)
 */

function EditorGraph($scope, $filter, explorationData) {
  $scope.updateViz = function() {
    explorationData.getData().then(function(data) {
      $scope.graphData = $scope.reformatResponse(
          data.states, data.init_state_id);
    });
  };

  // At the start, update the graph.
  $scope.updateViz();

  // Also update the graph when an update event is broadcast.
  $scope.$on('updateViz', $scope.updateViz);

  // Reformat the model into a response that is processable by d3.js.
  $scope.reformatResponse = function(states, initStateId) {
    var SENTINEL_DEPTH = 3000;
    var VERT_OFFSET = 20;
    var HORIZ_SPACING = 150;
    var VERT_SPACING = 100;
    var HORIZ_OFFSET = 100;
    var nodes = {};
    nodes[END_DEST] = {name: END_DEST, depth: SENTINEL_DEPTH, reachable: false};
    for (var state in states) {
      nodes[state] = {name: states[state].name, depth: SENTINEL_DEPTH, reachable: false};
    }
    nodes[initStateId].depth = 0;

    var maxDepth = 0;
    var seenNodes = [initStateId];
    var queue = [initStateId];
    var maxXDistPerLevel = {0: HORIZ_OFFSET};
    nodes[initStateId].y0 = VERT_OFFSET;
    nodes[initStateId].x0 = HORIZ_OFFSET;

    while (queue.length > 0) {
      var currNode = queue[0];
      queue.shift();
      nodes[currNode].reachable = true;
      if (currNode in states) {
        for (var i = 0; i < states[currNode].widget.rules.submit.length; i++) {
          // Assign levels to nodes only when they are first encountered.
          if (seenNodes.indexOf(states[currNode].widget.rules.submit[i].dest) == -1) {
            seenNodes.push(states[currNode].widget.rules.submit[i].dest);
            nodes[states[currNode].widget.rules.submit[i].dest].depth = nodes[currNode].depth + 1;
            nodes[states[currNode].widget.rules.submit[i].dest].y0 = (nodes[currNode].depth + 1) * VERT_SPACING + VERT_OFFSET;
            if (nodes[currNode].depth + 1 in maxXDistPerLevel) {
              nodes[states[currNode].widget.rules.submit[i].dest].x0 = maxXDistPerLevel[nodes[currNode].depth + 1] + HORIZ_SPACING;
              maxXDistPerLevel[nodes[currNode].depth + 1] += HORIZ_SPACING;
            } else {
              nodes[states[currNode].widget.rules.submit[i].dest].x0 = HORIZ_OFFSET;
              maxXDistPerLevel[nodes[currNode].depth + 1] = HORIZ_OFFSET;
            }
            maxDepth = Math.max(maxDepth, nodes[currNode].depth + 1);
            queue.push(states[currNode].widget.rules.submit[i].dest);
          }
        }
      }
    }

    var horizPositionForLastRow = HORIZ_OFFSET;
    for (var node in nodes) {
      if (nodes[node].depth == SENTINEL_DEPTH) {
        nodes[node].depth = maxDepth + 1;
        nodes[node].y0 = VERT_OFFSET + nodes[node].depth * VERT_SPACING;
        nodes[node].x0 = horizPositionForLastRow;
        horizPositionForLastRow += HORIZ_SPACING;
      }
    }

    // Assign unique IDs to each node.
    var idCount = 0;
    var nodeList = [];
    for (var node in nodes) {
      var nodeMap = nodes[node];
      nodeMap['hashId'] = node;
      nodeMap['id'] = idCount;
      nodes[node]['id'] = idCount;
      idCount++;
      nodeList.push(nodeMap);
    }

    var links = [];
    for (var state in states) {
      for (var i = 0; i < states[state].widget.rules.submit.length; i++) {
        links.push({
            source: nodeList[nodes[state].id],
            target: nodeList[nodes[states[state].widget.rules.submit[i].dest].id],
            name: $filter('parameterizeRule')({
                rule: states[state].widget.rules.submit[i].rule,
                inputs: states[state].widget.rules.submit[i].inputs
            })
        });
      }
    }

    return {nodes: nodeList, links: links, initStateId: initStateId};
  };
}


oppia.directive('stateGraphViz', function(explorationData) {
  // constants
  var w = 960,
      h = 4000,
      i = 0;
  var DEFAULT_CIRCLE_RADIUS = 30;
  // The following variable must be at least 3.
  var MAX_CATEGORY_LENGTH = 20;

  var getTextWidth = function(text) {
    return 40 + Math.min(MAX_CATEGORY_LENGTH, text.length) * 10;
  };

  return {
    restrict: 'E',
    scope: {
      val: '=',
      grouped: '='
    },
    link: function(scope, element, attrs) {
      scope.truncate = function(text) {
        if (text.length > MAX_CATEGORY_LENGTH) {
          return text.substring(0, MAX_CATEGORY_LENGTH - 3) + '...';
        } else {
          return text;<commit_msg>editor graph node overlap issue fixed<commit_after>// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Controllers for the editor's state graph.
 *
 * @author <EMAIL> (<NAME>)
 */

function EditorGraph($scope, $filter, explorationData) {
  $scope.updateViz = function() {
    explorationData.getData().then(function(data) {
      $scope.graphData = $scope.reformatResponse(
          data.states, data.init_state_id);
    });
  };

  // At the start, update the graph.
  $scope.updateViz();

  // Also update the graph when an update event is broadcast.
  $scope.$on('updateViz', $scope.updateViz);

  // Reformat the model into a response that is processable by d3.js.
  $scope.reformatResponse = function(states, initStateId) {
    var SENTINEL_DEPTH = 3000;
    var VERT_OFFSET = 20;
    var HORIZ_SPACING = 150;
    var VERT_SPACING = 100;
    var HORIZ_OFFSET = 100;
    var nodes = {};
    nodes[END_DEST] = {name: END_DEST, depth: SENTINEL_DEPTH, reachable: false};
    for (var state in states) {
      nodes[state] = {name: states[state].name, depth: SENTINEL_DEPTH, reachable: false};
    }
    nodes[initStateId].depth = 0;

    var maxDepth = 0;
    var seenNodes = [initStateId];
    var queue = [initStateId];
    var maxXDistPerLevel = {0: HORIZ_OFFSET};
    nodes[initStateId].y0 = VERT_OFFSET;
    nodes[initStateId].x0 = HORIZ_OFFSET;

    while (queue.length > 0) {
      var currNode = queue[0];
      queue.shift();
      nodes[currNode].reachable = true;
      if (currNode in states) {
        for (var i = 0; i < states[currNode].widget.rules.submit.length; i++) {
          // Assign levels to nodes only when they are first encountered.
          if (seenNodes.indexOf(states[currNode].widget.rules.submit[i].dest) == -1) {
            seenNodes.push(states[currNode].widget.rules.submit[i].dest);
            nodes[states[currNode].widget.rules.submit[i].dest].depth = nodes[currNode].depth + 1;
            nodes[states[currNode].widget.rules.submit[i].dest].y0 = (nodes[currNode].depth + 1) * VERT_SPACING + VERT_OFFSET;
            if (nodes[currNode].depth + 1 in maxXDistPerLevel) {
              nodes[states[currNode].widget.rules.submit[i].dest].x0 = maxXDistPerLevel[nodes[currNode].depth + 1] + HORIZ_SPACING;
              maxXDistPerLevel[nodes[currNode].depth + 1] += HORIZ_SPACING;
            } else {
              nodes[states[currNode].widget.rules.submit[i].dest].x0 = HORIZ_OFFSET;
              maxXDistPerLevel[nodes[currNode].depth + 1] = HORIZ_OFFSET;
            }
            maxDepth = Math.max(maxDepth, nodes[currNode].depth + 1);
            queue.push(states[currNode].widget.rules.submit[i].dest);
          }
        }
      }
    }

    var horizPositionForLastRow = HORIZ_OFFSET;
    for (var node in nodes) {
      if (nodes[node].depth == SENTINEL_DEPTH) {
        nodes[node].depth = maxDepth + 1;
        nodes[node].y0 = VERT_OFFSET + nodes[node].depth * VERT_SPACING;
        nodes[node].x0 = horizPositionForLastRow;
        horizPositionForLastRow += HORIZ_SPACING;
      }
    }

    // Assign unique IDs to each node.
    var idCount = 0;
    var nodeList = [];
    for (var node in nodes) {
      var nodeMap = nodes[node];
      nodeMap['hashId'] = node;
      nodeMap['id'] = idCount;
      nodes[node]['id'] = idCount;
      idCount++;
      nodeList.push(nodeMap);
    }

    var links = [];
    for (var state in states) {
      for (var i = 0; i < states[state].widget.rules.submit.length; i++) {
        links.push({
            source: nodeList[nodes[state].id],
            target: nodeList[nodes[states[state].widget.rules.submit[i].dest].id],
            name: $filter('parameterizeRule')({
                rule: states[state].widget.rules.submit[i].rule,
                inputs: states[state].widget.rules.submit[i].inputs
            })
        });
      }
    }

    return {nodes: nodeList, links: links, initStateId: initStateId};
  };
}


oppia.directive('stateGraphViz', function(explorationData) {
  // constants
  var w = 960,
      h = 4000,
      i = 0;
  var DEFAULT_CIRCLE_RADIUS = 30;
  // The following variable must be at least 3.
  var MAX_CATEGORY_LENGTH = 20;

  var getTextWidth = function(text) {
    return 40 + Math.min(MAX_CATEGORY_LENGTH, text.length) * 5;
  };

  return {
    restrict: 'E',
    scope: {
      val: '=',
      grouped: '='
    },
    link: function(scope, element, attrs) {
      scope.truncate = function(text) {
        if (text.length > MAX_CATEGORY_LENGTH) {
          return text.substring(0, MAX_CATEGORY_LENGTH - 3) + '...';
        } else {
          return text;
{'fraction_non_alphanumeric': 0.12987515726313753, 'fraction_numerical': 0.012871382947837027, 'mean_word_length': 2.643864598025388, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6337275', 'n_tokens_mistral': 3421, 'n_tokens_neox': 3280, 'n_words': 792}