<commit_before>/**
* User interface for tagging content.
* @class
* @name boom.tagger
*/
$.widget( 'boom.tagger', {
/** @lends boom.tagger */

	/**
	default config
	@property
	*/
	options: {
		/** URL prefix for ajax calls */
		base_url : '/cms/tags/',
		/** Type. One of asset|page */
		type : 'asset',
		/** ID of the item being tagged */
		id : null
	},

	_create : function(){

		$.boom.log( 'tag widget created' );

		var
			self = this,
			type = this.options.type,
			id = this.options.id;

		self._bind();

		self.bind_tree()
			.progress( function( $link ){

				self.remove( $link.attr( 'href' ) );

				$link.closest('li').remove();

			});

		self.picker( self.element.find( '.b-filter-input' ), type )
			.progress( function ( tag ) {
				self.add( tag );
			});
	},

	_init : function(){

	},

	_destroy : function(){

	},

	_bind : function(){
		// The add tag input box is hidden when the modal window opens.
		// Show it and give it focus when the add button is clicked.
		this.element
			.on('click', '#b-tags-add', function(){
				$('#b-tags-add-name').show().focus();
			})
			// Hide the add tag input box when it loses focus.
			.on('blur', '#b-tags-add-name', function(){
				$('#b-tags-add-name').val('').hide();
			});

	},

	/**
	Add a tag to an item.
	@param {String} tag Tag name
	*/
	add : function( tag ) {
		var
			self = this,
			type = this.options.type,
			id = this.options.id;

		$.boom.loader.show();

		$.post(
			self.options.base_url + type + '/add/' + id,
			{
				tag : tag.label
			}
			)
			.done(function(){

				$.get( self.options.base_url + type + '/list/' + id )
				.done( function( response ){
					var list = $( response ).find( 'ul' ).ui();
					self.element
					.find( 'ul' )
					.replaceWith( list );

				});
			});

		$.boom.loader.hide();
		$('#b-tags-add-name').val('').hide();
	},

	/**
	Remove a tag from an item.
	@param {String} tag Tag name
	*/
	remove : function( tag ) {
		var
			self = this,
			type = this.options.type,
			id = this.options.id;

			return $.post(
				self.options.base_url + type + '/remove/' + id,
				{tag : tag}
				);
	},

	/**
	Bind events to the tag list
	@param {String} selector jQuery selector for the list container
	@returns {Deferred} sends progress notfications to handle remove callbacks.
	*/
	bind_tree : function() {
		var remove = new $.Deferred();

		this.element
			// When hovering over an existing tag show a button to remove the tag from the page.
			// Then hide the button again when the mouse moves away.
			.on('mouseenter', '.b-tags-list li', function(){
				// If the ui-icon and ui-icon-close clases are added in the HTML then the crosses aren't hidden when the modal opens.
				// So we only add these classes when we need to show them.
				$(this)
					.find('a')
					.addClass('ui-icon ui-icon-close')
					.show()
					.end()
					.find('span')
					.addClass('active');
				// Adding the active class changes the margin-left of the text so that it doesn't shift to the right when then delete url becomes visible.
			})
			.on('mouseleave', '.b-tags-list li', function(){
				$(this)
					.find('a')
					.hide()
					.end()
					.find('span')
					.removeClass('active');
			})
			// Remove a tag from the page.
			.on('click', '.b-tags-remove', function(event){
				event.preventDefault();

				remove.notify( $(this) );
			});

			remove.progress( function(){
				$.boom.loader.hide();
			});

			return remove;
	},

	/**
	Autocomplete UI for finding and picking tags.
	@param {jQuery element} add_input HTML text input for the tag name.
	@param {String} type one of asset/page.
	@param {Array} tags optional array of tags to filter the search by
	@returns {Deferred} Promise which resolves with the chosen tag as {label : {tag_name}, value : {tag_id} }
	*/
	picker : function( add_input, type, tags ){

		console.log( add_input );

		var complete = new $.Deferred();
		tags = ( tags ) ? tags : [];

		add_input
			.autocomplete({
				delay: 200, // Time to wait after keypress before making the AJAX call.
				source: function( request, response ){
					$.ajax({
						url: '/cms/autocomplete/tags',
						dataType: 'json',
						data: {
							text : add_input.val(),
							type : (type == 'asset')? 1 : 2,
							tags : tags
						}
					})
					.done(function(data) {
						var suggestions = [];
						for ( name in data ) {
							suggestions.push({
								label : name,
								value : data[ name ]
							});
						}
						response( suggestions );
					});
				},
				select: function( event, ui ){
					event.preventDefault();
					add_input.val( '' );
					complete.notify( ui.item );
				}
			})
			.on( 'keypress', function( e ){
				// Add a tag when the enter key is pressed.
				// This allows us to add a tag which doesn't already exist.
				if (e.which == 13) {
					complete.notify( { label: add_input.val(), value: -1 } );
				}
			});

		return complete;
	}

});

/**
* User interface for searching by tag.
* @class
* @name boom.tag_search
* @extends boom.tagger
*/
$.widget( 'boom.tag_search', $.boom.tagger, {
	/** @lends boom.tag_search */

	/**
	default config
	@property
	*/
	options: {
		/** selected tags for this search */
		selected_tag_ids : [],
		/** tagmanager context for search */
		tagmanager : {}
	},

	_bind : function(){

	},

	/**
	Add a tag to the search.
	@param {String} tag Tag name
	*/
	add : function( tag ) {
		var
			self = this,
			selected_tag_ids = this.options.selected_tag_ids,
			tag_filter_list = this.element.find( 'ul.b-tags-list' );

		selected_tag_ids.push( tag.value );
		var link = $( '<a>', {
			href : '#',
			"class" : 'b-tags-remove',
			"data-tag_id" : tag.value
		});
		var label = $( '<span>').text( tag.label );

		$( '<li>' )
			.append( link )
			.append( label )
			.appendTo( tag_filter_list );

		self.do_search();
	},

	/**
	Remove a tag from the search.
	@param {String} tag Tag name
	*/
	remove : function( tag ) {
		var
			self = this,
			selected_tag_ids = this.options.selected_tag_ids;

			selected_tag_ids.splice( selected_tag_ids.indexOf( tag.value ), 1);
			self.do_search();
	},

	/**
	Autocomplete, filtering by all selected tags
	*/
	picker : function( add_input, type ) {
		var
			self = this,
			selected_tag_ids = this.options.selected_tag_ids;

		console.log( add_input );

		return $.boom.tagger.prototype.picker.call( this, add_input, type, selected_tag_ids );
	},

	/** Perform a search based on currently selected tags */
	do_search : function() {
		var
			self = this,
			selected_tag_ids = this.options.selected_tag_ids,
			tagmanager = this.options.tagmanager;

			console.log( selected_tag_ids );

		tagmanager.items.tag.get( selected_tag_ids.join( '-' ) );
	}

});

/**
* Deferred tagger for file uploads.
* @class
* @name boom.deferred_tagger
* @extends boom.tagger
*/
$.widget( 'boom.deferred_tagger', $.boom.tagger, {
	/** @lends boom.deferred_tagger */

	/**
	default config
	@property
	*/
	options: {
		/** selected tags */
		tags : [],
		/** URL prefix for ajax calls */
		base_url : '/cms/tags/',
		/** Type. One of asset|page */
		type : 'asset'
	},

	_bind : function(){

	},

	/**
	Add a tag to the search.
	@param {String} tag Tag name
	*/
	add : function( tag ) {
		var
			self = this,
			tags = this.options.tags,
			tag_list = this.element.find( 'ul.b-tags-list' );

			tags.push( tag.label );

			var link = $( '<a>', {
				href : '#',
				"class" : 'b-tags-remove',
				"data-tag_id" : tag.value
			});
			var label = $( '<span>').text( tag.label );

			$( '<li>' )
				.append( link )
				.append( label )
				.appendTo( tag_list );
	},

	/**
	Remove a tag from the search.
	@param {String} tag Tag name
	*/
	remove : function( tag ) {
		var
			self = this,
			tags = this.options.tags;

			tags.splice( selected_tag_ids.indexOf( tag.value ), 1);
			self.do_search();
	}

});
$.extend($.boom, {
	/**
	@lends $.boom
	*/
	/**
	* User interface for tagging content and searching by tag.
	* @class
	*/
	tags : {

		/**
		Base URL for the tags controller
		@property
		@type String
		@default '/cms/tags'
		*/
		base_url: '/cms/tags/',

		/**
		Container element for the tag UI.
		@property
		@type jQuery element
		@default null
		*/
		container : null,

		/**
		Bind events to the tag list
		@param {String} selector jQuery selector for the list container
		@returns {Deferred} sends progress notfications to handle remove callbacks.
		*/
		bind_tree : function( selector ) {
			var remove = new $.Deferred();

			$( selector )
				// When hovering over an existing tag show a button to remove the tag from the page.
				// Then hide the button again when the mouse moves away.
				.on('mouseenter', '.b-tags-list li', function(){
					// If the ui-icon and ui-icon-close clases are added in the HTML then the crosses aren't hidden when the modal opens.
					// So we only add these classes when we need to show them.
					$(this)
						.find('a')
						.addClass('ui-icon ui-icon-close')
						.show()
						.end()
						.find('span')
						.addClass('active');
					// Adding the active class changes the margin-left of the text so that it doesn't shift to the right when then delete url becomes visible.
				})
				.on('mouseleave', '.b-tags-list li', function(){
					$(this)
						.find('a')
						.hide()
						.end()
						.find('span')
						.removeClass('active');
				})
				// Remove a tag from the page.
				.on('click', '.b-tags-remove', function(event){
					event.preventDefault();

					$.boom.loader.show();

					remove.notify( $(this) );
				});

				remove.progress( function(){
					$.boom.loader.hide();
				});

				return remove;
		},

		/**
		Autocomplete UI for finding and picking tags.
		@param {jQuery element} add_input HTML text input for the tag name.
		@param {String} type one of asset/page.
		@param {Array} tags optional array of tags to filter the search by
		@returns {Deferred} Promise which resolves with the chosen tag as {label : {tag_name}, value : {tag_id} }
		*/
		picker : function( add_input, type, tags ){

			var complete = new $.Deferred();
			tags = ( tags ) ? tags : [];

			add_input
				.autocomplete({
					delay: 200, // Time to wait after keypress before making the AJAX call.
					source: function( request, response ){
						$.ajax({
							url: '/cms/autocomplete/tags',
							dataType: 'json',
							data: {
								text : add_input.val(),
								type : (type == 'asset')? 1 : 2,
								tags : tags
							}
						})
						.done(function(data) {
							var suggestions = [];
							for ( name in data ) {
								suggestions.push({
									label : name,
									value : data[ name ]
								});
							}
							response( suggestions );
						});
					},
					select: function( event, ui ){
						event.preventDefault();
						add_input.val( '' );
						complete.notify( ui.item );
					}
				})
				.on( 'keypress', function( e ){
					// Add a tag when the enter key is pressed.
					// This allows us to add a tag which doesn't already exist.
					if (e.which == 13) {
						complete.notify( { label: add_input.val(), value: -1 } );
					}
				});

			return complete;
		}
	}
});<commit_msg>delete $.boom.tags<commit_after>/**
* User interface for tagging content.
* @class
* @name boom.tagger
*/
$.widget( 'boom.tagger', {
/** @lends boom.tagger */

	/**
	default config
	@property
	*/
	options: {
		/** URL prefix for ajax calls */
		base_url : '/cms/tags/',
		/** Type. One of asset|page */
		type : 'asset',
		/** ID of the item being tagged */
		id : null
	},

	_create : function(){

		$.boom.log( 'tag widget created' );

		var
			self = this,
			type = this.options.type,
			id = this.options.id;

		self._bind();

		self.bind_tree()
			.progress( function( $link ){

				self.remove( $link.attr( 'href' ) );

				$link.closest('li').remove();

			});

		self.picker( self.element.find( '.b-filter-input' ), type )
			.progress( function ( tag ) {
				self.add( tag );
			});
	},

	_init : function(){

	},

	_destroy : function(){

	},

	_bind : function(){
		// The add tag input box is hidden when the modal window opens.
		// Show it and give it focus when the add button is clicked.
		this.element
			.on('click', '#b-tags-add', function(){
				$('#b-tags-add-name').show().focus();
			})
			// Hide the add tag input box when it loses focus.
			.on('blur', '#b-tags-add-name', function(){
				$('#b-tags-add-name').val('').hide();
			});

	},

	/**
	Add a tag to an item.
	@param {String} tag Tag name
	*/
	add : function( tag ) {
		var
			self = this,
			type = this.options.type,
			id = this.options.id;

		$.boom.loader.show();

		$.post(
			self.options.base_url + type + '/add/' + id,
			{
				tag : tag.label
			}
			)
			.done(function(){

				$.get( self.options.base_url + type + '/list/' + id )
				.done( function( response ){
					var list = $( response ).find( 'ul' ).ui();
					self.element
					.find( 'ul' )
					.replaceWith( list );

				});
			});

		$.boom.loader.hide();
		$('#b-tags-add-name').val('').hide();
	},

	/**
	Remove a tag from an item.
	@param {String} tag Tag name
	*/
	remove : function( tag ) {
		var
			self = this,
			type = this.options.type,
			id = this.options.id;

			return $.post(
				self.options.base_url + type + '/remove/' + id,
				{tag : tag}
				);
	},

	/**
	Bind events to the tag list
	@param {String} selector jQuery selector for the list container
	@returns {Deferred} sends progress notfications to handle remove callbacks.
	*/
	bind_tree : function() {
		var remove = new $.Deferred();

		this.element
			// When hovering over an existing tag show a button to remove the tag from the page.
			// Then hide the button again when the mouse moves away.
			.on('mouseenter', '.b-tags-list li', function(){
				// If the ui-icon and ui-icon-close clases are added in the HTML then the crosses aren't hidden when the modal opens.
				// So we only add these classes when we need to show them.
				$(this)
					.find('a')
					.addClass('ui-icon ui-icon-close')
					.show()
					.end()
					.find('span')
					.addClass('active');
				// Adding the active class changes the margin-left of the text so that it doesn't shift to the right when then delete url becomes visible.
			})
			.on('mouseleave', '.b-tags-list li', function(){
				$(this)
					.find('a')
					.hide()
					.end()
					.find('span')
					.removeClass('active');
			})
			// Remove a tag from the page.
			.on('click', '.b-tags-remove', function(event){
				event.preventDefault();

				remove.notify( $(this) );
			});

			remove.progress( function(){
				$.boom.loader.hide();
			});

			return remove;
	},

	/**
	Autocomplete UI for finding and picking tags.
	@param {jQuery element} add_input HTML text input for the tag name.
	@param {String} type one of asset/page.
	@param {Array} tags optional array of tags to filter the search by
	@returns {Deferred} Promise which resolves with the chosen tag as {label : {tag_name}, value : {tag_id} }
	*/
	picker : function( add_input, type, tags ){

		console.log( add_input );

		var complete = new $.Deferred();
		tags = ( tags ) ? tags : [];

		add_input
			.autocomplete({
				delay: 200, // Time to wait after keypress before making the AJAX call.
				source: function( request, response ){
					$.ajax({
						url: '/cms/autocomplete/tags',
						dataType: 'json',
						data: {
							text : add_input.val(),
							type : (type == 'asset')? 1 : 2,
							tags : tags
						}
					})
					.done(function(data) {
						var suggestions = [];
						for ( name in data ) {
							suggestions.push({
								label : name,
								value : data[ name ]
							});
						}
						response( suggestions );
					});
				},
				select: function( event, ui ){
					event.preventDefault();
					add_input.val( '' );
					complete.notify( ui.item );
				}
			})
			.on( 'keypress', function( e ){
				// Add a tag when the enter key is pressed.
				// This allows us to add a tag which doesn't already exist.
				if (e.which == 13) {
					complete.notify( { label: add_input.val(), value: -1 } );
				}
			});

		return complete;
	}

});

/**
* User interface for searching by tag.
* @class
* @name boom.tag_search
* @extends boom.tagger
*/
$.widget( 'boom.tag_search', $.boom.tagger, {
	/** @lends boom.tag_search */

	/**
	default config
	@property
	*/
	options: {
		/** selected tags for this search */
		selected_tag_ids : [],
		/** tagmanager context for search */
		tagmanager : {}
	},

	_bind : function(){

	},

	/**
	Add a tag to the search.
	@param {String} tag Tag name
	*/
	add : function( tag ) {
		var
			self = this,
			selected_tag_ids = this.options.selected_tag_ids,
			tag_filter_list = this.element.find( 'ul.b-tags-list' );

		selected_tag_ids.push( tag.value );
		var link = $( '<a>', {
			href : '#',
			"class" : 'b-tags-remove',
			"data-tag_id" : tag.value
		});
		var label = $( '<span>').text( tag.label );

		$( '<li>' )
			.append( link )
			.append( label )
			.appendTo( tag_filter_list );

		self.do_search();
	},

	/**
	Remove a tag from the search.
	@param {String} tag Tag name
	*/
	remove : function( tag ) {
		var
			self = this,
			selected_tag_ids = this.options.selected_tag_ids;

			selected_tag_ids.splice( selected_tag_ids.indexOf( tag.value ), 1);
			self.do_search();
	},

	/**
	Autocomplete, filtering by all selected tags
	*/
	picker : function( add_input, type ) {
		var
			self = this,
			selected_tag_ids = this.options.selected_tag_ids;

		console.log( add_input );

		return $.boom.tagger.prototype.picker.call( this, add_input, type, selected_tag_ids );
	},

	/** Perform a search based on currently selected tags */
	do_search : function() {
		var
			self = this,
			selected_tag_ids = this.options.selected_tag_ids,
			tagmanager = this.options.tagmanager;

			console.log( selected_tag_ids );

		tagmanager.items.tag.get( selected_tag_ids.join( '-' ) );
	}

});

/**
* Deferred tagger for file uploads.
* @class
* @name boom.deferred_tagger
* @extends boom.tagger
*/
$.widget( 'boom.deferred_tagger', $.boom.tagger, {
	/** @lends boom.deferred_tagger */

	/**
	default config
	@property
	*/
	options: {
		/** selected tags */
		tags : [],
		/** URL prefix for ajax calls */
		base_url : '/cms/tags/',
		/** Type. One of asset|page */
		type : 'asset'
	},

	_bind : function(){

	},

	/**
	Add a tag to the search.
	@param {String} tag Tag name
	*/
	add : function( tag ) {
		var
			self = this,
			tags = this.options.tags,
			tag_list = this.element.find( 'ul.b-tags-list' );

			tags.push( tag.label );

			var link = $( '<a>', {
				href : '#',
				"class" : 'b-tags-remove',
				"data-tag_id" : tag.value
			});
			var label = $( '<span>').text( tag.label );

			$( '<li>' )
				.append( link )
				.append( label )
				.appendTo( tag_list );
	},

	/**
	Remove a tag from the search.
	@param {String} tag Tag name
	*/
	remove : function( tag ) {
		var
			self = this,
			tags = this.options.tags;

			tags.splice( selected_tag_ids.indexOf( tag.value ), 1);
			self.do_search();
	}

});<|endoftext|>


<commit_before>describe("Command Builder", function(){
	it("should exist", function(){
		expect(buildCommand()).toBeTruthy();
	});

	it("should format a command name without parameters", function(){
		expect(buildCommand(["make"])).toEqual("[make]");
	});
});<commit_msg>red: Command Builder should format a command name with a single parameters<commit_after>describe("Command Builder", function(){
	it("should exist", function(){
		expect(buildCommand()).toBeTruthy();
	});

	it("should format a command name without parameters", function(){
		expect(buildCommand(["make"])).toEqual("[make]");
	});

	it("should format a command name with a single parameters", function(){
		expect(buildCommand(["make","clean"])).toEqual("[make:clean]");
	});
});<|endoftext|>


<commit_before>var Lexer = require('lex');

const formatToken = text => {
  text = text || '';
  const results = [];
  results.push('TOKEN_VALUE_START');
  results.push(
    '"' +
      text
        .replace(/\"/g, '"')
        .replace(/\\\[/, '[')
        .replace(/\\\]/, ']') +
      '"'
  );
  results.push('TOKEN_VALUE_END');
  return results;
};

const shouldBreak = text => {
  if (text.trim() === '' && (text.match(/\n/g) || []).length > 1) {
    return true;
  }
  return false;
};

let currentInput = null;

const lex = function(options) {
  let { row, column, outer, skipLists, inComponent, gotName } = Object.assign(
    {},
    {
      row: 1,
      column: 1,
      outer: true,
      skipLists: false,
      inComponent: false,
      gotName: false
    },
    options || {}
  );
  var lexer = new Lexer(function(chr) {
    let errorString = `
      Error lexing input, unexpected token: ${chr}

      Found near index ${this.index - 1}:

      ${currentInput.substring(
        Math.max(0, this.index - 10),
        Math.min(this.index + 10, currentInput.length - 1)
      )}
    `;
    throw new Error(errorString);
  });

  const recurse = (str, opts) => {
    return lex(Object.assign({ row, column, outer: false }, opts || {}))(str)
      .tokens;
  };

  var updatePosition = function(lexeme) {
    var lines = lexeme.split('\n');
    row += lines.length - 1;
    if (lines.length > 1) {
      column = 0;
    }
    column += lines[lines.length - 1].length;
  };

  // Rules at the front are pre-processed,
  // e.g. equations, and code snippets
  // that shouldn't be formatted.

  lexer.addRule(
    /\[\s*equation\s*([^\/\]]*)\s*\][\n\s\t]*(((?!(\[\s*\/equation\s*\])).\n?)*)[\n\s\t]*\[\s*\/\s*equation\s*\]/i,
    function(lexeme, props, innerText) {
      inComponent = false;
      if (this.reject) return;
      updatePosition(lexeme);
      return ['OPEN_BRACKET', 'COMPONENT_NAME']
        .concat(formatToken('equation'))
        .concat(recurse(props, { inComponent: true, gotName: true }))
        .concat(['CLOSE_BRACKET'])
        .concat(['WORDS'])
        .concat(formatToken(innerText))
        .concat(['OPEN_BRACKET', 'FORWARD_SLASH', 'COMPONENT_NAME'])
        .concat(formatToken('equation'))
        .concat(['CLOSE_BRACKET']);
    }
  );
  lexer.addRule(
    /\[\s*code\s*([^\/\]]*)\s*\][\n\s\t]*(((?!(\[\s*code\s*\])).)*)[\n\s\t]*\[\s*\/\s*code\s*\]/i,
    function(lexeme, props, innerText) {
      inComponent = false;
      if (this.reject) return;
      updatePosition(lexeme);
      return ['OPEN_BRACKET', 'COMPONENT_NAME']
        .concat(formatToken('code'))
        .concat(recurse(props, { inComponent: true, gotName: true }))
        .concat(['CLOSE_BRACKET'])
        .concat(['WORDS'])
        .concat(formatToken(innerText))
        .concat(['OPEN_BRACKET', 'FORWARD_SLASH', 'COMPONENT_NAME'])
        .concat(formatToken('code'))
        .concat(['CLOSE_BRACKET']);
    }
  );
  lexer.addRule(/`{4}(\S*)\n(((?!````)[\s\S])*[^\n])\n?\s*`{4}/g, function(
    lexeme,
    language,
    text
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['MULTILINE_CODE']
      .concat(formatToken(language))
      .concat(formatToken(text));
  });
  lexer.addRule(/`{3}(\S*)\n(((?!```)[\s\S])*[^\n])\n?\s*`{3}/g, function(
    lexeme,
    language,
    text
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['MULTILINE_CODE']
      .concat(formatToken(language))
      .concat(formatToken(text));
  });
  lexer.addRule(/```(((?!```)[^\n])+)```/, function(lexeme, text) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['INLINE_CODE'].concat(formatToken(text.trim()));
  });
  lexer.addRule(/``(((?!``)[^\n])+)``/, function(lexeme, text) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['INLINE_CODE'].concat(formatToken(text.trim()));
  });
  lexer.addRule(/`([^\n\`]+)`/, function(lexeme, text) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['INLINE_CODE'].concat(formatToken(text.trim()));
  });

  lexer.addRule(/[\s\n]*(#{1,6})\s*([^\n\[]+)[\n\s]*/gm, function(
    lexeme,
    hashes,
    text
  ) {
    if (this.reject) return;
    updatePosition(lexeme);
    return ['BREAK', 'HEADER_' + hashes.length]
      .concat(recurse(text, { skipLists: true }))
      .concat(['HEADER_END']);
  });

  lexer.addRule(/[\s\n]*>\s*([^\n\[]+)[\n\s]*/gm, function(lexeme, text) {
    if (this.reject) return;
    updatePosition(lexeme);
    return ['BREAK', 'QUOTE_START']
      .concat(recurse(text, { skipLists: true }))
      .concat(['QUOTE_END']);
  });

  lexer.addRule(/\*\*([^\s\n][^\*]*[^\s\n])\*\*(\s*)/g, function(
    lexeme,
    text,
    trailingSpace
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['STRONG']
      .concat(recurse(text, { skipLists: true }))
      .concat(['STRONG_END']);
    if (trailingSpace) {
      if (shouldBreak(trailingSpace)) {
        ret = ret.concat(['BREAK']);
      } else {
        ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
      }
    }
    return ret;
  });
  lexer.addRule(/__([^\s\n][^_]*[^\s\n])__(\s*)/g, function(
    lexeme,
    text,
    trailingSpace
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['STRONG']
      .concat(recurse(text, { skipLists: true }))
      .concat(['STRONG_END']);
    if (trailingSpace) {
      if (shouldBreak(trailingSpace)) {
        ret = ret.concat(['BREAK']);
      } else {
        ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
      }
    }
    return ret;
  });
  lexer.addRule(/\*([^\s\n\*][^\*]*[^\s\n\*])\*(\s*)/g, function(
    lexeme,
    text,
    trailingSpace
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['EM']
      .concat(recurse(text, { skipLists: true }))
      .concat(['EM_END']);
    if (trailingSpace) {
      if (shouldBreak(trailingSpace)) {
        ret = ret.concat(['BREAK']);
      } else {
        ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
      }
    }
    return ret;
  });
  lexer.addRule(/_([^\s\n_][^_]*[^\s\n_])_(\s*)/g, function(
    lexeme,
    text,
    trailingSpace
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['EM']
      .concat(recurse(text, { skipLists: true }))
      .concat(['EM_END']);
    if (trailingSpace) {
      if (shouldBreak(trailingSpace)) {
        ret = ret.concat(['BREAK']);
      } else {
        ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
      }
    }
    return ret;
  });

  lexer.addRule(/^\s*([\-\*]\s+([^\n]*)\n)*([\-\*]\s+([^\n]*)\n?)/gm, function(
    lexeme
  ) {
    this.reject = inComponent || skipLists;
    if (this.reject) return;
    updatePosition(lexeme);
    const items = lexeme.trim().split('\n');
    const matches = items.map(item => /[\-\*]\s*([^\n]*)/.exec(item)[1]);
    let output = ['BREAK', 'UNORDERED_LIST'];
    matches.forEach(item => {
      output = output.concat(['LIST_ITEM']).concat(recurse(item.trim() || ' '));
    });
    return output.concat(['LIST_END']);
  });

  lexer.addRule(/^\s*(\d+\.\s+([^\n]*)\n)*(\d+\.\s+([^\n]*)\n?)/gm, function(
    lexeme
  ) {
    this.reject = inComponent || skipLists;
    if (this.reject) return;
    updatePosition(lexeme);
    const items = lexeme.trim().split('\n');
    const matches = items.map(item => /\d+\.\s*([^\n]*)/.exec(item)[1]);
    let output = ['BREAK', 'ORDERED_LIST'];
    matches.forEach(item => {
      output = output.concat(['LIST_ITEM']).concat(recurse(item));
    });
    return output.concat(['LIST_END']);
  });

  lexer.addRule(/!\[([^\]]*)\]\(([^\)]*)\)/, function(lexeme, text, link) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['IMAGE'].concat(formatToken(text)).concat(formatToken(link));
  });

  lexer.addRule(/\[([^\]]*)\]\(([^\)]*)\)/, function(lexeme, text, link) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['LINK'].concat(formatToken(text)).concat(formatToken(link));
  });

  lexer.addRule(/(\n\s*\/\/[^\n]*|\/\/\s+[^\n]*)/, function(lexeme) {
    updatePosition(lexeme);
  });

  lexer.addRule(/\/(\n?[^`\*\[\/\n\]!\\\d_])*/gm, function(lexeme) {
    this.reject = inComponent || lexeme.trim() === '';
    if (this.reject) return;
    updatePosition(lexeme);
    return ['WORDS'].concat(formatToken(lexeme));
  });

  lexer.addRule(/(\n?[^`\*\[\/\n\]!\\\d_])+/, function(lexeme) {
    this.reject = inComponent || lexeme.trim() === '';
    if (this.reject) return;
    updatePosition(lexeme);
    return ['WORDS'].concat(formatToken(lexeme));
  });
  // Match on separately so we can greedily match the
  // other tags.
  lexer.addRule(/[!\d\*_`]\s*/, function(lexeme) {
    this.reject = inComponent || lexeme.trim() === '';
    if (this.reject) return;
    updatePosition(lexeme);
    return ['WORDS'].concat(formatToken(lexeme));
  });
  lexer.addRule(/\\[\[\]]?/, function(lexeme) {
    this.reject = inComponent || lexeme.trim() === '';
    if (this.reject) return;
    updatePosition(lexeme);
    return ['WORDS'].concat(formatToken(lexeme));
  });

  lexer.addRule(/\s*\n{2,}\s*/, function(lexeme) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['BREAK'];
  });

  lexer.addRule(/[ \t\n]+/, function(lexeme) {
    updatePosition(lexeme);
  });

  lexer.addRule(/\[/, function(lexeme) {
    inComponent = true;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['OPEN_BRACKET'];
  });

  lexer.addRule(/\]([ ]*)/, function(lexeme, trailingSpace) {
    inComponent = false;
    gotName = false;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['CLOSE_BRACKET'];
    if (trailingSpace) {
      ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
    }
    return ret;
  });

  lexer.addRule(/\//, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['FORWARD_SLASH'];
  });

  lexer.addRule(/true|false/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['BOOLEAN'].concat(formatToken(lexeme));
  });

  lexer.addRule(
    /[^+\-0-9:\s\/\]"'`\.]([^:\s\/\]"'`]*[^:\s\/\]"'`\.])*/,
    function(lexeme) {
      this.reject = !inComponent || gotName;
      if (this.reject) return;
      gotName = true;
      updatePosition(lexeme);
      return ['COMPONENT_NAME'].concat(formatToken(lexeme));
    }
  );
  lexer.addRule(/[^+\-0-9:\s\/\]"'`\.][^:\s\/\]"'`\.]*/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['COMPONENT_WORD'].concat(formatToken(lexeme));
  });

  lexer.addRule(/`[^`]*`/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['EXPRESSION'].concat(formatToken(lexeme));
  });

  lexer.addRule(/[+\-]?\.?[0-9]+\.?[0-9]*/, function(lexeme) {
    const multiplePeriods =
      (lexeme.match(new RegExp(/\./, 'g')) || []).length >= 2;
    this.reject = !inComponent || multiplePeriods;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['NUMBER'].concat(formatToken(lexeme));
  });

  lexer.addRule(/"[^"]*"/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['STRING'].concat(formatToken(lexeme));
  });
  lexer.addRule(/'([^']*)'/, function(lexeme, str) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['STRING'].concat(formatToken('"' + str + '"'));
  });

  lexer.addRule(/:/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['PARAM_SEPARATOR'];
  });

  lexer.addRule(/\s*$/, function(lexeme) {
    this.reject = !outer;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['EOF'];
  });

  return function(str) {
    currentInput = str;
    var vals = [];
    var output = [];
    var positions = [];

    lexer.input = str.trim();
    var token = lexer.lex();
    while (token) {
      output.push(token);
      positions.push([row, column]);
      token = lexer.lex();
    }
    return {
      tokens: output,
      positions: positions
    };
  };
};

module.exports = lex;
<commit_msg>matching updates to code preprocessor. trim for tests<commit_after>var Lexer = require('lex');

const formatToken = text => {
  text = text || '';
  const results = [];
  results.push('TOKEN_VALUE_START');
  results.push(
    '"' +
      text
        .replace(/\"/g, '"')
        .replace(/\\\[/, '[')
        .replace(/\\\]/, ']') +
      '"'
  );
  results.push('TOKEN_VALUE_END');
  return results;
};

const shouldBreak = text => {
  if (text.trim() === '' && (text.match(/\n/g) || []).length > 1) {
    return true;
  }
  return false;
};

let currentInput = null;

const lex = function(options) {
  let { row, column, outer, skipLists, inComponent, gotName } = Object.assign(
    {},
    {
      row: 1,
      column: 1,
      outer: true,
      skipLists: false,
      inComponent: false,
      gotName: false
    },
    options || {}
  );
  var lexer = new Lexer(function(chr) {
    let errorString = `
      Error lexing input, unexpected token: ${chr}

      Found near index ${this.index - 1}:

      ${currentInput.substring(
        Math.max(0, this.index - 10),
        Math.min(this.index + 10, currentInput.length - 1)
      )}
    `;
    throw new Error(errorString);
  });

  const recurse = (str, opts) => {
    return lex(Object.assign({ row, column, outer: false }, opts || {}))(str)
      .tokens;
  };

  var updatePosition = function(lexeme) {
    var lines = lexeme.split('\n');
    row += lines.length - 1;
    if (lines.length > 1) {
      column = 0;
    }
    column += lines[lines.length - 1].length;
  };

  // Rules at the front are pre-processed,
  // e.g. equations, and code snippets
  // that shouldn't be formatted.

  lexer.addRule(
    /\[\s*equation\s*([^\/\]]*)\s*\][\n\s\t]*(((?!(\[\s*\/equation\s*\])).\n?)*)[\n\s\t]*\[\s*\/\s*equation\s*\]/i,
    function(lexeme, props, innerText) {
      inComponent = false;
      if (this.reject) return;
      updatePosition(lexeme);
      return ['OPEN_BRACKET', 'COMPONENT_NAME']
        .concat(formatToken('equation'))
        .concat(recurse(props, { inComponent: true, gotName: true }))
        .concat(['CLOSE_BRACKET'])
        .concat(['WORDS'])
        .concat(formatToken(innerText.trim()))
        .concat(['OPEN_BRACKET', 'FORWARD_SLASH', 'COMPONENT_NAME'])
        .concat(formatToken('equation'))
        .concat(['CLOSE_BRACKET']);
    }
  );
  lexer.addRule(
    /\[\s*code\s*([^\/\]]*)\s*\][\n\s\t]*(((?!(\[\s*\/code\s*\])).\n?)*)[\n\s\t]*\[\s*\/\s*code\s*\]/i,
    function(lexeme, props, innerText) {
      inComponent = false;
      if (this.reject) return;
      updatePosition(lexeme);
      return ['OPEN_BRACKET', 'COMPONENT_NAME']
        .concat(formatToken('code'))
        .concat(recurse(props, { inComponent: true, gotName: true }))
        .concat(['CLOSE_BRACKET'])
        .concat(['WORDS'])
        .concat(formatToken(innerText.trim()))
        .concat(['OPEN_BRACKET', 'FORWARD_SLASH', 'COMPONENT_NAME'])
        .concat(formatToken('code'))
        .concat(['CLOSE_BRACKET']);
    }
  );
  lexer.addRule(/`{4}(\S*)\n(((?!````)[\s\S])*[^\n])\n?\s*`{4}/g, function(
    lexeme,
    language,
    text
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['MULTILINE_CODE']
      .concat(formatToken(language))
      .concat(formatToken(text));
  });
  lexer.addRule(/`{3}(\S*)\n(((?!```)[\s\S])*[^\n])\n?\s*`{3}/g, function(
    lexeme,
    language,
    text
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['MULTILINE_CODE']
      .concat(formatToken(language))
      .concat(formatToken(text));
  });
  lexer.addRule(/```(((?!```)[^\n])+)```/, function(lexeme, text) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['INLINE_CODE'].concat(formatToken(text.trim()));
  });
  lexer.addRule(/``(((?!``)[^\n])+)``/, function(lexeme, text) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['INLINE_CODE'].concat(formatToken(text.trim()));
  });
  lexer.addRule(/`([^\n\`]+)`/, function(lexeme, text) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['INLINE_CODE'].concat(formatToken(text.trim()));
  });

  lexer.addRule(/[\s\n]*(#{1,6})\s*([^\n\[]+)[\n\s]*/gm, function(
    lexeme,
    hashes,
    text
  ) {
    if (this.reject) return;
    updatePosition(lexeme);
    return ['BREAK', 'HEADER_' + hashes.length]
      .concat(recurse(text, { skipLists: true }))
      .concat(['HEADER_END']);
  });

  lexer.addRule(/[\s\n]*>\s*([^\n\[]+)[\n\s]*/gm, function(lexeme, text) {
    if (this.reject) return;
    updatePosition(lexeme);
    return ['BREAK', 'QUOTE_START']
      .concat(recurse(text, { skipLists: true }))
      .concat(['QUOTE_END']);
  });

  lexer.addRule(/\*\*([^\s\n][^\*]*[^\s\n])\*\*(\s*)/g, function(
    lexeme,
    text,
    trailingSpace
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['STRONG']
      .concat(recurse(text, { skipLists: true }))
      .concat(['STRONG_END']);
    if (trailingSpace) {
      if (shouldBreak(trailingSpace)) {
        ret = ret.concat(['BREAK']);
      } else {
        ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
      }
    }
    return ret;
  });
  lexer.addRule(/__([^\s\n][^_]*[^\s\n])__(\s*)/g, function(
    lexeme,
    text,
    trailingSpace
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['STRONG']
      .concat(recurse(text, { skipLists: true }))
      .concat(['STRONG_END']);
    if (trailingSpace) {
      if (shouldBreak(trailingSpace)) {
        ret = ret.concat(['BREAK']);
      } else {
        ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
      }
    }
    return ret;
  });
  lexer.addRule(/\*([^\s\n\*][^\*]*[^\s\n\*])\*(\s*)/g, function(
    lexeme,
    text,
    trailingSpace
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['EM']
      .concat(recurse(text, { skipLists: true }))
      .concat(['EM_END']);
    if (trailingSpace) {
      if (shouldBreak(trailingSpace)) {
        ret = ret.concat(['BREAK']);
      } else {
        ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
      }
    }
    return ret;
  });
  lexer.addRule(/_([^\s\n_][^_]*[^\s\n_])_(\s*)/g, function(
    lexeme,
    text,
    trailingSpace
  ) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['EM']
      .concat(recurse(text, { skipLists: true }))
      .concat(['EM_END']);
    if (trailingSpace) {
      if (shouldBreak(trailingSpace)) {
        ret = ret.concat(['BREAK']);
      } else {
        ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
      }
    }
    return ret;
  });

  lexer.addRule(/^\s*([\-\*]\s+([^\n]*)\n)*([\-\*]\s+([^\n]*)\n?)/gm, function(
    lexeme
  ) {
    this.reject = inComponent || skipLists;
    if (this.reject) return;
    updatePosition(lexeme);
    const items = lexeme.trim().split('\n');
    const matches = items.map(item => /[\-\*]\s*([^\n]*)/.exec(item)[1]);
    let output = ['BREAK', 'UNORDERED_LIST'];
    matches.forEach(item => {
      output = output.concat(['LIST_ITEM']).concat(recurse(item.trim() || ' '));
    });
    return output.concat(['LIST_END']);
  });

  lexer.addRule(/^\s*(\d+\.\s+([^\n]*)\n)*(\d+\.\s+([^\n]*)\n?)/gm, function(
    lexeme
  ) {
    this.reject = inComponent || skipLists;
    if (this.reject) return;
    updatePosition(lexeme);
    const items = lexeme.trim().split('\n');
    const matches = items.map(item => /\d+\.\s*([^\n]*)/.exec(item)[1]);
    let output = ['BREAK', 'ORDERED_LIST'];
    matches.forEach(item => {
      output = output.concat(['LIST_ITEM']).concat(recurse(item));
    });
    return output.concat(['LIST_END']);
  });

  lexer.addRule(/!\[([^\]]*)\]\(([^\)]*)\)/, function(lexeme, text, link) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['IMAGE'].concat(formatToken(text)).concat(formatToken(link));
  });

  lexer.addRule(/\[([^\]]*)\]\(([^\)]*)\)/, function(lexeme, text, link) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['LINK'].concat(formatToken(text)).concat(formatToken(link));
  });

  lexer.addRule(/(\n\s*\/\/[^\n]*|\/\/\s+[^\n]*)/, function(lexeme) {
    updatePosition(lexeme);
  });

  lexer.addRule(/\/(\n?[^`\*\[\/\n\]!\\\d_])*/gm, function(lexeme) {
    this.reject = inComponent || lexeme.trim() === '';
    if (this.reject) return;
    updatePosition(lexeme);
    return ['WORDS'].concat(formatToken(lexeme));
  });

  lexer.addRule(/(\n?[^`\*\[\/\n\]!\\\d_])+/, function(lexeme) {
    this.reject = inComponent || lexeme.trim() === '';
    if (this.reject) return;
    updatePosition(lexeme);
    return ['WORDS'].concat(formatToken(lexeme));
  });
  // Match on separately so we can greedily match the
  // other tags.
  lexer.addRule(/[!\d\*_`]\s*/, function(lexeme) {
    this.reject = inComponent || lexeme.trim() === '';
    if (this.reject) return;
    updatePosition(lexeme);
    return ['WORDS'].concat(formatToken(lexeme));
  });
  lexer.addRule(/\\[\[\]]?/, function(lexeme) {
    this.reject = inComponent || lexeme.trim() === '';
    if (this.reject) return;
    updatePosition(lexeme);
    return ['WORDS'].concat(formatToken(lexeme));
  });

  lexer.addRule(/\s*\n{2,}\s*/, function(lexeme) {
    this.reject = inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['BREAK'];
  });

  lexer.addRule(/[ \t\n]+/, function(lexeme) {
    updatePosition(lexeme);
  });

  lexer.addRule(/\[/, function(lexeme) {
    inComponent = true;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['OPEN_BRACKET'];
  });

  lexer.addRule(/\]([ ]*)/, function(lexeme, trailingSpace) {
    inComponent = false;
    gotName = false;
    if (this.reject) return;
    updatePosition(lexeme);
    var ret = ['CLOSE_BRACKET'];
    if (trailingSpace) {
      ret = ret.concat(['WORDS']).concat(formatToken(trailingSpace));
    }
    return ret;
  });

  lexer.addRule(/\//, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['FORWARD_SLASH'];
  });

  lexer.addRule(/true|false/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['BOOLEAN'].concat(formatToken(lexeme));
  });

  lexer.addRule(
    /[^+\-0-9:\s\/\]"'`\.]([^:\s\/\]"'`]*[^:\s\/\]"'`\.])*/,
    function(lexeme) {
      this.reject = !inComponent || gotName;
      if (this.reject) return;
      gotName = true;
      updatePosition(lexeme);
      return ['COMPONENT_NAME'].concat(formatToken(lexeme));
    }
  );
  lexer.addRule(/[^+\-0-9:\s\/\]"'`\.][^:\s\/\]"'`\.]*/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['COMPONENT_WORD'].concat(formatToken(lexeme));
  });

  lexer.addRule(/`[^`]*`/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['EXPRESSION'].concat(formatToken(lexeme));
  });

  lexer.addRule(/[+\-]?\.?[0-9]+\.?[0-9]*/, function(lexeme) {
    const multiplePeriods =
      (lexeme.match(new RegExp(/\./, 'g')) || []).length >= 2;
    this.reject = !inComponent || multiplePeriods;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['NUMBER'].concat(formatToken(lexeme));
  });

  lexer.addRule(/"[^"]*"/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['STRING'].concat(formatToken(lexeme));
  });
  lexer.addRule(/'([^']*)'/, function(lexeme, str) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['STRING'].concat(formatToken('"' + str + '"'));
  });

  lexer.addRule(/:/, function(lexeme) {
    this.reject = !inComponent;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['PARAM_SEPARATOR'];
  });

  lexer.addRule(/\s*$/, function(lexeme) {
    this.reject = !outer;
    if (this.reject) return;
    updatePosition(lexeme);
    return ['EOF'];
  });

  return function(str) {
    currentInput = str;
    var vals = [];
    var output = [];
    var positions = [];

    lexer.input = str.trim();
    var token = lexer.lex();
    while (token) {
      output.push(token);
      positions.push([row, column]);
      token = lexer.lex();
    }
    return {
      tokens: output,
      positions: positions
    };
  };
};

module.exports = lex;
<|endoftext|>


<commit_before>(function (angular) {

    angular.module('ReFigure')
        .service('CollectionSvc', CollectionService);

    CollectionService.$inject = ['$location', '$http', 'AuthService'];

    function CollectionService($location, $http, AuthService) {
        var service = this;

        if (AuthService.userInfo) {
            $http.defaults.headers.common['Authentication'] = AuthService.userInfo.Token;
        } else {
            $http.defaults.headers.common['Authentication'] = undefined;
        }

        service.create = function (params) {
            return $http
                .post(_gApiURL + "metapublication", params)
                .then(function (resp) {
                    // $location.path('/collections/' + resp.data.data.Metapublication.ID);
                    $location.path('/my-collections');
                });
        };

        service.read = function (id) {
            return $http
                .get(_gApiURL + "metapublication/" + id);
        };

        service.update = function (params) {
            return $http
                .put(_gApiURL + "metapublication", params)
                .then(function (resp) {
                    // $location.path('/collections/' + resp.data.data.Metapublication.ID);
                    $location.path('/my-collections');
                });
        };

        service.delete = function (id) {
            return $http
                .delete(_gApiURL + "metapublication/" + id)
                .then(function () {
                    $location.path('/my-collections');
                });
        };

        service.getUserCollections = function () {
            return $http
                .get(_gApiURL + "my-metapublications");
        };

        service.toggleFlag = function (params) {
            return $http
                .put(_gApiURL + "metapublication-flag", params);
        }

    }

})(window.angular);
<commit_msg>~ fix changing location<commit_after>(function (angular) {

    angular.module('ReFigure')
        .service('CollectionSvc', CollectionService);

    CollectionService.$inject = ['$location', '$http', 'AuthService'];

    function CollectionService($location, $http, AuthService) {
        var service = this;

        if (AuthService.userInfo) {
            $http.defaults.headers.common['Authentication'] = AuthService.userInfo.Token;
        } else {
            $http.defaults.headers.common['Authentication'] = undefined;
        }

        service.create = function (params) {
            return $http
                .post(_gApiURL + "metapublication", params)
                .then(function (resp) {
                    // $location.path('/collections/' + resp.data.data.Metapublication.ID);
                    $location.path('/');
                });
        };

        service.read = function (id) {
            return $http
                .get(_gApiURL + "metapublication/" + id);
        };

        service.update = function (params) {
            return $http
                .put(_gApiURL + "metapublication", params)
                .then(function (resp) {
                    // $location.path('/collections/' + resp.data.data.Metapublication.ID);
                    $location.path('/');
                });
        };

        service.delete = function (id) {
            return $http
                .delete(_gApiURL + "metapublication/" + id)
                .then(function () {
                    $location.path('/');
                });
        };

        service.getUserCollections = function () {
            return $http
                .get(_gApiURL + "my-metapublications");
        };

        service.toggleFlag = function (params) {
            return $http
                .put(_gApiURL + "metapublication-flag", params);
        }

    }

})(window.angular);
<|endoftext|>


<commit_before>// k / (150 * size)
var createElement = (tag, prop, style) => {
   let ele = document.createElement(tag)
   for (let i in (prop || [])) {
      ele[i] = prop[i]
   }
   for (let i in (style || [])) {
      ele.style[i] = style[i]
   }
   return ele
}
var createCanvas = (w, h) => {
   return createElement('canvas', {"width": w, "height": h})
}

{
   let style = createElement("style")
   document.body.appendChild(style)
   let stylesheet = style.sheet
   var addStyle = (rule) => {
      stylesheet.insertRule(rule, stylesheet.cssRules.length);
   }
}

var drawSakura = (size) => {
   let canvas = createCanvas(size, size)
   let scale = 150 / size
   if (canvas.getContext) {
      let ctx = canvas.getContext('2d')
      ctx.scale_bezierCurveTo = (x1, y1, x2, y2, ex, ey) => {
         ctx.bezierCurveTo(x1 / scale, y1 / scale, x2 / scale, y2 / scale, ex / scale, ey / scale)
      }
      ctx.scale_moveTo = (x1, y1) => ctx.moveTo(x1 / scale, y1 / scale)
      ctx.fillColor = (color) => {
         let k = ctx.fillStyle
         ctx.fillStyle = color
         ctx.fill()
         ctx.fillStyle = k
      }
      ctx.beginPath();
      ctx.scale_moveTo(75, 30)
      ctx.scale_bezierCurveTo(75, 30, 70, 20, 60, 10)
      ctx.scale_bezierCurveTo(60, 10, 10, 75, 75, 120)

      ctx.scale_moveTo(75, 30)
      ctx.scale_bezierCurveTo(75, 30, 80, 20, 90, 10)
      ctx.scale_bezierCurveTo(90, 10, 140, 75, 75, 120)
      ctx.fillColor("#FEDFE1")
   }
   return canvas
}

var createSakura = () => {
   let o = {
      size: randint(5, 80),
      vx: rand(0.2, 1.5),
      vy: rand(0.3, 2),
      rotatev: (Boolean(randint(0, 1)) ? -1 : +1) * rand(0.1, 0.5),
   }
   o.c = drawSakura(o.size)
   return o
}

var drawAnimation = (list, speed) => {
   // init
   let div = createElement('div', [], {"width": "100%", "height": "100%", "position": "fixed", "overflow": "hidden"})
   document.body.appendChild(div)
   list.forEach((obj, _index, _arr) => {
      obj.c.style.position = "absolute"
      let scalePosX = 1920 / speed
      let scalePosY = 1080 / speed
      let scaleRotate = 360 / speed
      obj.c.style.opacity = `${obj.size / 280}`
      obj.c.style.filter = `blur(${obj.size / 240}px)`
      obj.c.style.animation =
         `pX ${scalePosX / obj.vx}s infinite linear, \
          pY ${scalePosX / obj.vy}s infinite linear, \
          r ${scaleRotate / Math.abs(obj.rotatev)}s infinite linear ${obj.rotatev > 0 ? "" : "backwards"}`
      div.appendChild(obj.c)
   })
}

var sakura_main = (num, speed) => {
   addStyle("@keyframes pX {0% {left: -100px;} 100% {left: 100%;}}")
   addStyle("@keyframes pY {0% {top: -100px;} 100% {top: 100%;}}")
   addStyle("@keyframes r {0% {rotateX(0deg) rotateY(0deg) rotateZ(0deg);} \
                         100% {rotateX(360deg) rotateY(360deg) rotateZ(360deg);}}")
   var sakuraList = window.sakuraList = initArray(num || 300, createSakura)
   drawAnimation(sakuraList, speed || 60)
}
window.onload = () => { sakura_main(300) }<commit_msg>Script commit at 29/11/2017 22:11:46 UTC+8<commit_after>// k / (150 * size)
var createElement = (tag, prop, style) => {
   let ele = document.createElement(tag)
   for (let i in (prop || [])) {
      ele[i] = prop[i]
   }
   for (let i in (style || [])) {
      ele.style[i] = style[i]
   }
   return ele
}
var createCanvas = (w, h) => {
   return createElement('canvas', {"width": w, "height": h})
}

{
   let style = createElement("style")
   document.body.appendChild(style)
   let stylesheet = style.sheet
   var addStyle = (rule) => {
      stylesheet.insertRule(rule, stylesheet.cssRules.length);
   }
}

var drawSakura = (size) => {
   let canvas = createCanvas(size, size)
   let scale = 150 / size
   if (canvas.getContext) {
      let ctx = canvas.getContext('2d')
      ctx.scale_bezierCurveTo = (x1, y1, x2, y2, ex, ey) => {
         ctx.bezierCurveTo(x1 / scale, y1 / scale, x2 / scale, y2 / scale, ex / scale, ey / scale)
      }
      ctx.scale_moveTo = (x1, y1) => ctx.moveTo(x1 / scale, y1 / scale)
      ctx.fillColor = (color) => {
         let k = ctx.fillStyle
         ctx.fillStyle = color
         ctx.fill()
         ctx.fillStyle = k
      }
      ctx.beginPath();
      ctx.scale_moveTo(75, 30)
      ctx.scale_bezierCurveTo(75, 30, 70, 20, 60, 10)
      ctx.scale_bezierCurveTo(60, 10, 10, 75, 75, 120)

      ctx.scale_moveTo(75, 30)
      ctx.scale_bezierCurveTo(75, 30, 80, 20, 90, 10)
      ctx.scale_bezierCurveTo(90, 10, 140, 75, 75, 120)
      ctx.fillColor("#FEDFE1")
   }
   return canvas
}

var createSakura = () => {
   let o = {
      size: randint(5, 80),
      vx: rand(0.2, 1.5),
      vy: rand(0.3, 2),
      rotatev: (Boolean(randint(0, 1)) ? -1 : +1) * rand(0.1, 0.5),
   }
   o.c = drawSakura(o.size)
   return o
}

var drawAnimation = (list, speed) => {
   // init
   let div = createElement('div', [], {"width": "100%", "height": "100%", "position": "fixed", "overflow": "hidden"})
   document.body.appendChild(div)
   list.forEach((obj, _index, _arr) => {
      obj.c.style.position = "absolute"
      let scalePosX = 1920 / speed
      let scalePosY = 1080 / speed
      let scaleRotate = 360 / speed
      obj.c.style.opacity = `${obj.size / 280}`
      obj.c.style.filter = `blur(${obj.size / 240}px)`
      obj.c.style.animation =
         `pX ${scalePosX / obj.vx}s infinite linear, \
          pY ${scalePosX / obj.vy}s infinite linear, \
          r ${scaleRotate / Math.abs(obj.rotatev)}s infinite linear ${obj.rotatev > 0 ? "" : "backwards"}`
      div.appendChild(obj.c)
   })
}

var sakura_main = (num, speed) => {
   addStyle("@keyframes pX {0% {left: -100px;} 100% {left: 100%;}}")
   addStyle("@keyframes pY {0% {top: -100px;} 100% {top: 100%;}}")
   addStyle("@keyframes r {0% {transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg);}\
                           100% {transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);}}")
   var sakuraList = window.sakuraList = initArray(num || 300, createSakura)
   drawAnimation(sakuraList, speed || 60)
}
window.onload = () => { sakura_main(300) }<|endoftext|>


<commit_before>app.factory('Page', function($window) {
    var prefix = 'CourseMapper';
    var title = 'CourseMapper';
    return {
        title: function() {
            return title;
        },

        setTitle: function(newTitle) {
            title = newTitle;
            $window.document.title = title;
        },

        setTitleWithPrefix: function(newTitle) {
            title = prefix + ': ' + newTitle;
            $window.document.title = title;
        },

        xs: 768,
        sm: 992,
        md: 1200,

        defineDevSize: function(width){
            if(width < this.xs){
                return 'xs';
            } else if(width > xs && width <= this.sm){
                return 'sm';
            } else if(width > sm && width <= this.md){
                return 'md';
            } else if(width > this.md){
                return 'lg';
            }
        }
    };
});<commit_msg>fix xs sm lg device detection<commit_after>app.factory('Page', function($window) {
    var prefix = 'CourseMapper';
    var title = 'CourseMapper';
    return {
        title: function() {
            return title;
        },

        setTitle: function(newTitle) {
            title = newTitle;
            $window.document.title = title;
        },

        setTitleWithPrefix: function(newTitle) {
            title = prefix + ': ' + newTitle;
            $window.document.title = title;
        },

        xs: 768,
        sm: 992,
        md: 1200,

        defineDevSize: function(width){
            if(width < this.xs){
                return 'xs';
            } else if(width > this.xs && width <= this.sm){
                return 'sm';
            } else if(width > this.sm && width <= this.md){
                return 'md';
            } else if(width > this.md){
                return 'lg';
            }
        }
    };
});<|endoftext|>


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

Elm.Debugger = null;
Elm.debuggerAttach = function(module) {
  return {
    make: function(runtime) {
      var wrappedModule = debugModule(module, runtime);
      Elm.Debugger = debuggerInit(wrappedModule, runtime);
      return wrappedModule.moduleInstance;
    }
  };
};

function debugModule(module, runtime) {
  var programPaused = false;
  var recordedEvents = [];
  var eventCounter = 0;
  var asyncTimers = [];

  function wrapNotify(id, v) {
    var timestep = Date.now();

    if (programPaused) {
      // ignore notify because we are stepping
    }
    else {
      runtime.notify(id, v, timestep);
      recordEvent(id, v, timestep);
    }
  };

  function wrapRunDelayed(func, delayMs) {
    return safeSetTimeout(func, delayMs);
  };

  function safeSetTimeout(func, delayMs) {
    var timerId = setTimeout(func, delayMs);
    asyncTimers.push({ func:func, delayMs:delayMs, timerId:timerId });
    return timerId;
  }

  function recordEvent(id, v, timestep) {
    recordedEvents.push({ id:id, value:v, timestep:timestep });
  }

  function clearAsyncEvents() {
    asyncTimers.forEach(function(timer) {
      clearTimeout(timer.timerId);
    });
  }

  function clearRecordedEvents() {
    recordedEvents = [];
  }

  function getRecordedEvents() {
    return recordedEvents.slice();
  }

  function setPaused(v) {
    programPaused = v;
  }

  function getPaused() {
    return programPaused;
  }

  var wrappedRuntime = {
    notify: wrapNotify,
    runDelayed: wrapRunDelayed,
    node: runtime.node,
    display: runtime.display,
    id: runtime.id,
    addListener: runtime.addListener,
    inputs: runtime.inputs
  };

  var moduleInstance = module.make(wrappedRuntime);
  var moduleNodes = flattenNodes(wrappedRuntime.inputs);

  return {
    moduleInstance: moduleInstance,
    moduleNodes: moduleNodes,
    initialNodeValues: saveNodeValues(moduleNodes),
    initialAsyncTimers: asyncTimers.slice(),
    // API functions
    clearAsyncEvents: clearAsyncEvents,
    clearRecordedEvents: clearRecordedEvents,
    getRecordedEvents: getRecordedEvents,
    getPaused: getPaused,
    setPaused: setPaused
  };
}

function debuggerInit(debugModule, runtime) {
  var Signal = Elm.Signal.make(runtime);

  var tracePath = tracePathInit(runtime);
  var tracePathNode = A2(Signal.lift, tracePath.graphicsUpdate, debugModule.moduleInstance.main);
  runtime.node.parentNode.appendChild(tracePath.canvas);

  function resetProgram() {
    debugModule.clearAsyncEvents();
    restoreNodeValues(debugModule.moduleNodes, debugModule.initialNodeValues);
    redrawGraphics();
  }

  function restartProgram() {
    resetProgram();
    debugModule.clearRecordedEvents();
    debugModule.initialAsyncTimers.forEach(function(timer) {
      var func = timer.func;
      func();
    });
  }

  function stepTo(index) {
    if (!debugModule.getPaused()) {
      debugModule.setPaused(true);
      resetProgram();
    }

    if (index < 0 || index >= recordedEvents.length) {
      throw "Index out of bounds: " + index;
    }

    if (index < eventCounter) {
      resetProgram();
      eventCounter = 0;
    }

    assert(index >= eventCounter, "index must be bad");
    while (eventCounter < index) {
      var nextEvent = recordedEvents[eventCounter];
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);

      eventCounter += 1;
    }
    assert(eventCounter == index, "while loop didnt' work");
  }

  function getMaxSteps() {
    return recordedEvents.length - 1;
  }

  function doPlayback(eventList) {
    var x = eventList.shift();
    var time = x[2];
    runtime.notify(x[0], x[1], x[2]);

    if (eventList.length > 0) {
      var delta = eventList[0][2] - time;
      setTimeout(function() { doPlayback(eventList); }, delta);
    }
  }

  function redrawGraphics() {
    var graphicsNode = debugModule.moduleInstance.main.kids[0];
    graphicsNode.recv(Date.now(), true, debugModule.moduleInstance.main.id);
  }

  var elmDebugger = {
      reset: resetProgram,
      restart: restartProgram,
      getMaxSteps: getMaxSteps,
      stepTo: stepTo
  };

  return elmDebugger;
}

function Point(x, y) {
  this.x = x;
  this.y = y;

  this.translate = function(x, y) {
    return new Point(this.x + x, this.y + y);
  }

  this.equals = function(p) {
    return this.x == p.x && this.y == p.y;
  }
}

function tracePathInit(runtime) {
  var List = Elm.List.make(runtime);
  var tracePathCanvas = createCanvas();
  var tracePositions = {};

  function findPositions(currentScene) {
    var positions = {};
    function processElement(elem, offset) {
      if (elem.element.ctor == "Custom" && elem.element.type == "Collage")
      {
        List.map(F2(processForm)(offset))(elem.element.model.forms);
      }
      else if (elem.element.ctor == "Image" && elem.props.debugTracePathId)
      {
        positions[elem.props.debugTracePathId] = new Point(offset.x, offset.y);
      }
    }

    function processForm(offset, form) {
      if (form.form.ctor == "FElement")
      {
        processElement(form.form._0, offset.translate(form.x, -form.y));
      }
    }

    processElement(currentScene, new Point(0, 0));
    return positions;
  }

  function appendPositions(positions) {
    for (var id in positions) {
      var pos = positions[id];
      if (tracePositions.hasOwnProperty(id)) {
        var points = tracePositions[id];
        if (!pos.equals(points[points.length-1])) {
          points.push(pos);
        }
      }
      else {
        tracePositions[id] = [pos];
      }
    }
  }

  function graphicsUpdate(currentScene) {
    var ctx = tracePathCanvas.getContext('2d');
    ctx.clearRect(0, 0, tracePathCanvas.width, tracePathCanvas.height);
    ctx.fillText("Debugger trace path", 20, 20);

    ctx.save();
    ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
    appendPositions(findPositions(currentScene));
    for (var id in tracePositions)
    {
      ctx.beginPath();
      var points = tracePositions[id];
      for (var i=0; i < points.length; i++)
      {
        var p = points[i];
        if (i == 0) {
          ctx.moveTo(p.x, p.y);
        }
        else {
          ctx.lineTo(p.x, p.y);
        }
      }
    }
    ctx.lineWidth = 1;
    ctx.strokeStyle = "black";
    ctx.stroke();

    ctx.restore();
  }

  function clearTraces() {
    tracePositions = {};
  }

  return {
    graphicsUpdate: graphicsUpdate,
    canvas: tracePathCanvas,
    clearTraces: clearTraces
  }
}

function createCanvas() {
  var c = document.createElement('canvas');
  c.width = window.innerWidth;
  c.height = window.innerHeight;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  return c;
}

function assert(bool, msg) {
  if (!bool) {
    throw "Assertion error: " + msg;
  }
}

function saveNodeValues(allNodes) {
  var nodeValues = [];

  allNodes.forEach(function(node) {
    nodeValues.push({ value: node.value, id: node.id });
  });

  return nodeValues;
};

function restoreNodeValues(allNodes, nodeStates) {
  assert(allNodes.length == nodeStates.length, "saved program state has wrong length");
  for (var i=0; i < allNodes.length; i++) {
    var node = allNodes[i];
    var state = nodeStates[i];
    assert(node.id == state.id, "the nodes moved position");

    node.value = state.value;
  }
}

function flattenNodes(nodes) {
  var treeReduce = function(prev, cur, i, arr) {
    var flatKids = cur.kids.reduce(treeReduce, []);
    flatKids.push(cur);
    return prev.concat(flatKids);
  }

  return nodes.reduce(treeReduce, []);
};

}());
<commit_msg>Debugger: remove text on debug canvas.<commit_after>
(function() {
'use strict';

Elm.Debugger = null;
Elm.debuggerAttach = function(module) {
  return {
    make: function(runtime) {
      var wrappedModule = debugModule(module, runtime);
      Elm.Debugger = debuggerInit(wrappedModule, runtime);
      return wrappedModule.moduleInstance;
    }
  };
};

function debugModule(module, runtime) {
  var programPaused = false;
  var recordedEvents = [];
  var eventCounter = 0;
  var asyncTimers = [];

  function wrapNotify(id, v) {
    var timestep = Date.now();

    if (programPaused) {
      // ignore notify because we are stepping
    }
    else {
      runtime.notify(id, v, timestep);
      recordEvent(id, v, timestep);
    }
  };

  function wrapRunDelayed(func, delayMs) {
    return safeSetTimeout(func, delayMs);
  };

  function safeSetTimeout(func, delayMs) {
    var timerId = setTimeout(func, delayMs);
    asyncTimers.push({ func:func, delayMs:delayMs, timerId:timerId });
    return timerId;
  }

  function recordEvent(id, v, timestep) {
    recordedEvents.push({ id:id, value:v, timestep:timestep });
  }

  function clearAsyncEvents() {
    asyncTimers.forEach(function(timer) {
      clearTimeout(timer.timerId);
    });
  }

  function clearRecordedEvents() {
    recordedEvents = [];
  }

  function getRecordedEvents() {
    return recordedEvents.slice();
  }

  function setPaused(v) {
    programPaused = v;
  }

  function getPaused() {
    return programPaused;
  }

  var wrappedRuntime = {
    notify: wrapNotify,
    runDelayed: wrapRunDelayed,
    node: runtime.node,
    display: runtime.display,
    id: runtime.id,
    addListener: runtime.addListener,
    inputs: runtime.inputs
  };

  var moduleInstance = module.make(wrappedRuntime);
  var moduleNodes = flattenNodes(wrappedRuntime.inputs);

  return {
    moduleInstance: moduleInstance,
    moduleNodes: moduleNodes,
    initialNodeValues: saveNodeValues(moduleNodes),
    initialAsyncTimers: asyncTimers.slice(),
    // API functions
    clearAsyncEvents: clearAsyncEvents,
    clearRecordedEvents: clearRecordedEvents,
    getRecordedEvents: getRecordedEvents,
    getPaused: getPaused,
    setPaused: setPaused
  };
}

function debuggerInit(debugModule, runtime) {
  var Signal = Elm.Signal.make(runtime);

  var tracePath = tracePathInit(runtime);
  var tracePathNode = A2(Signal.lift, tracePath.graphicsUpdate, debugModule.moduleInstance.main);
  runtime.node.parentNode.appendChild(tracePath.canvas);

  function resetProgram() {
    debugModule.clearAsyncEvents();
    restoreNodeValues(debugModule.moduleNodes, debugModule.initialNodeValues);
    redrawGraphics();
  }

  function restartProgram() {
    resetProgram();
    debugModule.clearRecordedEvents();
    debugModule.initialAsyncTimers.forEach(function(timer) {
      var func = timer.func;
      func();
    });
  }

  function stepTo(index) {
    if (!debugModule.getPaused()) {
      debugModule.setPaused(true);
      resetProgram();
    }

    if (index < 0 || index >= recordedEvents.length) {
      throw "Index out of bounds: " + index;
    }

    if (index < eventCounter) {
      resetProgram();
      eventCounter = 0;
    }

    assert(index >= eventCounter, "index must be bad");
    while (eventCounter < index) {
      var nextEvent = recordedEvents[eventCounter];
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);

      eventCounter += 1;
    }
    assert(eventCounter == index, "while loop didnt' work");
  }

  function getMaxSteps() {
    return recordedEvents.length - 1;
  }

  function doPlayback(eventList) {
    var x = eventList.shift();
    var time = x[2];
    runtime.notify(x[0], x[1], x[2]);

    if (eventList.length > 0) {
      var delta = eventList[0][2] - time;
      setTimeout(function() { doPlayback(eventList); }, delta);
    }
  }

  function redrawGraphics() {
    var graphicsNode = debugModule.moduleInstance.main.kids[0];
    graphicsNode.recv(Date.now(), true, debugModule.moduleInstance.main.id);
  }

  var elmDebugger = {
      reset: resetProgram,
      restart: restartProgram,
      getMaxSteps: getMaxSteps,
      stepTo: stepTo
  };

  return elmDebugger;
}

function Point(x, y) {
  this.x = x;
  this.y = y;

  this.translate = function(x, y) {
    return new Point(this.x + x, this.y + y);
  }

  this.equals = function(p) {
    return this.x == p.x && this.y == p.y;
  }
}

function tracePathInit(runtime) {
  var List = Elm.List.make(runtime);
  var tracePathCanvas = createCanvas();
  var tracePositions = {};

  function findPositions(currentScene) {
    var positions = {};
    function processElement(elem, offset) {
      if (elem.element.ctor == "Custom" && elem.element.type == "Collage")
      {
        List.map(F2(processForm)(offset))(elem.element.model.forms);
      }
      else if (elem.element.ctor == "Image" && elem.props.debugTracePathId)
      {
        positions[elem.props.debugTracePathId] = new Point(offset.x, offset.y);
      }
    }

    function processForm(offset, form) {
      if (form.form.ctor == "FElement")
      {
        processElement(form.form._0, offset.translate(form.x, -form.y));
      }
    }

    processElement(currentScene, new Point(0, 0));
    return positions;
  }

  function appendPositions(positions) {
    for (var id in positions) {
      var pos = positions[id];
      if (tracePositions.hasOwnProperty(id)) {
        var points = tracePositions[id];
        if (!pos.equals(points[points.length-1])) {
          points.push(pos);
        }
      }
      else {
        tracePositions[id] = [pos];
      }
    }
  }

  function graphicsUpdate(currentScene) {
    var ctx = tracePathCanvas.getContext('2d');
    ctx.clearRect(0, 0, tracePathCanvas.width, tracePathCanvas.height);

    ctx.save();
    ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
    appendPositions(findPositions(currentScene));
    for (var id in tracePositions)
    {
      ctx.beginPath();
      var points = tracePositions[id];
      for (var i=0; i < points.length; i++)
      {
        var p = points[i];
        if (i == 0) {
          ctx.moveTo(p.x, p.y);
        }
        else {
          ctx.lineTo(p.x, p.y);
        }
      }
    }
    ctx.lineWidth = 1;
    ctx.strokeStyle = "black";
    ctx.stroke();

    ctx.restore();
  }

  function clearTraces() {
    tracePositions = {};
  }

  return {
    graphicsUpdate: graphicsUpdate,
    canvas: tracePathCanvas,
    clearTraces: clearTraces
  }
}

function createCanvas() {
  var c = document.createElement('canvas');
  c.width = window.innerWidth;
  c.height = window.innerHeight;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  return c;
}

function assert(bool, msg) {
  if (!bool) {
    throw "Assertion error: " + msg;
  }
}

function saveNodeValues(allNodes) {
  var nodeValues = [];

  allNodes.forEach(function(node) {
    nodeValues.push({ value: node.value, id: node.id });
  });

  return nodeValues;
};

function restoreNodeValues(allNodes, nodeStates) {
  assert(allNodes.length == nodeStates.length, "saved program state has wrong length");
  for (var i=0; i < allNodes.length; i++) {
    var node = allNodes[i];
    var state = nodeStates[i];
    assert(node.id == state.id, "the nodes moved position");

    node.value = state.value;
  }
}

function flattenNodes(nodes) {
  var treeReduce = function(prev, cur, i, arr) {
    var flatKids = cur.kids.reduce(treeReduce, []);
    flatKids.push(cur);
    return prev.concat(flatKids);
  }

  return nodes.reduce(treeReduce, []);
};

}());
<|endoftext|>


<commit_before>// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
var vscode = require('vscode');
var fs = require('fs');
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
function activate(context) {

    var pdflatex = vscode.commands.registerCommand('extension.latexCompile', function() {

        try {
            vscode.workspace.saveAll();
            var pathFull = vscode.window.activeTextEditor.document.fileName;

            if (vscode.workspace.getConfiguration('latexCompile').mainFile) {
                if (vscode.workspace.rootPath) {
                    pathFull = vscode.workspace.rootPath + "/" + vscode.workspace.getConfiguration('latexCompile').mainFile;
                } else {
                    throw new Error('Use of mainFile requires a folder to be opened');
                }
            }

            //Only path without file
            var path = getFilePath(pathFull); // get the current file path
            //Only file name and type
            var fileNameAndType = getFileNameAndType(pathFull);
            var fileName = getFileName(pathFull);
            var filePath = getFilePath(pathFull);
            // var fileType = getFileType(pathFull);
            var pdfFileName = filePath + fileName + ".pdf";

            //Check for file type
            if (getFileType(pathFull) != "tex") {
                //If not tex throw error with message
                throw new Error("Can't create PDF, open a .tex file.");
            }

            var command = 'cd ' + path + ' && ' + vscode.workspace.getConfiguration('latexCompile').compiler + ' ' + fileNameAndType;

            //Log the command to run
            console.log(command);

            setStatusBarText('Generating', "PDF");

            //Run the command
            var exec = require('child_process').exec,
                cmd = exec(command);

            //Make log file to contain console
            exec('cd ' + path + ' && type NUL > ' + fileName + ".vscodeLog");

            //Subscribe to output
            cmd.stdout.on('data', function(data) {
                //Logs output to console
                console.log(String(data));

                //If error is found in output, display an error to user
                if (String(data).toLowerCase().indexOf("error") > 0) {
                    //Show error
                    vscode.window.setStatusBarMessage("Can't create PDF, see " + getFileName(pathFull) + ".vscodeLog", 12000);

                    if (vscode.workspace.getConfiguration('latexCompile').openLogAfterError) {
                        var consoleLogFile = vscode.Uri.file(path + fileName + ".vscodeLog");

                        vscode.workspace.openTextDocument(consoleLogFile).then(function(d) {
                            vscode.window.showTextDocument(d);
                            // Open file, add console string, save file.
                            var fd = fs.openSync(path + fileName + ".vscodeLog", 'w+');
                            var buffer = new Buffer(String(data));
                            fs.writeSync(fd, buffer, 0, buffer.length);
                            fs.close(fd);

                        });

                    }

                }
            });

            cmd.stdout.on('close', function() {
                if (vscode.workspace.getConfiguration('latexCompile').openAfterCompile) {
                    setStatusBarText('Launching', "PDF");
                    if (process.platform == 'darwin') {
                        exec('open ' + pdfFileName);
                    } else {
                        exec(pdfFileName);
                    }
                } else {
                    vscode.window.showInformationMessage('PDF Compilled at ' + path);
                }
            });

        } catch (error) {
            //Catch error and show the user the message in the error
            vscode.window.showErrorMessage(error.message);
        }
    });
    //Function to get file name and type
    function getFileNameAndType(file) {
        var forwardSlash = file.lastIndexOf("/");
        var backSlash = file.lastIndexOf("\\");
        if (forwardSlash === -1 && backSlash === -1) {
            return file;
        }
        return file.substring((forwardSlash > backSlash) ? forwardSlash + 1 : backSlash + 1);
    }
    //Function to get only file type
    function getFileType(file) {
        var stringFile = getFileNameAndType(file);
        return stringFile.split(".")[1];
    }
    //Function to get only file name
    function getFileName(file) {
        var stringFile = getFileNameAndType(file);
        return stringFile.split(".")[0];
    }
    //Function to get only file path
    function getFilePath(file) {
        var path = file;
        path = path.match(/(.*)[\/\\]/)[1] || ''; // extract the directory from the path
        path += '/';
        return path;
    }
    context.subscriptions.push(pdflatex);
}
exports.activate = activate;

// this method is called when your extension is deactivated
function deactivate() {}
exports.deactivate = deactivate;

function setStatusBarText(what, docType) {
    var date = new Date();
    var text = what + ' [' + docType + '] ' + date.toLocaleTimeString();
    vscode.window.setStatusBarMessage(text, 1500);
}<commit_msg>Making it possible to cd to another drive<commit_after>// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
var vscode = require('vscode');
var fs = require('fs');
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
function activate(context) {

    var pdflatex = vscode.commands.registerCommand('extension.latexCompile', function() {

        try {
            vscode.workspace.saveAll();
            var pathFull = vscode.window.activeTextEditor.document.fileName;

            if (vscode.workspace.getConfiguration('latexCompile').mainFile) {
                if (vscode.workspace.rootPath) {
                    pathFull = vscode.workspace.rootPath + "/" + vscode.workspace.getConfiguration('latexCompile').mainFile;
                } else {
                    throw new Error('Use of mainFile requires a folder to be opened');
                }
            }

            //Only path without file
            var path = getFilePath(pathFull); // get the current file path
            //Only file name and type
            var fileNameAndType = getFileNameAndType(pathFull);
            var fileName = getFileName(pathFull);
            var filePath = getFilePath(pathFull);
            // var fileType = getFileType(pathFull);
            var pdfFileName = filePath + fileName + ".pdf";

            //Check for file type
            if (getFileType(pathFull) != "tex") {
                //If not tex throw error with message
                throw new Error("Can't create PDF, open a .tex file.");
            }

            var command = 'cd ' + path + ' && ' + vscode.workspace.getConfiguration('latexCompile').compiler + ' ' + fileNameAndType;

            //Log the command to run
            console.log(command);

            setStatusBarText('Generating', "PDF");

            //Run the command
            var exec = require('child_process').exec,
                cmd = exec(command);

            //Make log file to contain console
            exec('cd /d' + path + ' && type NUL > ' + fileName + ".vscodeLog");

            //Subscribe to output
            cmd.stdout.on('data', function(data) {
                //Logs output to console
                console.log(String(data));

                //If error is found in output, display an error to user
                if (String(data).toLowerCase().indexOf("error") > 0) {
                    //Show error
                    vscode.window.setStatusBarMessage("Can't create PDF, see " + getFileName(pathFull) + ".vscodeLog", 12000);

                    if (vscode.workspace.getConfiguration('latexCompile').openLogAfterError) {
                        var consoleLogFile = vscode.Uri.file(path + fileName + ".vscodeLog");

                        vscode.workspace.openTextDocument(consoleLogFile).then(function(d) {
                            vscode.window.showTextDocument(d);
                            // Open file, add console string, save file.
                            var fd = fs.openSync(path + fileName + ".vscodeLog", 'w+');
                            var buffer = new Buffer(String(data));
                            fs.writeSync(fd, buffer, 0, buffer.length);
                            fs.close(fd);

                        });

                    }

                }
            });

            cmd.stdout.on('close', function() {
                if (vscode.workspace.getConfiguration('latexCompile').openAfterCompile) {
                    setStatusBarText('Launching', "PDF");
                    if (process.platform == 'darwin') {
                        exec('open ' + pdfFileName);
                    } else {
                        exec(pdfFileName);
                    }
                } else {
                    vscode.window.showInformationMessage('PDF Compilled at ' + path);
                }
            });

        } catch (error) {
            //Catch error and show the user the message in the error
            vscode.window.showErrorMessage(error.message);
        }
    });
    //Function to get file name and type
    function getFileNameAndType(file) {
        var forwardSlash = file.lastIndexOf("/");
        var backSlash = file.lastIndexOf("\\");
        if (forwardSlash === -1 && backSlash === -1) {
            return file;
        }
        return file.substring((forwardSlash > backSlash) ? forwardSlash + 1 : backSlash + 1);
    }
    //Function to get only file type
    function getFileType(file) {
        var stringFile = getFileNameAndType(file);
        return stringFile.split(".")[1];
    }
    //Function to get only file name
    function getFileName(file) {
        var stringFile = getFileNameAndType(file);
        return stringFile.split(".")[0];
    }
    //Function to get only file path
    function getFilePath(file) {
        var path = file;
        path = path.match(/(.*)[\/\\]/)[1] || ''; // extract the directory from the path
        path += '/';
        return path;
    }
    context.subscriptions.push(pdflatex);
}
exports.activate = activate;

// this method is called when your extension is deactivated
function deactivate() {}
exports.deactivate = deactivate;

function setStatusBarText(what, docType) {
    var date = new Date();
    var text = what + ' [' + docType + '] ' + date.toLocaleTimeString();
    vscode.window.setStatusBarMessage(text, 1500);
}<|endoftext|>