<commit_before>(function () {
  "use strict";

  let RegExpTokenizer = function() { };
  let prototype = RegExpTokenizer.prototype;

  RegExpTokenizer.ESCAPE_CHAR_TYPES = [
    "ControlEscape",
    "ControlLetterEscape",
    "HexEscapeSequence",
    "UnicodeEscapeSequence",
    "IdentityEscape",
    "CharacterClassEscape"
  ];

  RegExpTokenizer.ESCAPE_CHARS = {
    "f": 0,
    "n": 0,
    "r": 0,
    "t": 0,
    "v": 0,
    "c": 1,
    "x": 2,
    "u": 3,
    "d": 5,
    "D": 5,
    "s": 5,
    "S": 5,
    "w": 5,
    "W": 5
  };

  RegExpTokenizer.ESCACPE_CHAR_CODES = {
    "0" : 0,
    "t" : 9,
    "n" : 10,
    "v" : 11,
    "f" : 12,
    "r" : 13
  };

  prototype.token = null;
  prototype.capturingGroups = [];

  prototype.tokenize = function(str) {
    function token(type, start, end, additionalProps) {
      this.type = type;
      if (additionalProps) {
        let self = this;
        Object.getOwnPropertyNames(additionalProps).forEach(function mapPropNames(name) {
          self[name] = additionalProps[name];
        });
      }
      this.loc = {
        start: start,
        end: end || start + 1
      }
    }

    this.token = null;
    this.capturingGroups = [];

    let length = str.length;
    let i = 0;
    let ast = new token("RegularExpression", i, length, {body: []});
    let parentToken = ast;
    let currentToken = ast;
    let stack = [];
    let charClass = false;
    while (i < length) {
      let char = str.charAt(i);
      let addToParent = true;
      let newParentToken = null;

      if (char === "(" && !charClass) {
        currentToken = new token("", i, null, {error: "noClosingBracket", body: []});
        this.parseGroup(str, currentToken);

        if (currentToken.type === "CapturingGroup") {
          this.capturingGroups.push(currentToken);
        }

        stack.push(parentToken);
        newParentToken = currentToken;
      } else if (char === "[") {
        let negated = (str[i + 1] === "^");
        charClass = true;
        currentToken = new token("CharacterClass", i, i + (negated ? 2 : 1),
            {negated: negated, error: "noClosingBracket", body: []});
        stack.push(parentToken);

        newParentToken = currentToken;
      } else if (char === "-" && charClass && str[i + 1] !== undefined &&
          str[i - 1] !== "[" && !(str[i - 1] === "^" && str[i - 2] === "[") &&
          str[i + 1] !== "]") {
        currentToken = new token("Range", currentToken.loc.start, i + 1,
            {left: currentToken, right: null});
        let parent = parentToken.hasOwnProperty("right") ? "right" : "body";
        parentToken[parent].pop();
        stack.push(parentToken);
        newParentToken = currentToken;
      } else if ((char === ")" && !charClass) || (char === "]" && charClass)) {
        if (char === "]" && charClass) {
          charClass = false;
        }

        if (stack.length > 0) {
          while (parentToken.type === "Alternation") {
            parentToken.loc.end = parentToken.right.length > 0 ?
                parentToken.right[parentToken.right.length - 1].loc.end :
                parentToken.loc.start + 1;
            parentToken = stack.pop();
          }

          let stackToken = stack.pop();
          currentToken = parentToken;
          if (currentToken.error === "noClosingBracket")
            delete currentToken.error;
          currentToken.loc.end = i + 1;
          newParentToken = stackToken;
          addToParent = false;
        } else {
          currentToken = new token("Literal", i, null, {value: char,
              error: "additionalClosingBracket"});
        }
      } else if (char === "\\") {
        let escapeSequenceToken = new token("", i);
        if (this.parseEscapeSequence(str, escapeSequenceToken)) {
          currentToken = escapeSequenceToken;
        } else {
          if (currentToken && currentToken.type === "Literal" && !charClass &&
              !currentToken.error) {
            currentToken.value += char;
            currentToken.loc.end++;
            addToParent = false;
          } else {
            currentToken = new token("Literal", i, null, {value: char});
          }
        }
      } else if (char === "." && !charClass) {
        currentToken = new token("AnyCharacter", i);
      } else if ("?*+{".contains(char) && !charClass) {
        let quantifierToken = new token("", i);
        this.parseQuantifier(str, quantifierToken);
        if (quantifierToken.type === "") {
          if (currentToken && currentToken.type === "Literal" && !currentToken.error) {
            currentToken.value += char;
            currentToken.loc.end++;
            addToParent = false;
          } else {
            currentToken = new token("Literal", i, null, {value: char});
          }
        } else {
          // Check whether the quantifier target is invalid
          if (currentToken.type.match(/Quantifier|Anchor|^Alternation$/) ||
              (currentToken.type.match(/Group|Look/) && currentToken.error) ||
              (currentToken.type === "IdentityEscape" &&
                  currentToken.char.toLowerCase() === "b")) {
            quantifierToken.error = "invalidQuantifierTarget";
          }
          currentToken = quantifierToken;
        }
      } else if ((char === "^" || char === "$") && !charClass) {
        currentToken = new token((char === "^" ? "Start" : "End") + "Anchor", i);
      } else if (char === "|" && !charClass) {
        let parent = parentToken.hasOwnProperty("right") ? "right" : "body";
        let startLoc = parentToken[parent][0] ? parentToken[parent][0].loc.start : i;
        currentToken = new token("Alternation", startLoc, i + 1,
            {left: parentToken[parent], right: []});
        parentToken[parent] = [];
        stack.push(parentToken);
        newParentToken = currentToken;
      } else {
        if (char === "/" && !charClass) {
          currentToken = new token("Literal", i, null, {value: char, error: "unescapedSlash"});
        } else if (currentToken && currentToken.type === "Literal" && !charClass &&
            !currentToken.error) {
          currentToken.value += char;
          currentToken.loc.end++;
          addToParent = false;
        } else {
          currentToken = new token("Literal", i, null, {value: char});
        }
      }

      if (parentToken.type === "Range" && parentToken !== currentToken) {
        parentToken.loc.end = currentToken.loc.end;
        newParentToken = stack.pop();
      }

      if (addToParent) {
        let parent = parentToken.hasOwnProperty("right") ? "right" : "body";
        if (Array.isArray(parentToken[parent])) {
          parentToken[parent].push(currentToken);
        } else {
          parentToken[parent] = currentToken;
        }
      }

      if (parentToken.type === "Range" && parentToken.right &&
          parentToken.left.value.charCodeAt(0) > parentToken.right.value.charCodeAt(0)) {
        parentToken.error = "invalidCharRange";
      }

      if (newParentToken) {
        parentToken = newParentToken;
      }

      i = currentToken.loc.end;
    }

    if (stack.length > 0) {
      parentToken.loc.end = currentToken.loc.end;

      while (stack.length > 0) {
        currentToken = parentToken;
        parentToken = stack.pop();
        parentToken.loc.end = currentToken.loc.end;
      }
    }

    this.token = ast;

    return ast;
  };

  prototype.parseGroup = function(str, token) {
    let match = str.substr(token.loc.start + 1, 3).match(/\?(?::|<?[!=])/);
    if (match) {
      if (match[0].contains(":")) {
        token.type = "NonCapturingGroup";
      } else {
        token.type = match[0].contains("=") ? "Positive" : "Negative";
        token.type += "Look" + (match[0].contains("<") ? "Behind" : "Ahead");

        if (match[0].contains("<"))
          token.error = "lookbehind";
      }

      token.loc.end += match[0].length;
    } else {
      token.type = "CapturingGroup";
    }
  };

  prototype.parseQuantifier = function(str, token) {
    switch (str[token.loc.start]) {
      case "?":
        token.type = "OptionalQuantifier";
        break;

      case "*":
        token.type = "ZeroOrMoreQuantifier";
        break;

      case "+":
        token.type = "OneOrMoreQuantifier";
        break;

      case "{":
        let repetitions = str.substr(token.loc.start).match(/^\{(\d*)(?:,(\d*))?\}/);

        // If no repetition is found, the following characters are parsed normally without error
        if (!repetitions || (!repetitions[1] && !repetitions[2])) {
          return;
        }

        token.type = "RepetitionQuantifier";
        token.loc.end += repetitions[0].length - 1;

        if (repetitions[2] !== undefined) {
          token.type = "Variable" + token.type;
          token.min = repetitions[1] !== "" ? Number(repetitions[1]) : null;
          token.max = repetitions[2] !== "" ? Number(repetitions[2]) : null;

          if (token.min > token.max && token.max !== null) {
            token.error = "invalidRepetitionRange";
          }
        } else {
          token.type = "Fixed" + token.type;
          token.repetitions = Number(repetitions[1]);
        }
    }

    if (str[token.loc.end] === "?") {
      token.lazy = true;
      token.loc.end++;
    } else {
      token.lazy = false;
    }
  };

  prototype.parseEscapeSequence = function(str, token) {
    let escapeCharType = RegExpTokenizer.ESCAPE_CHARS[str[token.loc.end]];
    if (escapeCharType === undefined)
      escapeCharType = 4;
    token.type = RegExpTokenizer.ESCAPE_CHAR_TYPES[escapeCharType];
    switch (token.type) {
      case "ControlEscape":
      case "IdentityEscape":
      case "CharacterClassEscape":
        token.char = str[token.loc.end];
        if (token.type === "ControlEscape") {
          token.value = RegExpTokenizer.ESCACPE_CHAR_CODES[token.char];
        }

        // Catch backslashes standing at the end of the regexp
        if (token.loc.end === str.length) {
          token.error = "danglingBackslash";
        } else {
          token.loc.end++;
        }
        break;

      case "ControlLetterEscape":
        token.char = str[token.loc.end + 1] || "";
        if (token.char === "") {
          token.value = token.char.charCodeAt(0) - 64;
          token.loc.end += 2;
        } else {
          token.loc.end++;
        }
        break;

      case "HexEscapeSequence": {
          let sequence = str.substr(token.loc.end + 1, 2);

          // Only if the four characters following the backslash are a hex value the token is
          // recognized as Unicode escape sequence, otherwise it's interpreted as identity escape
          if (sequence.match(/^[a-f0-9]{2}$/)) {
            token.sequence = sequence;
            token.loc.end += 3;
          } else {
            token.type = "IdentityEscape";
            token.char = "x";
            token.loc.end++;
          }
        }
        break;

      case "UnicodeEscapeSequence": {
          let sequence = str.substr(token.loc.end + 1, 4);

          // Only if the four characters following the backslash are a hex value the token is
          // recognized as Unicode escape sequence, otherwise it's interpreted as identity escape
          if (sequence.match(/^[a-f0-9]{4}$/)) {
            token.sequence = sequence;
            token.loc.end += 5;
          } else {
            token.type = "IdentityEscape";
            token.char = "u";
            token.loc.end++;
          }
        }
        break;
    }

    return true;
  };

  window.RegExpTokenizer = RegExpTokenizer;
})();<commit_msg>Fixed control letter escape recognition<commit_after>(function () {
  "use strict";

  let RegExpTokenizer = function() { };
  let prototype = RegExpTokenizer.prototype;

  RegExpTokenizer.ESCAPE_CHAR_TYPES = [
    "ControlEscape",
    "ControlLetterEscape",
    "HexEscapeSequence",
    "UnicodeEscapeSequence",
    "IdentityEscape",
    "CharacterClassEscape"
  ];

  RegExpTokenizer.ESCAPE_CHARS = {
    "f": 0,
    "n": 0,
    "r": 0,
    "t": 0,
    "v": 0,
    "c": 1,
    "x": 2,
    "u": 3,
    "d": 5,
    "D": 5,
    "s": 5,
    "S": 5,
    "w": 5,
    "W": 5
  };

  RegExpTokenizer.ESCACPE_CHAR_CODES = {
    "0" : 0,
    "t" : 9,
    "n" : 10,
    "v" : 11,
    "f" : 12,
    "r" : 13
  };

  prototype.token = null;
  prototype.capturingGroups = [];

  prototype.tokenize = function(str) {
    function token(type, start, end, additionalProps) {
      this.type = type;
      if (additionalProps) {
        let self = this;
        Object.getOwnPropertyNames(additionalProps).forEach(function mapPropNames(name) {
          self[name] = additionalProps[name];
        });
      }
      this.loc = {
        start: start,
        end: end || start + 1
      }
    }

    this.token = null;
    this.capturingGroups = [];

    let length = str.length;
    let i = 0;
    let ast = new token("RegularExpression", i, length, {body: []});
    let parentToken = ast;
    let currentToken = ast;
    let stack = [];
    let charClass = false;
    while (i < length) {
      let char = str.charAt(i);
      let addToParent = true;
      let newParentToken = null;

      if (char === "(" && !charClass) {
        currentToken = new token("", i, null, {error: "noClosingBracket", body: []});
        this.parseGroup(str, currentToken);

        if (currentToken.type === "CapturingGroup") {
          this.capturingGroups.push(currentToken);
        }

        stack.push(parentToken);
        newParentToken = currentToken;
      } else if (char === "[") {
        let negated = (str[i + 1] === "^");
        charClass = true;
        currentToken = new token("CharacterClass", i, i + (negated ? 2 : 1),
            {negated: negated, error: "noClosingBracket", body: []});
        stack.push(parentToken);

        newParentToken = currentToken;
      } else if (char === "-" && charClass && str[i + 1] !== undefined &&
          str[i - 1] !== "[" && !(str[i - 1] === "^" && str[i - 2] === "[") &&
          str[i + 1] !== "]") {
        currentToken = new token("Range", currentToken.loc.start, i + 1,
            {left: currentToken, right: null});
        let parent = parentToken.hasOwnProperty("right") ? "right" : "body";
        parentToken[parent].pop();
        stack.push(parentToken);
        newParentToken = currentToken;
      } else if ((char === ")" && !charClass) || (char === "]" && charClass)) {
        if (char === "]" && charClass) {
          charClass = false;
        }

        if (stack.length > 0) {
          while (parentToken.type === "Alternation") {
            parentToken.loc.end = parentToken.right.length > 0 ?
                parentToken.right[parentToken.right.length - 1].loc.end :
                parentToken.loc.start + 1;
            parentToken = stack.pop();
          }

          let stackToken = stack.pop();
          currentToken = parentToken;
          if (currentToken.error === "noClosingBracket")
            delete currentToken.error;
          currentToken.loc.end = i + 1;
          newParentToken = stackToken;
          addToParent = false;
        } else {
          currentToken = new token("Literal", i, null, {value: char,
              error: "additionalClosingBracket"});
        }
      } else if (char === "\\") {
        let escapeSequenceToken = new token("", i);
        if (this.parseEscapeSequence(str, escapeSequenceToken)) {
          currentToken = escapeSequenceToken;
        } else {
          if (currentToken && currentToken.type === "Literal" && !charClass &&
              !currentToken.error) {
            currentToken.value += char;
            currentToken.loc.end++;
            addToParent = false;
          } else {
            currentToken = new token("Literal", i, null, {value: char});
          }
        }
      } else if (char === "." && !charClass) {
        currentToken = new token("AnyCharacter", i);
      } else if ("?*+{".contains(char) && !charClass) {
        let quantifierToken = new token("", i);
        this.parseQuantifier(str, quantifierToken);
        if (quantifierToken.type === "") {
          if (currentToken && currentToken.type === "Literal" && !currentToken.error) {
            currentToken.value += char;
            currentToken.loc.end++;
            addToParent = false;
          } else {
            currentToken = new token("Literal", i, null, {value: char});
          }
        } else {
          // Check whether the quantifier target is invalid
          if (currentToken.type.match(/Quantifier|Anchor|^Alternation$/) ||
              (currentToken.type.match(/Group|Look/) && currentToken.error) ||
              (currentToken.type === "IdentityEscape" &&
                  currentToken.char.toLowerCase() === "b")) {
            quantifierToken.error = "invalidQuantifierTarget";
          }
          currentToken = quantifierToken;
        }
      } else if ((char === "^" || char === "$") && !charClass) {
        currentToken = new token((char === "^" ? "Start" : "End") + "Anchor", i);
      } else if (char === "|" && !charClass) {
        let parent = parentToken.hasOwnProperty("right") ? "right" : "body";
        let startLoc = parentToken[parent][0] ? parentToken[parent][0].loc.start : i;
        currentToken = new token("Alternation", startLoc, i + 1,
            {left: parentToken[parent], right: []});
        parentToken[parent] = [];
        stack.push(parentToken);
        newParentToken = currentToken;
      } else {
        if (char === "/" && !charClass) {
          currentToken = new token("Literal", i, null, {value: char, error: "unescapedSlash"});
        } else if (currentToken && currentToken.type === "Literal" && !charClass &&
            !currentToken.error) {
          currentToken.value += char;
          currentToken.loc.end++;
          addToParent = false;
        } else {
          currentToken = new token("Literal", i, null, {value: char});
        }
      }

      if (parentToken.type === "Range" && parentToken !== currentToken) {
        parentToken.loc.end = currentToken.loc.end;
        newParentToken = stack.pop();
      }

      if (addToParent) {
        let parent = parentToken.hasOwnProperty("right") ? "right" : "body";
        if (Array.isArray(parentToken[parent])) {
          parentToken[parent].push(currentToken);
        } else {
          parentToken[parent] = currentToken;
        }
      }

      if (parentToken.type === "Range" && parentToken.right &&
          parentToken.left.value.charCodeAt(0) > parentToken.right.value.charCodeAt(0)) {
        parentToken.error = "invalidCharRange";
      }

      if (newParentToken) {
        parentToken = newParentToken;
      }

      i = currentToken.loc.end;
    }

    if (stack.length > 0) {
      parentToken.loc.end = currentToken.loc.end;

      while (stack.length > 0) {
        currentToken = parentToken;
        parentToken = stack.pop();
        parentToken.loc.end = currentToken.loc.end;
      }
    }

    this.token = ast;

    return ast;
  };

  prototype.parseGroup = function(str, token) {
    let match = str.substr(token.loc.start + 1, 3).match(/\?(?::|<?[!=])/);
    if (match) {
      if (match[0].contains(":")) {
        token.type = "NonCapturingGroup";
      } else {
        token.type = match[0].contains("=") ? "Positive" : "Negative";
        token.type += "Look" + (match[0].contains("<") ? "Behind" : "Ahead");

        if (match[0].contains("<"))
          token.error = "lookbehind";
      }

      token.loc.end += match[0].length;
    } else {
      token.type = "CapturingGroup";
    }
  };

  prototype.parseQuantifier = function(str, token) {
    switch (str[token.loc.start]) {
      case "?":
        token.type = "OptionalQuantifier";
        break;

      case "*":
        token.type = "ZeroOrMoreQuantifier";
        break;

      case "+":
        token.type = "OneOrMoreQuantifier";
        break;

      case "{":
        let repetitions = str.substr(token.loc.start).match(/^\{(\d*)(?:,(\d*))?\}/);

        // If no repetition is found, the following characters are parsed normally without error
        if (!repetitions || (!repetitions[1] && !repetitions[2])) {
          return;
        }

        token.type = "RepetitionQuantifier";
        token.loc.end += repetitions[0].length - 1;

        if (repetitions[2] !== undefined) {
          token.type = "Variable" + token.type;
          token.min = repetitions[1] !== "" ? Number(repetitions[1]) : null;
          token.max = repetitions[2] !== "" ? Number(repetitions[2]) : null;

          if (token.min > token.max && token.max !== null) {
            token.error = "invalidRepetitionRange";
          }
        } else {
          token.type = "Fixed" + token.type;
          token.repetitions = Number(repetitions[1]);
        }
    }

    if (str[token.loc.end] === "?") {
      token.lazy = true;
      token.loc.end++;
    } else {
      token.lazy = false;
    }
  };

  prototype.parseEscapeSequence = function(str, token) {
    let escapeCharType = RegExpTokenizer.ESCAPE_CHARS[str[token.loc.end]];
    if (escapeCharType === undefined)
      escapeCharType = 4;
    token.type = RegExpTokenizer.ESCAPE_CHAR_TYPES[escapeCharType];
    switch (token.type) {
      case "ControlEscape":
      case "IdentityEscape":
      case "CharacterClassEscape":
        token.char = str[token.loc.end];
        if (token.type === "ControlEscape") {
          token.value = RegExpTokenizer.ESCACPE_CHAR_CODES[token.char];
        }

        // Catch backslashes standing at the end of the regexp
        if (token.loc.end === str.length) {
          token.error = "danglingBackslash";
        } else {
          token.loc.end++;
        }
        break;

      case "ControlLetterEscape":
        token.char = str[token.loc.end + 1] || "";
        if (token.char !== "") {
          token.value = token.char.charCodeAt(0) - 64;
          token.loc.end += 2;
        } else {
          token.loc.end++;
        }
        break;

      case "HexEscapeSequence": {
          let sequence = str.substr(token.loc.end + 1, 2);

          // Only if the four characters following the backslash are a hex value the token is
          // recognized as Unicode escape sequence, otherwise it's interpreted as identity escape
          if (sequence.match(/^[a-f0-9]{2}$/)) {
            token.sequence = sequence;
            token.loc.end += 3;
          } else {
            token.type = "IdentityEscape";
            token.char = "x";
            token.loc.end++;
          }
        }
        break;

      case "UnicodeEscapeSequence": {
          let sequence = str.substr(token.loc.end + 1, 4);

          // Only if the four characters following the backslash are a hex value the token is
          // recognized as Unicode escape sequence, otherwise it's interpreted as identity escape
          if (sequence.match(/^[a-f0-9]{4}$/)) {
            token.sequence = sequence;
            token.loc.end += 5;
          } else {
            token.type = "IdentityEscape";
            token.char = "u";
            token.loc.end++;
          }
        }
        break;
    }

    return true;
  };

  window.RegExpTokenizer = RegExpTokenizer;
})();<|endoftext|>


<commit_before>$(function () {

    // Initialize WYSIWYG editors.
    $('textarea[data-provides="anomaly.field_type.wysiwyg"]').each(function () {

        /**
         * Gather available buttons / plugins.
         */
        var buttons = $(this).data('available_buttons');
        var plugins = $(this).data('available_plugins');

        $(this).redactor({

            element: $(this),

            /**
             * Initialize the editor icons.
             */
            callbacks: {
                init: function () {

                    var icons = {};

                    $.each(buttons, function (k, v) {
                        if (v.icon) {
                            icons[v.button ? v.button : k] = '';
                        }
                    });

                    $.each(plugins, function (k, v) {
                        if (v.icon) {
                            icons[v.button ? v.button : k] = '';
                        }
                    });

                    $.each(this.button.all(), $.proxy(function (i, s) {

                        var key = $(s).attr('rel');

                        if (typeof icons[key] !== 'undefined') {
                            var icon = icons[key];
                            var button = this.button.get(key);
                            this.button.setIcon(button, icon);
                        }

                    }, this));
                }
            },

            /**
             * Settings
             */
            script: false,
            cleanOnPaste: true,
            replaceTags: [
                ['strike', 'del'],
                ['i', 'em'],
                ['b', 'strong'],
                ['big', 'strong'],
                ['strike', 'del']
            ],
            removeEmpty: ['strong', 'em', 'span', 'p'],

            /**
             * Features
             */
            minHeight: $(this).data('height'),
            placeholder: $(this).attr('placeholder'),
            folders: $(this).data('folders').toString().split(','),
            buttons: $(this).data('buttons').toString().split(','),
            plugins: $(this).data('plugins').toString().split(',')
        });
    });
});
<commit_msg>Prevents WYSIWYG editor from removing empty spans.<commit_after>$(function () {

    // Initialize WYSIWYG editors.
    $('textarea[data-provides="anomaly.field_type.wysiwyg"]').each(function () {

        /**
         * Gather available buttons / plugins.
         */
        var buttons = $(this).data('available_buttons');
        var plugins = $(this).data('available_plugins');

        $(this).redactor({

            element: $(this),

            /**
             * Initialize the editor icons.
             */
            callbacks: {
                init: function () {

                    var icons = {};

                    $.each(buttons, function (k, v) {
                        if (v.icon) {
                            icons[v.button ? v.button : k] = '';
                        }
                    });

                    $.each(plugins, function (k, v) {
                        if (v.icon) {
                            icons[v.button ? v.button : k] = '';
                        }
                    });

                    $.each(this.button.all(), $.proxy(function (i, s) {

                        var key = $(s).attr('rel');

                        if (typeof icons[key] !== 'undefined') {
                            var icon = icons[key];
                            var button = this.button.get(key);
                            this.button.setIcon(button, icon);
                        }

                    }, this));
                }
            },

            /**
             * Settings
             */
            script: false,
            cleanOnPaste: true,
            replaceTags: [
                ['strike', 'del'],
                ['i', 'em'],
                ['b', 'strong'],
                ['big', 'strong'],
                ['strike', 'del']
            ],
            removeEmpty: ['strong', 'em', 'p'],

            /**
             * Features
             */
            minHeight: $(this).data('height'),
            placeholder: $(this).attr('placeholder'),
            folders: $(this).data('folders').toString().split(','),
            buttons: $(this).data('buttons').toString().split(','),
            plugins: $(this).data('plugins').toString().split(',')
        });
    });
});
<|endoftext|>


<commit_before>'use strict';

/**
 *  ---- SUMMARY ----
 * RoomPosition prototypes :
 *
 * 12 - bordersAvoid() ---> make costMatrix avoid borders of pos.
 * 30 - getInternPath --->
 *
 * */

RoomPosition.prototype.bordersAvoid = function() {
  let i = 0;
  let delta = [
    [0, -1],
    [1, -1],
    [1, 0],
    [1, 1],
    [0, 1],
    [-1, 1],
    [-1, 0],
    [-1, -1]
  ];
  while (i < 8) {
    Game.rooms[this.roomName].CostMatrix.set(this.x + delta[i][0], this.y + delta[i][1], 250);
    i++;
  }
};

RoomPosition.prototype.getInternPath = function(target) {

  if (_.isString(target)) {
    target = Game.getObjectById(target).pos;//.findNearestTerrain(1);
  }

  if (!target || target.roomName !== this.roomName) {
    return false;
  }
  let range = 0;
  if (target.lookFor(LOOK_STRUCTURES).length
        || target.lookFor(LOOK_TERRAIN) === 'wall') {
            range = 1
        }

  return PathFinder.search(
    new RoomPosition(this.x, this.y, this.roomName), {
      pos: target,
      range: range
    }, {
      maxRooms: 1,
      swampCost: config.layout.swampCost,
      plainCost: config.layout.plainCost,
      roomCallback: (roomName) => Game.rooms[this.roomName].CostMatrix.clone()
    }
  ).path;
};<commit_msg>Fix wall cost reduced<commit_after>'use strict';

/**
 *  ---- SUMMARY ----
 * RoomPosition prototypes :
 *
 * 12 - bordersAvoid() ---> make costMatrix avoid borders of pos.
 * 30 - getInternPath --->
 *
 * */

RoomPosition.prototype.bordersAvoid = function() {
  let i = 0;
  let delta = [
    [0, -1],
    [1, -1],
    [1, 0],
    [1, 1],
    [0, 1],
    [-1, 1],
    [-1, 0],
    [-1, -1]
  ];
  while (i < 8) {
    pos = this;
    pos.x += delta[i][0];
    pos.y += delta[i][1];
    if (pos.lookFor(LOOK_TERRAIN) !== 'wall') {
        Game.rooms[this.roomName].CostMatrix.set(pos.x , pos.y, 250);
    }
    i++;
  }
};

RoomPosition.prototype.getInternPath = function(target) {

  if (_.isString(target)) {
    target = Game.getObjectById(target).pos;//.findNearestTerrain(1);
  }

  if (!target || target.roomName !== this.roomName) {
    return false;
  }
  let range = 0;
  if (target.lookFor(LOOK_STRUCTURES).length
        || target.lookFor(LOOK_TERRAIN) === 'wall') {
            range = 1
        }

  return PathFinder.search(
    new RoomPosition(this.x, this.y, this.roomName), {
      pos: target,
      range: range
    }, {
      maxRooms: 1,
      swampCost: config.layout.swampCost,
      plainCost: config.layout.plainCost,
      roomCallback: (roomName) => Game.rooms[this.roomName].CostMatrix.clone()
    }
  ).path;
};
<|endoftext|>


<commit_before>const express = require('express');
const router = express.Router();
const conn = require(process.env.PWD + '/conn');
const Util = require(process.env.PWD + '/util/Util')
const mailSender = require(process.env.PWD + '/util/MailSender')
const fe = require(process.env.PWD + '/model/FinishEvent')
const mi = require(process.env.PWD + '/model/MenuItem')
const m = require(process.env.PWD + '/model/MailSender')
const u = require(process.env.PWD + '/model/User')
const f = require(process.env.PWD + '/model/Find')
const fs = require('fs');
const moment = require('moment');
const md5 = require('md5');
const pdf = require('html-pdf');
const A4option = require(process.env.PWD + '/views/report/A4config')

router.get('/', fe.getAllFinishEvent, fe.filterEvents, u.allActive, function(req, res, next) {
  let flashMsg = req.session.flashMsg
  if(flashMsg) delete req.session.flashMsg
  req.allEventToFinish.map(e => {
    e.Type === 'I' ? e.Type = 'Internal' : e.Type = 'External'
    e.ResponsibleByName = Util.toTitleCase(e.ResponsibleByName)
    e.CreatedByName = Util.toTitleCase(e.CreatedByName)
    e.start = moment(e.start).format('DD/MM/YYYY HH:mm')
    e.end = moment(e.end).format('DD/MM/YYYY HH:mm')
    e.title = Util.toTitleCase(e.title)
  })
  res.render('finish-event/finish-event',{
    sess:req.session,
    allEventToFinish:req.allEventToFinish,
    allActiveUser:req.allActiveUser,
    flashMsg
  })
}).post('/searchFiltered', fe.getAllFinishEvent, fe.filterEvents, function(req, res, next) {
  req.allEventToFinish.map(e => {
    e.Type === 'I' ? e.Type = 'Internal' : e.Type = 'External'
    e.ResponsibleByName = Util.toTitleCase(e.ResponsibleByName)
    e.CreatedByName = Util.toTitleCase(e.CreatedByName)
    e.start = moment(e.start).format('DD/MM/YYYY HH:mm')
    e.end = moment(e.end).format('DD/MM/YYYY HH:mm')
    e.title = Util.toTitleCase(e.title)
  })

  res.json(req.allEventToFinish)
}).post('/write-down-page', fe.productOfEvent, mi.getAllProduct, function(req,res,next){
  res.render('finish-event/write-down-page',{
    sess:req.session,
    camposAproveitados:req.body,
    products: req.products,
    allProducts: req.allProduct
  })
// }).post('/close-event', fe.updateItemsFinishEvent, fe.updateStatusEvent, f.searchEventByCode, mi.productOfEvent, f.getRecipientsEmail, function(req,res,next){
}).post('/close-event', fe.clearProduct, fe.addProductUpdated, fe.updateStatusEvent, f.searchEventByCode, mi.productOfEvent, f.getRecipientsEmail, function(req,res,next){
  req.session.flashMsg = {
    type: 'alert-success',
    statusName: 'Finalized',
    text: req.body.EventCode
  }
  req.findEventByCode.msgDefault = 'Finalizado por: ' + req.findEventByCode.FinishedByMatricula_ID
  m.finishEvent(req.findEventByCode)
  console.log(req.body)
  res.redirect('/finish-event')
})

module.exports = router;
<commit_msg>filter after finish event<commit_after>const express = require('express');
const router = express.Router();
const conn = require(process.env.PWD + '/conn');
const Util = require(process.env.PWD + '/util/Util')
const mailSender = require(process.env.PWD + '/util/MailSender')
const fe = require(process.env.PWD + '/model/FinishEvent')
const mi = require(process.env.PWD + '/model/MenuItem')
const m = require(process.env.PWD + '/model/MailSender')
const u = require(process.env.PWD + '/model/User')
const f = require(process.env.PWD + '/model/Find')
const fs = require('fs');
const moment = require('moment');
const md5 = require('md5');
const pdf = require('html-pdf');
const A4option = require(process.env.PWD + '/views/report/A4config')

router.get('/', fe.getAllFinishEvent, fe.filterEvents, u.allActive, function(req, res, next) {
  let flashMsg = req.session.flashMsg
  if(flashMsg) delete req.session.flashMsg
  req.allEventToFinish.map(e => {
    e.Type === 'I' ? e.Type = 'Internal' : e.Type = 'External'
    e.ResponsibleByName = Util.toTitleCase(e.ResponsibleByName)
    e.CreatedByName = Util.toTitleCase(e.CreatedByName)
    e.start = moment(e.start).format('DD/MM/YYYY HH:mm')
    e.end = moment(e.end).format('DD/MM/YYYY HH:mm')
    e.title = Util.toTitleCase(e.title)
  })
  res.render('finish-event/finish-event',{
    sess:req.session,
    allEventToFinish:req.allEventToFinish,
    allActiveUser:req.allActiveUser,
    flashMsg
  })
}).post('/searchFiltered', fe.getAllFinishEvent, fe.filterEvents, function(req, res, next) {
  req.allEventToFinish.map(e => {
    e.Type === 'I' ? e.Type = 'Internal' : e.Type = 'External'
    e.ResponsibleByName = Util.toTitleCase(e.ResponsibleByName)
    e.CreatedByName = Util.toTitleCase(e.CreatedByName)
    e.start = moment(e.start).format('DD/MM/YYYY HH:mm')
    e.end = moment(e.end).format('DD/MM/YYYY HH:mm')
    e.title = Util.toTitleCase(e.title)
  })

  res.json(req.allEventToFinish)
}).post('/write-down-page', fe.productOfEvent, mi.getAllProduct, function(req,res,next){
  req.session.finishFilters = req.session.backupFinishFilters
  res.render('finish-event/write-down-page',{
    sess:req.session,
    camposAproveitados:req.body,
    products: req.products,
    allProducts: req.allProduct
  })
// }).post('/close-event', fe.updateItemsFinishEvent, fe.updateStatusEvent, f.searchEventByCode, mi.productOfEvent, f.getRecipientsEmail, function(req,res,next){
}).post('/close-event', fe.clearProduct, fe.addProductUpdated, fe.updateStatusEvent, f.searchEventByCode, mi.productOfEvent, f.getRecipientsEmail, function(req,res,next){
  req.session.flashMsg = {
    type: 'alert-success',
    statusName: 'Finalized',
    text: req.body.EventCode
  }
  req.findEventByCode.msgDefault = 'Finalizado por: ' + req.findEventByCode.FinishedByMatricula_ID
  m.finishEvent(req.findEventByCode)
  console.log(req.body)
  res.redirect('/finish-event')
})

module.exports = router;
<|endoftext|>


<commit_before>const fs = require('fs')
const models = require('../models/index')
const utils = require('../lib/utils')
const insecurity = require('../lib/insecurity')
const jade = require('jade')
const config = require('config')

module.exports = function getUserProfile () {
  return (req, res, next) => {
    fs.readFile('views/userProfile.jade', function (err, buf) {
      if (err) throw err
      const loggedInUser = insecurity.authenticatedUsers.get(req.cookies.token)
      if (loggedInUser) {
        models.User.findByPk(loggedInUser.data.id).then(user => {
          let jadeTemplate = buf.toString()
          let username = user.dataValues.username
          if (username.match(/#\{(.*)\}/) !== null && !utils.disableOnContainerEnv()) {
            req.app.locals.abused_ssti_bug = true
            const code = username.substring(2, username.length - 1)
            try {
              eval(code) // eslint-disable-line no-eval
            } catch (err) {
              username = '\\' + username
            }
          } else {
            username = '\\' + username
          }
          jadeTemplate = jadeTemplate.replace(/_username_/g, username)
          jadeTemplate = jadeTemplate.replace(/_emailHash_/g, insecurity.hash(user.dataValues.email))
          jadeTemplate = jadeTemplate.replace(/_title_/g, config.get('application.name'))
          jadeTemplate = jadeTemplate.replace(/_favicon_/g, favicon())
          jadeTemplate = jadeTemplate.replace(/_bgColor_/g, 'black')
          jadeTemplate = jadeTemplate.replace(/_textColor_/g, '#9d9d9d')
          jadeTemplate = jadeTemplate.replace(/_navColor_/g, '#263238')
          const fn = jade.compile(jadeTemplate)
          res.send(fn(user.dataValues))
        }).catch(error => {
          next(error)
        })
      } else {
        next(new Error('Blocked illegal activity by ' + req.connection.remoteAddress))
      }
    })
  }

  function favicon () {
    let icon = config.get('application.favicon')
    icon = decodeURIComponent(icon.substring(icon.lastIndexOf('/') + 1))
    return icon
  }
}
<commit_msg>Apply colors matching selected theme<commit_after>const fs = require('fs')
const models = require('../models/index')
const utils = require('../lib/utils')
const insecurity = require('../lib/insecurity')
const jade = require('jade')
const config = require('config')

const themes = {
  'bluegrey-lightgreen': {
    bgColor: '#303030',
    textColor: '#FFFFFF',
    navColor: '#546E7A'
  },
  'blue-lightblue': {
    bgColor: '#FAFAFA',
    textColor: '#000000',
    navColor: '#1976D2'
  },
  'deeppurple-amber': {
    bgColor: '#FAFAFA',
    textColor: '#000000',
    navColor: '#673AB7'
  },
  'indigo-pink': {
    bgColor: '#FAFAFA',
    textColor: '#000000',
    navColor: '#3F51B5'
  },
  'pink-bluegrey': {
    bgColor: '#303030',
    textColor: '#FFFFFF',
    navColor: '#C2185B'
  },
  'purple-green': {
    bgColor: '#303030',
    textColor: '#FFFFFF',
    navColor: '#7B1FA2'
  },
  'deeporange-indigo': {
    bgColor: '#FAFAFA',
    textColor: '#000000',
    navColor: '#E64A19'
  }
}

module.exports = function getUserProfile () {
  return (req, res, next) => {
    fs.readFile('views/userProfile.jade', function (err, buf) {
      if (err) throw err
      const loggedInUser = insecurity.authenticatedUsers.get(req.cookies.token)
      if (loggedInUser) {
        models.User.findByPk(loggedInUser.data.id).then(user => {
          let jadeTemplate = buf.toString()
          let username = user.dataValues.username
          if (username.match(/#\{(.*)\}/) !== null && !utils.disableOnContainerEnv()) {
            req.app.locals.abused_ssti_bug = true
            const code = username.substring(2, username.length - 1)
            try {
              eval(code) // eslint-disable-line no-eval
            } catch (err) {
              username = '\\' + username
            }
          } else {
            username = '\\' + username
          }
          const theme = themes[config.get('application.theme')]
          jadeTemplate = jadeTemplate.replace(/_username_/g, username)
          jadeTemplate = jadeTemplate.replace(/_emailHash_/g, insecurity.hash(user.dataValues.email))
          jadeTemplate = jadeTemplate.replace(/_title_/g, config.get('application.name'))
          jadeTemplate = jadeTemplate.replace(/_favicon_/g, favicon())
          jadeTemplate = jadeTemplate.replace(/_bgColor_/g, theme.bgColor)
          jadeTemplate = jadeTemplate.replace(/_textColor_/g, theme.textColor)
          jadeTemplate = jadeTemplate.replace(/_navColor_/g, theme.navColor)
          const fn = jade.compile(jadeTemplate)
          res.send(fn(user.dataValues))
        }).catch(error => {
          next(error)
        })
      } else {
        next(new Error('Blocked illegal activity by ' + req.connection.remoteAddress))
      }
    })
  }

  function favicon () {
    let icon = config.get('application.favicon')
    icon = decodeURIComponent(icon.substring(icon.lastIndexOf('/') + 1))
    return icon
  }
}
<|endoftext|>


<commit_before>// this begins the hiding process of things we want exposed to our
// internal code but not to the outside world (i.e. the application)
(function($)
{
	// internally map the tiRuntime object into a local variable
	// so we can continue to use it locally within our titanium.js
	// however, we want to remove it from the global scope so that
	// it's not accessible outside (i.e. the application)
	var runtime = tiRuntime;
	window.tiRuntime = null;

	// map our main native objects into the ti namespace
	// and hang ti off the global scope
	window.ti =
	{
		version: "<%=version%>",	// replaced at build time
		platform: "unknown",		// replaced below
		App: runtime.App,
		Dock: runtime.Dock,
		Menu: runtime.Menu,
		Window: runtime.Window
	};

	if (navigator.appVersion.indexOf("Win")!=-1) ti.platform = "win32";
	if (navigator.appVersion.indexOf("Mac")!=-1) ti.platform = "osx";
	if (navigator.appVersion.indexOf("Linux")!=-1) ti.platform = "linux";

	// expose jQuery into the ti namespace in case anyone wants to use
	// it outside of titanium.js
	ti.jQuery = $;

	// in the case that you have transparency on the window, we have to hack the browser's window
	// to cause the content view to take up 100% of the window (normally in HTML, it will only
	// take up the height of the contained element size)
	var windowOptions = ti.Window.getMainWindow();
	var windowTransparency = windowOptions.getTransparency();
	if (windowTransparency < 1.0)
 	{
		var style = "margin:auto;padding:auto;";
		if (windowOptions.isUsingChrome())
		{
			// if we're using custom chrome, setup without borders, margin, etc.
			style = "margin:0;padding:0;border:none;";
		}
		document.write('<style>body { opacity:' + windowTransparency + '; ' + style + ' } body > DIV { height:100% }</style>');
	}

	var readies = [], loaded = false;

	ti.ready = function(fn)
	{
		if (!loaded)
		{
			readies.push(fn);
		}
		else
		{
			// make the scope the ti object and pass
			// a reference to our jQuery pointer
			try
			{
				fn.call(ti,$);
			}
			catch (EE)
			{
				alert("Exception caught in ti.ready handler. Exception was: "+EE+" at line: "+EE.line);
			}
		}
	};

	$(function()
	{
		try
		{
			// we wrap in a try/catch<commit_msg>update to use new mainWindow/currentWindow API<commit_after>// this begins the hiding process of things we want exposed to our
// internal code but not to the outside world (i.e. the application)
(function($)
{
	// internally map the tiRuntime object into a local variable
	// so we can continue to use it locally within our titanium.js
	// however, we want to remove it from the global scope so that
	// it's not accessible outside (i.e. the application)
	var runtime = tiRuntime;
	window.tiRuntime = null;

	// map our main native objects into the ti namespace
	// and hang ti off the global scope
	window.ti =
	{
		version: "<%=version%>",	// replaced at build time
		platform: "unknown",		// replaced below
		App: runtime.App,
		Dock: runtime.Dock,
		Menu: runtime.Menu,
		Window: runtime.Window
	};

	if (navigator.appVersion.indexOf("Win")!=-1) ti.platform = "win32";
	if (navigator.appVersion.indexOf("Mac")!=-1) ti.platform = "osx";
	if (navigator.appVersion.indexOf("Linux")!=-1) ti.platform = "linux";

	// expose jQuery into the ti namespace in case anyone wants to use
	// it outside of titanium.js
	ti.jQuery = $;

	// in the case that you have transparency on the window, we have to hack the browser's window
	// to cause the content view to take up 100% of the window (normally in HTML, it will only
	// take up the height of the contained element size)
	var windowTransparency = ti.Window.currentWindow.getTransparency();
	if (windowTransparency < 1.0)
 	{
		var style = "margin:auto;padding:auto;";
		if (ti.Window.currentWindow.isUsingChrome())
		{
			// if we're using custom chrome, setup without borders, margin, etc.
			style = "margin:0;padding:0;border:none;";
		}
		document.write('<style>body { opacity:' + windowTransparency + '; ' + style + ' } body > DIV { height:100% }</style>');
	}

	var readies = [], loaded = false;

	ti.ready = function(fn)
	{
		if (!loaded)
		{
			readies.push(fn);
		}
		else
		{
			// make the scope the ti object and pass
			// a reference to our jQuery pointer
			try
			{
				fn.call(ti,$);
			}
			catch (EE)
			{
				alert("Exception caught in ti.ready handler. Exception was: "+EE+" at line: "+EE.line);
			}
		}
	};

	$(function()
	{
		try
		{
			// we wrap in a try/catch<|endoftext|>


<commit_before>/*jslint plusplus: true, vars: true, nomen: true */
/*global brackets, define */

// this file was composed with a big help from @MiguelCastillo extension Brackets-InteractiveLinter
// @see https://github.com/MiguelCastillo/Brackets-InteractiveLinter

define(function (require, exports) {
    "use strict";

    var _                   = brackets.getModule("thirdparty/lodash"),
        DocumentManager     = brackets.getModule("document/DocumentManager"),
        EditorManager       = brackets.getModule("editor/EditorManager"),
        Main                = require("./Main"),
        Preferences         = require("./Preferences");

    var cm = null,
        results = null,
        gutterName = "brackets-git-gutter",
        openWidgets = [];

    function clearOld() {
        var gutters = cm.getOption("gutters").slice(0),
            io = gutters.indexOf(gutterName);
        if (io !== -1) {
            gutters.splice(io, 1);
            cm.clearGutter(gutterName);
            cm.setOption("gutters", gutters);
            cm.off("gutterClick", gutterClick);
        }
        openWidgets.forEach(function (w) {
            if (w.visible) {
                w.visible = false;
                w.widget.clear();
            }
        });
    }

    function prepareGutter(_cm) {
        // if new instance is different from the old one, clean the old one
        if (cm && cm !== _cm) {
            clearOld();
        }
        cm = _cm;

        // if called with null, just clean the old instance
        if (!cm) {
            return;
        }

        // add our gutter if its not already available
        var gutters = cm.getOption("gutters").slice(0);
        if (gutters.indexOf(gutterName) === -1) {
            gutters.unshift(gutterName);
            cm.setOption("gutters", gutters);
            cm.on("gutterClick", gutterClick);
        }
    }

    function showGutters(_cm, _results) {
        prepareGutter(_cm);
        results = _results;
        //-
        cm.clearGutter(gutterName);
        results.forEach(function (obj) {
            cm.setGutterMarker(obj.line, gutterName, $("

")[0]);
        });
    }

    function gutterClick(cm, lineIndex, gutterId) {
        if (gutterId !== gutterName && gutterId !== "CodeMirror-linenumbers") {
            return;
        }

        var mark = _.find(results, function (o) { return o.line === lineIndex; });
        if (!mark) { return; }
        if (mark.parentMark) { mark = mark.parentMark; }

        if (!mark.lineWidget) {
            mark.lineWidget = {
                visible: false,
                element: $("

")
            };
            $("
").text(mark.content).appendTo(mark.lineWidget.element);
        }

        if (mark.lineWidget.visible !== true) {
            mark.lineWidget.visible = true;
            mark.lineWidget.widget = cm.addLineWidget(mark.line, mark.lineWidget.element[0], {
                coverGutter: false,
                noHScroll: false,
                above: true,
                showIfHidden: false
            });
            openWidgets.push(mark.lineWidget);
        } else {
            mark.lineWidget.visible = false;
            mark.lineWidget.widget.clear();
        }
    }

    function refresh() {
        if (!Preferences.get("useGitGutter")) {
            return;
        }

        var currentDoc = DocumentManager.getCurrentDocument();
        if (!currentDoc) { return; }

        var editor = EditorManager.getActiveEditor();
        if (!editor || !editor._codeMirror) {
            return;
        }
        prepareGutter(editor._codeMirror);

        var filename = currentDoc.file.fullPath.substring(Main.getProjectRoot().length);
        Main.gitControl.gitDiff(filename).then(function (diff) {
            var added = [],
                removed = [],
                modified = [],
                changesets = diff.split(/\n@@/).map(function (str) { return "@@" + str; });

            // remove part before first
            changesets.shift();

            changesets.forEach(function (str) {
                var m = str.match(/^@@ -([,0-9]+) \+([,0-9]+) @@/);
                var s1 = m[1].split(",");
                var s2 = m[2].split(",");

                // removed stuff
                var lineRemovedFrom;
                var lineFrom = parseInt(s2[0], 10);
                var lineCount = parseInt(s1[1], 10);
                if (isNaN(lineCount)) { lineCount = 1; }
                if (lineCount > 0) {
                    lineRemovedFrom = lineFrom > 0 ? lineFrom - 1 : 0;
                    removed.push({
                        type: "removed",
                        line: lineRemovedFrom,
                        content: str.split("\n")
                                    .filter(function (l) { return l.indexOf("-") === 0; })
                                    .map(function (l) { return l.substring(1); })
                                    .join("\n")
                    });
                }

                // added stuff
                lineFrom = parseInt(s2[0], 10);
                lineCount = parseInt(s2[1], 10);
                if (isNaN(lineCount)) { lineCount = 1; }
                var isModified = false;
                for (var i = lineFrom, lineTo = lineFrom + lineCount; i < lineTo; i++) {
                    var lineNo = i > 0 ? i - 1 : 0;
                    if (lineNo === lineRemovedFrom) {
                        // modified
                        var o = removed.pop();
                        o.type = "modified";
                        modified.push(o);
                        isModified = o;
                    } else {
                        // added new
                        added.push({
                            type: isModified ? "modified" : "added",
                            line: lineNo,
                            parentMark: isModified || null
                        });
                    }
                }
            });

            // fix displaying of removed lines
            removed.forEach(function (o) {
                o.line = o.line + 1;
            });

            showGutters(editor._codeMirror, [].concat(added, removed, modified));
        });
    }

    // API
    exports.refresh = refresh;

});
<commit_msg>Fix #135<commit_after>/*jslint plusplus: true, vars: true, nomen: true */
/*global brackets, define */

// this file was composed with a big help from @MiguelCastillo extension Brackets-InteractiveLinter
// @see https://github.com/MiguelCastillo/Brackets-InteractiveLinter

define(function (require, exports) {
    "use strict";

    var _                   = brackets.getModule("thirdparty/lodash"),
        DocumentManager     = brackets.getModule("document/DocumentManager"),
        EditorManager       = brackets.getModule("editor/EditorManager"),
        Main                = require("./Main"),
        Preferences         = require("./Preferences");

    var cm = null,
        results = null,
        gutterName = "brackets-git-gutter",
        openWidgets = [];

    function clearWidgets() {
        var lines = openWidgets.map(function (mark) {
            var w = mark.lineWidget;
            if (w.visible) {
                w.visible = false;
                w.widget.clear();
            }
            return mark.line;
        });
        openWidgets = [];
        return lines;
    }

    function clearOld() {
        var gutters = cm.getOption("gutters").slice(0),
            io = gutters.indexOf(gutterName);
        if (io !== -1) {
            gutters.splice(io, 1);
            cm.clearGutter(gutterName);
            cm.setOption("gutters", gutters);
            cm.off("gutterClick", gutterClick);
        }
        clearWidgets();
    }

    function prepareGutter(_cm) {
        // if new instance is different from the old one, clean the old one
        if (cm && cm !== _cm) {
            clearOld();
        }
        cm = _cm;

        // if called with null, just clean the old instance
        if (!cm) {
            return;
        }

        // add our gutter if its not already available
        var gutters = cm.getOption("gutters").slice(0);
        if (gutters.indexOf(gutterName) === -1) {
            gutters.unshift(gutterName);
            cm.setOption("gutters", gutters);
            cm.on("gutterClick", gutterClick);
        }
    }

    function showGutters(_cm, _results) {
        prepareGutter(_cm);
        results = _results;

        // get line numbers of currently opened widgets
        var openBefore = clearWidgets();

        cm.clearGutter(gutterName);
        results.forEach(function (obj) {
            cm.setGutterMarker(obj.line, gutterName, $("

")[0]);
        });

        // reopen widgets that were opened before refresh
        openBefore.forEach(function (lineNumber) {
            gutterClick(cm, lineNumber, gutterName);
        });
    }

    function gutterClick(cm, lineIndex, gutterId) {
        if (gutterId !== gutterName && gutterId !== "CodeMirror-linenumbers") {
            return;
        }

        var mark = _.find(results, function (o) { return o.line === lineIndex; });
        if (!mark) { return; }
        if (mark.parentMark) { mark = mark.parentMark; }

        if (!mark.lineWidget) {
            mark.lineWidget = {
                visible: false,
                element: $("

")
            };
            $("
").text(mark.content).appendTo(mark.lineWidget.element);
        }

        if (mark.lineWidget.visible !== true) {
            mark.lineWidget.visible = true;
            mark.lineWidget.widget = cm.addLineWidget(mark.line, mark.lineWidget.element[0], {
                coverGutter: false,
                noHScroll: false,
                above: true,
                showIfHidden: false
            });
            openWidgets.push(mark);
        } else {
            mark.lineWidget.visible = false;
            mark.lineWidget.widget.clear();
            var io = openWidgets.indexOf(mark);
            if (io !== -1) {
                openWidgets.splice(io, 1);
            }
        }
    }

    function refresh() {
        if (!Preferences.get("useGitGutter")) {
            return;
        }

        var currentDoc = DocumentManager.getCurrentDocument();
        if (!currentDoc) { return; }

        var editor = EditorManager.getActiveEditor();
        if (!editor || !editor._codeMirror) {
            return;
        }
        prepareGutter(editor._codeMirror);

        var filename = currentDoc.file.fullPath.substring(Main.getProjectRoot().length);
        Main.gitControl.gitDiff(filename).then(function (diff) {
            var added = [],
                removed = [],
                modified = [],
                changesets = diff.split(/\n@@/).map(function (str) { return "@@" + str; });

            // remove part before first
            changesets.shift();

            changesets.forEach(function (str) {
                var m = str.match(/^@@ -([,0-9]+) \+([,0-9]+) @@/);
                var s1 = m[1].split(",");
                var s2 = m[2].split(",");

                // removed stuff
                var lineRemovedFrom;
                var lineFrom = parseInt(s2[0], 10);
                var lineCount = parseInt(s1[1], 10);
                if (isNaN(lineCount)) { lineCount = 1; }
                if (lineCount > 0) {
                    lineRemovedFrom = lineFrom > 0 ? lineFrom - 1 : 0;
                    removed.push({
                        type: "removed",
                        line: lineRemovedFrom,
                        content: str.split("\n")
                                    .filter(function (l) { return l.indexOf("-") === 0; })
                                    .map(function (l) { return l.substring(1); })
                                    .join("\n")
                    });
                }

                // added stuff
                lineFrom = parseInt(s2[0], 10);
                lineCount = parseInt(s2[1], 10);
                if (isNaN(lineCount)) { lineCount = 1; }
                var isModified = false;
                for (var i = lineFrom, lineTo = lineFrom + lineCount; i < lineTo; i++) {
                    var lineNo = i > 0 ? i - 1 : 0;
                    if (lineNo === lineRemovedFrom) {
                        // modified
                        var o = removed.pop();
                        o.type = "modified";
                        modified.push(o);
                        isModified = o;
                    } else {
                        // added new
                        added.push({
                            type: isModified ? "modified" : "added",
                            line: lineNo,
                            parentMark: isModified || null
                        });
                    }
                }
            });

            // fix displaying of removed lines
            removed.forEach(function (o) {
                o.line = o.line + 1;
            });

            showGutters(editor._codeMirror, [].concat(added, removed, modified));
        });
    }

    // API
    exports.refresh = refresh;

});
<|endoftext|>