<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
 *
 *
 * Events
 * change - called after the col has been moved
 * displayHelper - called before the col has started moving TODO: change to beforeChange
 *
 */

(function($) {
  $.widget("jb.dragtable", {
  		//TODO: implement this
  		eventWidgetPrefix: 'dragtable',
  		// when a col is dragged use this to find the symantic elements, for speed
  		tableElemIndex:{
			head: '0',
			body: '1',
			foot: '2'
		},
		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'

		},

		_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() );

				var half = self.currentColumnCollection[0].clientWidth / 2,
				//console.log( e.pageX, self._findElementPosition(el.parent()[0]))

				parentOffset = self._findElementPosition(el.parent()[0]);

                //console.log( el, self );
                //console.log( $dragDisplay)
				$dragDisplay
				.attr( 'tabindex', -1 )
                .focus()
				.disableSelection()
				.css({
                    top: el[0].offsetTop,
                   //using the parentOff.set makes e.pageX reletive to the parent element. This fixes the issue of the drag display not showing up under cursor on drag.
                    left: ((e.pageX - parentOffset.x) + (parseInt('-' + half)))
				})
                .insertAfter( self.element )

				//get the colum count
				var colCount = self.element[ 0 ]
				.getElementsByTagName( 'thead' )[ 0 ]
				.getElementsByTagName( 'tr' )[ 0 ]
				.getElementsByTagName( 'th' )
				.length - 1;

				//console.log( 'col count', colCount );

                //drag the column around

                self.prevMouseX = e.pageX;

                	//console.log(dragDisplay)
				self._eventHelper('displayHelper', e ,{
					'draggable': $dragDisplay
				});

                $( document )
                .disableSelection()
                .css( 'cursor', 'move')
                .bind('mousemove.' + self.widgetEventPrefix, function( e ){

                    var columnPos = self._findElementPosition(self.currentColumnCollection[0]),
					half = self.currentColumnCollection[0].clientWidth / 2;

                    $dragDisplay
                    .css( 'left', ((e.pageX - parentOffset.x) + (parseInt('-' + half))) )

                    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;
							//move to the right only if x is greater than threshold and the current col isn' the last one
							if(e.pageX > threshold && colCount != self.startIndex ){
								//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.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.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);<commit_msg>code styling, added new lines and indent after var decorations<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
 *
 *
 * Events
 * change - called after the col has been moved
 * displayHelper - called before the col has started moving TODO: change to beforeChange
 *
 */

(function($) {
  $.widget("jb.dragtable", {
  		//TODO: implement this
  		eventWidgetPrefix: 'dragtable',
  		// when a col is dragged use this to find the symantic elements, for speed
  		tableElemIndex:{
			head: '0',
			body: '1',
			foot: '2'
		},
		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'

		},

		_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() );

				var half = self.currentColumnCollection[0].clientWidth / 2,
				//console.log( e.pageX, self._findElementPosition(el.parent()[0]))

				parentOffset = self._findElementPosition(el.parent()[0]);

                //console.log( el, self );
                //console.log( $dragDisplay)
				$dragDisplay
					.attr( 'tabindex', -1 )
	                .focus()
					.disableSelection()
					.css({
	                    top: el[0].offsetTop,
	                   //using the parentOff.set makes e.pageX reletive to the parent element. This fixes the issue of the drag display not showing up under cursor on drag.
	                    left: ((e.pageX - parentOffset.x) + (parseInt('-' + half)))
					})
	                .insertAfter( self.element )

				//get the colum count
				var colCount = self.element[ 0 ]
									.getElementsByTagName( 'thead' )[ 0 ]
									.getElementsByTagName( 'tr' )[ 0 ]
									.getElementsByTagName( 'th' )
									.length - 1;

				//console.log( 'col count', colCount );

                //drag the column around

                self.prevMouseX = e.pageX;

                	//console.log(dragDisplay)
				self._eventHelper('displayHelper', e ,{
					'draggable': $dragDisplay
				});

                $( document )
	                .disableSelection()
	                .css( 'cursor', 'move')
	                .bind('mousemove.' + self.widgetEventPrefix, function( e ){

                    var columnPos = self._findElementPosition(self.currentColumnCollection[0]),
						half = self.currentColumnCollection[0].clientWidth / 2;

                    $dragDisplay
                    	.css( 'left', ((e.pageX - parentOffset.x) + (parseInt('-' + half))) )

                    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;
							//move to the right only if x is greater than threshold and the current col isn' the last one
							if(e.pageX > threshold && colCount != self.startIndex ){
								//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.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);
				//TODO: switch to if else -> speed
				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;
				}

		 	}2

		 	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;
		},
		/*
		 * returns all element attrs in a string key="value" key2="value"
		 */
		_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 use - remove soon
		 */
		_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 jquery's 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.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;
			}

			var 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>// Description: If no img alt exists, insert alt=""
$('img:not([alt])').attr('alt', '""');<commit_msg>Removing extra quotes<commit_after>// Description: If no img alt exists, insert alt=""
$('img:not([alt])').attr('alt', ''
);<|endoftext|>


<commit_before>module.exports = {
  paths: {
    INBOX_URL: '/',
    COMPOSE_URL: '/compose',
    FOLDERS_URL: '/folder',
    THREADS_URL: '/thread'
  },

  // The indices of systemFolders are positive. The
  // actual folder IDs are negative. Remember to invert
  // when needed.
  systemFolders: ['Inbox', 'Sent', 'Drafts', 'Deleted'],

  // An array of objects containing the category name (label) and a
  // value for use with select, radio button inputs.
  messageCategories: [
    {
      label: 'Appointments',
      value: 'APPOINTMENTS'
    },
    {
      label: 'Education',
      value: 'EDUCATION'
    },
    {
      label: 'Medications',
      value: 'MEDICATIONS'
    },
    {
      label: 'Test results',
      value: 'TEST_RESULTS'
    },
    {
      label: 'Other (please add a subject)',
      value: 'OTHER'
    }
  ],

  composeMessage: {
    placeholders: {
      subject: 'Add an additional subject line',
      message: 'Type your message here'
    },
    errors: {
      attachments: {
        tooLarge: {
          title: 'Attachment size limit',
          text: 'The file(s) you are trying to attach exceed the 3MB attachment size limit and the total size of attachments cannot exceed 6MB'
        },
        tooMany: {
          title: 'Attachments limit',
          text: 'You may not attach more than four files.'
        }
      },
      subjectLine: {
        category: 'Please select a category.',
        subject: 'Please add subject description.'
      },
      message: 'Please enter your message.',
      recipient: 'Please select a recipient from your health care team.'
    },
    maxChars: {
      subject: 512
    },
    // Using bytes
    attachments: {
      maxNum: 4,
      maxSingleFile: 3000000,
      maxTotalFiles: 6000000
    }
  },

  allowedMimeTypes: [
    'text/plain',
    'application/pdf',
    'application/msword',
    'application/excel',
    'application/vnd.ms-excel',
    'image/gif',
    'image/jpg',
    'application/rtf',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    'image/png'
  ],

  createNewFolderSettings: {
    maxLength: 50,
    errorMessages: {
      empty: 'Please enter a folder name.',
      exists: 'The folder name you have chosen already exists. Please try another name.',
      patternMismatch: 'Only the letters A through Z, numbers, and spaces are allowed in folder names.'
    }
  }
};
<commit_msg>Removed no longer used paths in config.<commit_after>module.exports = {
  // The indices of systemFolders are positive. The
  // actual folder IDs are negative. Remember to invert
  // when needed.
  systemFolders: ['Inbox', 'Sent', 'Drafts', 'Deleted'],

  // An array of objects containing the category name (label) and a
  // value for use with select, radio button inputs.
  messageCategories: [
    {
      label: 'Appointments',
      value: 'APPOINTMENTS'
    },
    {
      label: 'Education',
      value: 'EDUCATION'
    },
    {
      label: 'Medications',
      value: 'MEDICATIONS'
    },
    {
      label: 'Test results',
      value: 'TEST_RESULTS'
    },
    {
      label: 'Other (please add a subject)',
      value: 'OTHER'
    }
  ],

  composeMessage: {
    placeholders: {
      subject: 'Add an additional subject line',
      message: 'Type your message here'
    },
    errors: {
      attachments: {
        tooLarge: {
          title: 'Attachment size limit',
          text: 'The file(s) you are trying to attach exceed the 3MB attachment size limit and the total size of attachments cannot exceed 6MB'
        },
        tooMany: {
          title: 'Attachments limit',
          text: 'You may not attach more than four files.'
        }
      },
      subjectLine: {
        category: 'Please select a category.',
        subject: 'Please add subject description.'
      },
      message: 'Please enter your message.',
      recipient: 'Please select a recipient from your health care team.'
    },
    maxChars: {
      subject: 512
    },
    // Using bytes
    attachments: {
      maxNum: 4,
      maxSingleFile: 3000000,
      maxTotalFiles: 6000000
    }
  },

  allowedMimeTypes: [
    'text/plain',
    'application/pdf',
    'application/msword',
    'application/excel',
    'application/vnd.ms-excel',
    'image/gif',
    'image/jpg',
    'application/rtf',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    'image/png'
  ],

  createNewFolderSettings: {
    maxLength: 50,
    errorMessages: {
      empty: 'Please enter a folder name.',
      exists: 'The folder name you have chosen already exists. Please try another name.',
      patternMismatch: 'Only the letters A through Z, numbers, and spaces are allowed in folder names.'
    }
  }
};
<|endoftext|>


<commit_before>/**
 *
 * Image Box (JS)
 *
 * @author Takuto Yanagida @ Space-Time Inc.
 * @version 2019-06-11
 *
 */

window.ST = window['ST'] || {};

(function (NS) {

	const TARGET_SELECTOR           = '.stile';
	const TARGET_SELECTOR_IMAGE_BOX = '.stile-image-box';
	const STILE_CLS_IMAGE_BOX       = 'image-box';
	const STILE_STATE_OPEN          = 'open';
	const STILE_STATE_VISIBLE       = 'visible';
	const SIZE_BOX_PADDING          = '4rem';

	NS.addInitializer(7, () => {
		const as1 = document.querySelectorAll(TARGET_SELECTOR + ' a');
		modifyImageAnchorStyle(as1);
		const as2 = document.querySelectorAll(TARGET_SELECTOR_IMAGE_BOX + ' a');
		modifyImageAnchorStyle(as2);
	});

	function modifyImageAnchorStyle(as) {
		const fas = filterImageLink(as);
		for (let i = 0; i < fas.length; i += 1) createBox(fas[i]);
	}

	function filterImageLink(as) {
		const ret = [];
		for (let i = 0; i < as.length; i += 1) {
			const a = as[i];
			if (!NS.containStile(a, 'link-image')) continue;
			const href = a.href;
			if (!href || !isImageUrl(href)) continue;
			const imgs = a.getElementsByTagName('img');
			if (imgs.length === 0) continue;
			const img = imgs[0];
			const src = img.dataset.src ? img.dataset.src : img.src;
			if (!src || !isImageUrl(src)) continue;
			ret.push(a);
		}
		return ret;
	}

	function isImageUrl(url) {
		url = url.split('?')[0];
		url = url.toLowerCase();
		const ss = url.split('.');
		const ext = ss[ss.length - 1];
		switch (ext) {
			case 'jpeg':
			case 'jpg':
			case 'png':
			case 'gif':
				return true;
		}
		return false;
	}

	function createBox(a) {
		const frame = document.createElement('div');
		NS.addStile(frame, STILE_CLS_IMAGE_BOX);
		const img = document.createElement('img');
		img.src = a.href;
		frame.appendChild(img);
		const closeBtn = document.createElement('span');
		frame.appendChild(closeBtn);
		document.body.appendChild(frame);

		a.addEventListener('click', (e) => { onOpen(e, frame, img); });
		frame.addEventListener('click', (e) => { onClose(e, frame); });
		enableTouchGesture(frame, img);

		img.addEventListener('click', (e) => { e.stopPropagation(); });
		frame.addEventListener('mousewheel', (e) => { e.preventDefault(); });
		return frame;
	}

	function onOpen(e, frame, img) {
		e.preventDefault();
		NS.addStile(frame, STILE_STATE_OPEN);
		const isPhone = NS.MEDIA_WIDTH.indexOf('phone') !== -1;
		if (checkLandscape(frame, img)) {
			img.style.minWidth = '';
			img.style.width = isPhone ? '100%' : 'calc(100% - ' + SIZE_BOX_PADDING + ')';
			img.style.height = 'auto';
		} else {
			img.style.minHeight = '';
			img.style.width = 'auto';
			img.style.maxWidth = 'none';
			img.style.height = isPhone ? '100%' : 'calc(100% - ' + SIZE_BOX_PADDING + ')';
		}
		centeringImage(frame, img);
		const delay = NS.BROWSER === 'ie11' ? 30 : 0;
		setTimeout(() => { NS.addStile(frame, STILE_STATE_VISIBLE); }, delay);
	}

	function onClose(e, frame) {
		e.preventDefault();
		NS.removeStile(frame, STILE_STATE_VISIBLE);
		setTimeout(() => { NS.removeStile(frame, STILE_STATE_OPEN); }, 200);
	}

	function enableTouchGesture(frame, img) {
		let isLandscape = true;
		let baseDist = 0;
		let scale = 1;
		let tid;
		let xS = 0, yS = 0;
		let isMoving = false;

		frame.addEventListener('touchstart', (e) => {
			isLandscape = checkLandscape(frame, img);
			baseDist = 0;
			if (!img.style.minWidth && !img.style.minHeight) scale = 1;
			if (e.touches.length === 1) {
				isMoving = true;
				xS = e.touches[0].screenX + frame.scrollLeft;
				yS = e.touches[0].screenY + frame.scrollTop;
			}
		}, true);
		frame.addEventListener('touchmove', (e) => {
			e.stopPropagation();
			e.preventDefault();

			frame.style.overflow = 'scroll';
			const ts = e.changedTouches;

			if (ts.length === 1 && isMoving) {
				frame.scrollLeft = xS - ts[0].screenX;
				frame.scrollTop  = yS - ts[0].screenY;
			} else if (ts.length > 1) {
				isMoving = false;
				const baseSize = isLandscape ? frame.clientWidth : frame.clientHeight;
				const dist = touchDistance(ts);
				const scx = (ts[0].screenX + ts[1].screenX) / 2;
				const scy = (ts[0].screenY + ts[1].screenY) / 2;
				const imgCx = (scx + frame.scrollLeft) / scale;
				const imgCy = (scy + frame.scrollTop)  / scale;

				clearTimeout(tid);

				if (baseDist) {
					const s = dist / (baseDist * scale);
					if (s && s !== Infinity) {
						scale *= s;
						scale = Math.min(4, scale);
						scale = Math.max(1, scale);
						if (isLandscape) {
							img.style.minWidth = (baseSize * scale) + 'px';
						} else {
							img.style.minHeight = (baseSize * scale) + 'px';
						}
						centeringImage(frame, img);

						frame.scrollLeft = imgCx * scale - scx;
						frame.scrollTop  = imgCy * scale - scy;
					}
					tid = setTimeout(() => { baseDist = 0; }, 100);
				} else {
					baseDist = dist / scale;
				}
			}
		}, true);
	}

	function checkLandscape(frame, img) {
		const winAspect = frame.offsetWidth / frame.offsetHeight;
		const imgAspect = img.offsetWidth / img.offsetHeight;
		return (winAspect < imgAspect);
	}

	function centeringImage(frame, img) {
		if (img.offsetWidth < frame.offsetWidth) {
			img.style.left = ((frame.offsetWidth - img.offsetWidth) / 2) + 'px';
		} else {
			img.style.left = 0;
		}
		if (img.offsetHeight < frame.offsetHeight) {
			img.style.top = ((frame.offsetHeight - img.offsetHeight) / 2) + 'px';
		} else {
			img.style.top = 0;
		}
	}

	function touchDistance(ts) {
		const x1 = ts[0].screenX;
		const y1 = ts[0].screenY;
		const x2 = ts[1].screenX;
		const y2 = ts[1].screenY;
		return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
	}

})(window.ST);
<commit_msg>Improve gesture of touch and mouse<commit_after>/**
 *
 * Image Box (JS)
 *
 * @author Takuto Yanagida @ Space-Time Inc.
 * @version 2019-06-11
 *
 */

window.ST = window['ST'] || {};

(function (NS) {

	const TARGET_SELECTOR           = '.stile';
	const TARGET_SELECTOR_IMAGE_BOX = '.stile-image-box';
	const STILE_CLS_IMAGE_BOX       = 'image-box';
	const STILE_STATE_OPEN          = 'open';
	const STILE_STATE_VISIBLE       = 'visible';
	const SIZE_BOX_PADDING          = '4rem';

	NS.addInitializer(7, () => {
		const as1 = document.querySelectorAll(TARGET_SELECTOR + ' a');
		modifyImageAnchorStyle(as1);
		const as2 = document.querySelectorAll(TARGET_SELECTOR_IMAGE_BOX + ' a');
		modifyImageAnchorStyle(as2);
	});

	function modifyImageAnchorStyle(as) {
		const fas = filterImageLink(as);
		for (let i = 0; i < fas.length; i += 1) createBox(fas[i]);
	}

	function filterImageLink(as) {
		const ret = [];
		for (let i = 0; i < as.length; i += 1) {
			const a = as[i];
			if (!NS.containStile(a, 'link-image')) continue;
			const href = a.href;
			if (!href || !isImageUrl(href)) continue;
			const imgs = a.getElementsByTagName('img');
			if (imgs.length === 0) continue;
			const img = imgs[0];
			const src = img.dataset.src ? img.dataset.src : img.src;
			if (!src || !isImageUrl(src)) continue;
			ret.push(a);
		}
		return ret;
	}

	function isImageUrl(url) {
		url = url.split('?')[0];
		url = url.toLowerCase();
		const ss = url.split('.');
		const ext = ss[ss.length - 1];
		switch (ext) {
			case 'jpeg':
			case 'jpg':
			case 'png':
			case 'gif':
				return true;
		}
		return false;
	}

	function createBox(a) {
		const frame = document.createElement('div');
		NS.addStile(frame, STILE_CLS_IMAGE_BOX);
		const img = document.createElement('img');
		img.src = a.href;
		frame.appendChild(img);
		const closeBtn = document.createElement('span');
		frame.appendChild(closeBtn);
		document.body.appendChild(frame);

		a.addEventListener('click', (e) => { onOpen(e, frame, img); });
		frame.addEventListener('click', (e) => { onClose(e, frame); });
		enableTouchGesture(frame, img);

		img.addEventListener('click', (e) => { e.stopPropagation(); });
		enableMouseGesture(frame, img);
		return frame;
	}

	function onOpen(e, frame, img) {
		e.preventDefault();
		NS.addStile(frame, STILE_STATE_OPEN);
		const isPhone = NS.MEDIA_WIDTH.indexOf('phone') !== -1;
		if (checkLandscape(frame, img)) {
			img.style.minWidth = '';
			img.style.width = isPhone ? '100%' : 'calc(100% - ' + SIZE_BOX_PADDING + ')';
			img.style.height = 'auto';
		} else {
			img.style.minHeight = '';
			img.style.width = 'auto';
			img.style.maxWidth = 'none';
			img.style.height = isPhone ? '100%' : 'calc(100% - ' + SIZE_BOX_PADDING + ')';
		}
		centeringImage(frame, img);
		const delay = NS.BROWSER === 'ie11' ? 30 : 0;
		setTimeout(() => { NS.addStile(frame, STILE_STATE_VISIBLE); }, delay);
	}

	function onClose(e, frame) {
		e.preventDefault();
		NS.removeStile(frame, STILE_STATE_VISIBLE);
		setTimeout(() => { NS.removeStile(frame, STILE_STATE_OPEN); }, 200);
	}

	function enableTouchGesture(frame, img) {
		let isLandscape = true;
		let baseDist = 0;
		let scale = 1;
		let tid;
		let xS = 0, yS = 0;

		frame.addEventListener('touchstart', (e) => {
			const ts = e.touches;
			isLandscape = checkLandscape(frame, img);
			baseDist = 0;
			if (!img.style.minWidth && !img.style.minHeight) scale = 1;
			if (ts.length === 1) {
				xS = ts[0].pageX - window.pageXOffset;
				yS = ts[0].pageY - window.pageYOffset;
			} else if (ts.length === 2) {
				xS = (ts[0].pageX + ts[1].pageX) / 2 - window.pageXOffset;
				yS = (ts[0].pageY + ts[1].pageY) / 2 - window.pageYOffset;
			}
		}, true);
		frame.addEventListener('touchmove', (e) => {
			e.stopPropagation();
			e.preventDefault();

			frame.style.overflow = 'scroll';
			const ts = e.touches;

			if (ts.length === 1) {
				const cx = ts[0].pageX - window.pageXOffset;
				const cy = ts[0].pageY - window.pageYOffset;
				frame.scrollLeft += xS - cx;
				frame.scrollTop  += yS - cy;
				xS = cx;
				yS = cy;
			} else if (ts.length > 1) {
				const baseSize = isLandscape ? frame.clientWidth : frame.clientHeight;
				const dist = touchDistance(ts);

				const scx = (ts[0].pageX + ts[1].pageX) / 2 - window.pageXOffset;
				const scy = (ts[0].pageY + ts[1].pageY) / 2 - window.pageYOffset;
				frame.scrollLeft += xS - scx;
				frame.scrollTop  += yS - scy;
				xS = scx;
				yS = scy;

				const imgCx = (scx + frame.scrollLeft) / scale;
				const imgCy = (scy + frame.scrollTop)  / scale;
				clearTimeout(tid);

				if (baseDist) {
					const s = dist / (baseDist * scale);
					if (s && s !== Infinity) {
						scale = Math.max(1, Math.min(4, scale * s));
						if (isLandscape) {
							img.style.minWidth = (baseSize * scale) + 'px';
						} else {
							img.style.minHeight = (baseSize * scale) + 'px';
						}
						centeringImage(frame, img);

						frame.scrollLeft = imgCx * scale - scx;
						frame.scrollTop  = imgCy * scale - scy;
					}
					tid = setTimeout(() => { baseDist = 0; }, 100);
				} else {
					baseDist = dist / scale;
				}
			}
		}, true);
	}

	function enableMouseGesture(frame, img) {
		let isLandscape = true;
		let scale = 1;
		let xS = 0, yS = 0;
		let isMoving = false;

		frame.addEventListener('mousedown', (e) => {
			xS = e.pageX - window.pageXOffset;
			yS = e.pageY - window.pageYOffset;
			isMoving = true;
		});
		frame.addEventListener('mousemove', (e) => {
			if (isMoving) {
				e.stopPropagation();
				e.preventDefault();
				frame.style.overflow = 'hidden';
				const cx = e.pageX - window.pageXOffset;
				const cy = e.pageY - window.pageYOffset;
				frame.scrollLeft += xS - cx;
				frame.scrollTop  += yS - cy;
				xS = cx;
				yS = cy;
			}
		});
		frame.addEventListener('mouseup', () => { isMoving = false; });

		frame.addEventListener('mousewheel', (e) => {
			e.stopPropagation();
			e.preventDefault();

			frame.style.overflow = 'hidden';
			isLandscape = checkLandscape(frame, img);
			const baseSize = isLandscape ? frame.clientWidth : frame.clientHeight;
			if (!img.style.minWidth && !img.style.minHeight) scale = 1;

			const scx = e.pageX - window.pageXOffset;
			const scy = e.pageY - window.pageYOffset;
			const imgCx = (scx + frame.scrollLeft) / scale;
			const imgCy = (scy + frame.scrollTop) / scale;

			const s = 0 > e.deltaY ? 1.1 : 0.9;
			scale = Math.max(1, Math.min(4, scale * s));
			if (isLandscape) {
				img.style.minWidth = (baseSize * scale) + 'px';
			} else {
				img.style.minHeight = (baseSize * scale) + 'px';
			}
			centeringImage(frame, img);

			frame.scrollLeft = imgCx * scale - scx;
			frame.scrollTop = imgCy * scale - scy;
		}, true);
	}

	function checkLandscape(frame, img) {
		const winAspect = frame.offsetWidth / frame.offsetHeight;
		const imgAspect = img.offsetWidth / img.offsetHeight;
		return (winAspect < imgAspect);
	}

	function centeringImage(frame, img) {
		if (img.offsetWidth < frame.offsetWidth) {
			img.style.left = ((frame.offsetWidth - img.offsetWidth) / 2) + 'px';
		} else {
			img.style.left = 0;
		}
		if (img.offsetHeight < frame.offsetHeight) {
			img.style.top = ((frame.offsetHeight - img.offsetHeight) / 2) + 'px';
		} else {
			img.style.top = 0;
		}
	}

	function touchDistance(ts) {
		const x1 = ts[0].screenX;
		const y1 = ts[0].screenY;
		const x2 = ts[1].screenX;
		const y2 = ts[1].screenY;
		return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
	}

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


<commit_before>'use strict';

import Reflux from 'reflux';
import { update } from 'react-addons-update';
import Actions from '../actions/Actions';
import { firebaseUrl } from '../util/constants';

import Firebase from 'firebase';
const baseRef = new Firebase(firebaseUrl);
const usersRef = baseRef.child('users');

const defaultUser = {
    uid: '',
    username: '',
    upvoted: null,
    submitted: null,
    isLoggedIn: false,
    md5hash: null,
    latestPost: null
};

let currentUser = Object.assign({}, defaultUser);

const UserStore = Reflux.createStore({

    listenables: Actions,

    init() {
        // triggered by auth changes
        baseRef.onAuth((authData) => {
            if (!authData) {
                // user is logged out
                usersRef.off();
                this.logoutCompleted();
            } else {
                // user is logged in
                this.loginCompleted(authData.uid);
            }
        });
    },

    logout() {
        baseRef.unauth();
    },

    logoutCompleted() {
        // reset currentUser to default
        currentUser = Object.assign({}, defaultUser);
        this.trigger(currentUser);
    },

    loginCompleted(userId) {
        // get username
        usersRef.child(userId).once('value', (profile) => {
            let {
                username,
                upvoted,
                submitted,
                md5hash
            } = profile.val();

            currentUser = {
                uid: userId,
                username: username,
                upvoted: upvoted,
                submitted: submitted || null,
                md5hash: md5hash,
                isLoggedIn: true,
                latestPost: null
            };

            // watch upvotes
            usersRef.child(`${userId}/upvoted`).on('value', (upvoted) => {
                this.updateUpvoted(upvoted.val());
            });

            // watch submissions
            usersRef.child(`${userId}/submitted`).on('value', (submitted) => {
                let submittedVal = submitted.val();
                this.updateSubmitted(submittedVal ? Object.keys(submittedVal) : []);
            });
        });
    },

    updateLatestPost(postId) {
        currentUser = update(currentUser, {
            latestPost: { $set: postId }
        });

        this.trigger(currentUser);
    },

    updateUpvoted(newUpvoted) {
        currentUser = update(currentUser, {
            upvoted: { $set: newUpvoted }
        });

        this.trigger(currentUser);
    },

    updateSubmitted(newSubmitted) {
        currentUser = update(currentUser, {
            submitted: { $set: newSubmitted || null }
        });

        this.trigger(currentUser);
    },

    getUserId(username) {
        // returns userId given username
        return new Promise((resolve) => {
            usersRef.orderByChild('username').equalTo(username).once('value', (user) => {
                let userId = Object.keys(user.val())[0];
                resolve(userId);
            });
        });
    },

    getDefaultData() {
        return currentUser;
    }
});

export default UserStore;
<commit_msg>fixes #22<commit_after>'use strict';

import Reflux from 'reflux';
import update from 'react-addons-update';
import Actions from '../actions/Actions';
import { firebaseUrl } from '../util/constants';

import Firebase from 'firebase';
const baseRef = new Firebase(firebaseUrl);
const usersRef = baseRef.child('users');

const defaultUser = {
    uid: '',
    username: '',
    upvoted: null,
    submitted: null,
    isLoggedIn: false,
    md5hash: null,
    latestPost: null
};

let currentUser = Object.assign({}, defaultUser);

const UserStore = Reflux.createStore({

    listenables: Actions,

    init() {
        // triggered by auth changes
        baseRef.onAuth((authData) => {
            if (!authData) {
                // user is logged out
                usersRef.off();
                this.logoutCompleted();
            } else {
                // user is logged in
                this.loginCompleted(authData.uid);
            }
        });
    },

    logout() {
        baseRef.unauth();
    },

    logoutCompleted() {
        // reset currentUser to default
        currentUser = Object.assign({}, defaultUser);
        this.trigger(currentUser);
    },

    loginCompleted(userId) {
        // get username
        usersRef.child(userId).once('value', (profile) => {
            let {
                username,
                upvoted,
                submitted,
                md5hash
            } = profile.val();

            currentUser = {
                uid: userId,
                username: username,
                upvoted: upvoted,
                submitted: submitted || null,
                md5hash: md5hash,
                isLoggedIn: true,
                latestPost: null
            };

            // watch upvotes
            usersRef.child(`${userId}/upvoted`).on('value', (upvoted) => {
                this.updateUpvoted(upvoted.val());
            });

            // watch submissions
            usersRef.child(`${userId}/submitted`).on('value', (submitted) => {
                let submittedVal = submitted.val();
                this.updateSubmitted(submittedVal ? Object.keys(submittedVal) : []);
            });
        });
    },

    updateLatestPost(postId) {
        currentUser = update(currentUser, {
            latestPost: { $set: postId }
        });

        this.trigger(currentUser);
    },

    updateUpvoted(newUpvoted) {
        currentUser = update(currentUser, {
            upvoted: { $set: newUpvoted }
        });

        this.trigger(currentUser);
    },

    updateSubmitted(newSubmitted) {
        currentUser = update(currentUser, {
            submitted: { $set: newSubmitted || null }
        });

        this.trigger(currentUser);
    },

    getUserId(username) {
        // returns userId given username
        return new Promise((resolve) => {
            usersRef.orderByChild('username').equalTo(username).once('value', (user) => {
                let userId = Object.keys(user.val())[0];
                resolve(userId);
            });
        });
    },

    getDefaultData() {
        return currentUser;
    }
});

export default UserStore;
<|endoftext|>


<commit_before>/* @flow */
/* eslint-disable no-use-before-define */

import { TypeComposer, InputTypeComposer } from 'graphql-compose';
import {
  GraphQLNonNull,
  GraphQLInputObjectType,
  GraphQLList,
  getNamedType,
} from 'graphql';
import getIndexesFromModel from '../../utils/getIndexesFromModel';
import { toDottedObject, upperFirst } from '../../utils';
import type {
  GraphQLFieldConfigArgumentMap,
  ExtendedResolveParams,
  MongooseModelT,
  filterHelperArgsOpts,
  filterOperatorsOpts,
  filterOperatorNames,
} from '../../definition';

export const OPERATORS_FIELDNAME = '_operators';

export const filterHelperArgs = (
  typeComposer: TypeComposer,
  model: MongooseModelT,
  opts: filterHelperArgsOpts
): GraphQLFieldConfigArgumentMap => {
  if (!(typeComposer instanceof TypeComposer)) {
    throw new Error('First arg for filterHelperArgs() should be instance of TypeComposer.');
  }

  if (!model || !model.modelName || !model.schema) {
    throw new Error(
      'Second arg for filterHelperArgs() should be instance of MongooseModel.'
    );
  }

  if (!opts || !opts.filterTypeName) {
    throw new Error('You should provide non-empty `filterTypeName` in options.');
  }

  const removeFields = [];
  if (opts.removeFields) {
    if (Array.isArray(opts.removeFields)) {
      removeFields.push(...opts.removeFields);
    } else {
      removeFields.push(opts.removeFields);
    }
  }

  if (opts.onlyIndexed) {
    const indexedFieldNames = getIndexedFieldNames(model);
    Object.keys(typeComposer.getFields()).forEach(fieldName => {
      if (indexedFieldNames.indexOf(fieldName) === -1) {
        removeFields.push(fieldName);
      }
    });
  }

  const filterTypeName: string = opts.filterTypeName;
  const inputComposer = typeComposer.getInputTypeComposer().clone(filterTypeName);
  inputComposer.removeField(removeFields);

  if (opts.requiredFields) {
    inputComposer.makeFieldsRequired(opts.requiredFields);
  }

  if (!opts.hasOwnProperty('operators') || opts.operators !== false) {
    addFieldsWithOperator(
      // $FlowFixMe
      `Operators${opts.filterTypeName}`,
      inputComposer,
      model,
      opts.operators || {}
    );
  }

  return {
    filter: {
      name: 'filter',
      type: opts.isRequired
        ? new GraphQLNonNull(inputComposer.getType())
        : inputComposer.getType(),
      description: opts.onlyIndexed
        ? 'Filter only by indexed fields'
        : 'Filter by fields',
    },
  };
};

export function filterHelper(resolveParams: ExtendedResolveParams): void {
  const filter = resolveParams.args && resolveParams.args.filter;
  if (filter && typeof filter === 'object' && Object.keys(filter).length > 0) {
    if (!filter[OPERATORS_FIELDNAME]) {
      resolveParams.query = resolveParams.query.where(toDottedObject(filter)); // eslint-disable-line
    } else {
      const operatorFields = Object.assign({}, filter[OPERATORS_FIELDNAME]);
      const simpleFields = Object.assign({}, filter);
      delete simpleFields[OPERATORS_FIELDNAME];

      if (Object.keys(simpleFields).length > 0) {
        resolveParams.query = resolveParams.query.where(toDottedObject(simpleFields)); // eslint-disable-line
      }
      Object.keys(operatorFields).forEach(fieldName => {
        const fieldOperators = Object.assign({}, operatorFields[fieldName]);
        const criteria = {};
        Object.keys(fieldOperators).forEach(operatorName => {
          criteria[`$${operatorName}`] = fieldOperators[operatorName];
        });
        if (Object.keys(criteria).length > 0) {
          // $FlowFixMe
          resolveParams.query = resolveParams.query.find({ // eslint-disable-line
            [fieldName]: criteria,
          });
        }
      });
    }
  }
}

export function getIndexedFieldNames(model: MongooseModelT): string[] {
  const indexes = getIndexesFromModel(model);

  const fieldNames = [];
  indexes.forEach((indexData) => {
    const keys = Object.keys(indexData);
    fieldNames.push(keys[0]);
  });

  return fieldNames;
}

export function addFieldsWithOperator(
  typeName: string,
  inputComposer: InputTypeComposer,
  model: MongooseModelT,
  operatorsOpts: filterOperatorsOpts
) {
  const operatorsComposer = new InputTypeComposer(new GraphQLInputObjectType({
    name: typeName,
    fields: {},
  }));

  const availableOperators: filterOperatorNames[]
    = ['gt', 'gte', 'lt', 'lte', 'ne', 'in[]', 'nin[]'];

  // if `opts.resolvers.[resolverName].filter.operators` is empty and not disabled via `false`
  // then fill it up with indexed fields
  const indexedFields = getIndexedFieldNames(model);
  if (operatorsOpts !== false && Object.keys(operatorsOpts).length === 0) {
    indexedFields.forEach(fieldName => {
      operatorsOpts[fieldName] = availableOperators; // eslint-disable-line
    });
  }

  const existedFields = inputComposer.getFields();
  Object.keys(existedFields).forEach(fieldName => {
    if (operatorsOpts[fieldName] && operatorsOpts[fieldName] !== false) {
      const fields = {};
      let operators;
      if (operatorsOpts[fieldName] && Array.isArray(operatorsOpts[fieldName])) {
        operators = operatorsOpts[fieldName];
      } else {
        operators = availableOperators;
      }
      operators.forEach(operatorName => {
        // unwrap from GraphQLNonNull and GraphQLList, if present
        const namedType = getNamedType(existedFields[fieldName].type);
        if (namedType) {
          if (operatorName.slice(-2) === '[]') {
            // wrap with GraphQLList, if operator required this with `[]`
            const newName = operatorName.slice(0, -2);
            fields[newName] = {
              ...existedFields[fieldName],
              type: new GraphQLList(namedType),
            };
          } else {
            fields[operatorName] = {
              ...existedFields[fieldName],
              type: namedType,
            };
          }
        }
      });
      if (Object.keys(fields).length > 0) {
        operatorsComposer.addField(fieldName, {
          type: new GraphQLInputObjectType({
            name: `${upperFirst(fieldName)}${typeName}`,
            fields,
          }),
          description: 'Filter value by operator(s)',
        });
      }
    }
  });

  if (Object.keys(operatorsComposer.getFields()).length > 0) {
    inputComposer.addField(OPERATORS_FIELDNAME, {
      type: operatorsComposer.getType(),
      description: 'List of fields that can be filtered via operators',
    });
  }
}
<commit_msg>fix filterHelper, it does not return `filter` arg, if its fields are empty<commit_after>/* @flow */
/* eslint-disable no-use-before-define */

import { TypeComposer, InputTypeComposer } from 'graphql-compose';
import {
  GraphQLNonNull,
  GraphQLInputObjectType,
  GraphQLList,
  getNamedType,
} from 'graphql';
import getIndexesFromModel from '../../utils/getIndexesFromModel';
import { toDottedObject, upperFirst } from '../../utils';
import type {
  GraphQLFieldConfigArgumentMap,
  ExtendedResolveParams,
  MongooseModelT,
  filterHelperArgsOpts,
  filterOperatorsOpts,
  filterOperatorNames,
} from '../../definition';

export const OPERATORS_FIELDNAME = '_operators';

export const filterHelperArgs = (
  typeComposer: TypeComposer,
  model: MongooseModelT,
  opts: filterHelperArgsOpts
): GraphQLFieldConfigArgumentMap => {
  if (!(typeComposer instanceof TypeComposer)) {
    throw new Error('First arg for filterHelperArgs() should be instance of TypeComposer.');
  }

  if (!model || !model.modelName || !model.schema) {
    throw new Error(
      'Second arg for filterHelperArgs() should be instance of MongooseModel.'
    );
  }

  if (!opts || !opts.filterTypeName) {
    throw new Error('You should provide non-empty `filterTypeName` in options.');
  }

  const removeFields = [];
  if (opts.removeFields) {
    if (Array.isArray(opts.removeFields)) {
      removeFields.push(...opts.removeFields);
    } else {
      removeFields.push(opts.removeFields);
    }
  }

  if (opts.onlyIndexed) {
    const indexedFieldNames = getIndexedFieldNames(model);
    Object.keys(typeComposer.getFields()).forEach(fieldName => {
      if (indexedFieldNames.indexOf(fieldName) === -1) {
        removeFields.push(fieldName);
      }
    });
  }

  const filterTypeName: string = opts.filterTypeName;
  const inputComposer = typeComposer.getInputTypeComposer().clone(filterTypeName);
  inputComposer.removeField(removeFields);

  if (opts.requiredFields) {
    inputComposer.makeFieldsRequired(opts.requiredFields);
  }

  if (!opts.hasOwnProperty('operators') || opts.operators !== false) {
    addFieldsWithOperator(
      // $FlowFixMe
      `Operators${opts.filterTypeName}`,
      inputComposer,
      model,
      opts.operators || {}
    );
  }

  if (inputComposer.getFieldNames().length === 0) {
    return {};
  }

  return {
    filter: {
      name: 'filter',
      type: opts.isRequired
        ? new GraphQLNonNull(inputComposer.getType())
        : inputComposer.getType(),
      description: opts.onlyIndexed
        ? 'Filter only by indexed fields'
        : 'Filter by fields',
    },
  };
};

export function filterHelper(resolveParams: ExtendedResolveParams): void {
  const filter = resolveParams.args && resolveParams.args.filter;
  if (filter && typeof filter === 'object' && Object.keys(filter).length > 0) {
    if (!filter[OPERATORS_FIELDNAME]) {
      resolveParams.query = resolveParams.query.where(toDottedObject(filter)); // eslint-disable-line
    } else {
      const operatorFields = Object.assign({}, filter[OPERATORS_FIELDNAME]);
      const simpleFields = Object.assign({}, filter);
      delete simpleFields[OPERATORS_FIELDNAME];

      if (Object.keys(simpleFields).length > 0) {
        resolveParams.query = resolveParams.query.where(toDottedObject(simpleFields)); // eslint-disable-line
      }
      Object.keys(operatorFields).forEach(fieldName => {
        const fieldOperators = Object.assign({}, operatorFields[fieldName]);
        const criteria = {};
        Object.keys(fieldOperators).forEach(operatorName => {
          criteria[`$${operatorName}`] = fieldOperators[operatorName];
        });
        if (Object.keys(criteria).length > 0) {
          // $FlowFixMe
          resolveParams.query = resolveParams.query.find({ // eslint-disable-line
            [fieldName]: criteria,
          });
        }
      });
    }
  }
}

export function getIndexedFieldNames(model: MongooseModelT): string[] {
  const indexes = getIndexesFromModel(model);

  const fieldNames = [];
  indexes.forEach((indexData) => {
    const keys = Object.keys(indexData);
    fieldNames.push(keys[0]);
  });

  return fieldNames;
}

export function addFieldsWithOperator(
  typeName: string,
  inputComposer: InputTypeComposer,
  model: MongooseModelT,
  operatorsOpts: filterOperatorsOpts
) {
  const operatorsComposer = new InputTypeComposer(new GraphQLInputObjectType({
    name: typeName,
    fields: {},
  }));

  const availableOperators: filterOperatorNames[]
    = ['gt', 'gte', 'lt', 'lte', 'ne', 'in[]', 'nin[]'];

  // if `opts.resolvers.[resolverName].filter.operators` is empty and not disabled via `false`
  // then fill it up with indexed fields
  const indexedFields = getIndexedFieldNames(model);
  if (operatorsOpts !== false && Object.keys(operatorsOpts).length === 0) {
    indexedFields.forEach(fieldName => {
      operatorsOpts[fieldName] = availableOperators; // eslint-disable-line
    });
  }

  const existedFields = inputComposer.getFields();
  Object.keys(existedFields).forEach(fieldName => {
    if (operatorsOpts[fieldName] && operatorsOpts[fieldName] !== false) {
      const fields = {};
      let operators;
      if (operatorsOpts[fieldName] && Array.isArray(operatorsOpts[fieldName])) {
        operators = operatorsOpts[fieldName];
      } else {
        operators = availableOperators;
      }
      operators.forEach(operatorName => {
        // unwrap from GraphQLNonNull and GraphQLList, if present
        const namedType = getNamedType(existedFields[fieldName].type);
        if (namedType) {
          if (operatorName.slice(-2) === '[]') {
            // wrap with GraphQLList, if operator required this with `[]`
            const newName = operatorName.slice(0, -2);
            fields[newName] = {
              ...existedFields[fieldName],
              type: new GraphQLList(namedType),
            };
          } else {
            fields[operatorName] = {
              ...existedFields[fieldName],
              type: namedType,
            };
          }
        }
      });
      if (Object.keys(fields).length > 0) {
        operatorsComposer.addField(fieldName, {
          type: new GraphQLInputObjectType({
            name: `${upperFirst(fieldName)}${typeName}`,
            fields,
          }),
          description: 'Filter value by operator(s)',
        });
      }
    }
  });

  if (Object.keys(operatorsComposer.getFields()).length > 0) {
    inputComposer.addField(OPERATORS_FIELDNAME, {
      type: operatorsComposer.getType(),
      description: 'List of fields that can be filtered via operators',
    });
  }
}
<|endoftext|>


<commit_before>/*
 Language: jboss-cli
 Author: Raphaël Parrëe <rparree@edc4it.com>
 Description: language definition jboss cli
 Category: config
 */

function (hljs) {
  var PARAM = {
    className: "attribute",
    begin: /[\w-]+ *=/,
    excludeEnd: true,
    relevance: -1
  };
  var PARAMSBLOCK = {
    className: 'params',
    begin: /\(/,
    end: /\)/,
    contains: [PARAM],
    relevance : 0
  };
  var OPERATION = {
    className: 'function',
    begin: /:[\w\-.]+/,
    relevance: 0
  };
  var PATH = {
    className: 'symbol',
    begin: /\B(([\/.])[\w\-.\/=]+)+/,
  };
  var COMMAND_PARAMS = {
    className: 'params',
    begin: /--[\w\-=\/]+/,
  };
  return {
    aliases: ['wildfly-cli'],
    lexemes: '[a-z\-]+',
    keywords: {
      keyword: 'alias batch cd clear command connect connection-factory connection-info data-source deploy ' +
      'deployment-info deployment-overlay echo echo-dmr help history if jdbc-driver-info jms-queue|20 jms-topic|20 ls ' +
      'patch pwd quit read-attribute read-operation reload rollout-plan run-batch set shutdown try unalias ' +
      'undeploy unset version xa-data-source', // module
      literal: 'true false'
    },
    contains: [
      hljs.HASH_COMMENT_MODE,
      hljs.QUOTE_STRING_MODE,
      COMMAND_PARAMS,
      OPERATION,
      PATH,
      PARAMSBLOCK
    ]
  }
}
<commit_msg>Tweaks to jboss-cli<commit_after>/*
 Language: jboss-cli
 Author: Raphaël Parrëe <rparree@edc4it.com>
 Description: language definition jboss cli
 Category: config
 */

function (hljs) {
  var PARAM = {
    begin: /[\w-]+ *=/, returnBegin: true,
    relevance: 0,
    contains: [{className: 'attr', begin: /[\w-]+/}]
  };
  var PARAMSBLOCK = {
    className: 'params',
    begin: /\(/,
    end: /\)/,
    contains: [PARAM],
    relevance : 0
  };
  var OPERATION = {
    className: 'function',
    begin: /:[\w\-.]+/,
    relevance: 0
  };
  var PATH = {
    className: 'string',
    begin: /\B(([\/.])[\w\-.\/=]+)+/,
  };
  var COMMAND_PARAMS = {
    className: 'params',
    begin: /--[\w\-=\/]+/,
  };
  return {
    aliases: ['wildfly-cli'],
    lexemes: '[a-z\-]+',
    keywords: {
      keyword: 'alias batch cd clear command connect connection-factory connection-info data-source deploy ' +
      'deployment-info deployment-overlay echo echo-dmr help history if jdbc-driver-info jms-queue|20 jms-topic|20 ls ' +
      'patch pwd quit read-attribute read-operation reload rollout-plan run-batch set shutdown try unalias ' +
      'undeploy unset version xa-data-source', // module
      literal: 'true false'
    },
    contains: [
      hljs.HASH_COMMENT_MODE,
      hljs.QUOTE_STRING_MODE,
      COMMAND_PARAMS,
      OPERATION,
      PATH,
      PARAMSBLOCK
    ]
  }
}
<|endoftext|>


<commit_before>/*
 * @class Marker
 * @inherits Interactive layer
 * @aka L.Marker
 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 *
 * @example
 *
 * ```js
 * L.marker([50.5, 30.5]).addTo(map);
 * ```
 */

L.Marker = L.Layer.extend({

	// @section
	// @aka Marker options
	options: {
		// @option icon: Icon = *
		// Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used.
		icon: new L.Icon.Default(),

		// Option inherited from "Interactive layer" abstract class
		interactive: true,

		// @option draggable: Boolean = false
		// Whether the marker is draggable with mouse/touch or not.
		draggable: false,

		// @option keyboard: Boolean = true
		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
		keyboard: true,

		// @option title: String = ''
		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
		title: '',

		// @option alt: String = ''
		// Text for the `alt` attribute of the icon image (useful for accessibility).
		alt: '',

		// @option zIndexOffset: Number = 0
		// By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
		zIndexOffset: 0,

		// @option opacity: Number = 1.0
		// The opacity of the marker.
		opacity: 1,

		// @option riseOnHover: Boolean = false
		// If `true`, the marker will get on top of others when you hover the mouse over it.
		riseOnHover: false,

		// @option riseOffset: Number = 250
		// The z-index offset used for the `riseOnHover` feature.
		riseOffset: 250,

		// @option pane: String = 'markerPane'
		// `Map pane` where the markers icon will be added.
		pane: 'markerPane',

		// FIXME: shadowPane is no longer a valid option
		nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
	},

	/* @section
	 *
	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
	 */

	initialize: function (latlng, options) {
		L.setOptions(this, options);
		this._latlng = L.latLng(latlng);
	},

	onAdd: function (map) {
		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;

		if (this._zoomAnimated) {
			map.on('zoomanim', this._animateZoom, this);
		}

		this._initIcon();
		this.update();
	},

	onRemove: function (map) {
		if (this.dragging && this.dragging.enabled()) {
			this.options.draggable = true;
			this.dragging.removeHooks();
		}

		if (this._zoomAnimated) {
			map.off('zoomanim', this._animateZoom, this);
		}

		this._removeIcon();
		this._removeShadow();
	},

	getEvents: function () {
		return {
			zoom: this.update,
			viewreset: this.update,
			rotate: this.update
		};
	},

	// @method getLatLng: LatLng
	// Returns the current geographical position of the marker.
	getLatLng: function () {
		return this._latlng;
	},

	// @method setLatLng(latlng: LatLng): this
	// Changes the marker position to the given point.
	setLatLng: function (latlng) {
		var oldLatLng = this._latlng;
		this._latlng = L.latLng(latlng);
		this.update();

		// @event move: Event
		// Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
	},

	// @method setZIndexOffset(offset: Number): this
	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
	setZIndexOffset: function (offset) {
		this.options.zIndexOffset = offset;
		return this.update();
	},

	// @method setIcon(icon: Icon): this
	// Changes the marker icon.
	setIcon: function (icon) {

		this.options.icon = icon;

		if (this._map) {
			this._initIcon();
			this.update();
		}

		if (this._popup) {
			this.bindPopup(this._popup, this._popup.options);
		}

		return this;
	},

	getElement: function () {
		return this._icon;
	},

	update: function () {

		if (this._icon) {
			var pos = this._map.latLngToLayerPoint(this._latlng).round();
			this._setPos(pos);
		}

		return this;
	},

	_initIcon: function () {
		var options = this.options,
		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');

		var icon = options.icon.createIcon(this._icon),
		    addIcon = false;

		// if we're not reusing the icon, remove the old one and init new one
		if (icon !== this._icon) {
			if (this._icon) {
				this._removeIcon();
			}
			addIcon = true;

			if (options.title) {
				icon.title = options.title;
			}
			if (options.alt) {
				icon.alt = options.alt;
			}
		}

		L.DomUtil.addClass(icon, classToAdd);

		if (options.keyboard) {
			icon.tabIndex = '0';
		}

		this._icon = icon;

		if (options.riseOnHover) {
			this.on({
				mouseover: this._bringToFront,
				mouseout: this._resetZIndex
			});
		}

		var newShadow = options.icon.createShadow(this._shadow),
		    addShadow = false;

		if (newShadow !== this._shadow) {
			this._removeShadow();
			addShadow = true;
		}

		if (newShadow) {
			L.DomUtil.addClass(newShadow, classToAdd);
		}
		this._shadow = newShadow;

		if (options.opacity < 1) {
			this._updateOpacity();
		}

		if (addIcon) {
			this.getPane().appendChild(this._icon);
		}
		this._initInteraction();
		if (newShadow && addShadow) {
			this.getPane('shadowPane').appendChild(this._shadow);
		}
	},

	_removeIcon: function () {
		if (this.options.riseOnHover) {
			this.off({
				mouseover: this._bringToFront,
				mouseout: this._resetZIndex
			});
		}

		L.DomUtil.remove(this._icon);
		this.removeInteractiveTarget(this._icon);

		this._icon = null;
	},

	_removeShadow: function () {
		if (this._shadow) {
			L.DomUtil.remove(this._shadow);
		}
		this._shadow = null;
	},

	_setPos: function (pos) {
		var iconAnchor = this.options.icon.options.iconAnchor || new L.Point(0, 0);
		if (this._map._rotate) {
			L.DomUtil.setPosition(this._icon, pos, -this._map._bearing || 0, pos.add(iconAnchor));
		} else {
			L.DomUtil.setPosition(this._icon, pos);
		}

		if (this._shadow) {
			if (this._map._rotate) {
				if (this.options.icon.options.shadowAnchor){
					L.DomUtil.setPosition(this._shadow, pos, -this._map._bearing || 0, pos.add(this.options.icon.options.shadowAnchor));
				} else {
					L.DomUtil.setPosition(this._shadow, pos, -this._map._bearing || 0, pos.add(iconAnchor));
				}
			} else {
				L.DomUtil.setPosition(this._shadow, pos);
			}
		}

		this._zIndex = pos.y + this.options.zIndexOffset;

		this._resetZIndex();
	},

	_updateZIndex: function (offset) {
		this._icon.style.zIndex = this._zIndex + offset;
	},

	_animateZoom: function (opt) {
		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();

		this._setPos(pos);
	},

	_initInteraction: function () {

		if (!this.options.interactive) { return; }

		L.DomUtil.addClass(this._icon, 'leaflet-interactive');

		this.addInteractiveTarget(this._icon);

		if (L.Handler.MarkerDrag) {
			var draggable = this.options.draggable;
			if (this.dragging) {
				draggable = this.dragging.enabled();
				this.dragging.disable();
			}

			this.dragging = new L.Handler.MarkerDrag(this);

			if (draggable) {
				this.dragging.enable();
			}
		}
	},

	// @method setOpacity(opacity: Number): this
	// Changes the opacity of the marker.
	setOpacity: function (opacity) {
		this.options.opacity = opacity;
		if (this._map) {
			this._updateOpacity();
		}

		return this;
	},

	_updateOpacity: function () {
		var opacity = this.options.opacity;

		L.DomUtil.setOpacity(this._icon, opacity);

		if (this._shadow) {
			L.DomUtil.setOpacity(this._shadow, opacity);
		}
	},

	_bringToFront: function () {
		this._updateZIndex(this.options.riseOffset);
	},

	_resetZIndex: function () {
		this._updateZIndex(0);
	}
});

// factory L.marker(latlng: LatLng, options? : Marker options)

// @factory L.marker(latlng: LatLng, options? : Marker options)
// Instantiates a Marker object given a geographical point and optionally an options object.
L.marker = function (latlng, options) {
	return new L.Marker(latlng, options);
};
<commit_msg>optional_marker_rotate: markers will by default remain upright, but that should be able to be overriden by specifying the markerRotate property to false.<commit_after>/*
 * @class Marker
 * @inherits Interactive layer
 * @aka L.Marker
 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 *
 * @example
 *
 * ```js
 * L.marker([50.5, 30.5]).addTo(map);
 * ```
 */

L.Marker = L.Layer.extend({

	// @section
	// @aka Marker options
	options: {
		// @option icon: Icon = *
		// Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used.
		icon: new L.Icon.Default(),

		// Option inherited from "Interactive layer" abstract class
		interactive: true,

		// @option draggable: Boolean = false
		// Whether the marker is draggable with mouse/touch or not.
		draggable: false,

		// @option keyboard: Boolean = true
		// Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
		keyboard: true,

		// @option title: String = ''
		// Text for the browser tooltip that appear on marker hover (no tooltip by default).
		title: '',

		// @option alt: String = ''
		// Text for the `alt` attribute of the icon image (useful for accessibility).
		alt: '',

		// @option zIndexOffset: Number = 0
		// By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
		zIndexOffset: 0,

		// @option opacity: Number = 1.0
		// The opacity of the marker.
		opacity: 1,

		// @option riseOnHover: Boolean = false
		// If `true`, the marker will get on top of others when you hover the mouse over it.
		riseOnHover: false,

		// @option riseOffset: Number = 250
		// The z-index offset used for the `riseOnHover` feature.
		riseOffset: 250,

		// @option pane: String = 'markerPane'
		// `Map pane` where the markers icon will be added.
		pane: 'markerPane',

		// FIXME: shadowPane is no longer a valid option
		nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],

		// @option markerRotate: Boolean = true
		// Whether the marker remains upright when rotating the map
		markerRotate: true
	},

	/* @section
	 *
	 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
	 */

	initialize: function (latlng, options) {
		L.setOptions(this, options);
		this._latlng = L.latLng(latlng);
	},

	onAdd: function (map) {
		this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;

		if (this._zoomAnimated) {
			map.on('zoomanim', this._animateZoom, this);
		}

		this._initIcon();
		this.update();
	},

	onRemove: function (map) {
		if (this.dragging && this.dragging.enabled()) {
			this.options.draggable = true;
			this.dragging.removeHooks();
		}

		if (this._zoomAnimated) {
			map.off('zoomanim', this._animateZoom, this);
		}

		this._removeIcon();
		this._removeShadow();
	},

	getEvents: function () {
		return {
			zoom: this.update,
			viewreset: this.update,
			rotate: this.update
		};
	},

	// @method getLatLng: LatLng
	// Returns the current geographical position of the marker.
	getLatLng: function () {
		return this._latlng;
	},

	// @method setLatLng(latlng: LatLng): this
	// Changes the marker position to the given point.
	setLatLng: function (latlng) {
		var oldLatLng = this._latlng;
		this._latlng = L.latLng(latlng);
		this.update();

		// @event move: Event
		// Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
	},

	// @method setZIndexOffset(offset: Number): this
	// Changes the [zIndex offset](#marker-zindexoffset) of the marker.
	setZIndexOffset: function (offset) {
		this.options.zIndexOffset = offset;
		return this.update();
	},

	// @method setIcon(icon: Icon): this
	// Changes the marker icon.
	setIcon: function (icon) {

		this.options.icon = icon;

		if (this._map) {
			this._initIcon();
			this.update();
		}

		if (this._popup) {
			this.bindPopup(this._popup, this._popup.options);
		}

		return this;
	},

	getElement: function () {
		return this._icon;
	},

	update: function () {

		if (this._icon) {
			var pos = this._map.latLngToLayerPoint(this._latlng).round();
			this._setPos(pos);
		}

		return this;
	},

	_initIcon: function () {
		var options = this.options,
		    classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');

		var icon = options.icon.createIcon(this._icon),
		    addIcon = false;

		// if we're not reusing the icon, remove the old one and init new one
		if (icon !== this._icon) {
			if (this._icon) {
				this._removeIcon();
			}
			addIcon = true;

			if (options.title) {
				icon.title = options.title;
			}
			if (options.alt) {
				icon.alt = options.alt;
			}
		}

		L.DomUtil.addClass(icon, classToAdd);

		if (options.keyboard) {
			icon.tabIndex = '0';
		}

		this._icon = icon;

		if (options.riseOnHover) {
			this.on({
				mouseover: this._bringToFront,
				mouseout: this._resetZIndex
			});
		}

		var newShadow = options.icon.createShadow(this._shadow),
		    addShadow = false;

		if (newShadow !== this._shadow) {
			this._removeShadow();
			addShadow = true;
		}

		if (newShadow) {
			L.DomUtil.addClass(newShadow, classToAdd);
		}
		this._shadow = newShadow;

		if (options.opacity < 1) {
			this._updateOpacity();
		}

		if (addIcon) {
			this.getPane().appendChild(this._icon);
		}
		this._initInteraction();
		if (newShadow && addShadow) {
			this.getPane('shadowPane').appendChild(this._shadow);
		}
	},

	_removeIcon: function () {
		if (this.options.riseOnHover) {
			this.off({
				mouseover: this._bringToFront,
				mouseout: this._resetZIndex
			});
		}

		L.DomUtil.remove(this._icon);
		this.removeInteractiveTarget(this._icon);

		this._icon = null;
	},

	_removeShadow: function () {
		if (this._shadow) {
			L.DomUtil.remove(this._shadow);
		}
		this._shadow = null;
	},

	_setPos: function (pos) {
		var iconAnchor = this.options.icon.options.iconAnchor || new L.Point(0, 0);
		if (this._map._rotate && this.options.markerRotate) {
			L.DomUtil.setPosition(this._icon, pos, -this._map._bearing || 0, pos.add(iconAnchor));
		} else {
			L.DomUtil.setPosition(this._icon, pos);
		}

		if (this._shadow) {
			if (this._map._rotate && this.options.markerRotate) {
				if (this.options.icon.options.shadowAnchor){
					L.DomUtil.setPosition(this._shadow, pos, -this._map._bearing || 0, pos.add(this.options.icon.options.shadowAnchor));
				} else {
					L.DomUtil.setPosition(this._shadow, pos, -this._map._bearing || 0, pos.add(iconAnchor));
				}
			} else {
				L.DomUtil.setPosition(this._shadow, pos);
			}
		}

		this._zIndex = pos.y + this.options.zIndexOffset;

		this._resetZIndex();
	},

	_updateZIndex: function (offset) {
		this._icon.style.zIndex = this._zIndex + offset;
	},

	_animateZoom: function (opt) {
		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();

		this._setPos(pos);
	},

	_initInteraction: function () {

		if (!this.options.interactive) { return; }

		L.DomUtil.addClass(this._icon, 'leaflet-interactive');

		this.addInteractiveTarget(this._icon);

		if (L.Handler.MarkerDrag) {
			var draggable = this.options.draggable;
			if (this.dragging) {
				draggable = this.dragging.enabled();
				this.dragging.disable();
			}

			this.dragging = new L.Handler.MarkerDrag(this);

			if (draggable) {
				this.dragging.enable();
			}
		}
	},

	// @method setOpacity(opacity: Number): this
	// Changes the opacity of the marker.
	setOpacity: function (opacity) {
		this.options.opacity = opacity;
		if (this._map) {
			this._updateOpacity();
		}

		return this;
	},

	_updateOpacity: function () {
		var opacity = this.options.opacity;

		L.DomUtil.setOpacity(this._icon, opacity);

		if (this._shadow) {
			L.DomUtil.setOpacity(this._shadow, opacity);
		}
	},

	_bringToFront: function () {
		this._updateZIndex(this.options.riseOffset);
	},

	_resetZIndex: function () {
		this._updateZIndex(0);
	}
});

// factory L.marker(latlng: LatLng, options? : Marker options)

// @factory L.marker(latlng: LatLng, options? : Marker options)
// Instantiates a Marker object given a geographical point and optionally an options object.
L.marker = function (latlng, options) {
	return new L.Marker(latlng, options);
};
<|endoftext|>


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

  if (window.location.hash !== '#debug') {
    try {
      chrome.runtime.onMessage.addListener(request => {
        if (request.action === 'display' && request.data && request.schema) {
          showData(request.data, request.schema);
        }
      });
    } catch (e) {
      console.log('Chrome API not found. Loading outside of extension context with example data.');
      loadExample('yelp');
    }
  } else {
    loadExample('yelp');
  }

  function loadExample(scriptName) {
    let script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = `examples/${scriptName}.js`;
    script.onload = showExampleData;
    document.body.appendChild(script);
  }

  function makeRenderFunction(rowName) {
    return (val, type, row) => {
      if (row[rowName])
        return `<a href="${row[rowName]}">${val}</a>`;
      else
        return val;
    };
  }

  function showExampleData() {
    showData(exampleData, exampleSchema);
  }

  // Message object properties:
  // text: 'text'
  // type: 'warning' | 'success' | 'error' (will be added as CSS class name)
  // retry: Whether we're showing the same message again (triggers animation)
  function showMessage(msg) {
    if (!msg.retry) {
      $('#messages').empty();
      $('#messages').append(`<div class="${msg.type} message">${msg.text}</div>`);
      $('#messages').show();
    }
    else {
      $('#messages').fadeOut().fadeIn();
    }
  }

  // retry: Whether this was user-triggered.
  function checkLoginStatus(retry) {
    $.get('http://freeyourstuff.cc/api/loginstatus').done(res => {
      // Hide previous warning messages
      $('#messages').hide();
      if (typeof res == 'object' && res.loggedIn) {
        $('#publish').show();
        $('#signedout').hide();
        clearInterval(window.interval);
      } else {
        $('#signedout').show();
        $('#publish').hide();
      }
    }).fail(err => {
      showMessage({
        text: `We can't reach freeyourstuff.cc right now, so you can't publish your data. Sorry! You can still download it or` +
          ` <a id="retry" href="#">retry connecting.</a>`,
        type: 'warning',
        retry
      }, true);
      $('#retry').off();
      $('#retry').click((e) => {
        e.preventDefault();
        checkLoginStatus(true);
      });
    });
  }

  function getLinkOpener(url) {
    return function() {
      $(this).prop('disabled', true);
      window.open(url);
      startPinging();
    };
  }

  function startPinging() {
    $('#pinging').show();
    window.interval = setInterval(checkLoginStatus, 1000);
  }

  function stopPinging() {
    $('#facebook,#google,#twitter,#local').prop('disabled', false);
    clearInterval(window.interval);
    $('#pinging').fadeOut();
  }

  function getPublishClickHandler(json) {
    return function(e) {
      $('#publish').prop('disabled', true); // Prevent repeat submission
      $.ajax({
        type: 'POST',
        contentType: 'application/json',
        url: 'http://freeyourstuff.cc/api/siteset',
        data: json,
        dataType: 'json'
      }).done(res => {
        $('#publish').hide();
        showMessage({
          text: 'Your data was successfully published! Thank you for contributing to the commons. :-)',
          type: 'success'
        });
      }).fail(err => {
        $('#publish').prop('disabled', false);

        let text = 'There was a problem publishing your data. Sorry! :-(';
        if (err.responseJSON.error) {
          text += ' We got the following error message:' +
            `<br><br><code>${err.responseJSON.error}</code>`;
        }
        text += '

You can help us out by ' +
        'filing a bug.';
        showMessage({
          text,
          type: 'error'
        });
      });
    };
  }

  function showData(data, schema) {

    let json = JSON.stringify(data, null, 2);
    // We need to URI-encode it so we can stash it into the href attribute
    let encodedJSON = encodeURIComponent(json);
    $('#download').attr('href', `data:application/json;charset=utf-8,${encodedJSON}`);
    $('#download').attr('download', 'data.json');

    $('#publish').click(getPublishClickHandler(json));
    $('#facebook').click(getLinkOpener('http://freeyourstuff.cc/auth/facebook'));
    $('#twitter').click(getLinkOpener('http://freeyourstuff.cc/auth/twitter'));
    $('#google').click(getLinkOpener('http://freeyourstuff.cc/auth/google'));
    $('#local').click(getLinkOpener('http://freeyourstuff.cc/signin'));
    $('#stop').click(stopPinging);

    checkLoginStatus();

    if (data.schemaKey !== schema.schema.key) {
      throw new Error('Schemas do not match. Cannot process data.');
    }

    for (let setName in data) {
      // Exclude metadata
      if (setName == 'schemaKey' || setName == 'schemaVersion')
        continue;

      let columns = [];
      let fields = [];

      // Loop through each data object in the set
      for (let dataObj of data[setName].data) {
        // Aggregate information from the schema as we go --
        // this way we only have metadata we have actual data for
        for (let prop in dataObj) {
          if (fields.indexOf(prop) == -1) {
            if (!schema[setName].data[prop].describes) {
              columns.push({
                data: prop,
                title: schema[setName].data[prop].label.en,
                defaultContent: ''
              });
              fields.push(prop);
            } else {
              // We're encountering data that "describes" other data, i.e. a URL
              // for some text elsewhere. Let's locate the relevant column and
              // add a render function if it doesn't already have one.
              for (let col of columns) {
                if (col.data == schema[setName].data[prop].describes) {
                  if (!col.render)
                    col.render = makeRenderFunction(prop);
                  break;
                }
              }
            }
          }
        }
      }

      $('#results').append(`<h3>${schema[setName].label.en}</h3>`);
      $('#results').append(`<table id="result_${setName}" class="table table-hover table-responsive">`);
      $(`#result_${setName}`).dataTable({
        "data": data[setName].data,
        "columns": columns
      });
    }
  }
})();
<commit_msg>Fix #5: Remove event handler once a result set is displayed.<commit_after>(function() {
  'use strict';

  if (window.location.hash !== '#debug') {
    try {
      chrome.runtime.onMessage.addListener(displayHandler);
    } catch (e) {
      console.log('Chrome API not found. Loading outside of extension context with example data.');
      loadExample('yelp');
    }
  } else {
    loadExample('yelp');
  }

  function displayHandler(request) {
    if (request.action === 'display' && request.data && request.schema) {
      showData(request.data, request.schema);
      chrome.runtime.onMessage.removeListener(displayHandler);
    }
  }

  function loadExample(scriptName) {
    let script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = `examples/${scriptName}.js`;
    script.onload = showExampleData;
    document.body.appendChild(script);
  }

  function makeRenderFunction(rowName) {
    return (val, type, row) => {
      if (row[rowName])
        return `<a href="${row[rowName]}">${val}</a>`;
      else
        return val;
    };
  }

  function showExampleData() {
    showData(exampleData, exampleSchema);
  }

  // Message object properties:
  // text: 'text'
  // type: 'warning' | 'success' | 'error' (will be added as CSS class name)
  // retry: Whether we're showing the same message again (triggers animation)
  function showMessage(msg) {
    if (!msg.retry) {
      $('#messages').empty();
      $('#messages').append(`<div class="${msg.type} message">${msg.text}</div>`);
      $('#messages').show();
    }
    else {
      $('#messages').fadeOut().fadeIn();
    }
  }

  // retry: Whether this was user-triggered.
  function checkLoginStatus(retry) {
    $.get('http://freeyourstuff.cc/api/loginstatus').done(res => {
      // Hide previous warning messages
      $('#messages').hide();
      if (typeof res == 'object' && res.loggedIn) {
        $('#publish').show();
        $('#signedout').hide();
        clearInterval(window.interval);
      } else {
        $('#signedout').show();
        $('#publish').hide();
      }
    }).fail(err => {
      showMessage({
        text: `We can't reach freeyourstuff.cc right now, so you can't publish your data. Sorry! You can still download it or` +
          ` <a id="retry" href="#">retry connecting.</a>`,
        type: 'warning',
        retry
      }, true);
      $('#retry').off();
      $('#retry').click((e) => {
        e.preventDefault();
        checkLoginStatus(true);
      });
    });
  }

  function getLinkOpener(url) {
    return function() {
      $(this).prop('disabled', true);
      window.open(url);
      startPinging();
    };
  }

  function startPinging() {
    $('#pinging').show();
    window.interval = setInterval(checkLoginStatus, 1000);
  }

  function stopPinging() {
    $('#facebook,#google,#twitter,#local').prop('disabled', false);
    clearInterval(window.interval);
    $('#pinging').fadeOut();
  }

  function getPublishClickHandler(json) {
    return function(e) {
      $('#publish').prop('disabled', true); // Prevent repeat submission
      $.ajax({
        type: 'POST',
        contentType: 'application/json',
        url: 'http://freeyourstuff.cc/api/siteset',
        data: json,
        dataType: 'json'
      }).done(res => {
        $('#publish').hide();
        showMessage({
          text: 'Your data was successfully published! Thank you for contributing to the commons. :-)',
          type: 'success'
        });
      }).fail(err => {
        $('#publish').prop('disabled', false);

        let text = 'There was a problem publishing your data. Sorry! :-(';
        if (err.responseJSON.error) {
          text += ' We got the following error message:' +
            `<br><br><code>${err.responseJSON.error}</code>`;
        }
        text += '

You can help us out by ' +
        'filing a bug.';
        showMessage({
          text,
          type: 'error'
        });
      });
    };
  }

  function showData(data, schema) {

    let json = JSON.stringify(data, null, 2);
    // We need to URI-encode it so we can stash it into the href attribute
    let encodedJSON = encodeURIComponent(json);
    $('#download').attr('href', `data:application/json;charset=utf-8,${encodedJSON}`);
    $('#download').attr('download', 'data.json');

    $('#publish').click(getPublishClickHandler(json));
    $('#facebook').click(getLinkOpener('http://freeyourstuff.cc/auth/facebook'));
    $('#twitter').click(getLinkOpener('http://freeyourstuff.cc/auth/twitter'));
    $('#google').click(getLinkOpener('http://freeyourstuff.cc/auth/google'));
    $('#local').click(getLinkOpener('http://freeyourstuff.cc/signin'));
    $('#stop').click(stopPinging);

    checkLoginStatus();

    if (data.schemaKey !== schema.schema.key) {
      throw new Error('Schemas do not match. Cannot process data.');
    }

    for (let setName in data) {
      // Exclude metadata
      if (setName == 'schemaKey' || setName == 'schemaVersion')
        continue;

      let columns = [];
      let fields = [];

      // Loop through each data object in the set
      for (let dataObj of data[setName].data) {
        // Aggregate information from the schema as we go --
        // this way we only have metadata we have actual data for
        for (let prop in dataObj) {
          if (fields.indexOf(prop) == -1) {
            if (!schema[setName].data[prop].describes) {
              columns.push({
                data: prop,
                title: schema[setName].data[prop].label.en,
                defaultContent: ''
              });
              fields.push(prop);
            } else {
              // We're encountering data that "describes" other data, i.e. a URL
              // for some text elsewhere. Let's locate the relevant column and
              // add a render function if it doesn't already have one.
              for (let col of columns) {
                if (col.data == schema[setName].data[prop].describes) {
                  if (!col.render)
                    col.render = makeRenderFunction(prop);
                  break;
                }
              }
            }
          }
        }
      }

      $('#results').append(`<h3>${schema[setName].label.en}</h3>`);
      $('#results').append(`<table id="result_${setName}" class="table table-hover table-responsive">`);
      $(`#result_${setName}`).dataTable({
        "data": data[setName].data,
        "columns": columns
      });
    }
  }
})();
<|endoftext|>