<commit_before>/*!
 * dragtable - jquery ui widget to re-order table columns
 * version 3.0
 *
 * Copyright (c) 2010, Jesse Baird <jebaird@gmail.com>
 * 12/2/2010
 * https://github.com/jebaird/dragtable
 *
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 *
 *
 * Forked from https://github.com/akottr/dragtable - Andres Koetter akottr@gmail.com
 *
 *
 *
 *
 * quick down and and dirty on how this works
 * ###########################################
 * so when a column is selected we grab all of the cells in that row and clone append them to a semi copy of the parent table and the
 * "real" cells get a place holder class witch is removed when the dragstop event is triggered
 *
 * TODO:
 * add / create / edit css framework
 * add drag handles
 * click event to handle drag
 * ignore class
 *
 *
 * clean up the api - event driven like ui autocompleate
 * make it easy to have a button swap colums
 *
 */

(function($) {
  $.widget("jb.dragtable", {
  		//TODO: implement this
  		eventWidgetPrefix: 'dragtable',

		options: {
			//used to the col headers, data containted in here is used to set / get the name of the col
			dataHeader:'data-header',
			//handle
			handle:'.dragtable-drag-handle',
			//optional call back used when the col order has changed
			change: $.noop,

			//called after we create the drag display allows user to customize the look of the col in drag
			displayHelper: $.noop,

			//TODO: Faze these out
			// when a col is dragged use this to find the symantic elements, for speed
			tableElemIndex:{
				head: '0',
				body: '1',
				foot: '2'
			}
		},

		_create: function() {

			//console.log(this);
			//used start/end of drag
			this.startIndex = null;
			this.endIndex = null;
			this.currentColumnCollection = [];//the refferences to the table cells that are getting dragged

			//used on drag event to detect what direction the mouse is moving
			//init on drag start
			this.prevMouseX = 0;

			var self = this,
			o = self.options,
			el = self.element;

			//grab the ths and the handles and bind them
			el.delegate('thead th:not( :has(' + o.handle + ')), ' + o.handle, 'mousedown.' + self.widgetEventPrefix, function(e){
				var $handle = $(this);
				//make sure we are working with a th instead of a handle
				if($handle.hasClass(o.handle.replace('.',''))){
					$handle = $handle.closest('th');
				}

				var $dragDisplay = self.getCol( $handle.index() );
				//console.log(dragDisplay)
				self._eventHelper('displayHelper',{},{
					'draggable':$dragDisplay
				});

                //console.log( el, self );

				$dragDisplay
                .focus()
				.disableSelection()
				.css({
					position:'absolute',
                    top: el[0].offsetTop,
                    left: e.pageX
				})
                .appendTo(document.body)

                //drag the column around

                self.prevMouseX = e.pageX;

                $( document )
                .disableSelection()
                .css( 'cursor', 'move')
                .bind('mousemove.' + self.widgetEventPrefix, function( e ){
                    $dragDisplay
                    .css({
    					position:'absolute',
                        left: e.pageX,
                        zindex: 100
				    })

                    var columnPos = self._findElementPosition(self.currentColumnCollection[0]),
					half = self.currentColumnCollection[0].clientWidth / 2;
                    if(e.pageX < self.prevMouseX){
							var threshold = columnPos.x - half;
							if(e.pageX < threshold){
								//console.info('move left');
								self._swapCol(self.startIndex-1);
								self._eventHelper('change',e);

							}

						}else{
							var threshold = columnPos.x + half;
							if(e.pageX > threshold){
								//console.info('move right');
								self._swapCol(self.startIndex+1);
								self._eventHelper('change',e);

							}
						}
						//update mouse position
						self.prevMouseX = e.pageX;

                })
                .one( 'mouseup.dragtable',function(){
                    $( document )
                    .css({
                        cursor: 'auto'
                    })
                    .enableSelection()
                    .unbind( 'mousemove.' + self.widgetEventPrefix );
                    self._dropCol($dragDisplay);
                    self.prevMouseX = 0;
                });

				//############
			});

		},

		_setOption: function(option, value) {
			$.Widget.prototype._setOption.apply( this, arguments );

		},

		/*
		 * get the selected index cell out of table row
		 * works dam fast
		 */
		_getCells: function( elem, index ){
			var ei = this.options.tableElemIndex,

			//TODO: clean up this format
			tds = {
				'semantic':{
					'0': [],//head throws error if ei.head or ei['head']
					'1': [],//body
					'2': []//footer
				},
				'array':[]
			};

			//console.log(index);
			//check does this col exsist
			if(index <= -1 || typeof elem.rows[0].cells[index] == 'undefined'){
				return tds;
			}

			for(var i = 0, length = elem.rows.length; i < length; i++){
				var td = elem.rows[i].cells[index];

				tds.array.push(td);

				switch(td.parentNode.parentNode.nodeName){
					case 'THEAD':
					case 'thead':
						tds.semantic[ei.head].push(td);
						break;
					case 'TFOOT':
					case 'tfoot':
						tds.semantic[ei.foot].push(td);
						break;
					default:
						tds.semantic[ei.body].push(td);
						break;
				}

		 	}

		 	return tds;
		},
		/*
		 * return and array of children excluding text nodes
		 * used only on this.element
		 */
		_getChildren: function(){

			var children = this.element[0].childNodes,
			ret = [];
			for(var i = 0, length = children.length; i < length; i++){
				var e = children[i];
				if(e.nodeType == 1){
					ret.push(e);
				}
			}

			return ret;
		},

		_getElementAttributes: function(element){

        	var attrsString = '',
	        attrs = element.attributes;
	        for(var i=0, length = attrs.length; i < length; i++) {
	            attrsString += attrs[i].nodeName + '="' + attrs[i].nodeValue+'"';
	        }
	        return attrsString;
		},
		/*
		 * currently not uses
		 */
		_swapNodes: function(a, b) {
        	var aparent = a.parentNode,
        	asibling = a.nextSibling === b ? a : a.nextSibling;
        	b.parentNode.insertBefore(a, b);
        	aparent.insertBefore(b, asibling);
     	},
     	/*
     	 * faster than swap nodes
     	 * only works if a b parent are the same, works great for colums
     	 */
     	_swapCells: function(a, b) {
        	a.parentNode.insertBefore(b, a);
     	},
     	/*
     	 * use this instead of jquerys offset, in the cases were using is faster than creating a jquery collection
     	 */
		_findElementPosition: function( oElement ) {
			if( typeof( oElement.offsetParent ) != 'undefined' ) {
				for( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) {
					posX += oElement.offsetLeft;
					posY += oElement.offsetTop;
				}
				return {'x':posX, 'y':posY };
			} else {
				return {'x':oElement.x, 'y':oElement.y };
			}
		},
		/*
		 * used to tirgger optional events
		 */
		_eventHelper: function(eventName ,eventObj, additionalData){
			this._trigger(
				eventName,
				eventObj,
				$.extend({
					column: this.currentColumnCollection,
					order: this.order()
				},additionalData)
			);
		},
		/*
		 * build copy of table and attach the selected col to it, also removes the select col out of the table
		 * @returns copy of table with the selected col
		 */
		getCol: function(index){
			//console.log('index of col '+index);
			//drag display is just simple html
			//console.profile('selectCol');

			//colHeader.addClass('ui-state-disabled')

			var $table = this.element,
			self = this,
			eIndex = self.options.tableElemIndex,
			//BUG: IE thinks that this table is disabled, dont know how that happend
			$dragDisplay = $('

')
			.addClass('dragtable-drag-col');

			//start and end are the same to start out with
			self.startIndex = self.endIndex = index;

		 	var cells = self._getCells($table[0], index);
			self.currentColumnCollection = cells.array;
			//console.log(cells);
			//################################

			//TODO: convert to for in // its faster than each
			$.each(cells.semantic,function(k,collection){
				//dont bother processing if there is nothing here

				if(collection.length == 0){
					return;
				}

                if ( k == '0' ){
                    var target = document.createElement('thead');
						$dragDisplay[0].appendChild(target);
						//
						// var target = $('

')
						// .appendTo($dragDisplay);
                }else{
                    var target = document.createElement('tbody');
						$dragDisplay[0].appendChild(target);
						// var target = $('

')
						// .appendTo($dragDisplay);

                }

				for(var i = 0,length = collection.length; i < length; i++){

					var clone = collection[i].cloneNode(true);
					collection[i].className+=' dragtable-col-placeholder';
					var tr = document.createElement('tr');
					tr.appendChild(clone);
					//console.log(tr);

					target.appendChild(tr);
					//collection[i]=;
				}
			});
    		// console.log($dragDisplay);
    		//console.profileEnd('selectCol')
            $dragDisplay  = $('

').append($dragDisplay)
    		return $dragDisplay;
		},

		/*
		 * move column left or right
		 */
		_swapCol: function( to ){

			//cant swap if same postion
			if(to == this.startIndex){
				return false;
			}

			from = this.startIndex;
			this.endIndex = to;

	        if(from < to) {
	        	//console.log('move right');
	        	for(var i = from; i < to; i++) {
	        		var row2 = this._getCells(this.element[0],i+1);
	        	//	console.log(row2)
	        		for(var j = 0, length = row2.array.length; j < length; j++){
	          			this._swapCells(this.currentColumnCollection[j],row2.array[j]);
	          		}
	          	}
	        } else {
	        	//console.log('move left');
	        	for(var i = from; i > to; i--) {
	            	var row2 = this._getCells(this.element[0],i-1);
	            	for(var j = 0, length = row2.array.length; j < length; j++){
	          			this._swapCells(row2.array[j],this.currentColumnCollection[j]);
	          		}
	        	}
	        }

	        this.startIndex = this.endIndex;
		},
		/*
		 * called when drag start is finished
		 */
		_dropCol: function($dragDisplay){
		//	console.profile('dropCol');
			var self = this;

			if($dragDisplay){
				$dragDisplay.remove();
			}
			//remove placeholder class
			for(var i = 0, length = self.currentColumnCollection.length; i < length; i++){
				var td = self.currentColumnCollection[i];

				td.className = td.className.replace(' dragtable-col-placeholder','');
			}

		},
		/*
		 * get / set the current order of the cols
		 */
		order: function(order){
			var self = this,
			elem = self.element,
			options = self.options,
			headers = elem.find('thead tr:first').children('th');

			if(order == undefined){
				//get
				var ret = [];
				headers.each(function(){
					var header = this.getAttribute(options.dataHeader);
					if(header == null){
						//the attr is missing so grab the text and use that
						header = $(this).text();
					}

					ret.push(header);

				});

				return ret;

			}else{
				//set
				//headers and order have to match up
				if(order.length != headers.length){
					//console.log('length not the same')
					return self;
				}
				for(var i = 0, length = order.length; i < length; i++){

					 var start = headers.filter('['+ options.dataHeader +'='+ order[i] +']').index();
					 if(start != -1){
					 	//console.log('start index '+start+' - swap to '+i);
					 	self.startIndex = start;

						self.currentColumnCollection = self._getCells(self.element[0], start).array;

					 	self._swapCol(i);
					 }

				}
				self._eventHelper('change',{});
				return self;
			}
		},

		destroy: function() {
			this.element.undelegate( 'thead th:not( :has(' + o.handle + ')), ' + o.handle, 'mousedown.' + self.widgetEventPrefix );

		}

	});

})(jQuery);<commit_msg>Fixed error on destroy()<commit_after>/*!
 * dragtable - jquery ui widget to re-order table columns
 * version 3.0
 *
 * Copyright (c) 2010, Jesse Baird <jebaird@gmail.com>
 * 12/2/2010
 * https://github.com/jebaird/dragtable
 *
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 *
 *
 * Forked from https://github.com/akottr/dragtable - Andres Koetter akottr@gmail.com
 *
 *
 *
 *
 * quick down and and dirty on how this works
 * ###########################################
 * so when a column is selected we grab all of the cells in that row and clone append them to a semi copy of the parent table and the
 * "real" cells get a place holder class witch is removed when the dragstop event is triggered
 *
 * TODO:
 * add / create / edit css framework
 * add drag handles
 * click event to handle drag
 * ignore class
 *
 *
 * clean up the api - event driven like ui autocompleate
 * make it easy to have a button swap colums
 *
 */

(function($) {
  $.widget("jb.dragtable", {
  		//TODO: implement this
  		eventWidgetPrefix: 'dragtable',

		options: {
			//used to the col headers, data containted in here is used to set / get the name of the col
			dataHeader:'data-header',
			//handle
			handle:'.dragtable-drag-handle',
			//optional call back used when the col order has changed
			change: $.noop,

			//called after we create the drag display allows user to customize the look of the col in drag
			displayHelper: $.noop,

			//TODO: Faze these out
			// when a col is dragged use this to find the symantic elements, for speed
			tableElemIndex:{
				head: '0',
				body: '1',
				foot: '2'
			}
		},

		_create: function() {

			//console.log(this);
			//used start/end of drag
			this.startIndex = null;
			this.endIndex = null;
			this.currentColumnCollection = [];//the refferences to the table cells that are getting dragged

			//used on drag event to detect what direction the mouse is moving
			//init on drag start
			this.prevMouseX = 0;

			var self = this,
			o = self.options,
			el = self.element;

			//grab the ths and the handles and bind them
			el.delegate('thead th:not( :has(' + o.handle + ')), ' + o.handle, 'mousedown.' + self.widgetEventPrefix, function(e){
				var $handle = $(this);
				//make sure we are working with a th instead of a handle
				if($handle.hasClass(o.handle.replace('.',''))){
					$handle = $handle.closest('th');
				}

				var $dragDisplay = self.getCol( $handle.index() );
				//console.log(dragDisplay)
				self._eventHelper('displayHelper',{},{
					'draggable':$dragDisplay
				});

                //console.log( el, self );

				$dragDisplay
                .focus()
				.disableSelection()
				.css({
					position:'absolute',
                    top: el[0].offsetTop,
                    left: e.pageX
				})
                .appendTo(document.body)

                //drag the column around

                self.prevMouseX = e.pageX;

                $( document )
                .disableSelection()
                .css( 'cursor', 'move')
                .bind('mousemove.' + self.widgetEventPrefix, function( e ){
                    $dragDisplay
                    .css({
    					position:'absolute',
                        left: e.pageX,
                        zindex: 100
				    })

                    var columnPos = self._findElementPosition(self.currentColumnCollection[0]),
					half = self.currentColumnCollection[0].clientWidth / 2;
                    if(e.pageX < self.prevMouseX){
							var threshold = columnPos.x - half;
							if(e.pageX < threshold){
								//console.info('move left');
								self._swapCol(self.startIndex-1);
								self._eventHelper('change',e);

							}

						}else{
							var threshold = columnPos.x + half;
							if(e.pageX > threshold){
								//console.info('move right');
								self._swapCol(self.startIndex+1);
								self._eventHelper('change',e);

							}
						}
						//update mouse position
						self.prevMouseX = e.pageX;

                })
                .one( 'mouseup.dragtable',function(){
                    $( document )
                    .css({
                        cursor: 'auto'
                    })
                    .enableSelection()
                    .unbind( 'mousemove.' + self.widgetEventPrefix );
                    self._dropCol($dragDisplay);
                    self.prevMouseX = 0;
                });

				//############
			});

		},

		_setOption: function(option, value) {
			$.Widget.prototype._setOption.apply( this, arguments );

		},

		/*
		 * get the selected index cell out of table row
		 * works dam fast
		 */
		_getCells: function( elem, index ){
			var ei = this.options.tableElemIndex,

			//TODO: clean up this format
			tds = {
				'semantic':{
					'0': [],//head throws error if ei.head or ei['head']
					'1': [],//body
					'2': []//footer
				},
				'array':[]
			};

			//console.log(index);
			//check does this col exsist
			if(index <= -1 || typeof elem.rows[0].cells[index] == 'undefined'){
				return tds;
			}

			for(var i = 0, length = elem.rows.length; i < length; i++){
				var td = elem.rows[i].cells[index];

				tds.array.push(td);

				switch(td.parentNode.parentNode.nodeName){
					case 'THEAD':
					case 'thead':
						tds.semantic[ei.head].push(td);
						break;
					case 'TFOOT':
					case 'tfoot':
						tds.semantic[ei.foot].push(td);
						break;
					default:
						tds.semantic[ei.body].push(td);
						break;
				}

		 	}

		 	return tds;
		},
		/*
		 * return and array of children excluding text nodes
		 * used only on this.element
		 */
		_getChildren: function(){

			var children = this.element[0].childNodes,
			ret = [];
			for(var i = 0, length = children.length; i < length; i++){
				var e = children[i];
				if(e.nodeType == 1){
					ret.push(e);
				}
			}

			return ret;
		},

		_getElementAttributes: function(element){

        	var attrsString = '',
	        attrs = element.attributes;
	        for(var i=0, length = attrs.length; i < length; i++) {
	            attrsString += attrs[i].nodeName + '="' + attrs[i].nodeValue+'"';
	        }
	        return attrsString;
		},
		/*
		 * currently not uses
		 */
		_swapNodes: function(a, b) {
        	var aparent = a.parentNode,
        	asibling = a.nextSibling === b ? a : a.nextSibling;
        	b.parentNode.insertBefore(a, b);
        	aparent.insertBefore(b, asibling);
     	},
     	/*
     	 * faster than swap nodes
     	 * only works if a b parent are the same, works great for colums
     	 */
     	_swapCells: function(a, b) {
        	a.parentNode.insertBefore(b, a);
     	},
     	/*
     	 * use this instead of jquerys offset, in the cases were using is faster than creating a jquery collection
     	 */
		_findElementPosition: function( oElement ) {
			if( typeof( oElement.offsetParent ) != 'undefined' ) {
				for( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) {
					posX += oElement.offsetLeft;
					posY += oElement.offsetTop;
				}
				return {'x':posX, 'y':posY };
			} else {
				return {'x':oElement.x, 'y':oElement.y };
			}
		},
		/*
		 * used to tirgger optional events
		 */
		_eventHelper: function(eventName ,eventObj, additionalData){
			this._trigger(
				eventName,
				eventObj,
				$.extend({
					column: this.currentColumnCollection,
					order: this.order()
				},additionalData)
			);
		},
		/*
		 * build copy of table and attach the selected col to it, also removes the select col out of the table
		 * @returns copy of table with the selected col
		 */
		getCol: function(index){
			//console.log('index of col '+index);
			//drag display is just simple html
			//console.profile('selectCol');

			//colHeader.addClass('ui-state-disabled')

			var $table = this.element,
			self = this,
			eIndex = self.options.tableElemIndex,
			//BUG: IE thinks that this table is disabled, dont know how that happend
			$dragDisplay = $('

')
			.addClass('dragtable-drag-col');

			//start and end are the same to start out with
			self.startIndex = self.endIndex = index;

		 	var cells = self._getCells($table[0], index);
			self.currentColumnCollection = cells.array;
			//console.log(cells);
			//################################

			//TODO: convert to for in // its faster than each
			$.each(cells.semantic,function(k,collection){
				//dont bother processing if there is nothing here

				if(collection.length == 0){
					return;
				}

                if ( k == '0' ){
                    var target = document.createElement('thead');
						$dragDisplay[0].appendChild(target);
						//
						// var target = $('

')
						// .appendTo($dragDisplay);
                }else{
                    var target = document.createElement('tbody');
						$dragDisplay[0].appendChild(target);
						// var target = $('

')
						// .appendTo($dragDisplay);

                }

				for(var i = 0,length = collection.length; i < length; i++){

					var clone = collection[i].cloneNode(true);
					collection[i].className+=' dragtable-col-placeholder';
					var tr = document.createElement('tr');
					tr.appendChild(clone);
					//console.log(tr);

					target.appendChild(tr);
					//collection[i]=;
				}
			});
    		// console.log($dragDisplay);
    		//console.profileEnd('selectCol')
            $dragDisplay  = $('

').append($dragDisplay)
    		return $dragDisplay;
		},

		/*
		 * move column left or right
		 */
		_swapCol: function( to ){

			//cant swap if same postion
			if(to == this.startIndex){
				return false;
			}

			from = this.startIndex;
			this.endIndex = to;

	        if(from < to) {
	        	//console.log('move right');
	        	for(var i = from; i < to; i++) {
	        		var row2 = this._getCells(this.element[0],i+1);
	        	//	console.log(row2)
	        		for(var j = 0, length = row2.array.length; j < length; j++){
	          			this._swapCells(this.currentColumnCollection[j],row2.array[j]);
	          		}
	          	}
	        } else {
	        	//console.log('move left');
	        	for(var i = from; i > to; i--) {
	            	var row2 = this._getCells(this.element[0],i-1);
	            	for(var j = 0, length = row2.array.length; j < length; j++){
	          			this._swapCells(row2.array[j],this.currentColumnCollection[j]);
	          		}
	        	}
	        }

	        this.startIndex = this.endIndex;
		},
		/*
		 * called when drag start is finished
		 */
		_dropCol: function($dragDisplay){
		//	console.profile('dropCol');
			var self = this;

			if($dragDisplay){
				$dragDisplay.remove();
			}
			//remove placeholder class
			for(var i = 0, length = self.currentColumnCollection.length; i < length; i++){
				var td = self.currentColumnCollection[i];

				td.className = td.className.replace(' dragtable-col-placeholder','');
			}

		},
		/*
		 * get / set the current order of the cols
		 */
		order: function(order){
			var self = this,
			elem = self.element,
			options = self.options,
			headers = elem.find('thead tr:first').children('th');

			if(order == undefined){
				//get
				var ret = [];
				headers.each(function(){
					var header = this.getAttribute(options.dataHeader);
					if(header == null){
						//the attr is missing so grab the text and use that
						header = $(this).text();
					}

					ret.push(header);

				});

				return ret;

			}else{
				//set
				//headers and order have to match up
				if(order.length != headers.length){
					//console.log('length not the same')
					return self;
				}
				for(var i = 0, length = order.length; i < length; i++){

					 var start = headers.filter('['+ options.dataHeader +'='+ order[i] +']').index();
					 if(start != -1){
					 	//console.log('start index '+start+' - swap to '+i);
					 	self.startIndex = start;

						self.currentColumnCollection = self._getCells(self.element[0], start).array;

					 	self._swapCol(i);
					 }

				}
				self._eventHelper('change',{});
				return self;
			}
		},

		destroy: function() {
			var self = this,
			o = self.options;
			this.element.undelegate( 'thead th:not( :has(' + o.handle + ')), ' + o.handle, 'mousedown.' + self.widgetEventPrefix );

		}

	});

})(jQuery);<|endoftext|>


<commit_before>import PIXI from 'pixi.js';
import { NodeConfig } from './NodeConfig';
import { WardNode, BoxNode, TowerNode } from './MapNodes';
import { NEUTRAL, WARD, BOX, TOWER, SENTRY, PULL } from '../constants/MapConstants';

let instance = null;
let stage;
let renderer;

const handleClick = (event) => {
  console.log(event.target.id + ' ' + event.target.x + ' ' + event.target.y);
};

/*
  The references to the nodes on the map stored within a JSON-like
  object. The format is:
{
  <groupName>: {          // 'wards', 'camps', etc
    <filterName>: {       // 'runes', 'offense', etc
      neutral: [MapNode, MapNode, MapNode, ...],
      radiant: [...etc],
      dire: [...etc]
    }
  }
}
*/
const containerMap = {};

/*
  PixiManager is a bridge between MapContent and all Pixi-related operations and entities
*/
export default class PixiManager {
  constructor(width, height, domTarget, faction, filters) {
    if (!instance) {
      instance = this;
      stage = new PIXI.Container();
      renderer = PIXI.autoDetectRenderer(width, height, { antialias: true, transparent: true });
      domTarget.appendChild(renderer.view);
      this.loadResources(this.initialize.bind(this, faction, filters));
    }
    return instance;
  }

  loadResources(onComplete) {
    PIXI.loader
      .add('images/minimap683.png')
      .load(onComplete);
  }

  initialize(faction, filters) {
    const bgSprite = new PIXI.Sprite(PIXI.loader.resources['images/minimap683.png'].texture);
    stage.addChild(bgSprite);
    stage.scale.set(0.66, 0.66);
    this.readConfig(NodeConfig);
    this.recieveFilters(faction, filters);
    this.update();
  }

  /*
    Starts reading in a configuration group by group.
    Groups are semantically related collections of filters
    like 'wards' or 'camps'
  */
  readConfig(config) {
    for (const groupKey in config) {
      const group = config[groupKey];
      // Create a new object at this group name
      containerMap[groupKey] = {};
      // Read in the group
      this.readFilter(group, groupKey);
    }
  }

  /*
    Starts reading in a group filter by filter
    Filters are a category of nodes on the map identified by their color
  */
  readFilter(group, groupKey) {
    for (const filterKey in group) {
      // Store the filter
      const filter = group[filterKey];
      // Create a new object at this container name
      containerMap[groupKey][filterKey] = {};

      /*
        Some filters should display different nodes depending on the active
        faction so all available factions will be read
      */
      for (const factionKey in filter) {
        // Read in the filter nodes for every faction
        const container = this.readFaction(filter[factionKey]);
        // Store the refs to the container
        containerMap[groupKey][filterKey][factionKey] = container;
        // Add the container to the stage
        stage.addChild(container);
      }
    }
  }

  /*
    Starts reading in a filter faction by faction
    Filters are a category of nodes on the map identified by their color
  */
  readFaction(factionFilter) {
    const color = factionFilter.color;
    const onClick = (event) => {
      console.log(event.target.id + ' ' + event.target.x + ' ' + event.target.y);
    };

    const container = new PIXI.Container();     // Create an empty array for every node
    // Iterate through the filter points
    for (let i = 0; i < factionFilter.points.length; i++) {
      const point = factionFilter.points[i];
      // Create a new node out of the points configuration
      const node = new MapNode(point.id, point.x, point.y, color, onClick);
      container.addChild(node);             // Add the new node to the container
    }
    return container;
  }

  recieveFilters(faction, filters) {
    for (const groupKey in containerMap) {
      for (const filterKey in containerMap[groupKey]) {
        for (const factionKey in containerMap[groupKey][filterKey]) {
          const container = containerMap[groupKey][filterKey][factionKey];
          if (factionKey === NEUTRAL || factionKey === faction) {
            container.visible = filters[groupKey][filterKey];
          }
          else if (factionKey !== faction) {
            container.visible = false;
          }
        }
      }
    }
  }

  update() {
    renderer.render(stage);
  }
}
<commit_msg>Render nodes based on type<commit_after>import PIXI from 'pixi.js';
import { NodeConfig } from './NodeConfig';
import { WardNode, BoxNode, TowerNode } from './MapNodes';
import { NEUTRAL, WARD, BOX, TOWER, SENTRY, PULL } from '../constants/MapConstants';

let instance = null;
let stage;
let renderer;

const handleClick = (event) => {
  console.log(event.target.id + ' ' + event.target.x + ' ' + event.target.y);
};

/*
  The references to the nodes on the map stored within a JSON-like
  object. The format is:
{
  <groupName>: {          // 'wards', 'camps', etc
    <filterName>: {       // 'runes', 'offense', etc
      neutral: [WardNode, WardNode, WardNode, ...],
      radiant: [...etc],
      dire: [...etc]
    }
  }
}
*/
const containerMap = {};

/*
  PixiManager is a bridge between MapContent and all Pixi-related operations and entities
*/
export default class PixiManager {
  constructor(width, height, domTarget, faction, filters) {
    if (!instance) {
      instance = this;
      stage = new PIXI.Container();
      renderer = PIXI.autoDetectRenderer(width, height, { antialias: true, transparent: true });
      domTarget.appendChild(renderer.view);
      this.loadResources(this.initialize.bind(this, faction, filters));
    }
    return instance;
  }

  loadResources(onComplete) {
    PIXI.loader
      .add('images/minimap683.png')
      .load(onComplete);
  }

  initialize(faction, filters) {
    const bgSprite = new PIXI.Sprite(PIXI.loader.resources['images/minimap683.png'].texture);
    stage.addChild(bgSprite);
    stage.scale.set(0.66, 0.66);
    this.readConfig(NodeConfig);
    this.recieveFilters(faction, filters);
    this.update();
  }

  /*
    Starts reading in a configuration group by group.
    Groups are semantically related collections of filters
    like 'wards' or 'camps'
  */
  readConfig(config) {
    for (const groupKey in config) {
      const group = config[groupKey];
      // Create a new object at this group name
      containerMap[groupKey] = {};
      // Read in the group
      this.readFilter(group, groupKey);
    }
  }

  /*
    Starts reading in a group filter by filter
    Filters are a category of nodes on the map identified by their color
  */
  readFilter(group, groupKey) {
    for (const filterKey in group) {
      // Store the filter
      const filter = group[filterKey];
      // Create a new object at this container name
      containerMap[groupKey][filterKey] = {};

      /*
        Some filters should display different nodes depending on the active
        faction so all available factions will be read
      */
      for (const factionKey in filter) {
        // Read in the filter nodes for every faction
        const container = this.readFaction(filter[factionKey]);
        // Store the refs to the container
        containerMap[groupKey][filterKey][factionKey] = container;
        // Add the container to the stage
        stage.addChild(container);
      }
    }
  }

  /*
    Starts reading in a filter faction by faction
    Filters are a category of nodes on the map identified by their color
  */
  readFaction(factionFilter) {
    const attributes = factionFilter.attributes;
    const type = factionFilter.type;

    // Create a container for the nodes
    const container = new PIXI.Container();

    // Iterate through the filter points
    for (let i = 0; i < factionFilter.points.length; i++) {
      const point = factionFilter.points[i];
      // Create a new node out of the points configuration
      const node = this.selectConstructor(type, attributes, point);
      // Add the new node to the container
      if (node) {
        container.addChild(node);
      }
    }
    return container;
  }

  selectConstructor(type, attributes, point) {
    let newNode = null;
    switch(type) {
      case WARD:
        newNode = new WardNode(attributes.color,
                               point.id, point.x, point.y, handleClick);
        break;
      case BOX:
        newNode = new BoxNode(attributes.alpha,
                              attributes.color,
                              attributes.outlineColor,
                              point.verts, point.id, point.x, point.y, handleClick);
        break;
      case TOWER:
        newNode = new TowerNode(attributes.towerColor,
                                attributes.detectionColor,
                                attributes.alpha,
                                attributes.range,
                                point.id, point.x, point.y, handleClick);
        break;
    }
    return newNode;
  }

  recieveFilters(faction, filters) {
    for (const groupKey in containerMap) {
      for (const filterKey in containerMap[groupKey]) {
        for (const factionKey in containerMap[groupKey][filterKey]) {
          const container = containerMap[groupKey][filterKey][factionKey];
          if (factionKey === NEUTRAL || factionKey === faction) {
            container.visible = filters[groupKey][filterKey];
          }
          else if (factionKey !== faction) {
            container.visible = false;
          }
        }
      }
    }
  }

  update() {
    renderer.render(stage);
  }
}
<|endoftext|>


<commit_before>var EventEmitter = require("events").EventEmitter;
var lazy = require("lazy.js");

var AppDispatcher = require("../AppDispatcher");
var AppsEvents = require("../events/AppsEvents");
var appScheme = require("../stores/appScheme");
var AppStatus = require("../constants/AppStatus");
var HealthStatus = require("../constants/HealthStatus");
var TasksEvents = require("../events/TasksEvents");
var TaskStatus = require("../constants/TaskStatus");
var QueueStore = require("./QueueStore");
var QueueEvents = require("../events/QueueEvents");

function removeApp(apps, appId) {
  return lazy(apps).reject({
    id: appId
  }).value();
}

function removeTask(tasks, relatedAppId, taskId) {
  return lazy(tasks).reject(function (task) {
    return task.id === taskId && task.appId === relatedAppId;
  }).value();
}

function getTaskHealth(task) {
  var nullResult = true;
  var health = false;
  var healthCheckResults = task.healthCheckResults;

  if (healthCheckResults != null) {
    health = lazy(healthCheckResults).every(function (hcr) {
      if (hcr.firstSuccess) {
        nullResult = false;
        return hcr.alive;
      }

      return false;
    });
  }

  if (!health && nullResult) { // health check has not returned yet
    return HealthStatus.UNKNOWN;
  }

  return health ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY;
}

function setTaskStatus(task) {
  if (task.startedAt != null) {
    task.status = TaskStatus.STARTED;
    task.updatedAt = task.startedAt;
  } else if (task.stagedAt != null) {
    task.status = TaskStatus.STAGED;
    task.updatedAt = task.stagedAt;
  }
}

function processApp(app) {
  app = lazy(appScheme).extend(app).value();

  app.status = AppStatus.RUNNING;
  if (app.deployments.length > 0) {
    app.status = AppStatus.DEPLOYING;
  } else if (app.instances === 0 && app.tasksRunning === 0) {
    app.status = AppStatus.SUSPENDED;
  }

  app.tasks = lazy(app.tasks).map(function (task) {
    task.healthStatus = getTaskHealth(task);
    setTaskStatus(task);
    return task;
  }).value();

  return app;
}

function processApps(apps) {
  return lazy(apps).map(function (app) {
    return processApp(app);
  }).value();
}

function applyAppDelayStatus(apps, queue) {
  let changed = false;

  queue.forEach(function (entry) {
    if (entry.app == null || entry.delay == null) {
      return;
    }

    let status;

    if (entry.delay.overdue === false && entry.delay.timeLeftSeconds > 0) {
      status = AppStatus.DELAYED;
    } else if (entry.delay.overdue === true) {
      status = AppStatus.WAITING;
    }

    if (status) {
      let app = lazy(apps).findWhere({"id": entry.app.id});
      if (app == null) {
        return;
      }
      if (app.status !== status) {
        changed = true;
      }
      app.status = status;
    }

  });

  return changed;
}

var AppsStore = lazy(EventEmitter.prototype).extend({
  // Array of apps objects recieved from the "apps/"-endpoint
  apps: [],
  // Object of the current app recieved from the "apps/[appid]"-endpoint
  // This endpoint delievers more data, like the tasks on the app.
  currentApp: appScheme,

  getCurrentApp: function (appId) {
    if (appId === this.currentApp.id) {
      return this.currentApp;
    }

    var shallowApp = lazy(this.apps).findWhere({id: appId});
    if (shallowApp) {
      return shallowApp;
    }

    return appScheme;
  },

  getTask: function (appId, taskId) {
    return lazy(this.getCurrentApp(appId).tasks).findWhere({"id": taskId});
  }
}).value();

AppDispatcher.register(function (action) {
  QueueStore.on(QueueEvents.CHANGE, function () {
    let change = applyAppDelayStatus(AppsStore.apps, QueueStore.queue);
    if (change) {
      AppsStore.emit(AppsEvents.CHANGE);
    }
  });

  switch (action.actionType) {
    case AppsEvents.REQUEST_APPS:
      AppsStore.apps = processApps(action.data.body.apps);
      applyAppDelayStatus(AppsStore.apps, QueueStore.queue);
      AppsStore.emit(AppsEvents.CHANGE);
      break;
    case AppsEvents.REQUEST_APPS_ERROR:
      AppsStore.emit(AppsEvents.REQUEST_APPS_ERROR, action.data.body);
      break;
    case AppsEvents.REQUEST_APP:
      AppsStore.currentApp = processApp(action.data.body.app);
      AppsStore.emit(AppsEvents.CHANGE);
      break;
    case AppsEvents.REQUEST_APP_ERROR:
      AppsStore.emit(AppsEvents.REQUEST_APP_ERROR, action.data.body);
      break;
    case AppsEvents.CREATE_APP:
      AppsStore.apps.push(processApp(action.data.body));
      AppsStore.emit(AppsEvents.CREATE_APP);
      AppsStore.emit(AppsEvents.CHANGE);
      break;
    case AppsEvents.CREATE_APP_ERROR:
      AppsStore.emit(
        AppsEvents.CREATE_APP_ERROR,
        action.data.body,
        action.data.status
      );
      break;
    case AppsEvents.DELETE_APP:
      AppsStore.apps =
        removeApp(AppsStore.apps, action.appId);
      AppsStore.emit(AppsEvents.DELETE_APP);
      break;
    case AppsEvents.DELETE_APP_ERROR:
      AppsStore.emit(AppsEvents.DELETE_APP_ERROR, action.data.body);
      break;
    case AppsEvents.RESTART_APP:
      AppsStore.emit(AppsEvents.RESTART_APP);
      break;
    case AppsEvents.RESTART_APP_ERROR:
      AppsStore.emit(AppsEvents.RESTART_APP_ERROR, action.data.body);
      break;
    case AppsEvents.SCALE_APP:
      AppsStore.emit(AppsEvents.SCALE_APP);
      break;
    case AppsEvents.SCALE_APP_ERROR:
      AppsStore.emit(AppsEvents.SCALE_APP_ERROR, action.data.body);
      break;
    case AppsEvents.APPLY_APP:
      AppsStore.emit(AppsEvents.APPLY_APP);
      break;
    case AppsEvents.APPLY_APP_ERROR:
      AppsStore.emit(AppsEvents.APPLY_APP_ERROR, action.data.body);
      break;
    case TasksEvents.DELETE:
      AppsStore.currentApp.tasks =
        removeTask(
          AppsStore.currentApp.tasks,
          action.appId,
          action.taskId
        );
      AppsStore.emit(AppsEvents.CHANGE);
      break;
    case TasksEvents.DELETE_ERROR:
      AppsStore.emit(TasksEvents.DELETE_ERROR, action.data.body);
      break;
  }
});

module.exports = AppsStore;
<commit_msg>Queue listener should register only once<commit_after>var EventEmitter = require("events").EventEmitter;
var lazy = require("lazy.js");

var AppDispatcher = require("../AppDispatcher");
var AppsEvents = require("../events/AppsEvents");
var appScheme = require("../stores/appScheme");
var AppStatus = require("../constants/AppStatus");
var HealthStatus = require("../constants/HealthStatus");
var TasksEvents = require("../events/TasksEvents");
var TaskStatus = require("../constants/TaskStatus");
var QueueStore = require("./QueueStore");
var QueueEvents = require("../events/QueueEvents");

function removeApp(apps, appId) {
  return lazy(apps).reject({
    id: appId
  }).value();
}

function removeTask(tasks, relatedAppId, taskId) {
  return lazy(tasks).reject(function (task) {
    return task.id === taskId && task.appId === relatedAppId;
  }).value();
}

function getTaskHealth(task) {
  var nullResult = true;
  var health = false;
  var healthCheckResults = task.healthCheckResults;

  if (healthCheckResults != null) {
    health = lazy(healthCheckResults).every(function (hcr) {
      if (hcr.firstSuccess) {
        nullResult = false;
        return hcr.alive;
      }

      return false;
    });
  }

  if (!health && nullResult) { // health check has not returned yet
    return HealthStatus.UNKNOWN;
  }

  return health ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY;
}

function setTaskStatus(task) {
  if (task.startedAt != null) {
    task.status = TaskStatus.STARTED;
    task.updatedAt = task.startedAt;
  } else if (task.stagedAt != null) {
    task.status = TaskStatus.STAGED;
    task.updatedAt = task.stagedAt;
  }
}

function processApp(app) {
  app = lazy(appScheme).extend(app).value();

  app.status = AppStatus.RUNNING;
  if (app.deployments.length > 0) {
    app.status = AppStatus.DEPLOYING;
  } else if (app.instances === 0 && app.tasksRunning === 0) {
    app.status = AppStatus.SUSPENDED;
  }

  app.tasks = lazy(app.tasks).map(function (task) {
    task.healthStatus = getTaskHealth(task);
    setTaskStatus(task);
    return task;
  }).value();

  return app;
}

function processApps(apps) {
  return lazy(apps).map(function (app) {
    return processApp(app);
  }).value();
}

function applyAppDelayStatus(apps, queue) {
  var changed = false;

  queue.forEach(function (entry) {
    if (entry.app == null || entry.delay == null) {
      return;
    }

    var status;

    if (entry.delay.overdue === false && entry.delay.timeLeftSeconds > 0) {
      status = AppStatus.DELAYED;
    } else if (entry.delay.overdue === true) {
      status = AppStatus.WAITING;
    }

    if (status) {
      let app = lazy(apps).findWhere({"id": entry.app.id});
      if (app == null) {
        return;
      }
      if (app.status !== status) {
        changed = true;
      }
      app.status = status;
    }

  });

  return changed;
}

var AppsStore = lazy(EventEmitter.prototype).extend({
  // Array of apps objects recieved from the "apps/"-endpoint
  apps: [],
  // Object of the current app recieved from the "apps/[appid]"-endpoint
  // This endpoint delievers more data, like the tasks on the app.
  currentApp: appScheme,

  getCurrentApp: function (appId) {
    if (appId === this.currentApp.id) {
      return this.currentApp;
    }

    var shallowApp = lazy(this.apps).findWhere({id: appId});
    if (shallowApp) {
      return shallowApp;
    }

    return appScheme;
  },

  getTask: function (appId, taskId) {
    return lazy(this.getCurrentApp(appId).tasks).findWhere({"id": taskId});
  }
}).value();

QueueStore.on(QueueEvents.CHANGE, function () {
  var change = applyAppDelayStatus(AppsStore.apps, QueueStore.queue);
  if (change) {
    AppsStore.emit(AppsEvents.CHANGE);
  }
});

AppDispatcher.register(function (action) {
  switch (action.actionType) {
    case AppsEvents.REQUEST_APPS:
      AppsStore.apps = processApps(action.data.body.apps);
      applyAppDelayStatus(AppsStore.apps, QueueStore.queue);
      AppsStore.emit(AppsEvents.CHANGE);
      break;
    case AppsEvents.REQUEST_APPS_ERROR:
      AppsStore.emit(AppsEvents.REQUEST_APPS_ERROR, action.data.body);
      break;
    case AppsEvents.REQUEST_APP:
      AppsStore.currentApp = processApp(action.data.body.app);
      AppsStore.emit(AppsEvents.CHANGE);
      break;
    case AppsEvents.REQUEST_APP_ERROR:
      AppsStore.emit(AppsEvents.REQUEST_APP_ERROR, action.data.body);
      break;
    case AppsEvents.CREATE_APP:
      AppsStore.apps.push(processApp(action.data.body));
      AppsStore.emit(AppsEvents.CREATE_APP);
      AppsStore.emit(AppsEvents.CHANGE);
      break;
    case AppsEvents.CREATE_APP_ERROR:
      AppsStore.emit(
        AppsEvents.CREATE_APP_ERROR,
        action.data.body,
        action.data.status
      );
      break;
    case AppsEvents.DELETE_APP:
      AppsStore.apps =
        removeApp(AppsStore.apps, action.appId);
      AppsStore.emit(AppsEvents.DELETE_APP);
      break;
    case AppsEvents.DELETE_APP_ERROR:
      AppsStore.emit(AppsEvents.DELETE_APP_ERROR, action.data.body);
      break;
    case AppsEvents.RESTART_APP:
      AppsStore.emit(AppsEvents.RESTART_APP);
      break;
    case AppsEvents.RESTART_APP_ERROR:
      AppsStore.emit(AppsEvents.RESTART_APP_ERROR, action.data.body);
      break;
    case AppsEvents.SCALE_APP:
      AppsStore.emit(AppsEvents.SCALE_APP);
      break;
    case AppsEvents.SCALE_APP_ERROR:
      AppsStore.emit(AppsEvents.SCALE_APP_ERROR, action.data.body);
      break;
    case AppsEvents.APPLY_APP:
      AppsStore.emit(AppsEvents.APPLY_APP);
      break;
    case AppsEvents.APPLY_APP_ERROR:
      AppsStore.emit(AppsEvents.APPLY_APP_ERROR, action.data.body);
      break;
    case TasksEvents.DELETE:
      AppsStore.currentApp.tasks =
        removeTask(
          AppsStore.currentApp.tasks,
          action.appId,
          action.taskId
        );
      AppsStore.emit(AppsEvents.CHANGE);
      break;
    case TasksEvents.DELETE_ERROR:
      AppsStore.emit(TasksEvents.DELETE_ERROR, action.data.body);
      break;
  }
});

module.exports = AppsStore;
<|endoftext|>


<commit_before>var observableDiff = require('deep-diff').observableDiff;

var default_data = {
  active: false,
  name: 'JSON',
  schema_url: '',
  doc: {},
  editor: 'json'
};

module.exports = {
  data: function() {
    return default_data;
  },
  components: {
    // TODO: make these dynamic...somehow
    'json': require('../json-editor'),
    'vue-schema': require('../vue-schema')
  },
  replace: true,
  template: require('./template.html'),
  computed: {
    name: function() {
      // TODO: this.$root.mess must end!
      if (this.$root.types[this.doc.type].name) {
        return this.$root.types[this.doc.type].name;
      }
    },
    types: function() {
      // get the types list from $root, but in Vue.js' special select format
      var type_names = Object.keys(this.$root.types);
      var options = [];
      for (var i = 0; i < type_names.length; i++) {
        options.push({
          text: this.$root.types[type_names[i]].name,
          value: type_names[i]
        });
      }
      return options;
    }
  },
  watch: {
    schema_url: function() {
      this.editor = 'vue-schema';
    },
    'doc.type': function() {
      if (undefined != this.$root.types[this.doc.type]
          && undefined != this.$root.types[this.doc.type].components.editor) {
        this.editor = this.$root.types[this.doc.type].components.editor;
      } else {
        // default to JSON editor
        this.editor = 'json';
      }
    }
  },
  created: function() {
    document.body.classList.add('dimmed', 'dimmable', 'scrolling');
    // hide all menus
    this.$root.ui.menu = '';
  },
  beforeCompile: function() {
    var self = this;
    var blank_doc = BlueInk.component(this.editor).options.data();

    if (Object.keys(this.doc).length < 2
        && undefined !== this.doc.type) {
      // we have "stub" / initiation doc, so reset to defaults
      this.doc = blank_doc.doc;
    }

    // trigger new keys into Vue.js' observable-ness
    observableDiff(this.doc, blank_doc.doc,
      function(diff) {
        // if we've got a new key addition, trigger an $add
        if (diff.kind === 'N') {
          self.doc.$add(diff.path.join('.'), diff.rhs);
        }
      });
  },
  compiled: function() {
    // connect the editor.doc and modal docs for change watching
    this.$watch('doc', function() {
      this.$.editor.doc = this.doc;
    },
    // watch the entire doc
    true,
    // and triger changes (such as editor component choice) now
    true);
  },
  methods: {
    destroy: function() {
      // TODO: danger: this could remove a sites version of these :( namespace?
      document.body.classList.remove('dimmed', 'dimmable', 'scrolling');
      this.$destroy(true);
    },
    del: function() {
      var self = this;
      self.$db.get(self.doc._id, function(err, doc) {
        if (doc) {
          self.$db.remove(doc, function() {
            alert('The ' + doc.type + ' has been deleted.');
            // TODO: remove preview of removed item
            self.$emit('afterDel');
            self.destroy();
          });
        }
      });
    },
    save: function() {
      var self = this;
      // get doc from editor
      var doc = this.$.editor.output();

      // add/force time data
      // TODO: is this really the best place?
      // TODO: should I keep these under a `meta` or `blueink` key?
      if (!('created' in doc) || isNaN(Date.parse(doc.created))) {
        // we're missing `created` so let's...create it
        doc.created = (new Date()).toISOString();
      }
      // updated is always the latest timestamp
      doc.updated = (new Date()).toISOString();

      // save doc
      self.$db.post(doc, function(err, resp) {
        if (err) {
          // TODO: maybe tell somebody...
          console.log('error: ', err);
        } else {
          // TODO: trigger content reload, etc.
          self.$emit('saved', doc);
          self.destroy();
        }
      });
    }
  },
  filters: {
    encodeURIComponent: function(v) {
      return encodeURIComponent(v);
    }
  }
};
<commit_msg>Avoid a race condition where $root disappears<commit_after>var observableDiff = require('deep-diff').observableDiff;

var default_data = {
  active: false,
  name: 'JSON',
  schema_url: '',
  doc: {},
  editor: 'json'
};

module.exports = {
  data: function() {
    return default_data;
  },
  components: {
    // TODO: make these dynamic...somehow
    'json': require('../json-editor'),
    'vue-schema': require('../vue-schema')
  },
  replace: true,
  template: require('./template.html'),
  computed: {
    name: function() {
      // TODO: this.$root.mess must end!
      if (this.$root.types[this.doc.type].name) {
        return this.$root.types[this.doc.type].name;
      }
    },
    types: function() {
      var options = [];
      if (this.$root) {
        // get the types list from $root, but in Vue.js' special select format
        var type_names = Object.keys(this.$root.types);
        for (var i = 0; i < type_names.length; i++) {
          options.push({
            text: this.$root.types[type_names[i]].name,
            value: type_names[i]
          });
        }
      }
      return options;
    }
  },
  watch: {
    schema_url: function() {
      this.editor = 'vue-schema';
    },
    'doc.type': function() {
      if (undefined != this.$root.types[this.doc.type]
          && undefined != this.$root.types[this.doc.type].components.editor) {
        this.editor = this.$root.types[this.doc.type].components.editor;
      } else {
        // default to JSON editor
        this.editor = 'json';
      }
    }
  },
  created: function() {
    document.body.classList.add('dimmed', 'dimmable', 'scrolling');
    // hide all menus
    this.$root.ui.menu = '';
  },
  beforeCompile: function() {
    var self = this;
    var blank_doc = BlueInk.component(this.editor).options.data();

    if (Object.keys(this.doc).length < 2
        && undefined !== this.doc.type) {
      // we have "stub" / initiation doc, so reset to defaults
      this.doc = blank_doc.doc;
    }

    // trigger new keys into Vue.js' observable-ness
    observableDiff(this.doc, blank_doc.doc,
      function(diff) {
        // if we've got a new key addition, trigger an $add
        if (diff.kind === 'N') {
          self.doc.$add(diff.path.join('.'), diff.rhs);
        }
      });
  },
  compiled: function() {
    // connect the editor.doc and modal docs for change watching
    this.$watch('doc', function() {
      this.$.editor.doc = this.doc;
    },
    // watch the entire doc
    true,
    // and triger changes (such as editor component choice) now
    true);
  },
  methods: {
    destroy: function() {
      // TODO: danger: this could remove a sites version of these :( namespace?
      document.body.classList.remove('dimmed', 'dimmable', 'scrolling');
      this.$destroy(true);
    },
    del: function() {
      var self = this;
      self.$db.get(self.doc._id, function(err, doc) {
        if (doc) {
          self.$db.remove(doc, function() {
            alert('The ' + doc.type + ' has been deleted.');
            // TODO: remove preview of removed item
            self.$emit('afterDel');
            self.destroy();
          });
        }
      });
    },
    save: function() {
      var self = this;
      // get doc from editor
      var doc = this.$.editor.output();

      // add/force time data
      // TODO: is this really the best place?
      // TODO: should I keep these under a `meta` or `blueink` key?
      if (!('created' in doc) || isNaN(Date.parse(doc.created))) {
        // we're missing `created` so let's...create it
        doc.created = (new Date()).toISOString();
      }
      // updated is always the latest timestamp
      doc.updated = (new Date()).toISOString();

      // save doc
      self.$db.post(doc, function(err, resp) {
        if (err) {
          // TODO: maybe tell somebody...
          console.log('error: ', err);
        } else {
          // TODO: trigger content reload, etc.
          self.$emit('saved', doc);
          self.destroy();
        }
      });
    }
  },
  filters: {
    encodeURIComponent: function(v) {
      return encodeURIComponent(v);
    }
  }
};
<|endoftext|>


<commit_before>;(function(factory) {
    //CommonJS
    if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
        factory(require("knockout"), require("jQuery"), exports);
        //AMD
    } else if (typeof define === "function" && define.amd) {
        define(["knockout", "jQuery", "exports"], factory);
        //normal script tag
    } else {
        factory(ko, $);
    }
}(function(ko, $) {

        function updater(state,cb){
            if (state.updating)
                return;

            state.updating=true;
            cb();
            state.updating=false;
        }

      ko.bindingHandlers.tokenfield = {
        init: function(element, valueAccessor, allBindings) {
            var $element=$(element),  value= valueAccessor(),
                tokens = value.tokens, options = allBindings.get('tokenfieldOptions'), state ={updating:false},
                display = options.display, tokenoptions = value.predefined,
                tokenFactory =value.tokenFactory || function(){return null;},
                counterCount = 0, finder = options.finder, typeaheadSource;

            ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                $element.tokenfield('destroy');
            });

            $element.data('tokenfield_ko_options',options);
            $element.data('tokenfield_ko_state',state);

            if (!finder){
                var localsource = new Bloodhound({
                    datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.label);},
                    queryTokenizer: Bloodhound.tokenizers.whitespace,
                    local: $.map(tokenoptions,function(el){return {value:el,label:el[display]};})
                });

                localsource.initialize();

                typeaheadSource =localsource.ttAdapter();
            }
            else{
                typeaheadSource = function (query, syncResults, asyncResults){
                    finder(query).then(function(val){
                        asyncResults($.map(val,function(el){return {value:el,label:el[display]};}));
                    });
                };
            }

            options.typeahead =[options.typeahead || null,{displayKey:'label',source:typeaheadSource}];

            $element.tokenfield(options)
            .on('tokenfield:createtoken', function (e) {
                if ((state.updating) || (e.fromSelection))
                    return true;

                //set temp label
                e.attrs.value = counterCount;

                var created = (function(count){
                    return tokenFactory(e.attrs.label,function(res){
                        $.each($element.tokenfield('getTokens'),function(index,element){
                            if (element.value===count){
                                if (res===null){

                                }
                                else{
                                    element.value=res;
                                    element.label = res[display];
                                }
                            }
                        });
                    });
                    })(counterCount);

                counterCount++;

                if (created===null)
                    e.preventDefault();
                else if (typeof(created) != 'undefined'){
                     e.attrs.value = created;
                }
            })
            .on('tokenfield:createdtoken', function (e) {
                updater(state,function(){
                    var value = e.attrs.value;
                    if (value){
                        tokens.push(value);
                        console.log(value);
                    }
                });
            })
            .on('tokenfield:editedtoken', function (e) {
            })
            .on('tokenfield:edittoken', function (e) {
            })
            .on('tokenfield:removetoken', function (e) {
                 updater(state,function(){
                    tokens.splice($(e.relatedTarget).index(),1);
                });
            }).
            on('tokenfield:sortedtoken', function (e) {
                updater(state,function(){
                    var element = tokens()[e.oldPosition];
                    tokens.splice(e.oldPosition,1);
                    tokens.splice(e.newPosition,0,element);
                });
            });
        },

        update: function(element, valueAccessor, allBindingsAccessor, deprecated, bindingContext) {
            var $element=$(element), options= $element.data('tokenfield_ko_options'),
                value = ko.utils.unwrapObservable(valueAccessor()) || [], display = options.display;

             console.log('update');

            updater($element.data('tokenfield_ko_state'),function(){

                $element.tokenfield('setTokens', $.map(value.tokens(),function(el){
                    return {value:el, label:ko.utils.unwrapObservable(el[display])};
                }) );
            });
        }
    };

}));
<commit_msg>Manage close token<commit_after>;(function(factory) {
    //CommonJS
    if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
        factory(require("knockout"), require("jQuery"), exports);
        //AMD
    } else if (typeof define === "function" && define.amd) {
        define(["knockout", "jQuery", "exports"], factory);
        //normal script tag
    } else {
        factory(ko, $);
    }
}(function(ko, $) {

        function updater(state,cb){
            if (state.updating)
                return;

            state.updating=true;
            cb();
            state.updating=false;
        }

      ko.bindingHandlers.tokenfield = {
        init: function(element, valueAccessor, allBindings) {
            var $element=$(element),  value= valueAccessor(),
                tokens = value.tokens, options = allBindings.get('tokenfieldOptions'), state ={updating:false},
                display = options.display, tokenoptions = value.predefined,
                tokenFactory =value.tokenFactory || function(){return null;},
                counterCount = 0, finder = options.finder, typeaheadSource;

            ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                $element.tokenfield('destroy');
            });

            $element.data('tokenfield_ko_options',options);
            $element.data('tokenfield_ko_state',state);

            if (!finder){
                var localsource = new Bloodhound({
                    datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.label);},
                    queryTokenizer: Bloodhound.tokenizers.whitespace,
                    local: $.map(tokenoptions,function(el){return {value:el,label:el[display]};})
                });

                localsource.initialize();

                typeaheadSource =localsource.ttAdapter();
            }
            else{
                typeaheadSource = function (query, syncResults, asyncResults){
                    finder(query).then(function(val){
                        asyncResults($.map(val,function(el){return {value:el,label:el[display]};}));
                    });
                };
            }

            options.typeahead =[options.typeahead || null,{displayKey:'label',source:typeaheadSource}];

            $element.tokenfield(options)
            .on('tokenfield:createtoken', function (e) {
                if ((state.updating) || (e.fromSelection))
                    return true;

                //set temp label
                e.attrs.value = counterCount;

                var created = (function(count){
                    return tokenFactory(e.attrs.label,function(res){
                        $.each($element.tokenfield('getTokens'),function(index,element){
                            if (element.value===count){
                                if (res===null){

                                }
                                else{
                                    element.value=res;
                                    element.label = res[display];
                                }
                            }
                        });
                    });
                    })(counterCount++);

                if (created===null)
                    e.preventDefault();
                else if (typeof(created) != 'undefined'){
                     e.attrs.value = created;
                }
            })
            .on('tokenfield:createdtoken', function (e) {
                updater(state,function(){
                    var value = e.attrs.value;
                    if (value){
                        tokens.push(value);
                        console.log(value);
                    }
                });
            })
            .on('tokenfield:editedtoken', function (e) {
            })
            .on('tokenfield:edittoken', function (e) {
            })
            .on('tokenfield:removetoken', function (e) {
                 updater(state,function(){
                    tokens.splice($(e.relatedTarget).index(),1);
                });
            }).
            on('tokenfield:sortedtoken', function (e) {
                updater(state,function(){
                    var element = tokens()[e.oldPosition];
                    tokens.splice(e.oldPosition,1);
                    tokens.splice(e.newPosition,0,element);
                });
            });
        },

        update: function(element, valueAccessor, allBindingsAccessor, deprecated, bindingContext) {
            var $element=$(element), options= $element.data('tokenfield_ko_options'),
                value = ko.utils.unwrapObservable(valueAccessor()) || [], display = options.display;

             console.log('update');

            updater($element.data('tokenfield_ko_state'),function(){

                $element.tokenfield('setTokens', $.map(value.tokens(),function(el){
                    return {value:el, label:ko.utils.unwrapObservable(el[display])};
                }) );
            });
        }
    };

}));
<|endoftext|>


<commit_before>/*
 * L.Canvas handles Canvas vector layers rendering and mouse events handling. All Canvas-specific code goes here.
 */

L.Canvas = L.Renderer.extend({

	onAdd: function () {
		L.Renderer.prototype.onAdd.call(this);

		this._layers = this._layers || {};

		// redraw vectors since canvas is cleared upon removal
		this._draw();
	},

	_initContainer: function () {
		var container = this._container = document.createElement('canvas');

		L.DomEvent
			.on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 66, this), this)
			.on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this)
			.on(container, 'mouseout', this._handleMouseOut, this);

		this._ctx = container.getContext('2d');
	},

	_update: function () {
		if (this._map._animatingZoom && this._bounds) { return; }

		this._drawnLayers = [];

		L.Renderer.prototype._update.call(this);

		var b = this._bounds,
		    container = this._container,
		    size = b.getSize(),
		    m = L.Browser.retina ? 2 : 1;

		L.DomUtil.setPosition(container, b.min);

		// set canvas size (also clearing it); use double size on retina
		container.width = m * size.x;
		container.height = m * size.y;
		container.style.width = size.x + 'px';
		container.style.height = size.y + 'px';

		if (L.Browser.retina) {
			this._ctx.scale(2, 2);
		}

		// translate so we use the same path coordinates after canvas element moves
		this._ctx.translate(-b.min.x, -b.min.y);
	},

	_initPath: function (layer) {
		this._layers[L.stamp(layer)] = layer;
	},

	_addPath: L.Util.falseFn,

	_removePath: function (layer) {
		layer._removed = true;
		this._requestRedraw(layer);
	},

	_updatePath: function (layer) {
		this._redrawBounds = layer._pxBounds;
		this._draw(true);
		layer._project();
		layer._update();
		this._draw();
		this._redrawBounds = null;
	},

	_updateStyle: function (layer) {
		this._requestRedraw(layer);
	},

	_requestRedraw: function (layer) {
		if (!this._map) { return; }

		this._redrawBounds = this._redrawBounds || new L.Bounds();
		this._redrawBounds.extend(layer._pxBounds.min).extend(layer._pxBounds.max);

		this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this);
	},

	_redraw: function () {
		this._redrawRequest = null;

		this._draw(true); // clear layers in redraw bounds
		this._draw(); // draw layers

		this._redrawBounds = null;
	},

	_draw: function (clear) {
		this._clear = clear;
		var layer;

		for (var id in this._layers) {
			layer = this._layers[id];
			if (!this._redrawBounds || layer._pxBounds.intersects(this._redrawBounds)) {
				layer._updatePath();
			}
			if (clear && layer._removed) {
				delete layer._removed;
				delete this._layers[id];
			}
		}
	},

	_updatePoly: function (layer, closed) {

		var i, j, len2, p,
		    parts = layer._parts,
		    len = parts.length,
		    ctx = this._ctx;

		if (!len) { return; }

		this._drawnLayers.push(layer);

		ctx.beginPath();

		for (i = 0; i < len; i++) {
			for (j = 0, len2 = parts[i].length; j < len2; j++) {
				p = parts[i][j];
				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
			}
			if (closed) {
				ctx.closePath();
			}
		}

		this._fillStroke(ctx, layer);

		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
	},

	_updateCircle: function (layer) {

		if (layer._empty()) { return; }

		var p = layer._point,
		    ctx = this._ctx,
		    r = layer._radius,
		    s = (layer._radiusY || r) / r;

		if (s !== 1) {
			ctx.save();
			ctx.scale(1, s);
		}

		ctx.beginPath();
		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);

		if (s !== 1) {
			ctx.restore();
		}

		this._fillStroke(ctx, layer);
	},

	_fillStroke: function (ctx, layer) {
		var clear = this._clear,
		    options = layer.options;

		ctx.globalCompositeOperation = clear ? 'destination-out' : 'source-over';

		if (options.fill) {
			ctx.globalAlpha = clear ? 1 : options.fillOpacity;
			ctx.fillStyle = options.fillColor || options.color;
			ctx.fill(options.fillRule || 'evenodd');
		}

		if (options.stroke && options.weight !== 0) {
			ctx.globalAlpha = clear ? 1 : options.opacity;

			// if clearing shape, do it with the previously drawn line width
			layer._prevWeight = ctx.lineWidth = clear ? layer._prevWeight + 1 : options.weight;

			ctx.strokeStyle = options.color;
			ctx.lineCap = options.lineCap;
			ctx.lineJoin = options.lineJoin;
			ctx.stroke();
		}
	},

	// Canvas obviously doesn't have mouse events for individual drawn objects,
	// so we emulate that by calculating what's under the mouse on mousemove/click manually

	_onClick: function (e) {
		var point = this._map.mouseEventToLayerPoint(e);

		for (var id in this._layers) {
			if (this._layers[id]._containsPoint(point)) {
				L.DomEvent._fakeStop(e);
				this._fireEvent(this._layers[id], e);
			}
		}
	},

	_onMouseMove: function (e) {
		if (!this._map || this._map.dragging._draggable._moving || this._map._animatingZoom) { return; }

		var point = this._map.mouseEventToLayerPoint(e);
		this._handleMouseOut(e, point);
		this._handleMouseHover(e, point);
	},

	_handleMouseOut: function (e, point) {
		var layer = this._hoveredLayer;
		if (layer && (e.type === 'mouseout' || !layer._containsPoint(point))) {
			// if we're leaving the layer, fire mouseout
			L.DomUtil.removeClass(this._container, 'leaflet-interactive');
			this._fireEvent(layer, e, 'mouseout');
			this._hoveredLayer = null;
		}
	},

	_handleMouseHover: function (e, point) {
		var id, layer;
		if (!this._hoveredLayer) {
			for (id in this._drawnLayers) {
				layer = this._drawnLayers[id];
				if (layer.options.interactive && layer._containsPoint(point)) {
					L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
					this._fireEvent(layer, e, 'mouseover');
					this._hoveredLayer = layer;
					break;
				}
			}
		}
		if (this._hoveredLayer) {
			this._fireEvent(this._hoveredLayer, e);
		}
	},

	_fireEvent: function (layer, e, type) {
		this._map._fireDOMEvent(e, type || e.type, [layer]);
	},

	// TODO _bringToFront & _bringToBack, pretty tricky

	_bringToFront: L.Util.falseFn,
	_bringToBack: L.Util.falseFn
});

L.Browser.canvas = (function () {
	return !!document.createElement('canvas').getContext;
}());

L.canvas = function (options) {
	return L.Browser.canvas ? new L.Canvas(options) : null;
};

L.Polyline.prototype._containsPoint = function (p, closed) {
	var i, j, k, len, len2, part,
	    w = this._clickTolerance();

	if (!this._pxBounds.contains(p)) { return false; }

	// hit detection for polylines
	for (i = 0, len = this._parts.length; i < len; i++) {
		part = this._parts[i];

		for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
			if (!closed && (j === 0)) { continue; }

			if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
				return true;
			}
		}
	}
	return false;
};

L.Polygon.prototype._containsPoint = function (p) {
	var inside = false,
	    part, p1, p2, i, j, k, len, len2;

	if (!this._pxBounds.contains(p)) { return false; }

	// ray casting algorithm for detecting if point is in polygon
	for (i = 0, len = this._parts.length; i < len; i++) {
		part = this._parts[i];

		for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
			p1 = part[j];
			p2 = part[k];

			if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
				inside = !inside;
			}
		}
	}

	// also check if it's on polygon stroke
	return inside || L.Polyline.prototype._containsPoint.call(this, p, true);
};

L.CircleMarker.prototype._containsPoint = function (p) {
	return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
};
<commit_msg>Use ID lookup instead to avoid re-adding layer on style change<commit_after>/*
 * L.Canvas handles Canvas vector layers rendering and mouse events handling. All Canvas-specific code goes here.
 */

L.Canvas = L.Renderer.extend({

	onAdd: function () {
		L.Renderer.prototype.onAdd.call(this);

		this._layers = this._layers || {};

		// redraw vectors since canvas is cleared upon removal
		this._draw();
	},

	_initContainer: function () {
		var container = this._container = document.createElement('canvas');

		L.DomEvent
			.on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 66, this), this)
			.on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this)
			.on(container, 'mouseout', this._handleMouseOut, this);

		this._ctx = container.getContext('2d');
	},

	_update: function () {
		if (this._map._animatingZoom && this._bounds) { return; }

		this._drawnLayers = {};

		L.Renderer.prototype._update.call(this);

		var b = this._bounds,
		    container = this._container,
		    size = b.getSize(),
		    m = L.Browser.retina ? 2 : 1;

		L.DomUtil.setPosition(container, b.min);

		// set canvas size (also clearing it); use double size on retina
		container.width = m * size.x;
		container.height = m * size.y;
		container.style.width = size.x + 'px';
		container.style.height = size.y + 'px';

		if (L.Browser.retina) {
			this._ctx.scale(2, 2);
		}

		// translate so we use the same path coordinates after canvas element moves
		this._ctx.translate(-b.min.x, -b.min.y);
	},

	_initPath: function (layer) {
		this._layers[L.stamp(layer)] = layer;
	},

	_addPath: L.Util.falseFn,

	_removePath: function (layer) {
		layer._removed = true;
		this._requestRedraw(layer);
	},

	_updatePath: function (layer) {
		this._redrawBounds = layer._pxBounds;
		this._draw(true);
		layer._project();
		layer._update();
		this._draw();
		this._redrawBounds = null;
	},

	_updateStyle: function (layer) {
		this._requestRedraw(layer);
	},

	_requestRedraw: function (layer) {
		if (!this._map) { return; }

		this._redrawBounds = this._redrawBounds || new L.Bounds();
		this._redrawBounds.extend(layer._pxBounds.min).extend(layer._pxBounds.max);

		this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this);
	},

	_redraw: function () {
		this._redrawRequest = null;

		this._draw(true); // clear layers in redraw bounds
		this._draw(); // draw layers

		this._redrawBounds = null;
	},

	_draw: function (clear) {
		this._clear = clear;
		var layer;

		for (var id in this._layers) {
			layer = this._layers[id];
			if (!this._redrawBounds || layer._pxBounds.intersects(this._redrawBounds)) {
				layer._updatePath();
			}
			if (clear && layer._removed) {
				delete layer._removed;
				delete this._layers[id];
			}
		}
	},

	_updatePoly: function (layer, closed) {

		var i, j, len2, p,
		    parts = layer._parts,
		    len = parts.length,
		    ctx = this._ctx;

		if (!len) { return; }

		this._drawnLayers[layer._leaflet_id] = layer;

		ctx.beginPath();

		for (i = 0; i < len; i++) {
			for (j = 0, len2 = parts[i].length; j < len2; j++) {
				p = parts[i][j];
				ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
			}
			if (closed) {
				ctx.closePath();
			}
		}

		this._fillStroke(ctx, layer);

		// TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
	},

	_updateCircle: function (layer) {

		if (layer._empty()) { return; }

		var p = layer._point,
		    ctx = this._ctx,
		    r = layer._radius,
		    s = (layer._radiusY || r) / r;

		if (s !== 1) {
			ctx.save();
			ctx.scale(1, s);
		}

		ctx.beginPath();
		ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);

		if (s !== 1) {
			ctx.restore();
		}

		this._fillStroke(ctx, layer);
	},

	_fillStroke: function (ctx, layer) {
		var clear = this._clear,
		    options = layer.options;

		ctx.globalCompositeOperation = clear ? 'destination-out' : 'source-over';

		if (options.fill) {
			ctx.globalAlpha = clear ? 1 : options.fillOpacity;
			ctx.fillStyle = options.fillColor || options.color;
			ctx.fill(options.fillRule || 'evenodd');
		}

		if (options.stroke && options.weight !== 0) {
			ctx.globalAlpha = clear ? 1 : options.opacity;

			// if clearing shape, do it with the previously drawn line width
			layer._prevWeight = ctx.lineWidth = clear ? layer._prevWeight + 1 : options.weight;

			ctx.strokeStyle = options.color;
			ctx.lineCap = options.lineCap;
			ctx.lineJoin = options.lineJoin;
			ctx.stroke();
		}
	},

	// Canvas obviously doesn't have mouse events for individual drawn objects,
	// so we emulate that by calculating what's under the mouse on mousemove/click manually

	_onClick: function (e) {
		var point = this._map.mouseEventToLayerPoint(e);

		for (var id in this._layers) {
			if (this._layers[id]._containsPoint(point)) {
				L.DomEvent._fakeStop(e);
				this._fireEvent(this._layers[id], e);
			}
		}
	},

	_onMouseMove: function (e) {
		if (!this._map || this._map.dragging._draggable._moving || this._map._animatingZoom) { return; }

		var point = this._map.mouseEventToLayerPoint(e);
		this._handleMouseOut(e, point);
		this._handleMouseHover(e, point);
	},

	_handleMouseOut: function (e, point) {
		var layer = this._hoveredLayer;
		if (layer && (e.type === 'mouseout' || !layer._containsPoint(point))) {
			// if we're leaving the layer, fire mouseout
			L.DomUtil.removeClass(this._container, 'leaflet-interactive');
			this._fireEvent(layer, e, 'mouseout');
			this._hoveredLayer = null;
		}
	},

	_handleMouseHover: function (e, point) {
		var id, layer;
		if (!this._hoveredLayer) {
			for (id in this._drawnLayers) {
				layer = this._drawnLayers[id];
				if (layer.options.interactive && layer._containsPoint(point)) {
					L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
					this._fireEvent(layer, e, 'mouseover');
					this._hoveredLayer = layer;
					break;
				}
			}
		}
		if (this._hoveredLayer) {
			this._fireEvent(this._hoveredLayer, e);
		}
	},

	_fireEvent: function (layer, e, type) {
		this._map._fireDOMEvent(e, type || e.type, [layer]);
	},

	// TODO _bringToFront & _bringToBack, pretty tricky

	_bringToFront: L.Util.falseFn,
	_bringToBack: L.Util.falseFn
});

L.Browser.canvas = (function () {
	return !!document.createElement('canvas').getContext;
}());

L.canvas = function (options) {
	return L.Browser.canvas ? new L.Canvas(options) : null;
};

L.Polyline.prototype._containsPoint = function (p, closed) {
	var i, j, k, len, len2, part,
	    w = this._clickTolerance();

	if (!this._pxBounds.contains(p)) { return false; }

	// hit detection for polylines
	for (i = 0, len = this._parts.length; i < len; i++) {
		part = this._parts[i];

		for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
			if (!closed && (j === 0)) { continue; }

			if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
				return true;
			}
		}
	}
	return false;
};

L.Polygon.prototype._containsPoint = function (p) {
	var inside = false,
	    part, p1, p2, i, j, k, len, len2;

	if (!this._pxBounds.contains(p)) { return false; }

	// ray casting algorithm for detecting if point is in polygon
	for (i = 0, len = this._parts.length; i < len; i++) {
		part = this._parts[i];

		for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
			p1 = part[j];
			p2 = part[k];

			if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
				inside = !inside;
			}
		}
	}

	// also check if it's on polygon stroke
	return inside || L.Polyline.prototype._containsPoint.call(this, p, true);
};

L.CircleMarker.prototype._containsPoint = function (p) {
	return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
};
<|endoftext|>