<commit_before>"use strict";

var React                       = require('react-tools/build/modules/React'),
    parseInlineMarkup           = require('../parser').parseInlineMarkup,
    contentEditableLineMetrics  = require('../content-editable-line-metrics'),
    textNodes                   = require('../content-editable-text-nodes'),
    keys                        = require('../keys'),
    DOMObserver                 = require('../dom-observer'),
    Focusable                   = require('../focusable');

function isToken(node) {
  return node.dataset && node.dataset.token !== undefined;
}

function closestTextLeft(node) {
  while (node.previousSibling) {
    node = node.previousSibling
    if (!isToken(node)) return node;
  }
}

function closestTextRight(node) {
  while (node.nextSibling) {
    node = node.nextSibling;
    if (!isToken(node)) return node;
  }
}

module.exports = React.createClass({
  mixins: [Focusable, DOMObserver],

  getTokens: function() {
    return textNodes(this.getDOMNode(), true);
  },

  getCaretPosition: function() {
    return contentEditableLineMetrics(this.getDOMNode());
  },

  getCaretOffset: function(withMarkup) {
    var tokens = this.getTokens(),
        selection = document.getSelection(),
        node = selection.focusNode,
        offset = selection.focusOffset;

    if (!node)
      return;

    if (isToken(node.parentNode))
      node = node.parentNode;

    if (withMarkup) {
      return node.__totalIndex + offset;
    } else {
      if (isToken(node)) {
        var text = closestTextLeft(node);
        if (text)
          return text.__index + text.__length;
        var text = closestTextRight(node);
        if (text)
          return text.__index;
        return 0;
      } else {
        return node.__index + offset;
      }
    }

  },

  restoreCaretOffset: function() {
    var tokens = this.getTokens(),
        offset = this.state.focusOffset || 0;

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];
      if (offset >= token.__totalIndex &&
          offset <= token.__totalIndex + token.__length) {
        offset = offset - token.__totalIndex;
        setSingleRange(token, offset);
        return;
      }
    }

    // no node found, this was probably a whitespace node
    var node = this.getDOMNode(),
        lastToken = tokens[tokens.length - 1];

    if (lastToken) {
      setSingleRange(lastToken, lastToken.__length);
    } else {
      setSingleRange(node, 0);
    }
  },

  getContent: function(withMarkup) {
    var content = '';
    var tokens = this.getTokens();

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];
      if (token.dataset && token.dataset.token !== undefined) {
        if (withMarkup)
          content = content + token.textContent;
      } else {
        content = content + token.textContent;
      }
    }

    return content;
  },

  getAnnotations: function() {
    var annotations = [];
    var tokens = this.getTokens();

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];

      if (token.dataset && token.dataset.token !== undefined)
        continue;

      if (!token.parentNode)
        continue;

      var annotationType = token.parentNode.dataset.annotationType;

      if (annotationType && token.__length > 0)
        annotations.push({
          type: annotationType,
          range: [token.__index, token.__index + token.__length]
        });
    }

    return annotations;
  },

  getInitialState: function() {
    return {
      content: this.props.content,
      annotations: this.props.annotations,
      focusOffset: this.props.focusOffset
    }
  },

  componentWillReceiveProps: function(props) {
    this.state.content = props.content;
    this.state.annotations = props.annotations;
    this.state.focusOffset = props.focusOffset;
  },

  componentDidUpdate: function() {
    if (this.props.focus)
      this.restoreCaretOffset();
  },

  componentDidMount: function() {
    this.parse();
  },

  onDOMChanges: function() {
    this.parse();
  },

  parse: function() {
    var content = this.getContent(true);

    if (this.props.onMarkupUpdate && this.props.onMarkupUpdate(content))
      return;

    var update = parseInlineMarkup(content);

    if (this.props.onUpdate && this.props.onUpdate({
          content: update.content,
          annotations: update.annotations,
          markdown: content,
          focusOffset: this.getCaretOffset(true)
        }))
      return;

    this.setState({
      content: update.content,
      annotations: update.annotations,
      focusOffset: this.getCaretOffset(true)
    });
  },

  onSelect: function(e) {
    this.state.focusOffset = this.getCaretOffset(true);

    if (this.props.onSelect)
      this.props.onSelect(e)

    if (this.props.onCaretOffsetUpdate)
      this.props.onCaretOffsetUpdate(this.state.focusOffset)
  },

  onKeyDown: function(e) {
    if (e.keyCode === keys.SPACE) {
      var node = this.getDOMNode(),
          selection = document.getSelection(),
          tokens = this.getTokens(),
          lastToken = tokens[tokens.length - 1],
          token = selection.focusNode;

      while (token !== node && tokens.indexOf(token) === -1)
        token = token.parentNode;

      if (token === node) {
        e.preventDefault();
        document.execCommand('insertText', false, ' ');
      } else if (token.__totalIndex + selection.focusOffset ===
          lastToken.__totalIndex + lastToken.__length) {
        e.preventDefault();
        document.execCommand('insertText', false, ' ');
      }
    }

    if (this.props.onKeyDown)
      this.props.onKeyDown(e);
  },

  renderAnnotatedContent: function(content) {
    var text = this.state.content,
        annotations = this.state.annotations,
        nodes = [];

    // assume they are sorted by its range
    if (annotations && annotations.length > 0) {

      // part before the first annotation
      if (annotations[0].range[0] > 0) {
        nodes.push(genText(text.substring(0, annotations[0].range[0])));
      }

      var prev;

      for (var i = 0, length = annotations.length; i < length; i++) {
        var annotation = annotations[i];

        // part before the prev and the current annotation
        if (prev && annotation.range[0] - prev.range[1] >= 1) {
          nodes.push(genText(text.substring(prev.range[1], annotation.range[0])));
        }

        // annotation itself
        nodes.push(genAnnotation({
          type: annotation.type,
          content: text.substring.apply(text, annotation.range)
        }));

        prev = annotation;
      }

      // part after the last annotation
      var last = annotations[annotations.length - 1].range;
      if (last[1] < text.length) {
        nodes.push(genText(text.substring(last[1], text.length)));
      }

    } else {
      nodes.push(genText(text));
    }
    return nodes.join('');
  },

  render: function() {
    var content = this.renderAnnotatedContent();
    return this.transferPropsTo(
      React.DOM.div({
        contentEditable: "true",
        className: "Editor",
        onSelect: this.onSelect,
        onKeyDown: this.onKeyDown,
        dangerouslySetInnerHTML: {__html: content}
      }));
  }
});

function setSingleRange(node, offset) {
  var rng = document.createRange();
  rng.setStart(node, offset);
  rng.collapse(true);
  var selection = document.getSelection();
  selection.removeAllRanges();
  selection.addRange(rng);
}

function genAnnotation(annotation) {
  var inner;
  switch (annotation.type) {
    case 'em':
      inner = genToken('*') +
        annotation.content +
        genToken('*');
      break;
    case 'strong':
      inner = genToken('*') +
        genToken('*') +
        annotation.content +
        genToken('*') +
        genToken('*');
      break;
    case 'inlinecode':
      inner = genToken('`') +
        annotation.content +
        genToken('`');
      break;
    default:
      console.error('unknown annotation type');
  }
  return '' + inner + '';
}

function genToken(token) {
  return '' + token + '';
}

function genText(text) {
  return text
    .replace(/</g, '<')
    .replace(/>/g, '>');
}
<commit_msg>gardening<commit_after>"use strict";

var React                       = require('react-tools/build/modules/React'),
    parseInlineMarkup           = require('../parser').parseInlineMarkup,
    contentEditableLineMetrics  = require('../content-editable-line-metrics'),
    textNodes                   = require('../content-editable-text-nodes'),
    keys                        = require('../keys'),
    DOMObserver                 = require('../dom-observer'),
    Focusable                   = require('../focusable');

function isToken(node) {
  return node.dataset && node.dataset.token !== undefined;
}

function closestTextLeft(node) {
  while (node.previousSibling) {
    node = node.previousSibling
    if (!isToken(node)) return node;
  }
}

function closestTextRight(node) {
  while (node.nextSibling) {
    node = node.nextSibling;
    if (!isToken(node)) return node;
  }
}

module.exports = React.createClass({
  mixins: [Focusable, DOMObserver],

  getTokens: function() {
    return textNodes(this.getDOMNode(), true);
  },

  getCaretPosition: function() {
    var node = this.getDOMNode(),
        metrics = contentEditableLineMetrics(node);
    return metrics;
  },

  getCaretOffset: function(withMarkup) {
    var tokens = this.getTokens(),
        selection = document.getSelection(),
        node = selection.focusNode,
        offset = selection.focusOffset;

    if (!node)
      return;

    if (isToken(node.parentNode))
      node = node.parentNode;

    if (withMarkup) {
      return node.__totalIndex + offset;
    } else {
      if (isToken(node)) {
        var text = closestTextLeft(node);
        if (text)
          return text.__index + text.__length;
        var text = closestTextRight(node);
        if (text)
          return text.__index;
        return 0;
      } else {
        return node.__index + offset;
      }
    }

  },

  restoreCaretOffset: function() {
    var tokens = this.getTokens(),
        offset = this.state.focusOffset || 0;

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];
      if (offset >= token.__totalIndex &&
          offset <= token.__totalIndex + token.__length) {
        offset = offset - token.__totalIndex;
        setSingleRange(token, offset);
        return;
      }
    }

    // no node found, this was probably a whitespace node
    var node = this.getDOMNode(),
        lastToken = tokens[tokens.length - 1];

    if (lastToken) {
      setSingleRange(lastToken, lastToken.__length);
    } else {
      setSingleRange(node, 0);
    }
  },

  getContent: function(withMarkup) {
    var content = '';
    var tokens = this.getTokens();

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];
      if (token.dataset && token.dataset.token !== undefined) {
        if (withMarkup)
          content = content + token.textContent;
      } else {
        content = content + token.textContent;
      }
    }

    return content;
  },

  getAnnotations: function() {
    var annotations = [];
    var tokens = this.getTokens();

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];

      if (token.dataset && token.dataset.token !== undefined)
        continue;

      if (!token.parentNode)
        continue;

      var annotationType = token.parentNode.dataset.annotationType;

      if (annotationType && token.__length > 0)
        annotations.push({
          type: annotationType,
          range: [token.__index, token.__index + token.__length]
        });
    }

    return annotations;
  },

  getInitialState: function() {
    return {
      content: this.props.content,
      annotations: this.props.annotations,
      focusOffset: this.props.focusOffset
    }
  },

  componentWillReceiveProps: function(props) {
    this.state.content = props.content;
    this.state.annotations = props.annotations;
    this.state.focusOffset = props.focusOffset;
  },

  componentDidUpdate: function() {
    if (this.props.focus)
      this.restoreCaretOffset();
  },

  componentDidMount: function() {
    this.parse();
  },

  onDOMChanges: function() {
    this.parse();
  },

  parse: function() {
    var content = this.getContent(true);

    if (this.props.onMarkupUpdate && this.props.onMarkupUpdate(content))
      return;

    var update = parseInlineMarkup(content);

    if (this.props.onUpdate && this.props.onUpdate({
          content: update.content,
          annotations: update.annotations,
          markdown: content,
          focusOffset: this.getCaretOffset(true)
        }))
      return;

    this.setState({
      content: update.content,
      annotations: update.annotations,
      focusOffset: this.getCaretOffset(true)
    });
  },

  onSelect: function(e) {
    this.state.focusOffset = this.getCaretOffset(true);

    if (this.props.onSelect)
      this.props.onSelect(e)

    if (this.props.onCaretOffsetUpdate)
      this.props.onCaretOffsetUpdate(this.state.focusOffset)
  },

  onKeyDown: function(e) {
    if (e.keyCode === keys.SPACE) {
      var node = this.getDOMNode(),
          selection = document.getSelection(),
          tokens = this.getTokens(),
          lastToken = tokens[tokens.length - 1],
          token = selection.focusNode;

      while (token !== node && tokens.indexOf(token) === -1)
        token = token.parentNode;

      if (token === node) {
        e.preventDefault();
        document.execCommand('insertText', false, ' ');
      } else if (token.__totalIndex + selection.focusOffset ===
          lastToken.__totalIndex + lastToken.__length) {
        e.preventDefault();
        document.execCommand('insertText', false, ' ');
      }
    }

    if (this.props.onKeyDown)
      this.props.onKeyDown(e);
  },

  renderAnnotatedContent: function(content) {
    var text = this.state.content,
        annotations = this.state.annotations,
        nodes = [];

    // assume they are sorted by its range
    if (annotations && annotations.length > 0) {

      // part before the first annotation
      if (annotations[0].range[0] > 0) {
        nodes.push(genText(text.substring(0, annotations[0].range[0])));
      }

      var prev;

      for (var i = 0, length = annotations.length; i < length; i++) {
        var annotation = annotations[i];

        // part before the prev and the current annotation
        if (prev && annotation.range[0] - prev.range[1] >= 1) {
          nodes.push(genText(text.substring(prev.range[1], annotation.range[0])));
        }

        // annotation itself
        nodes.push(genAnnotation({
          type: annotation.type,
          content: text.substring.apply(text, annotation.range)
        }));

        prev = annotation;
      }

      // part after the last annotation
      var last = annotations[annotations.length - 1].range;
      if (last[1] < text.length) {
        nodes.push(genText(text.substring(last[1], text.length)));
      }

    } else {
      nodes.push(genText(text));
    }
    return nodes.join('');
  },

  render: function() {
    var content = this.renderAnnotatedContent();
    return this.transferPropsTo(
      React.DOM.div({
        contentEditable: "true",
        className: "Editor",
        onSelect: this.onSelect,
        onKeyDown: this.onKeyDown,
        dangerouslySetInnerHTML: {__html: content}
      }));
  }
});

function setSingleRange(node, offset) {
  var rng = document.createRange();
  rng.setStart(node, offset);
  rng.collapse(true);
  var selection = document.getSelection();
  selection.removeAllRanges();
  selection.addRange(rng);
}

function genAnnotation(annotation) {
  var inner;
  switch (annotation.type) {
    case 'em':
      inner = genToken('*') +
        annotation.content +
        genToken('*');
      break;
    case 'strong':
      inner = genToken('*') +
        genToken('*') +
        annotation.content +
        genToken('*') +
        genToken('*');
      break;
    case 'inlinecode':
      inner = genToken('`') +
        annotation.content +
        genToken('`');
      break;
    default:
      console.error('unknown annotation type');
  }
  return '' + inner + '';
}

function genToken(token) {
  return '' + token + '';
}

function genText(text) {
  return text
    .replace(/</g, '<')
    .replace(/>/g, '>');
}
<|endoftext|>


<commit_before><commit_msg>Use array index and record size to calculate offset for constructor.<commit_after><|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of getgauge/html-report.

// getgauge/html-report is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// getgauge/html-report is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with getgauge/html-report.  If not, see <http://www.gnu.org/licenses/>.

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
	var angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;

	return {
		x: centerX + (radius * Math.cos(angleInRadians)),
		y: centerY + (radius * Math.sin(angleInRadians))
	};
}

function describeArc(x, y, radius, startAngle, endAngle) {
	var start = polarToCartesian(x, y, radius, endAngle);
	var end = polarToCartesian(x, y, radius, startAngle);

	var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

	return [
		"M", start.x, start.y,
		"A", radius, radius, 0, arcSweep, 0, end.x, end.y,
		"L", x, y,
		"L", start.x, start.y, "Z"
	].join(" ");
}

function filterSpecList (status) {
    $('#listOfSpecifications li.spec-name').each(function() {
        if($(this).hasClass(status)) {
            $(this).show();
        }
        else {
            $(this).hide();
        }
    });
}

function showFirstSpecContent () {
    $('li.spec-name:visible:first').click();
    if($('li.spec-name:visible:first').length===0){
        $('#specificationContainer').hide();
    }
}

var initializers = {
    "initializeFilters" : function () {
        if (sessionStorage.FilterStatus) {
            filterSpecList(sessionStorage.FilterStatus);
            $('.spec-filter').each(function() {
                if ($(this).data('status')===sessionStorage.FilterStatus) {
                    $(this).addClass('active');
                }
            });
        }
        else {
            $('.total-specs').addClass('active');
        }
    },
    "attachScenarioToggle" : function () {
        $('.row-selector').click(function() {
            $('.row-selector').each(function() { $(this).removeClass('selected');});
            $(this).addClass('selected');
            var tr=$(this).data('rowindex');
            $(".scenario-container").each(function(){
                if($(this).data('tablerow')===tr) { $(this).show();} else {$(this).hide();}
            });
        });
    },
    "attachSpecFilter" : function () {
        var resetState = function() {
            $('.spec-filter, .total-specs').each(function(){
                $(this).removeClass('active');}
            );
        };
        $('.spec-filter, #pie-chart path.shadow').click(function(){
            resetState();
            var status = $(this).data('status');
            sessionStorage.FilterStatus = status;
            filterSpecList(status);
            showFirstSpecContent();
            $(this).addClass('active');
        });
        $('.total-specs').click(function () {
            resetState();
            $('#listOfSpecifications li.spec-name').each(function() {
                $(this).show();
            });
            sessionStorage.removeItem('FilterStatus');
            showFirstSpecContent();
            $(this).addClass('active');
        });
    },
    "registerHovercards" : function () {
        $('.hoverable').mouseenter(function(e) {
            $(this).next('.hovercard').css({top: e.clientY + 10, left: e.clientX +10}).delay(100).fadeIn();
        }).mouseleave(function() {
            $(this).next('.hovercard').delay(100).fadeOut('fast');
        });
    },
    "registerConceptToggle" : function () {
        $('.concept').click(function() {
            var conceptSteps = $(this).next('.concept-steps');
            var iconClass = $(conceptSteps).is(':visible') ? "plus" : "minus";
            $(conceptSteps).fadeToggle('fast', 'linear');
            $(this).find("i.fa").removeClass("fa-minus-square").removeClass("fa-plus-square").addClass("fa-"+iconClass+"-square");
        });
    },
    "registerMessageToggle" : function () {
        $('.message-container i.fa').click(function() {
            var messages = $(this).next('.messages');
            var iconClass = messages.is(':visible') ? "plus" : "minus";
            messages.fadeToggle('fast', 'linear');
            $(this).removeClass("fa-minus-square").removeClass("fa-plus-square").addClass("fa-"+iconClass+"-square");
        });
    },
    "registerErrorContainerToggle" : function() {
        $(".error-container .toggle-show").click(function(){
            var self = $(this);
            self.next('.exception-container').stop().toggleClass('hidden');
            self.text(self.text().indexOf("Show") >0  ? "[Hide details]" : "[Show details]");
        });
    },
    "registerSearch" : function () {
        $('#searchSpecifications').change(function() {
            if(!index) return;
            searchText = $(this).val();
            tagMatches = index.tags[searchText];
            specMatches=[];
            for(var spec in index.specs) {
                if(index.specs.hasOwnProperty(spec) && spec.startsWith(searchText)) {
                    index.specs[spec].forEach(function(x) {specMatches.push(x)});
                }
            }
            $(".spec-list a").each(function() {
								var relPath = $(this).attr('href').split("/");
								var fileName = relPath[relPath.length - 1];
								var existsIn = function(arr) {
										if (arr === undefined) {
												return false;
										}
										arr = $.grep(arr, function(a, i) {
												return a.indexOf(fileName) > -1;
										});
										return arr.length > 0;
								}
                if(existsIn(tagMatches) || existsIn(specMatches) || searchText === '') {
										$(this).show();
								}
                else{
                    $(this).hide();
								}
            })
            showFirstSpecContent();
        });
    },
    "registerSearchAutocomplete" : function () {
        new autoComplete({
            selector: 'input[id="searchSpecifications"]',
            minChars: 1,
            source: function(term, suggest){
                term = term.toLowerCase();
                var tagChoices = Object.keys(index.tags);
                var specChoices = Object.keys(index.specs);
                var suggestions = [];
                var suggestionPredicate = function(type) {
                    return function(x) {
                        if(x.toLowerCase().startsWith(term))
                            suggestions.push([x, type]);
                    }
                };
                tagChoices.forEach(suggestionPredicate("tag"));
                specChoices.forEach(suggestionPredicate("spec"));
                suggest(suggestions);
            },
            renderItem: function (item, search){
                iconClass = item[1] == "tag" ? "tags" : "bars"
                return '
 ' + item[0] + '
';
            },
            onSelect: function(e, term, item){
                $('#searchSpecifications').val($(item).data('value')).change();
            }
        });
    },
    "initializeClipboard" : function () {
        new Clipboard('.clipboard-btn');
    },
    "drawPieChart" : function () {
        var results = $("#pie-chart").data("results").split(",").map(Number);
        var total = Number($("#pie-chart").data("total"));
        var paths = $("#pie-chart path.status")
        var startAngle = 0;
        for(i=0;i<results.length;i++)
        {
            coveredAngle=startAngle + results[i]*360/total;
            if(total===results[i])
            coveredAngle-=0.05;
            $(paths[i]).attr('d',describeArc(200, 75, 72, startAngle, coveredAngle));
            $(paths[i]).next('path.shadow').attr('d',describeArc(200, 75, 75, startAngle, coveredAngle));
            if(results[i]===0 || total===results[i])
                $(paths[i]).attr('stroke-width',0);
            startAngle=coveredAngle;
        }
    }
};

$(function () {
    $.each(initializers, function(k,v){v();});
});<commit_msg>Format main.js<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of getgauge/html-report.

// getgauge/html-report is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// getgauge/html-report is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with getgauge/html-report.  If not, see <http://www.gnu.org/licenses/>.

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    var angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;

    return {
        x: centerX + (radius * Math.cos(angleInRadians)),
        y: centerY + (radius * Math.sin(angleInRadians))
    };
}

function describeArc(x, y, radius, startAngle, endAngle) {
    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    return [
        "M", start.x, start.y,
        "A", radius, radius, 0, arcSweep, 0, end.x, end.y,
        "L", x, y,
        "L", start.x, start.y, "Z"
    ].join(" ");
}

function filterSpecList(status) {
    $('#listOfSpecifications li.spec-name').each(function() {
        if ($(this).hasClass(status)) {
            $(this).show();
        } else {
            $(this).hide();
        }
    });
}

function showFirstSpecContent() {
    $('li.spec-name:visible:first').click();
    if ($('li.spec-name:visible:first').length === 0) {
        $('#specificationContainer').hide();
    }
}

var initializers = {
    "initializeFilters": function() {
        if (sessionStorage.FilterStatus) {
            filterSpecList(sessionStorage.FilterStatus);
            $('.spec-filter').each(function() {
                if ($(this).data('status') === sessionStorage.FilterStatus) {
                    $(this).addClass('active');
                }
            });
        } else {
            $('.total-specs').addClass('active');
        }
    },
    "attachScenarioToggle": function() {
        $('.row-selector').click(function() {
            $('.row-selector').each(function() { $(this).removeClass('selected'); });
            $(this).addClass('selected');
            var tr = $(this).data('rowindex');
            $(".scenario-container").each(function() {
                if ($(this).data('tablerow') === tr) { $(this).show(); } else { $(this).hide(); }
            });
        });
    },
    "attachSpecFilter": function() {
        var resetState = function() {
            $('.spec-filter, .total-specs').each(function() {
                $(this).removeClass('active');
            });
        };
        $('.spec-filter, #pie-chart path.shadow').click(function() {
            resetState();
            var status = $(this).data('status');
            sessionStorage.FilterStatus = status;
            filterSpecList(status);
            showFirstSpecContent();
            $(this).addClass('active');
        });
        $('.total-specs').click(function() {
            resetState();
            $('#listOfSpecifications li.spec-name').each(function() {
                $(this).show();
            });
            sessionStorage.removeItem('FilterStatus');
            showFirstSpecContent();
            $(this).addClass('active');
        });
    },
    "registerHovercards": function() {
        $('.hoverable').mouseenter(function(e) {
            $(this).next('.hovercard').css({ top: e.clientY + 10, left: e.clientX + 10 }).delay(100).fadeIn();
        }).mouseleave(function() {
            $(this).next('.hovercard').delay(100).fadeOut('fast');
        });
    },
    "registerConceptToggle": function() {
        $('.concept').click(function() {
            var conceptSteps = $(this).next('.concept-steps');
            var iconClass = $(conceptSteps).is(':visible') ? "plus" : "minus";
            $(conceptSteps).fadeToggle('fast', 'linear');
            $(this).find("i.fa").removeClass("fa-minus-square").removeClass("fa-plus-square").addClass("fa-" + iconClass + "-square");
        });
    },
    "registerMessageToggle": function() {
        $('.message-container i.fa').click(function() {
            var messages = $(this).next('.messages');
            var iconClass = messages.is(':visible') ? "plus" : "minus";
            messages.fadeToggle('fast', 'linear');
            $(this).removeClass("fa-minus-square").removeClass("fa-plus-square").addClass("fa-" + iconClass + "-square");
        });
    },
    "registerErrorContainerToggle": function() {
        $(".error-container .toggle-show").click(function() {
            var self = $(this);
            self.next('.exception-container').stop().toggleClass('hidden');
            self.text(self.text().indexOf("Show") > 0 ? "[Hide details]" : "[Show details]");
        });
    },
    "registerSearch": function() {
        $('#searchSpecifications').change(function() {
            if (!index) return;
            searchText = $(this).val();
            tagMatches = index.tags[searchText];
            specMatches = [];
            for (var spec in index.specs) {
                if (index.specs.hasOwnProperty(spec) && spec.startsWith(searchText)) {
                    index.specs[spec].forEach(function(x) { specMatches.push(x) });
                }
            }
            $(".spec-list a").each(function() {
                var relPath = $(this).attr('href').split("/");
                var fileName = relPath[relPath.length - 1];
                var existsIn = function(arr) {
                    if (arr === undefined) {
                        return false;
                    }
                    arr = $.grep(arr, function(a, i) {
                        return a.indexOf(fileName) > -1;
                    });
                    return arr.length > 0;
                }
                if (existsIn(tagMatches) || existsIn(specMatches) || searchText === '') {
                    $(this).show();
                } else {
                    $(this).hide();
                }
            })
            showFirstSpecContent();
        });
    },
    "registerSearchAutocomplete": function() {
        new autoComplete({
            selector: 'input[id="searchSpecifications"]',
            minChars: 1,
            source: function(term, suggest) {
                term = term.toLowerCase();
                var tagChoices = Object.keys(index.tags);
                var specChoices = Object.keys(index.specs);
                var suggestions = [];
                var suggestionPredicate = function(type) {
                    return function(x) {
                        if (x.toLowerCase().startsWith(term))
                            suggestions.push([x, type]);
                    }
                };
                tagChoices.forEach(suggestionPredicate("tag"));
                specChoices.forEach(suggestionPredicate("spec"));
                suggest(suggestions);
            },
            renderItem: function(item, search) {
                iconClass = item[1] == "tag" ? "tags" : "bars"
                return '
 ' + item[0] + '
';
            },
            onSelect: function(e, term, item) {
                $('#searchSpecifications').val($(item).data('value')).change();
            }
        });
    },
    "initializeClipboard": function() {
        new Clipboard('.clipboard-btn');
    },
    "drawPieChart": function() {
        var results = $("#pie-chart").data("results").split(",").map(Number);
        var total = Number($("#pie-chart").data("total"));
        var paths = $("#pie-chart path.status")
        var startAngle = 0;
        for (i = 0; i < results.length; i++) {
            coveredAngle = startAngle + results[i] * 360 / total;
            if (total === results[i])
                coveredAngle -= 0.05;
            $(paths[i]).attr('d', describeArc(200, 75, 72, startAngle, coveredAngle));
            $(paths[i]).next('path.shadow').attr('d', describeArc(200, 75, 75, startAngle, coveredAngle));
            if (results[i] === 0 || total === results[i])
                $(paths[i]).attr('stroke-width', 0);
            startAngle = coveredAngle;
        }
    }
};

$(function() {
    $.each(initializers, function(k, v) { v(); });
});
<|endoftext|>


<commit_before>// asynchronous self-invoking function to not pollute global namespace
(function(window, document, undefined) {
  var TAB_KEY_CODE = 9;
  var M_KEY_CODE = 77;

  var SOFT_TAB = '    ';
  var SOFT_TAB_LENGTH = SOFT_TAB.length;

  var ONLY_WHITESPACE_REGEX = /^\s*$/;
  var WHITESPACE_SPLIT_REGEX = /\s+$/g;

  /* Throttle the given function, condensing multiple calls into one call after
   * the given timeout period. In other words, allow at most one call to go
   * through per timeout period. Returns the throttled function.
   *
   * Arguments:
   * fn -- the function to throttle
   * timeout -- the timeout to throttle for
   */
  function throttle(fn, timeout) {
    return function throttledFn() {
      if (!throttledFn.timer) {
        // keep track of the arguments to the function and the context
        var args = arguments;
        var that = this;

        // call the function after the provided timeout
        throttledFn.timer = setTimeout(function() {
          fn.apply(that, args);

          // finished calling the function; unset the timer
          throttledFn.timer = undefined;
        }, timeout);
      }
    };
  }

  /* Remove whitespace on the edges of this string. */
  String.prototype.trim = function() {
    return this.replace(/(^\s+|\s+$)/g, '');
  };

  var key = 'mystyle_' + window.location.hostname;
  chrome.storage.sync.get(key, function(my_css) {
    console.log(JSON.stringify(my_css));

    window.addEventListener('DOMContentLoaded', function(event) {
      var head = document.getElementsByTagName('head')[0];
      var body = document.body;

      var style = document.createElement('style');
      var textarea = document.createElement('textarea');

      // hide textarea by default
      textarea.style.display = 'none';
      textarea.id = 'my-style-input';
      textarea.spellcheck = false;

      head.appendChild(style);
      body.appendChild(textarea);

      style.innerHTML = my_css[key] || localStorage.myStyle || '';
      textarea.value = style.innerHTML;
      textarea.placeholder = '/* Enter your styles here. */';

      // alt + click on an element adds its selector to the textarea
      body.addEventListener('click', function(event) {
        // ensure textarea is actually displayed
        if (textarea.style.display.indexOf('none') === -1 &&
          event.target.id !== textarea.id && event.altKey) {
          var i = 0;
          var target = event.target;
          var elemClass = target.className.split(' ') || '';
          var stylesList = [];
          var existingStyles = '';
          var selector = '';
          var cssStatement;
          var textToAdd;

          // selector starts with the tag
          selector += target.tagName.toLowerCase();

          // include ID if there is one
          if (target.id) {
            selector += '#' + target.id;
          }

          // include all classes found
          for (i = 0; i < elemClass.length; i++) {
            if (!ONLY_WHITESPACE_REGEX.test(elemClass[i])) {
              selector += '.' + elemClass[i];
            }
          }

          // fill CSS with styles defined in the style attribute
          if (target.getAttribute('style')) {
            stylesList = target.getAttribute('style').split(';');

            // keep track of CSS properties already defined in style attribute
            for (i = 0; i < stylesList.length; i++) {
              // condense mutliple whitespace into one space
              cssStatement = stylesList[i].split(WHITESPACE_SPLIT_REGEX)
                .join(' ').trim();

              if (!ONLY_WHITESPACE_REGEX.test(cssStatement)) {
                existingStyles += SOFT_TAB + cssStatement.toLowerCase() + ";\n";
              }
            }
          }

          // construct text to add to textarea
          if (selector) {
            // add existing styles in braces
            if (existingStyles) {
              existingStyles = "{\n" + existingStyles + "}";
            } else {
              existingStyles = "{\n\n}";
            }

            textToAdd = '\n' + selector + ' ' + existingStyles;
            textarea.value += textToAdd;

            // highlight added text for easy removal
            textarea.focus();
            textarea.setSelectionRange(textarea.value.length - textToAdd.length,
              textarea.value.length);
          }

          event.preventDefault();
        }
      });

      /* Save styles persistently in local storage. */
      var saveStyles = throttle(function() {
        localStorage.myStyle = style.innerHTML;
        var key = "mystyle_" + window.location.hostname;
        var obj = {};
        obj[key] = style.innerHTML;
        chrome.storage.sync.set(obj, function() {
        });
      }, 500);

      /* Updates styles with content in textarea and saves styles. */
      function updateAndSaveStyles() {
        style.innerHTML = textarea.value;
        saveStyles();
      }

      // continually update styles with textarea content
      textarea.addEventListener('keyup', updateAndSaveStyles);
      textarea.addEventListener('change', updateAndSaveStyles);

      // pressing tab should insert spaces instead of focusing another element
      textarea.addEventListener('keydown', function(event) {
        var value = textarea.value;
        var caret = textarea.selectionStart;

        // if tab is pressed, insert four spaces
        if (event.keyCode === TAB_KEY_CODE) {
          textarea.value = value.substring(0, caret) + SOFT_TAB +
            value.substring(caret);

          // move caret to after soft tab
          textarea.setSelectionRange(caret + SOFT_TAB_LENGTH, caret +
            SOFT_TAB_LENGTH);

          // prevent default tab action that shifts focus to the next element
          event.preventDefault();
        }
      });

      window.addEventListener('keydown', function(event) {
        // control + m toggles text area
        if (event.ctrlKey && event.keyCode === M_KEY_CODE) {
          if (textarea.style.display == 'none') {
            textarea.style.display = 'block';
          } else {
            textarea.style.display = 'none';
          }
        }
      });
    });
  });
})(this, this.document);
<commit_msg>fix: DOMContentLoaded registered too late<commit_after>// asynchronous self-invoking function to not pollute global namespace
(function(window, document, undefined) {
  var TAB_KEY_CODE = 9;
  var M_KEY_CODE = 77;

  var SOFT_TAB = '    ';
  var SOFT_TAB_LENGTH = SOFT_TAB.length;

  var ONLY_WHITESPACE_REGEX = /^\s*$/;
  var WHITESPACE_SPLIT_REGEX = /\s+$/g;

  /* Throttle the given function, condensing multiple calls into one call after
   * the given timeout period. In other words, allow at most one call to go
   * through per timeout period. Returns the throttled function.
   *
   * Arguments:
   * fn -- the function to throttle
   * timeout -- the timeout to throttle for
   */
  function throttle(fn, timeout) {
    return function throttledFn() {
      if (!throttledFn.timer) {
        // keep track of the arguments to the function and the context
        var args = arguments;
        var that = this;

        // call the function after the provided timeout
        throttledFn.timer = setTimeout(function() {
          fn.apply(that, args);

          // finished calling the function; unset the timer
          throttledFn.timer = undefined;
        }, timeout);
      }
    };
  }

  /* Remove whitespace on the edges of this string. */
  String.prototype.trim = function() {
    return this.replace(/(^\s+|\s+$)/g, '');
  };

  window.addEventListener('DOMContentLoaded', function(event) {
    var key = 'mystyle_' + window.location.hostname;
    chrome.storage.sync.get(key, function(my_css) {
      console.log(JSON.stringify(my_css));

      var head = document.getElementsByTagName('head')[0];
      var body = document.body;

      var style = document.createElement('style');
      var textarea = document.createElement('textarea');

      // hide textarea by default
      textarea.style.display = 'none';
      textarea.id = 'my-style-input';
      textarea.spellcheck = false;

      head.appendChild(style);
      body.appendChild(textarea);

      style.innerHTML = my_css[key] || localStorage.myStyle || '';
      textarea.value = style.innerHTML;
      textarea.placeholder = '/* Enter your styles here. */';

      // alt + click on an element adds its selector to the textarea
      body.addEventListener('click', function(event) {
        // ensure textarea is actually displayed
        if (textarea.style.display.indexOf('none') === -1 &&
          event.target.id !== textarea.id && event.altKey) {
          var i = 0;
          var target = event.target;
          var elemClass = target.className.split(' ') || '';
          var stylesList = [];
          var existingStyles = '';
          var selector = '';
          var cssStatement;
          var textToAdd;

          // selector starts with the tag
          selector += target.tagName.toLowerCase();

          // include ID if there is one
          if (target.id) {
            selector += '#' + target.id;
          }

          // include all classes found
          for (i = 0; i < elemClass.length; i++) {
            if (!ONLY_WHITESPACE_REGEX.test(elemClass[i])) {
              selector += '.' + elemClass[i];
            }
          }

          // fill CSS with styles defined in the style attribute
          if (target.getAttribute('style')) {
            stylesList = target.getAttribute('style').split(';');

            // keep track of CSS properties already defined in style attribute
            for (i = 0; i < stylesList.length; i++) {
              // condense mutliple whitespace into one space
              cssStatement = stylesList[i].split(WHITESPACE_SPLIT_REGEX)
                .join(' ').trim();

              if (!ONLY_WHITESPACE_REGEX.test(cssStatement)) {
                existingStyles += SOFT_TAB + cssStatement.toLowerCase() + ";\n";
              }
            }
          }

          // construct text to add to textarea
          if (selector) {
            // add existing styles in braces
            if (existingStyles) {
              existingStyles = "{\n" + existingStyles + "}";
            } else {
              existingStyles = "{\n\n}";
            }

            textToAdd = '\n' + selector + ' ' + existingStyles;
            textarea.value += textToAdd;

            // highlight added text for easy removal
            textarea.focus();
            textarea.setSelectionRange(textarea.value.length - textToAdd.length,
              textarea.value.length);
          }

          event.preventDefault();
        }
      });

      /* Save styles persistently in local storage. */
      var saveStyles = throttle(function() {
        localStorage.myStyle = style.innerHTML;
        var key = "mystyle_" + window.location.hostname;
        var obj = {};
        obj[key] = style.innerHTML;
        chrome.storage.sync.set(obj, function() {
        });
      }, 500);

      /* Updates styles with content in textarea and saves styles. */
      function updateAndSaveStyles() {
        style.innerHTML = textarea.value;
        saveStyles();
      }

      // continually update styles with textarea content
      textarea.addEventListener('keyup', updateAndSaveStyles);
      textarea.addEventListener('change', updateAndSaveStyles);

      // pressing tab should insert spaces instead of focusing another element
      textarea.addEventListener('keydown', function(event) {
        var value = textarea.value;
        var caret = textarea.selectionStart;

        // if tab is pressed, insert four spaces
        if (event.keyCode === TAB_KEY_CODE) {
          textarea.value = value.substring(0, caret) + SOFT_TAB +
            value.substring(caret);

          // move caret to after soft tab
          textarea.setSelectionRange(caret + SOFT_TAB_LENGTH, caret +
            SOFT_TAB_LENGTH);

          // prevent default tab action that shifts focus to the next element
          event.preventDefault();
        }
      });

      window.addEventListener('keydown', function(event) {
        // control + m toggles text area
        if (event.ctrlKey && event.keyCode === M_KEY_CODE) {
          if (textarea.style.display == 'none') {
            textarea.style.display = 'block';
          } else {
            textarea.style.display = 'none';
          }
        }
      });
    });
  });
})(this, this.document);
<|endoftext|>


<commit_before>"use strict";

var React                       = require('react-tools/build/modules/React'),
    parseInlineMarkup           = require('../parser').parseInlineMarkup,
    contentEditableLineMetrics  = require('../content-editable-line-metrics'),
    textNodes                   = require('../content-editable-text-nodes'),
    keys                        = require('../keys'),
    DOMObserver                 = require('../dom-observer'),
    Focusable                   = require('../focusable');

module.exports = React.createClass({
  mixins: [Focusable, DOMObserver],

  getTokens: function() {
    return textNodes(this.getDOMNode(), true);
  },

  getCaretPosition: function() {
    return contentEditableLineMetrics(this.getDOMNode());
  },

  getCaretOffset: function() {
    this.getTokens();

    var selection = document.getSelection(),
        node = selection.focusNode,
        offset = selection.focusOffset;

    if (!node)
      return;

    if (node.parentNode.dataset.token !== undefined) {
      node = node.parentNode;
    }

    return node.__totalIndex + offset;
  },

  restoreCaretOffset: function() {
    var tokens = this.getTokens(),
        offset = this.state.focusOffset || 0;

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];
      if (offset >= token.__totalIndex &&
          offset <= token.__totalIndex + token.__length) {
        offset = offset - token.__totalIndex;
        setSingleRange(token, offset);
        return;
      }
    }

    // no node found, this was probably a whitespace node
    var node = this.getDOMNode(),
        lastToken = tokens[tokens.length - 1];

    if (lastToken) {
      setSingleRange(lastToken, lastToken.__length);
    } else {
      setSingleRange(node, 0);
    }
  },

  getContent: function(withMarkup) {
    var content = '';
    var tokens = this.getTokens();

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];
      if (token.dataset && token.dataset.token !== undefined) {
        if (withMarkup)
          content = content + token.textContent;
      } else {
        content = content + token.textContent;
      }
    }

    return content;
  },

  getAnnotations: function() {
    var annotations = [];
    var tokens = this.getTokens();

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];

      if (token.dataset && token.dataset.token !== undefined)
        continue;

      if (!token.parentNode)
        continue;

      var annotationType = token.parentNode.dataset.annotationType;

      if (annotationType && token.__length > 0)
        annotations.push({
          type: annotationType,
          range: [token.__index, token.__index + token.__length]
        });
    }

    return annotations;
  },

  getInitialState: function() {
    return {
      content: this.props.content,
      annotations: this.props.annotations,
      focusOffset: this.props.focusOffset
    }
  },

  componentWillReceiveProps: function(props) {
    if (props.content)
      this.state.content = props.content;
    if (props.annotations)
      this.state.annotations = props.annotations;
    if (props.focusOffset)
      this.state.focusOffset = props.focusOffset;
  },

  componentDidUpdate: function() {
    this.restoreCaretOffset();
  },

  onDOMChanges: function() {
    this.parse();
  },

  parse: function() {
    var content = this.getContent(true);
    var update = parseInlineMarkup(content);

    if (this.props.onUpdate)
      this.props.onUpdate(update);

    this.setState({
      content: update.content,
      annotations: update.annotations,
      focusOffset: this.getCaretOffset()
    });
  },

  onSelect: function(e) {
    this.state.focusOffset = this.getCaretOffset();

    if (this.props.onSelect)
      this.props.onSelect(e, this.state.focusOffset);
  },

  onKeyDown: function(e) {
    if (e.keyCode === keys.SPACE) {
      var node = this.getDOMNode(),
          selection = document.getSelection(),
          tokens = this.getTokens(),
          lastToken = tokens[tokens.length - 1],
          token = selection.focusNode;

      while (token !== node && tokens.indexOf(token) === -1)
        token = token.parentNode;

      if (token === node) {
        e.preventDefault();
        document.execCommand('insertText', false, ' ');
      } else if (token.__totalIndex + selection.focusOffset ===
          lastToken.__totalIndex + lastToken.__length) {
        e.preventDefault();
        document.execCommand('insertText', false, ' ');
      }
    }

    if (this.props.onKeyDown)
      this.props.onKeyDown(e);
  },

  renderAnnotatedContent: function(content) {
    var text = this.state.content,
        annotations = this.state.annotations,
        nodes = [];

    // assume they are sorted by its range
    if (annotations && annotations.length > 0) {

      // part before the first annotation
      if (annotations[0].range[0] > 0) {
        nodes.push(genText(text.substring(0, annotations[0].range[0])));
      }

      var prev;

      for (var i = 0, length = annotations.length; i < length; i++) {
        var annotation = annotations[i];

        // part before the prev and the current annotation
        if (prev && annotation.range[0] - prev.range[1] >= 1) {
          nodes.push(genText(text.substring(prev.range[1], annotation.range[0])));
        }

        // annotation itself
        nodes.push(genAnnotation({
          type: annotation.type,
          content: text.substring.apply(text, annotation.range)
        }));

        prev = annotation;
      }

      // part after the last annotation
      var last = annotations[annotations.length - 1].range;
      if (last[1] < text.length) {
        nodes.push(genText(text.substring(last[1], text.length)));
      }

    } else {
      nodes.push(genText(text));
    }
    return nodes.join('');
  },

  render: function() {
    var content = this.renderAnnotatedContent();
    return this.transferPropsTo(
      React.DOM.div({
        contentEditable: "true",
        className: "Editor",
        onSelect: this.onSelect,
        onKeyDown: this.onKeyDown,
        dangerouslySetInnerHTML: {__html: content}
      }));
  }
});

function setSingleRange(node, offset) {
  var rng = document.createRange();
  rng.setStart(node, offset);
  rng.collapse(true);
  var selection = document.getSelection();
  selection.removeAllRanges();
  selection.addRange(rng);
}

function genAnnotation(annotation) {
  var inner;
  switch (annotation.type) {
    case 'em':
      inner = genToken('*') +
        annotation.content +
        genToken('*');
      break;
    case 'strong':
      inner = genToken('*') +
        genToken('*') +
        annotation.content +
        genToken('*') +
        genToken('*');
      break;
    case 'inlinecode':
      inner = genToken('`') +
        annotation.content +
        genToken('`');
      break;
    default:
      console.error('unknown annotation type');
  }
  return '' + inner + '';
}

function genToken(token) {
  return '' + token + '';
}

function genText(text) {
  return text
    .replace(/</g, '<')
    .replace(/>/g, '>');
}
<commit_msg>editors/rich: restore caret position only if editor has focus<commit_after>"use strict";

var React                       = require('react-tools/build/modules/React'),
    parseInlineMarkup           = require('../parser').parseInlineMarkup,
    contentEditableLineMetrics  = require('../content-editable-line-metrics'),
    textNodes                   = require('../content-editable-text-nodes'),
    keys                        = require('../keys'),
    DOMObserver                 = require('../dom-observer'),
    Focusable                   = require('../focusable');

module.exports = React.createClass({
  mixins: [Focusable, DOMObserver],

  getTokens: function() {
    return textNodes(this.getDOMNode(), true);
  },

  getCaretPosition: function() {
    return contentEditableLineMetrics(this.getDOMNode());
  },

  getCaretOffset: function() {
    this.getTokens();

    var selection = document.getSelection(),
        node = selection.focusNode,
        offset = selection.focusOffset;

    if (!node)
      return;

    if (node.parentNode.dataset.token !== undefined) {
      node = node.parentNode;
    }

    return node.__totalIndex + offset;
  },

  restoreCaretOffset: function() {
    var tokens = this.getTokens(),
        offset = this.state.focusOffset || 0;

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];
      if (offset >= token.__totalIndex &&
          offset <= token.__totalIndex + token.__length) {
        offset = offset - token.__totalIndex;
        setSingleRange(token, offset);
        return;
      }
    }

    // no node found, this was probably a whitespace node
    var node = this.getDOMNode(),
        lastToken = tokens[tokens.length - 1];

    if (lastToken) {
      setSingleRange(lastToken, lastToken.__length);
    } else {
      setSingleRange(node, 0);
    }
  },

  getContent: function(withMarkup) {
    var content = '';
    var tokens = this.getTokens();

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];
      if (token.dataset && token.dataset.token !== undefined) {
        if (withMarkup)
          content = content + token.textContent;
      } else {
        content = content + token.textContent;
      }
    }

    return content;
  },

  getAnnotations: function() {
    var annotations = [];
    var tokens = this.getTokens();

    for (var i = 0, length = tokens.length; i < length; i++) {
      var token = tokens[i];

      if (token.dataset && token.dataset.token !== undefined)
        continue;

      if (!token.parentNode)
        continue;

      var annotationType = token.parentNode.dataset.annotationType;

      if (annotationType && token.__length > 0)
        annotations.push({
          type: annotationType,
          range: [token.__index, token.__index + token.__length]
        });
    }

    return annotations;
  },

  getInitialState: function() {
    return {
      content: this.props.content,
      annotations: this.props.annotations,
      focusOffset: this.props.focusOffset
    }
  },

  componentWillReceiveProps: function(props) {
    if (props.content)
      this.state.content = props.content;
    if (props.annotations)
      this.state.annotations = props.annotations;
    if (props.focusOffset)
      this.state.focusOffset = props.focusOffset;
  },

  componentDidUpdate: function() {
    if (this.props.focus)
      this.restoreCaretOffset();
  },

  onDOMChanges: function() {
    this.parse();
  },

  parse: function() {
    var content = this.getContent(true);
    var update = parseInlineMarkup(content);

    if (this.props.onUpdate)
      this.props.onUpdate(update);

    this.setState({
      content: update.content,
      annotations: update.annotations,
      focusOffset: this.getCaretOffset()
    });
  },

  onSelect: function(e) {
    this.state.focusOffset = this.getCaretOffset();

    if (this.props.onSelect)
      this.props.onSelect(e, this.state.focusOffset);
  },

  onKeyDown: function(e) {
    if (e.keyCode === keys.SPACE) {
      var node = this.getDOMNode(),
          selection = document.getSelection(),
          tokens = this.getTokens(),
          lastToken = tokens[tokens.length - 1],
          token = selection.focusNode;

      while (token !== node && tokens.indexOf(token) === -1)
        token = token.parentNode;

      if (token === node) {
        e.preventDefault();
        document.execCommand('insertText', false, ' ');
      } else if (token.__totalIndex + selection.focusOffset ===
          lastToken.__totalIndex + lastToken.__length) {
        e.preventDefault();
        document.execCommand('insertText', false, ' ');
      }
    }

    if (this.props.onKeyDown)
      this.props.onKeyDown(e);
  },

  renderAnnotatedContent: function(content) {
    var text = this.state.content,
        annotations = this.state.annotations,
        nodes = [];

    // assume they are sorted by its range
    if (annotations && annotations.length > 0) {

      // part before the first annotation
      if (annotations[0].range[0] > 0) {
        nodes.push(genText(text.substring(0, annotations[0].range[0])));
      }

      var prev;

      for (var i = 0, length = annotations.length; i < length; i++) {
        var annotation = annotations[i];

        // part before the prev and the current annotation
        if (prev && annotation.range[0] - prev.range[1] >= 1) {
          nodes.push(genText(text.substring(prev.range[1], annotation.range[0])));
        }

        // annotation itself
        nodes.push(genAnnotation({
          type: annotation.type,
          content: text.substring.apply(text, annotation.range)
        }));

        prev = annotation;
      }

      // part after the last annotation
      var last = annotations[annotations.length - 1].range;
      if (last[1] < text.length) {
        nodes.push(genText(text.substring(last[1], text.length)));
      }

    } else {
      nodes.push(genText(text));
    }
    return nodes.join('');
  },

  render: function() {
    var content = this.renderAnnotatedContent();
    return this.transferPropsTo(
      React.DOM.div({
        contentEditable: "true",
        className: "Editor",
        onSelect: this.onSelect,
        onKeyDown: this.onKeyDown,
        dangerouslySetInnerHTML: {__html: content}
      }));
  }
});

function setSingleRange(node, offset) {
  var rng = document.createRange();
  rng.setStart(node, offset);
  rng.collapse(true);
  var selection = document.getSelection();
  selection.removeAllRanges();
  selection.addRange(rng);
}

function genAnnotation(annotation) {
  var inner;
  switch (annotation.type) {
    case 'em':
      inner = genToken('*') +
        annotation.content +
        genToken('*');
      break;
    case 'strong':
      inner = genToken('*') +
        genToken('*') +
        annotation.content +
        genToken('*') +
        genToken('*');
      break;
    case 'inlinecode':
      inner = genToken('`') +
        annotation.content +
        genToken('`');
      break;
    default:
      console.error('unknown annotation type');
  }
  return '' + inner + '';
}

function genToken(token) {
  return '' + token + '';
}

function genText(text) {
  return text
    .replace(/</g, '<')
    .replace(/>/g, '>');
}
<|endoftext|>


<commit_before>var five = require("../lib/johnny-five.js"),
    __ = require("../lib/fn.js"),
    board, Navigator, navigator, servos,
    expandedWhich, directionMap, scale;

directionMap = {
  reverse: {
    right: "left",
    left: "right",
    fwd: "rev",
    rev: "fwd"
  },
  translations: [
    {
      f: "forward",
      r: "reverse",
      fwd: "forward",
      rev: "reverse"
    },
    {
      r: "right",
      l: "left"
    }
  ]
};

scale = function( speed, low, high ) {
  return Math.floor( five.Fn.map( speed, 0, 5, low, high ) );
};

/**
 * Navigator
 * @param {Object} opts Optional properties object
 */
function Navigator( opts ) {

  // Boe Navigator continuous are calibrated to stop at 90°
  this.center = opts.center || 90;

  // Initialize the right and left cooperative servos
  this.servos = {
    right: new five.Servo({ pin: opts.right, type: "continuous" }),
    left: new five.Servo({ pin: opts.left, type: "continuous" })
  };

  // Set the initial servo cooperative direction
  this.direction = opts.direction || {
    right: this.center,
    left: this.center
  };

  // Store the cooperative speed
  this.speed = opts.speed === undefined ? 0 : opts.speed;

  // Store a recallable history of movement
  // TODO: Include in savable history
  this.history = [];

  // Initial direction
  this.which = "forward";

  // Track directional state
  this.isTurning = false;

  // Wait 10ms, send fwd pulse on, then off to
  // "wake up" the servos
  setTimeout(function() {
    this.fwd(1).fwd(0);
  }.bind(this), 10);
}

Navigator.DIR_MAP = directionMap;

/**
 * move Move the bot in an arbitrary direction
 * @param  {Number} right Speed/Direction of right servo
 * @param  {Number} left  Speed/Direction of left servo
 * @return {Object} this
 */
Navigator.prototype.move = function( right, left ) {

  // Quietly ignore duplicate instructions
  if ( this.direction.right === right &&
        this.direction.left === left ) {
    return this;
  }

  // Cooperative servo motion.
  // Servos are mounted opposite of each other,
  // the values for left and right will be in
  // opposing directions.
  this.servos.right.move( right );
  this.servos.left.move( left );

  // Push a record object into the history
  this.history.push({
    timestamp: Date.now(),
    right: right,
    left: left
  });

  // Update the stored direction state
  this.direction.right = right;
  this.direction.left = left;

  return this;
};

[
  /**
   * forward Move the bot forward
   * fwd Move the bot forward
   *
   * @param  {Number} 0-5, 0 is stopped, 5 is fastest
   * @return {Object} this
   */
  {
    name: "forward",
    abbr: "fwd",
    args: function( center, val ) {
      return [ center - (val - center), val ];
    }
  },

  /**
   * reverse Move the bot in reverse
   * rev Move the bot in reverse
   *
   * @param  {Number}0-5, 0 is stopped, 5 is fastest
   * @return {Object} this
   */
  {
    name: "reverse",
    abbr: "rev",
    args: function( center, val ) {
      return [ val, center - (val - center) ];
    }
  }

].forEach(function( dir ) {

  var method = function( speed ) {
    // Set default direction method
    speed = speed === undefined ? 1 : speed;

    this.speed = speed;
    this.which = dir.name;

    return this.move.apply( this,
      dir.args( this.center, scale( speed, this.center, 110 ) )
    );
  };

  Navigator.prototype[ dir.name ] = Navigator.prototype[ dir.abbr ] = method;
});

/**
 * stop Stops the bot, regardless of current direction
 * @return {Object} this
 */
Navigator.prototype.stop = function() {
  this.speed = this.center;
  this.which = "stop";

  return this.move( this.center, this.center );
};

[
  /**
   * right Turn the bot right
   * @return {Object} this
   */
  "right",

  /**
   * left Turn the bot left
   * @return {Object} this
   */
  "left"

].forEach(function( dir ) {
  Navigator.prototype[ dir ] = function() {

    // Use direction value and reverse direction map to
    // derive the direction values for moving the
    // cooperative servos
    var actual = this.direction[ directionMap.reverse[ dir ] ];

    if ( !this.isTurning ) {
      // Set turning lock
      this.isTurning = true;

      // Send turning command
      this.move( actual, actual );

      // Cap turning time
      setTimeout(function() {

        // Restore direction after turn
        this[ this.which ]( this.speed );

        // Release turning lock
        this.isTurning = false;

      }.bind(this), 500 );
    }

    return this;
  };
});

expandedWhich = function( which ) {
  var parts;

  if ( which.length === 2 ) {
    parts = [ which[0], which[1] ];
  }

  if ( /\-/.test(which) ) {
    parts = which.split("-");
  }

  return parts.map(function( val, i ) {
    console.log( val );
    return directionMap.translations[ i ][ val ];
  }).join("-");
};

/**
 * pivot Pivot the bot with combo directions:
 * rev Move the bot in reverse
 *
 * @param  {String} which Combination directions:
 *                        "forward-right", "forward-left",
 *                        "reverse-right", "reverse-left"
 *                        (aliased as: "f-l", "f-r", "r-r", "r-l")
 *
 * @return {Object} this
 */
Navigator.prototype.pivot = function( which, time ) {
  var actual, directions, scaled;

  scaled = scale( this.speed, this.center, 110 );

  directions = {
    "forward-right": function() {
      this.move( this.center, scaled );
    },
    "forward-left": function() {
      this.move( this.center - (scaled - this.center), this.center );
    },
    "reverse-right": function() {
      this.move( scaled, this.center );
    },
    "reverse-left": function() {
      this.move( this.center, this.center - (scaled - this.center) );
    }
  };

  which = directions[ which ] || directions[ expandedWhich( which ) ];

  which.call( this, this.speed );

  setTimeout(function() {

    this[ this.which ]( this.speed );

  }.bind(this), time || 1000 );

  return this;
};

// Begin program when the board, serial and
// firmata are connected and ready

board = new five.Board();
board.on("ready", function() {

  // TODO: Refactor into modular program code

  var center, collideAt, degrees, step, facing,
  range, laser, look, isScanning, scanner, ping, mag, bearing;

  // Collision distance (inches)
  collideAt = 6;

  // Servo scanning steps (degrees)
  step = 2;

  // Current facing direction
  facing = "";

  // Scanning range (degrees)
  range = [ 10, 170 ];

  // Servo center point (degrees)
  center = ( (range[1] - range[0]) / 2 ) + range[0];

  // Starting scanner scanning position (degrees)
  degrees = center;

  // Direction to look after releasing scanner lock (degrees)
  // look = {
  //   forward: center,
  //   left: 130,
  //   right: 40
  // };

  // Scanning state
  isScanning = true;

  // New base navigator
  // right servo = pin 10, left servo = pin 11
  navigator = new Navigator({
    right: 10,
    left: 11
  });

  // Inject navigator object into REPL
  this.repl.inject({
    b: navigator
  });

  // The laser is just a special case Led
  laser = new five.Led(9);

  // ping instance (distance detection)
  ping = new five.Ping(7);

  // compass/magnetometer
  // mag = new five.Magnetometer();

  // Servo scanner instance (panning)
  scanner = new five.Servo({
    pin: 12,
    range: range
  });

  // Initialize the scanner at it's center point
  // Will be exactly half way between the range's
  // lower and upper bound
  scanner.center();

  // Wait 1000ms, then initialize forward movement
  this.wait( 1000, function() {
    navigator.fwd(3);
  });

  // Scanner/Panning loop
  this.loop( 50, function() {
    var bounds;

    bounds = {
      left: center + 15, //center + 10,
      right: center - 15 //center - 10
    };

    // During course change, scanning is paused to avoid
    // overeager redirect instructions[1]
    if ( isScanning ) {
      // Calculate the next step position
      if ( degrees >= scanner.range[1] || degrees <= scanner.range[0] ) {
        step *= -1;
      }

      // Update the position in degrees
      degrees += step;

      // The following three conditions will help determine
      // which way the navigator should turn if a potential collideAt
      // may occur in the ping "change" event handler[2]
      if ( degrees > bounds.left ) {
        facing = "left";
      }

      if ( degrees < bounds.right ) {
        facing = "right";
      }

      // if ( degrees > bounds.right && degrees < bounds.left ) {
      if ( __.range( bounds.right, bounds.left ).indexOf( degrees ) > -1 ) {
        facing = "fwd";
      }

      scanner.move( degrees );
    }
  });

  // Compass heading monitor
  // mag.on("headingchange", function() {

  //   if ( !/[\-by]/.test(this.bearing.name) && this.bearing.name !== bearing ) {
  //     bearing = this.bearing.name;

  //     console.log( this.bearing );
  //   }
  // });

  // [2] ping "change" events are emitted when the value of a
  // distance reading has changed since the previous reading
  //
  // TODO: Avoid false positives?
  ping.on("read", function( err ) {
    var release = 750,
        distance = Math.abs(this.inches),
        isReverse = false,
        turnTo;

    if ( navigator.isTurning ) {
      return;
    }

    // If distance value is null or NaN
    if ( distance === null || isNaN(distance) ) {
      return;
    }

    // Detect collideAt
    // && isScanning
    if ( distance <= collideAt && isScanning ) {

      laser.strobe();

      // Scanning lock will prevent multiple collideAt
      // detections piling up for the same obstacle
      isScanning = false;

      // Determine direction to turn
      turnTo = Navigator.DIR_MAP.reverse[ facing ];

      // Set reversal flag.
      isReverse = turnTo === "rev";

      // Log collideAt detection to REPL
      console.log(
        [ Date.now(),
          "\tCollision detected " + this.inches + " inches away.",
          "\tTurning " + turnTo.toUpperCase() + " to avoid"
        ].join("\n")
      );

      // Turn the navigator
      navigator[ turnTo ]( navigator.speed );

      if ( isReverse ) {
        release = 1500;
      }

      // [1] Allow Nms to pass and release the scanning lock
      // by setting isScanning state to true.
      board.wait( release, function() {
        console.log( "Release Scanner Lock" );

        degrees = 89;

        scanner.center();

        if ( isReverse ) {
          // navigator.fwd( navigator.speed );
          navigator.pivot("reverse-right");
          navigator.which = "fwd";
        }

        laser.brightness(0);
        isScanning = true;
      });
    }
  });
});

// References
//
// http://www.maxbotix.com/documents/MB1010_Datasheet.pdf
<commit_msg>Latest iteration of Navigator constructor;<commit_after>var five = require("../lib/johnny-five.js"),
    __ = require("../lib/fn.js"),
    board, Navigator, navigator, servos,
    pivotExpansion, directionMap, scale;

directionMap = {
  reverse: {
    right: "left",
    left: "right",
    fwd: "rev",
    rev: "fwd"
  },
  translations: [
    {
      f: "forward",
      r: "reverse",
      fwd: "forward",
      rev: "reverse"
    },
    {
      r: "right",
      l: "left"
    }
  ]
};

scale = function( speed, low, high ) {
  return Math.floor( five.Fn.map( speed, 0, 5, low, high ) );
};

/**
 * Navigator
 * @param {Object} opts Optional properties object
 */
function Navigator( opts ) {

  // Boe Navigator continuous are calibrated to stop at 90°
  this.center = opts.center || 90;

  // Initialize the right and left cooperative servos
  this.servos = {
    right: new five.Servo({ pin: opts.right, type: "continuous" }),
    left: new five.Servo({ pin: opts.left, type: "continuous" })
  };

  // Set the initial servo cooperative direction
  this.direction = opts.direction || {
    right: this.center,
    left: this.center
  };

  this.compass = opts.compass || null;
  this.gripper = opts.gripper || null;

  // Store the cooperative speed
  this.speed = opts.speed === undefined ? 0 : opts.speed;

  // Store a recallable history of movement
  // TODO: Include in savable history
  this.history = [];

  // Initial direction
  this.which = "forward";

  // Track directional state
  this.isTurning = false;

  // Wait 10ms, send fwd pulse on, then off to
  // "wake up" the servos
  setTimeout(function() {
    this.fwd(1).fwd(0);
  }.bind(this), 10);
}

Navigator.DIR_MAP = directionMap;

/**
 * move Move the bot in an arbitrary direction
 * @param  {Number} right Speed/Direction of right servo
 * @param  {Number} left  Speed/Direction of left servo
 * @return {Object} this
 */
Navigator.prototype.move = function( right, left ) {

  // Quietly ignore duplicate instructions
  if ( this.direction.right === right &&
        this.direction.left === left ) {
    return this;
  }

  // Cooperative servo motion.
  // Servos are mounted opposite of each other,
  // the values for left and right will be in
  // opposing directions.
  this.servos.right.move( right );
  this.servos.left.move( left );

  // Push a record object into the history
  this.history.push({
    timestamp: Date.now(),
    right: right,
    left: left
  });

  // Update the stored direction state
  this.direction.right = right;
  this.direction.left = left;

  return this;
};

[
  /**
   * forward Move the bot forward
   * fwd Move the bot forward
   *
   * @param  {Number} 0-5, 0 is stopped, 5 is fastest
   * @return {Object} this
   */
  {
    name: "forward",
    abbr: "fwd",
    args: function( center, val ) {
      return [ center - (val - center), val ];
    }
  },

  /**
   * reverse Move the bot in reverse
   * rev Move the bot in reverse
   *
   * @param  {Number}0-5, 0 is stopped, 5 is fastest
   * @return {Object} this
   */
  {
    name: "reverse",
    abbr: "rev",
    args: function( center, val ) {
      return [ val, center - (val - center) ];
    }
  }

].forEach(function( dir ) {

  var method = function( speed ) {
    // Set default direction method
    speed = speed === undefined ? 1 : speed;

    this.speed = speed;
    this.which = dir.name;

    return this.move.apply( this,
      dir.args( this.center, scale( speed, this.center, 110 ) )
    );
  };

  Navigator.prototype[ dir.name ] = Navigator.prototype[ dir.abbr ] = method;
});

/**
 * stop Stops the bot, regardless of current direction
 * @return {Object} this
 */
Navigator.prototype.stop = function() {
  this.speed = this.center;
  this.which = "stop";

  return this.move( this.center, this.center );
};

[
  /**
   * right Turn the bot right
   * @return {Object} this
   */
  "right",

  /**
   * left Turn the bot left
   * @return {Object} this
   */
  "left"

].forEach(function( dir ) {
  Navigator.prototype[ dir ] = function( time ) {

    // Use direction value and reverse direction map to
    // derive the direction values for moving the
    // cooperative servos
    var actual = this.direction[ directionMap.reverse[ dir ] ];

    time = time || 500;

    if ( !this.isTurning ) {
      // Set turning lock
      this.isTurning = true;

      // Send turning command
      this.move( actual, actual );

      // Cap turning time
      setTimeout(function() {

        // Restore direction after turn
        this[ this.which ]( this.speed || 2 );

        // Release turning lock
        this.isTurning = false;

      }.bind(this), time );
    }

    return this;
  };
});

pivotExpansion = function( which ) {
  var parts;

  if ( which.length === 2 ) {
    parts = [ which[0], which[1] ];
  }

  if ( /\-/.test(which) ) {
    parts = which.split("-");
  }

  return parts.map(function( val, i ) {
    console.log( val );
    return directionMap.translations[ i ][ val ];
  }).join("-");
};

/**
 * pivot Pivot the bot with combo directions:
 * rev Move the bot in reverse
 *
 * @param  {String} which Combination directions:
 *                        "forward-right", "forward-left",
 *                        "reverse-right", "reverse-left"
 *                        (aliased as: "f-l", "f-r", "r-r", "r-l")
 *
 * @return {Object} this
 */
Navigator.prototype.pivot = function( which, time ) {
  var actual, directions, scaled;

  scaled = scale( this.speed, this.center, 110 );

  directions = {
    "forward-right": function() {
      this.move( this.center, scaled );
    },
    "forward-left": function() {
      this.move( this.center - (scaled - this.center), this.center );
    },
    "reverse-right": function() {
      this.move( scaled, this.center );
    },
    "reverse-left": function() {
      this.move( this.center, this.center - (scaled - this.center) );
    }
  };

  which = directions[ which ] || directions[ pivotExpansion( which ) ];

  which.call( this, this.speed );

  setTimeout(function() {

    this[ this.which ]( this.speed );

  }.bind(this), time || 1000 );

  return this;
};

// Begin program when the board, serial and
// firmata are connected and ready

(board = new five.Board()).on("ready", function() {

  // TODO: Refactor into modular program code

  var center, collideAt, degrees, step, facing,
  range, laser, look, isScanning, scanner, gripper, isGripping, sonar, gripAt, ping, mag, bearing;

  // Collision distance (inches)
  collideAt = 6;

  gripAt = 2;

  // Servo scanning steps (degrees)
  step = 2;

  // Current facing direction
  facing = "";

  // Scanning range (degrees)
  range = [ 10, 170 ];

  // Servo center point (degrees)
  center = ( (range[1] - range[0]) / 2 ) + range[0];

  // Starting scanner scanning position (degrees)
  degrees = center;

  // Direction to look after releasing scanner lock (degrees)
  // look = {
  //   forward: center,
  //   left: 130,
  //   right: 40
  // };

  // Scanning state
  isScanning = true;

  // Gripping state
  isGripping = false;

  // compass/magnetometer
  mag = new five.Magnetometer();

  // Servo gripper
  gripper = new five.Servo({
    pin: 13,
    range: [ 20, 180 ]
  });

  gripper.move(20);

  // New base navigator
  // right servo = pin 10, left servo = pin 11
  navigator = new Navigator({
    right: 10,
    left: 11,
    compass: mag,
    gripper: gripper
  });

  // The laser is just a special case Led
  laser = new five.Led(9);

  // Digital PWM (range)
  ping = new five.Ping(7);

  // Analog Voltage (range)
  // sonar = new five.Sonar("A0");

  // Servo scanner instance (panning)
  scanner = new five.Servo({
    pin: 12,
    range: range
  });

  // Inject navigator object into REPL
  this.repl.inject({
    b: navigator,
    g: gripper
  });

  // Initialize the scanner at it's center point
  // Will be exactly half way between the range's
  // lower and upper bound
  scanner.center();

  // Wait 1000ms, then initialize forward movement
  this.wait( 1000, function() {
    // navigator.fwd(3);
  });

  // Scanner/Panning loop
  this.loop( 50, function() {
    var bounds;

    bounds = {
      left: center + 15, //center + 10,
      right: center - 15 //center - 10
    };

    // During course change, scanning is paused to avoid
    // overeager redirect instructions[1]
    if ( isScanning ) {
      // Calculate the next step position
      if ( degrees >= scanner.range[1] || degrees <= scanner.range[0] ) {
        step *= -1;
      }

      // Update the position in degrees
      degrees += step;

      // The following three conditions will help determine
      // which way the navigator should turn if a potential collideAt
      // may occur in the ping "change" event handler[2]
      if ( degrees > bounds.left ) {
        facing = "left";
      }

      if ( degrees < bounds.right ) {
        facing = "right";
      }

      // if ( degrees > bounds.right && degrees < bounds.left ) {
      if ( __.range( bounds.right, bounds.left ).indexOf( degrees ) > -1 ) {
        facing = "fwd";
      }

      scanner.move( degrees );
    }
  });

  // sonar.on("change", function() {
  // ping.on("change", function() {
  //   var distance = Math.abs(this.inches);

  //   // TODO: Wrap this behaviour in an abstraction
  //   if ( distance <= collideAt && !isGripping ) {
  //     gripper.max();

  //     // simulate drop instruction
  //     setTimeout(function() {
  //       isGripping = false;
  //       gripper.min();
  //     }, 5000);
  //   }
  // });

  // Compass heading monitor
  // mag.on("headingchange", function() {

  //   if ( !/[\-by]/.test(this.bearing.name) && this.bearing.name !== bearing ) {
  //     bearing = this.bearing.name;

  //     console.log( this.bearing );
  //   }
  // });

  // [2] ping "change" events are emitted when the value of a
  // distance reading has changed since the previous reading
  //
  // TODO: Avoid false positives?
  ping.on("read", function( err ) {
    var release = 750,
        distance = Math.abs(this.inches),
        isReverse = false,
        turnTo;

    if ( navigator.isTurning ) {
      return;
    }

    // If distance value is null or NaN
    if ( distance === null || isNaN(distance) ) {
      return;
    }

    // Detect collideAt
    // && isScanning
    if ( distance <= collideAt && isScanning ) {

      laser.strobe();

      // Scanning lock will prevent multiple collideAt
      // detections piling up for the same obstacle
      isScanning = false;

      // Determine direction to turn
      turnTo = Navigator.DIR_MAP.reverse[ facing ];

      // Set reversal flag.
      isReverse = turnTo === "rev";

      // Log collideAt detection to REPL
      console.log(
        [ Date.now(),
          "\tCollision detected " + this.inches + " inches away.",
          "\tTurning " + turnTo.toUpperCase() + " to avoid"
        ].join("\n")
      );

      // Turn the navigator
      navigator[ turnTo ]( navigator.speed );

      if ( isReverse ) {
        release = 1500;
      }

      // [1] Allow Nms to pass and release the scanning lock
      // by setting isScanning state to true.
      board.wait( release, function() {
        console.log( "Release Scanner Lock" );

        degrees = 89;

        scanner.center();

        if ( isReverse ) {
          // navigator.fwd( navigator.speed );
          navigator.pivot("reverse-right");
          navigator.which = "fwd";
        }

        laser.brightness(0);
        isScanning = true;
      });
    }
  });
});

// References
//
// http://www.maxbotix.com/documents/MB1010_Datasheet.pdf
<|endoftext|>


<commit_before>const Gyps = _ => {
  const gyps = Object.create(null);

  let last;

  /* Emit an event, calling all observers.
   * @param data Data object to be used on calling observers
   */
  gyps.emit = data => {
    last = data;
    return gyps;
  };

  /* Add a new observer. If an event was emitted before, the observer is called
   * with the last value.
   * @param observer Observer function to be added
   */
  gyps.observe = observer => {
    last ? observer(last) : null;
    const emit = gyps.emit;
    gyps.emit = data => {
      emit(data);
      observer(data);
      return gyps;
    };
    return gyps;
  };

  /* Map all values to a constant.
   * @param value Constant that will be emitted
   */
  gyps.constant = value => gyps.map(_ => value);

  /* Only emit values that pass the predicate.
   * @param predicate Test function, returns true if pass
   */
  gyps.filter = predicate => {
    const filter = Gyps();
    gyps.observe(data => predicate(data) && filter.emit(data));
    return filter;
  };

  /* Transform a observable of observables into a observable of values emitted
   * by values of the original observable.
   */
  gyps.flatten = _ => {
    const flatten = Gyps();
    gyps.observe(stream => stream.observe(data => flatten.emit(data)));
    return flatten;
  };

  /* Transform each value by a function.
   * @param mapper Function that will be applied to each value
   */
  gyps.map = mapper => {
    const map = Gyps();
    gyps.observe(data => map.emit(mapper(data)));
    return map;
  };

  /* Combine multiple observables into one.
   * @param streams Observables to be joined
   */
  gyps.merge = (...streams) => {
    const merge = Gyps();
    [gyps, ...streams]
      .forEach(stream => stream.observe(data => merge.emit(data)));
    return merge;
  };

  /* Scan
   */
  gyps.scan = (reducer, initial) => {
    const scan = Gyps();
    let value = initial;
    gyps.observe(data => scan.emit(value = reducer(value, data)));
    return scan;
  };

  /* Trigger
   */
  gyps.trigger = value$ => {
    const trigger = Gyps();
    let value;
    value$.observe(x => value = x);
    gyps.observe(_ => trigger.emit(value));
    return trigger;
  };

  /* Wrap
   */
  gyps.wrap = key => gyps.map(value => ({ [key]: value }));

  return gyps;
};
<commit_msg>Document scan<commit_after>const Gyps = _ => {
  const gyps = Object.create(null);

  let last;

  /* Emit an event, calling all observers.
   * @param data Data object to be used on calling observers
   */
  gyps.emit = data => {
    last = data;
    return gyps;
  };

  /* Add a new observer. If an event was emitted before, the observer is called
   * with the last value.
   * @param observer Observer function to be added
   */
  gyps.observe = observer => {
    last ? observer(last) : null;
    const emit = gyps.emit;
    gyps.emit = data => {
      emit(data);
      observer(data);
      return gyps;
    };
    return gyps;
  };

  /* Map all values to a constant.
   * @param value Constant that will be emitted
   */
  gyps.constant = value => gyps.map(_ => value);

  /* Only emit values that pass the predicate.
   * @param predicate Test function, returns true if pass
   */
  gyps.filter = predicate => {
    const filter = Gyps();
    gyps.observe(data => predicate(data) && filter.emit(data));
    return filter;
  };

  /* Transform a observable of observables into a observable of values emitted
   * by values of the original observable.
   */
  gyps.flatten = _ => {
    const flatten = Gyps();
    gyps.observe(stream => stream.observe(data => flatten.emit(data)));
    return flatten;
  };

  /* Transform each value by a function.
   * @param mapper Function that will be applied to each value
   */
  gyps.map = mapper => {
    const map = Gyps();
    gyps.observe(data => map.emit(mapper(data)));
    return map;
  };

  /* Combine multiple observables into one.
   * @param streams Observables to be joined
   */
  gyps.merge = (...streams) => {
    const merge = Gyps();
    [gyps, ...streams]
      .forEach(stream => stream.observe(data => merge.emit(data)));
    return merge;
  };

  /* Accumulate values using a function.
   * @param reducer Function that receives the accumulated value and a new
   *   value, and return the new accumulated
   * @param initial Initial value
   */
  gyps.scan = (reducer, initial) => {
    const scan = Gyps();
    let value = initial;
    gyps.observe(data => scan.emit(value = reducer(value, data)));
    return scan;
  };

  /* Trigger
   */
  gyps.trigger = value$ => {
    const trigger = Gyps();
    let value;
    value$.observe(x => value = x);
    gyps.observe(_ => trigger.emit(value));
    return trigger;
  };

  /* Wrap
   */
  gyps.wrap = key => gyps.map(value => ({ [key]: value }));

  return gyps;
};
<|endoftext|>


<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file defines the commonly-used functionality needed by a stone ridge
 * test suite. This must be run under xpcshell running in JS v1.8 mode.
 */

var STONERIDGE_FINISHED = null;
var STONERIDGE_RESULTS = null;

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;

/*
 * Store some results for writing once we're all done
 */
function do_write_result(key, start, stop) {
  var startms = start.valueOf();
  var stopms = stop.valueOf();

  var val = {'start':startms, 'stop':stopms, 'total':stopms - startms};

  if (STONERIDGE_RESULTS.hasOwnProperty(key)) {
    STONERIDGE_RESULTS[key].push(val);
  } else {
    STONERIDGE_RESULTS[key] = [val];
  }
}

/*
 * This is used to indicate that the tests are done. Now that we know we're
 * done, we can write the results to disk for the python harness to do its thing
 * with.
 */
function do_test_finish() {
  STONERIDGE_FINISHED = true;
}

/*
 * This is only here for symmetry with xpcshell unit tests, stone ridge assumes
 * everything it runs is going to be asynchronous.
 */
function do_test_pending() {}

function _do_save_results() {
  // Create a file pointing to our output directory
  var ofile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  ofile.initWithPath(_SR_OUT_SUBDIR);

  // And use the file determined by our caller
  ofile.append(_SR_OUT_FILE);

  // Now get an output stream for our file
  var ostream = Cc["@mozilla.org/network/file-output-stream;1"].
                createInstance(Ci.nsIFileOutputStream);
  ostream.init(ofile, -1, -1, 0);

  var jstring = JSON.stringify(STONERIDGE_RESULTS);
  ostream.write(jstring, jstring.length);
  ostream.close();
}

function make_channel(url) {
  var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  return ios.newChannel(url, "", null);
}

/*
 * The main entry point for all stone ridge tests
 */
function do_stoneridge() {
  STONERIDGE_FINISHED = false;
  STONERIDGE_RESULTS = {};

  run_test();

  // Pump the event loop until we're told to stop
  var thread = Cc["@mozilla.org/thread-manager;1"].
               getService().currentThread;
  while (!STONERIDGE_FINISHED)
    thread.processNextEvent(true);
  while (thread.hasPendingEvents())
    thread.processNextEvent(true);

  _do_save_results();
}
<commit_msg>Start with some jshint-inspired cleanups to head.js<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file defines the commonly-used functionality needed by a stone ridge
 * test suite. This must be run under xpcshell running in JS v1.8 mode.
 */

/*jshint curly:true, indent:4, latedef:true, undef:true, unused:true,
  trailing:true, es5:true, esnext:true*/
/*global Components:true, run_test:true, _SR_OUT_SUBDIR:true,
   _SR_OUT_FILE:true*/

var STONERIDGE_FINISHED = null;
var STONERIDGE_RESULTS = null;

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;

/*
 * Store some results for writing once we're all done
 */
function do_write_result(key, start, stop) {
    var startms = start.valueOf();
    var stopms = stop.valueOf();

    var val = {'start': startms, 'stop': stopms, 'total': stopms - startms};

    if (STONERIDGE_RESULTS.hasOwnProperty(key)) {
        STONERIDGE_RESULTS[key].push(val);
    } else {
        STONERIDGE_RESULTS[key] = [val];
    }
}

/*
 * This is used to indicate that the tests are done. Now that we know we're
 * done, we can write the results to disk for the python harness to do its thing
 * with.
 */
function do_test_finish() {
    STONERIDGE_FINISHED = true;
}

/*
 * This is only here for symmetry with xpcshell unit tests, stone ridge assumes
 * everything it runs is going to be asynchronous.
 */
function do_test_pending() {}

function _do_save_results() {
    // Create a file pointing to our output directory
    var ofile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
    ofile.initWithPath(_SR_OUT_SUBDIR);

    // And use the file determined by our caller
    ofile.append(_SR_OUT_FILE);

    // Now get an output stream for our file
    var ostream = Cc["@mozilla.org/network/file-output-stream;1"].
                  createInstance(Ci.nsIFileOutputStream);
    ostream.init(ofile, -1, -1, 0);

    var jstring = JSON.stringify(STONERIDGE_RESULTS);
    ostream.write(jstring, jstring.length);
    ostream.close();
}

function make_channel(url) {
    var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
    return ios.newChannel(url, "", null);
}

/*
 * The main entry point for all stone ridge tests
 */
function do_stoneridge() {
    STONERIDGE_FINISHED = false;
    STONERIDGE_RESULTS = {};

    run_test();

    // Pump the event loop until we're told to stop
    var thread = Cc["@mozilla.org/thread-manager;1"].
                 getService().currentThread;
    while (!STONERIDGE_FINISHED) {
        thread.processNextEvent(true);
    }
    while (thread.hasPendingEvents()) {
        thread.processNextEvent(true);
    }

    _do_save_results();
}
<|endoftext|>


<commit_before>const bot = require('./bot');

var config = require('./config');

bot.on('message', message => {
    var channel = message.channel;

    if (channel && channel.id !== config.help_channel) {
        return;
    }

    message = message.content.toLowerCase();
    //Error updating
    if (message.includes('updating') && message.includes('forever')) {
        channel.sendMessage('Follow this article \n http://battlelog.co/wiki.php?article=27');
    }
    //Error debugging
    if (message.includes('unkown dynamicoption')) {
        channel.sendMessage('Follow the second part of this article. \n http://battlelog.co/post.php?id=9744');
    }
    //Mouse stuck after launch ingame
    if (message.includes('mouse') && message.includes('stuck')) {
        channel.sendMessage('follow this article\nhttp://battlelog.co/wiki.php?article=7');
    }
    //Invalid CD key Fix
    if (message.includes('invalid') && message.includes('cdkey')) {
        channel.sendMessage('follow these articles\nhttps://battlelog.co/post.php?id=16014&page=1\nhttp://battlelog.co/wiki.php?article=28');
    }
    //Installation Errors
    if (message.includes('installation') && message.includes('failed')) {
        channel.sendMessage('follow this article\nhttp://battlelog.co/post.php?id=17435#p57246');
    }
    //Black screen after launch
    if (message.includes('black screen') && message.includes('help')) {
        channel.sendMessage('Follow this article \nhttp://battlelog.co/post.php?id=9744');
    }
    //Login Errors
    if (message.includes('error') && message.includes('api')) {
        channel.sendMessage('Follow this article \n https://battlelog.co/post.php?id=17435#p93129');
    }
    //DirectX9 errors after launhcer
    if (message.includes('directx9') && message.includes('error')) {
        channel.sendMessage('Follow this article \n https://battlelog.co/wiki.php?article=14');
    }
    //Server Rules Battlelog
    if (message.includes('server') && message.includes('rules')) {
        channel.sendMessage('Follow this article \n https://battlelog.co/post.php?id=19581');
    }
    //Howto Report players
    if (message.includes('howto') && message.includes('report')) {
        channel.sendMessage('Follow this article \n https://battlelog.co/post.php?id=15931');
    }
});
<commit_msg>block obsene word<commit_after>const bot = require('./bot');

var config = require('./config');

bot.on('message', message => {
    var channel = message.channel;

    if (channel && channel.id !== config.help_channel) {
        return;
    }
    var user = message.author;
    message = message.content.toLowerCase();
    //Error updating
    if (message.includes('updating') && message.includes('forever')) {
        channel.sendMessage('Follow this article \n http://battlelog.co/wiki.php?article=27');
    }
    //Error debugging
    if (message.includes('unkown dynamicoption')) {
        channel.sendMessage('Follow the second part of this article. \n http://battlelog.co/post.php?id=9744');
    }
    //Mouse stuck after launch ingame
    if (message.includes('mouse') && message.includes('stuck')) {
        channel.sendMessage('follow this article\nhttp://battlelog.co/wiki.php?article=7');
    }
    //Invalid CD key Fix
    if (message.includes('invalid') && message.includes('cdkey')) {
        channel.sendMessage('follow these articles\nhttps://battlelog.co/post.php?id=16014&page=1\nhttp://battlelog.co/wiki.php?article=28');
    }
    //Installation Errors
    if (message.includes('installation') && message.includes('failed')) {
        channel.sendMessage('follow this article\nhttp://battlelog.co/post.php?id=17435#p57246');
    }
    //Black screen after launch
    if (message.includes('black screen') && message.includes('help')) {
        channel.sendMessage('Follow this article \nhttp://battlelog.co/post.php?id=9744');
    }
    //Login Errors
    if (message.includes('error') && message.includes('api')) {
        channel.sendMessage('Follow this article \n https://battlelog.co/post.php?id=17435#p93129');
    }
    //DirectX9 errors after launhcer
    if (message.includes('directx9') && message.includes('error')) {
        channel.sendMessage('Follow this article \n https://battlelog.co/wiki.php?article=14');
    }
    //Server Rules Battlelog
    if (message.includes('server') && message.includes('rules')) {
        channel.sendMessage('Follow this article \n https://battlelog.co/post.php?id=19581');
    }
    //Howto Report players
    if (message.includes('howto') && message.includes('report')) {
        channel.sendMessage('Follow this article \n https://battlelog.co/post.php?id=15931');
    }
    if(message.includes('fuck'))
	channel.sendMessage("WARNING **"+user.username+"**: Dont use Obsene words");
});
<|endoftext|>