<commit_before>import Styletron from 'styletron';
import { injectStyle as _injectStyle } from 'styletron-utils';
import { defineElement } from 'domvm';

const styletronSingleton = new Styletron();

export function injectStyle(...styles) {
    return _injectStyle(styletronSingleton, Object.assign({}, ...styles));
}

function isObject(obj) {
    return typeof obj === 'object' && !Array.isArray(obj);
}

export function el(sig, ...attrsOrChildren) {
    let attrs = {};
    let children = attrsOrChildren;
    if (attrsOrChildren.length && isObject(attrsOrChildren[0])) {
        attrs = attrsOrChildren[0];
        children = attrsOrChildren.slice(1);
        if (isObject(attrs.css)) {
            const className = injectStyle(Object.assign(attrs.css, attrs.styles || {}));
            attrs.className = `${className} ${attrs.className || ''}`.trim();
            delete attrs.css;
            delete attrs.styles;
        }
    }
    return defineElement(sig, attrs, ...children);
}

export function styled(styles, tagName='div') {
    const className = injectStyle(styles);

    return (...props) => {
        const attrIndex = props.length && isObject(props[0]) ? 0 : -1;
        const attrs = (
            attrIndex === -1
            ? { class: className }
            : Object.assign(
                {},
                props[0],
                {className: `${className} ${props[0].className || ''}`.trim()})
        );

        if (isObject(attrs.css)) {
            attrs.styles = styles;
            attrs.class = props[0].class || '';
        }

        const children = props.slice(attrIndex + 1);
        return el(tagName, attrs, children);
    };
}
<commit_msg>Fix style service not rendering included style objects<commit_after>import Styletron from 'styletron';
import { injectStyle as _injectStyle } from 'styletron-utils';
import { defineElement } from 'domvm';

const styletronSingleton = new Styletron();

export function injectStyle(...styles) {
    return _injectStyle(styletronSingleton, Object.assign({}, ...styles));
}

function isObject(obj) {
    return typeof obj === 'object' && !Array.isArray(obj);
}

export function el(sig, ...attrsOrChildren) {
    let attrs = {};
    let children = attrsOrChildren;
    if (attrsOrChildren.length && isObject(attrsOrChildren[0])) {
        attrs = attrsOrChildren[0];
        children = attrsOrChildren.slice(1);
        if (isObject(attrs.css)) {
            const className = injectStyle(Object.assign(attrs.css, attrs.styles || {}));
            attrs.class = `${className} ${attrs.class || ''}`.trim();
            delete attrs.css;
            delete attrs.styles;
        }
    }
    return defineElement(sig, attrs, ...children);
}

export function styled(styles, tagName='div') {
    const className = injectStyle(styles);

    return (...props) => {
        const attrIndex = props.length && isObject(props[0]) ? 0 : -1;
        const attrs = (
            attrIndex === -1
            ? { class: className }
            : Object.assign(
                {},
                props[0],
                {className: `${className} ${props[0].className || ''}`.trim()})
        );

        if (isObject(attrs.css)) {
            attrs.styles = styles;
            attrs.class = props[0].class || '';
        }

        const children = props.slice(attrIndex + 1);
        return el(tagName, attrs, children);
    };
}
<|endoftext|>


<commit_before>import {
    createAction,
    createTask,
    createTaskFactory,
    createTaskAction
} from "./create-action";
import createStoreEnhancer from "./middleware";
import { setToken, cancelTask, resetTask, doLogin, doLogout } from "./actions";
import graphReducer, {
    createTaskSelector,
    createVertexSelector,
    getTaskState,
    getTokenState,
    getMyId,
    getMyRoles
} from "./reducer";
import ReduxToken from "./token";
import whenTask from "./when-task";
import implicitOauth, { loginTaskSelector } from "./implicit-oauth";

//a tiny wrapper to make it easier on the caller
export const createToken = (...args) => new ReduxToken(...args);

export {
    graphReducer,
    createVertexSelector,
    createTaskSelector,
    createAction,
    createTask,
    createTaskFactory,
    createTaskAction,
    cancelTask,
    resetTask,
    createStoreEnhancer,
    setToken,
    getTaskState,
    getMyId,
    getMyRoles,
    getTokenState,
    whenTask,
    implicitOauth,
    loginTaskSelector,
    doLogin,
    doLogout
};
<commit_msg>Expose `setOnLogoutHook`<commit_after>import {
    createAction,
    createTask,
    createTaskFactory,
    createTaskAction
} from "./create-action";
import createStoreEnhancer from "./middleware";
import {
    setToken,
    cancelTask,
    resetTask,
    doLogin,
    doLogout,
    setOnLogoutHook
} from "./actions";
import graphReducer, {
    createTaskSelector,
    createVertexSelector,
    getTaskState,
    getTokenState,
    getMyId,
    getMyRoles
} from "./reducer";
import ReduxToken from "./token";
import whenTask from "./when-task";
import implicitOauth, { loginTaskSelector } from "./implicit-oauth";

//a tiny wrapper to make it easier on the caller
export const createToken = (...args) => new ReduxToken(...args);

export {
    graphReducer,
    createVertexSelector,
    createTaskSelector,
    createAction,
    createTask,
    createTaskFactory,
    createTaskAction,
    cancelTask,
    resetTask,
    createStoreEnhancer,
    setToken,
    getTaskState,
    getMyId,
    getMyRoles,
    getTokenState,
    whenTask,
    implicitOauth,
    loginTaskSelector,
    doLogin,
    doLogout,
    setOnLogoutHook
};
<|endoftext|>


<commit_before>import Marionette from 'backbone.marionette';
import urlParse from 'url-parse';

import template from './DownloadSelectionView.hbs';
import './DownloadSelectionView.css';
import SelectionListView from './SelectionListView';
import { downloadCustom, getDownloadInfos } from '../../download/';
import metalinkTemplate from '../Metalink.hbs';

const DownloadView = Marionette.CompositeView.extend({
  template,
  templateHelpers() {
    return {
      termsAndConditionsUrl: this.termsAndConditionsUrl,
      downloadEnabled: this.downloadEnabled,
    };
  },
  className: 'download-view',
  childView: SelectionListView,
  childViewContainer: '.selection-lists',
  buildChildView(child, ChildViewClass) {
    return new ChildViewClass({
      model: child,
      collection: child.get('downloadSelection'),
      mapModel: this.mapModel,
      highlightModel: this.highlightModel,
    });
  },

  events: {
    'click .start-download': 'onStartDownloadClicked',
    'click .download-as-metalink': 'onDownloadAsMetalinkClicked',
    'click .download-as-url-list': 'onDownloadAsUrlListClicked',
    'click .deselect-all': 'onDeselectAllClicked',
    'change .terms-and-conditions': 'onTermsAndAndConditionsChange',
  },

  initialize(options) {
    this.mapModel = options.mapModel;
    this.filtersModel = options.filtersModel;
    this.highlightModel = options.highlightModel;
    this.termsAndConditionsUrl = options.termsAndConditionsUrl;
    this.downloadEnabled = options.downloadEnabled;

    this.collection.each((searchModel) => {
      this.listenTo(
        searchModel.get('downloadSelection'), 'reset update', this.onDownloadSelectionChange
      );
    });

    this.onStartDownload = options.onStartDownload;
  },

  onAttach() {
    this.triggerMethod('update:status', this._infoBadge());
  },

  onStartDownloadClicked() {
    this.onStartDownload();
  },

  onDownloadAsMetalinkClicked() {
    this._getDownloadInfos()
      .then((items) => {
        let content = metalinkTemplate({
          date: (new Date()).toISOString(),
          items,
        });
        content = content.replace(/[\n]/g, '\r\n');
        downloadCustom('download-files.meta4', 'application/metalink4+xml', content);
      });
  },

  onDownloadAsUrlListClicked() {
    this._getDownloadInfos()
      .then((infos) => {
        downloadCustom('url-list.txt', 'text/plain',
          infos.map(info => info.href).join('\r\n')
        );
      });
  },

  onDeselectAllClicked() {
    this.collection.each(searchModel =>
      searchModel.get('downloadSelection').reset([])
    );
  },

  onTermsAndAndConditionsChange() {
    this.hasAcceptedTerms = this.$('.terms-and-conditions').is(':checked');
    this.checkButtons();
  },

  onDownloadSelectionChange() {
    this.checkButtons();
  },

  checkButtons() {
    const totalCount = this.collection.reduce((count, searchModel) => (
      count + searchModel.get('downloadSelection').length
    ), 0);

    let enabled = totalCount > 0 && this.downloadEnabled;
    if (this.termsAndConditionsUrl) {
      enabled = enabled && this.hasAcceptedTerms;
    }

    this.$('.download-control')
      .find('button')
      .prop('disabled', !enabled);

    this.triggerMethod('update:status', this._infoBadge(totalCount));
  },

  _infoBadge(totalCount = 0) {
    return `<span>(${totalCount})</span>`;
  },

  _getDownloadInfos(options) {
    function flatten(arr) {
      return arr.reduce((acc, val) => acc.concat(val), []);
    }

    const chunks = this.collection
      .map(searchModel =>
        searchModel.get('downloadSelection')
          .map(recordModel => getDownloadInfos(
            searchModel.get('layerModel'), this.filtersModel, recordModel, options)
          )
      );

    return Promise.all(flatten(chunks))
      .then(received => flatten(received));
  }
});

export default DownloadView;
<commit_msg>Splitting download functionality: Allow metalink/urllist for any protocol, but disable full download for S3<commit_after>import Marionette from 'backbone.marionette';
import urlParse from 'url-parse';

import template from './DownloadSelectionView.hbs';
import './DownloadSelectionView.css';
import SelectionListView from './SelectionListView';
import { downloadCustom, getDownloadInfos } from '../../download/';
import metalinkTemplate from '../Metalink.hbs';

const DownloadView = Marionette.CompositeView.extend({
  template,
  templateHelpers() {
    return {
      termsAndConditionsUrl: this.termsAndConditionsUrl,
      downloadEnabled: this.downloadEnabled,
    };
  },
  className: 'download-view',
  childView: SelectionListView,
  childViewContainer: '.selection-lists',
  buildChildView(child, ChildViewClass) {
    return new ChildViewClass({
      model: child,
      collection: child.get('downloadSelection'),
      mapModel: this.mapModel,
      highlightModel: this.highlightModel,
    });
  },

  events: {
    'click .start-download': 'onStartDownloadClicked',
    'click .download-as-metalink': 'onDownloadAsMetalinkClicked',
    'click .download-as-url-list': 'onDownloadAsUrlListClicked',
    'click .deselect-all': 'onDeselectAllClicked',
    'change .terms-and-conditions': 'onTermsAndAndConditionsChange',
  },

  initialize(options) {
    this.mapModel = options.mapModel;
    this.filtersModel = options.filtersModel;
    this.highlightModel = options.highlightModel;
    this.termsAndConditionsUrl = options.termsAndConditionsUrl;
    this.downloadEnabled = options.downloadEnabled;

    this.collection.each((searchModel) => {
      this.listenTo(
        searchModel.get('downloadSelection'), 'reset update', this.onDownloadSelectionChange
      );
    });

    this.onStartDownload = options.onStartDownload;
  },

  onAttach() {
    this.triggerMethod('update:status', this._infoBadge());
  },

  onStartDownloadClicked() {
    this.onStartDownload();
  },

  onDownloadAsMetalinkClicked() {
    this._getDownloadInfos()
      .then((items) => {
        let content = metalinkTemplate({
          date: (new Date()).toISOString(),
          items,
        });
        content = content.replace(/[\n]/g, '\r\n');
        downloadCustom('download-files.meta4', 'application/metalink4+xml', content);
      });
  },

  onDownloadAsUrlListClicked() {
    this._getDownloadInfos()
      .then((infos) => {
        downloadCustom('url-list.txt', 'text/plain',
          infos.map(info => info.href).join('\r\n')
        );
      });
  },

  onDeselectAllClicked() {
    this.collection.each(searchModel =>
      searchModel.get('downloadSelection').reset([])
    );
  },

  onTermsAndAndConditionsChange() {
    this.hasAcceptedTerms = this.$('.terms-and-conditions').is(':checked');
    this.checkButtons();
  },

  onDownloadSelectionChange() {
    this.checkButtons();
  },

  checkButtons() {
    const totalCountNotS3 = this.collection
      .filter(searchModel => searchModel.get('layerModel').get('download.protocol') !== 'S3')
      .reduce((count, searchModel) => (
        count + searchModel.get('downloadSelection').length
      ), 0);

    const totalCount = this.collection
      .reduce((count, searchModel) => (
        count + searchModel.get('downloadSelection').length
      ), 0);

    let fullDownloadEnabled = totalCountNotS3 > 0 && this.downloadEnabled;
    let textDownloadEnabled = totalCount > 0 && this.downloadEnabled;
    if (this.termsAndConditionsUrl) {
      fullDownloadEnabled = fullDownloadEnabled && this.hasAcceptedTerms;
      textDownloadEnabled = textDownloadEnabled && this.hasAcceptedTerms;
    }

    this.$('.start-download')
      .prop('disabled', !fullDownloadEnabled);

    this.$('.dropdown-toggle')
      .prop('disabled', !textDownloadEnabled);

    this.triggerMethod('update:status', this._infoBadge(totalCount));
  },

  _infoBadge(totalCount = 0) {
    return `<span>(${totalCount})</span>`;
  },

  _getDownloadInfos(options) {
    function flatten(arr) {
      return arr.reduce((acc, val) => acc.concat(val), []);
    }

    const chunks = this.collection
      .map(searchModel =>
        searchModel.get('downloadSelection')
          .map(recordModel => getDownloadInfos(
            searchModel.get('layerModel'), this.filtersModel, recordModel, options)
          )
      );

    return Promise.all(flatten(chunks))
      .then(received => flatten(received));
  }
});

export default DownloadView;
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008-2016, Massachusetts Institute of Technology (MIT)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
define(['iweb/CoreModule', 'ol', './TokenManager', './ArcGISFeatureRequestManager'],
		function(Core, ol, TokenManager, ArcGISFeatureRequest){

	// matches href tags with relative urls
	//var relativeHrefRegex = /<href>(?!http|#)(.*)<\/href>/gi;
	var relativeHrefRegex = /<href>(?!http|#)(.*?)<\/href>/gi;

	return Ext.define('modules.datalayer.builder', {

		constructor: function() {
			this.mediator = Core.Mediator.getInstance();
			ArcGISFeatureRequest.init();
		},

		buildLayer: function(type, config){
			if(type == "wms"){
				return this.buildWMSLayer(config.url, config.layername, config);
			}else if(type == "wfs"){
				return this.buildWFSLayer(config.url, config.layername, config);
			}else if(type == "kml"){
				return this.buildKMLLayer(config.url, config.layername, config);
			}else if(type == "kmz"){
				return this.buildKMLLayer(config.url, config.layername, config);
			}else if(type == "bing"){
				return this.buildBingLayer(config.url, config.layername, config);
			}else if(type == "osm"){
				return this.buildOSMLayer(config.url, config.layername, config);
			}else if(type == "xyz"){
				return this.buildXYZLayer(config.url, config.layername, config);
			}else if(type == "arcgisrest"){
				return this.buildArcGisLayer(config.url, config.layername, config);
			}else if(type == "geojson"){
				return this.buildGeoJsonLayer(config.url, config.layername, config);
			}else if(type == "gpx"){
				return this.buildGpxLayer(config.url, config.layername, config);
			}
		},

		buildWMSLayer: function (url, layername, config) {
			var attrs = (config.attributes) ? JSON.parse(config.attributes) : {};
			return new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.TileWMS(/** @type {olx.source.TileWMSOptions} */ ({
					url: url,
					params: {
							'LAYERS': layername ,
							'TILED': true,
							'VERSION': attrs.version || '1.3.0'
					}
				}))
			});
		},

		buildWFSLayer: function (url, layername, config, style) {
			var _mediator = this.mediator;
			var _eventManager = Core.EventManager;

			// format used to parse WFS GetFeature responses
			var wfsFormat = new ol.format.WFS();

			var vectorSource = new ol.source.Vector({
			  loader: function(extent, resolution, projection) {
				  var requestUrl = Ext.String.format(
						  "{0}//{1}/nics/proxy?url={2}?version=1.1.0&request=GetFeature&typename={3}" +
						  "&srsname=EPSG:3857&bbox={4}{5}",
					  window.location.protocol,
					  window.location.host,
					  url, layername, extent.join(','), ',EPSG:3857');

					if(config.secure){
						var token = TokenManager.getToken(config.datasourceid);

						if(token){
							requestUrl = requestUrl + "&token=" + token;
						}else{
							return; //Don't attempt to get features w/o a token
						}
					}

					var loadFeatures = function(data, status){
						//remove all previous features in this extent, to avoid flash on refresh
						this.getFeaturesInExtent(extent).forEach(function(feature){
							if(feature.getGeometry().intersectsExtent(extent)) {
								this.removeFeature(feature);
							}
						}, this);

						//add the new features
						this.addFeatures(wfsFormat.readFeatures(data));
					};

					var handler = loadFeatures.bind(this);

					$.ajax({
				      url: requestUrl,
				      dataType: 'xml',
				      success: handler,
				      error: function(param1, status, error){}
				   });
				},
			  strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ({
			    maxZoom: 19
			  })),
			  projection: 'EPSG:3857'
			});

			// tells the mapController we can handle a defered reload
			// meaning we will handle removing features when new ones come in
			vectorSource.set("deferReload", true);

			//create default style
			var stroke = new ol.style.Stroke({
	    	   color: '#3399CC',
	    	   width: 1.25
	    	 });

			var fill = new ol.style.Fill({
				color: 'rgba(215, 40, 40, 0.9)'
	        });

			var style = new ol.style.Style({
    	     image: new ol.style.Circle({
    	         fill: fill,
    	         stroke: stroke,
    	         radius: 5
    	       }),
	           fill: fill,
    	       stroke: stroke
    	     });

			return new ol.layer.Vector({
				opacity: config.opacity || 1,
				source: vectorSource,
				style: style
		    });
		},

		buildKMLLayer: function(url, layername, config){
			var baseUrl = url;
			if(layername){
				url = url + layername;
				baseUrl = url.substring(0, url.lastIndexOf("/") + 1);
			}

			var format = new ol.format.KML({
				extractStyles: true,
				showPointNames: false
			});

			var vectorSource = new ol.source.Vector({
		      url: "proxy?url=" + url,
			  format: format,
			  projection: 'EPSG:3857',

			  loader: function(extent, resolution, projection) {

				    var loadFeatures = function(source, status){
				    	//remove all features when we get results, to avoid flash on refresh
						this.getFeatures().forEach(this.removeFeature, this);

				    	if (source) {
				    		//ol kml format uses kml document baseURI for relative URLs. since we are
				    		//using the proxy and preprocesing the text, baseURI is not properly set.
				    		source = source.replace(relativeHrefRegex, '<href>'+ baseUrl +'$1</href>');

				    		var networkLinks = this.getFormat().readNetworkLinks(source);

				    		var _this = this;
				    		for(var i=0; i<networkLinks.length; i++){
				    			 $.ajax({
								      url: "proxy?url=" + networkLinks[i].href,
								      dataType: 'text',
								      success: function(source, status){
								    	  if(source){

								    		  source = source.replace(relativeHrefRegex, '<href>'+ baseUrl +'$1</href>');

								    		  if(source.indexOf("<kml") == -1){
								    			   var header = "<kml xmlns=\"http://www.opengis.net/kml/2.2\" ";
													header += "xmlns:gx=\"http://www.google.com/kml/ext/2.2\" ";
													header += "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ";
													header += "xsi:schemaLocation=\"http://www.opengis.net/kml/2.2 http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd http://www.google.com/kml/ext/2.2 http://code.google.com/apis/kml/schema/kml22gx.xsd\">";
													header += source;
													header += "</kml>";

													source = header;
								    		   }

								    		  	// Replace BalloonStyle tags
												var start = source.indexOf('<BalloonStyle>');

												while (start != -1){
													var end = source.indexOf('</BalloonStyle>') + 15;
													var insertIndex = source.indexOf('</Style>', end) + 8;

													if (insertIndex != -1)
													{
														var balloonStyle = source.substring(start, end);

														var description = balloonStyle.replace('<BalloonStyle><text>', '<description>')
														.replace('</text></BalloonStyle>', '</description>');

														source = source.slice(0, insertIndex) + description + source.slice(insertIndex);
														source = source.replace(balloonStyle, "");
													}
													var nextStart = source.indexOf("<BalloonStyle>");
													if (nextStart < start) {
														start = -1;
													} else {
														start = nextStart;
													}
												}

												_this.addFeatures(_this.getFormat().readFeatures(
															 ol.xml.parse(source),
												        	 {featureProjection: 'EPSG:3857'}));
								    	  }
								      },
								      error: function(param1, status, error){},
								      scope: this
								   });
				    		}

				    		this.addFeatures(this.getFormat().readFeatures(
				    				 ol.xml.parse(source),
				                	 {featureProjection: 'EPSG:3857'}));
			            }
				    };

					var handler = loadFeatures.bind(this);

					$.ajax({
				      url: this.getUrl(),
				      dataType: 'text',
				      success: handler,
				      error: function(param1, status, error){}
				   });
				}
	        });

			// tells the mapController we can handle a defered reload
			// meaning we will handle removing features when new ones come in
			vectorSource.set("deferReload", true);

			return new ol.layer.Vector({
			  opacity: config.opacity || 1,
			  source: vectorSource
			});
		},

		buildBingLayer: function(url, layername, config) {
			var attrs = (config.attributes) ? JSON.parse(config.attributes) : {};
			return new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.BingMaps({
					key: Core.Config.getProperty("maps.bing.apikey"),
					imagerySet: attrs.type,
					maxZoom: 19
				})
			});
		},

		buildOSMLayer: function(url, layername, config) {
			return new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.OSM()
			});
		},

		buildXYZLayer: function(url, layername, config) {
			var attrs = (config.attributes) ? JSON.parse(config.attributes) : {};
			return new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.XYZ({
					url: url,
					maxZoom: attrs.maxZoom || 18
				})
			});
		},

		buildGeoJsonLayer: function(url, layername, config){
			if(layername){
				url = url + layername;
			}

			var vectorSource = new ol.source.Vector({
			  url: url,
			  format: new ol.format.GeoJSON()
	        });

			return new ol.layer.Vector({
			  opacity: config.opacity || 1,
			  source: vectorSource
			});

		},

		buildGpxLayer: function(url, layername, config){
			if(layername){
				url = url + layername;
			}

			var vectorSource = new ol.source.Vector({
			  url: "proxy?url=" + url,
			  format: new ol.format.GPX(),
			  projection: 'EPSG:3857'
	        });

			return new ol.layer.Vector({
			  opacity: config.opacity || 1,
			  source: vectorSource
			});

		},

		buildArcGisLayer: function(url, layername, config) {

			var layer = new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.TileArcGISRest({
					url: url,
					params: {'LAYERS': 'show:' + layername }
				})
			});
			ArcGISFeatureRequest.addLayer(layer);

			return layer;

		}
	});
});
<commit_msg>OES52 Update query params to switch comma for ampersand and to add the wfs service<commit_after>/*
 * Copyright (c) 2008-2016, Massachusetts Institute of Technology (MIT)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
define(['iweb/CoreModule', 'ol', './TokenManager', './ArcGISFeatureRequestManager'],
		function(Core, ol, TokenManager, ArcGISFeatureRequest){

	// matches href tags with relative urls
	//var relativeHrefRegex = /<href>(?!http|#)(.*)<\/href>/gi;
	var relativeHrefRegex = /<href>(?!http|#)(.*?)<\/href>/gi;

	return Ext.define('modules.datalayer.builder', {

		constructor: function() {
			this.mediator = Core.Mediator.getInstance();
			ArcGISFeatureRequest.init();
		},

		buildLayer: function(type, config){
			if(type == "wms"){
				return this.buildWMSLayer(config.url, config.layername, config);
			}else if(type == "wfs"){
				return this.buildWFSLayer(config.url, config.layername, config);
			}else if(type == "kml"){
				return this.buildKMLLayer(config.url, config.layername, config);
			}else if(type == "kmz"){
				return this.buildKMLLayer(config.url, config.layername, config);
			}else if(type == "bing"){
				return this.buildBingLayer(config.url, config.layername, config);
			}else if(type == "osm"){
				return this.buildOSMLayer(config.url, config.layername, config);
			}else if(type == "xyz"){
				return this.buildXYZLayer(config.url, config.layername, config);
			}else if(type == "arcgisrest"){
				return this.buildArcGisLayer(config.url, config.layername, config);
			}else if(type == "geojson"){
				return this.buildGeoJsonLayer(config.url, config.layername, config);
			}else if(type == "gpx"){
				return this.buildGpxLayer(config.url, config.layername, config);
			}
		},

		buildWMSLayer: function (url, layername, config) {
			var attrs = (config.attributes) ? JSON.parse(config.attributes) : {};
			return new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.TileWMS(/** @type {olx.source.TileWMSOptions} */ ({
					url: url,
					params: {
							'LAYERS': layername ,
							'TILED': true,
							'VERSION': attrs.version || '1.3.0'
					}
				}))
			});
		},

		buildWFSLayer: function (url, layername, config, style) {
			var _mediator = this.mediator;
			var _eventManager = Core.EventManager;

			// format used to parse WFS GetFeature responses
			var wfsFormat = new ol.format.WFS();

			var vectorSource = new ol.source.Vector({
                         loader: function(extent, resolution, projection) {
				 var requestUrl = Ext.String.format(
						  "{0}//{1}/nics/proxy?url={2}?version=1.1.0&service=WFS&request=GetFeature&typename={3}" +
						  "&srsname=EPSG:3857&bbox={4}{5}",
					  window.location.protocol,
					  window.location.host,
					  url, layername, extent.join(','), '&EPSG:3857');

					if(config.secure){
						var token = TokenManager.getToken(config.datasourceid);

						if(token){
							requestUrl = requestUrl + "&token=" + token;
						}else{
							return; //Don't attempt to get features w/o a token
						}
					}

					var loadFeatures = function(data, status){
						//remove all previous features in this extent, to avoid flash on refresh
						this.getFeaturesInExtent(extent).forEach(function(feature){
							if(feature.getGeometry().intersectsExtent(extent)) {
								this.removeFeature(feature);
							}
						}, this);

						//add the new features
						this.addFeatures(wfsFormat.readFeatures(data));
					};

					var handler = loadFeatures.bind(this);

					$.ajax({
				      url: requestUrl,
				      dataType: 'xml',
				      success: handler,
				      error: function(param1, status, error){}
				   });
				},
			  strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ({
			    maxZoom: 19
			  })),
			  projection: 'EPSG:3857'
			});

			// tells the mapController we can handle a defered reload
			// meaning we will handle removing features when new ones come in
			vectorSource.set("deferReload", true);

			//create default style
			var stroke = new ol.style.Stroke({
	    	   color: '#3399CC',
	    	   width: 1.25
	    	 });

			var fill = new ol.style.Fill({
				color: 'rgba(215, 40, 40, 0.9)'
	        });

			var style = new ol.style.Style({
    	     image: new ol.style.Circle({
    	         fill: fill,
    	         stroke: stroke,
    	         radius: 5
    	       }),
	           fill: fill,
    	       stroke: stroke
    	     });

			return new ol.layer.Vector({
				opacity: config.opacity || 1,
				source: vectorSource,
				style: style
		    });
		},

		buildKMLLayer: function(url, layername, config){
			var baseUrl = url;
			if(layername){
				url = url + layername;
				baseUrl = url.substring(0, url.lastIndexOf("/") + 1);
			}

			var format = new ol.format.KML({
				extractStyles: true,
				showPointNames: false
			});

			var vectorSource = new ol.source.Vector({
		      url: "proxy?url=" + url,
			  format: format,
			  projection: 'EPSG:3857',

			  loader: function(extent, resolution, projection) {

				    var loadFeatures = function(source, status){
				    	//remove all features when we get results, to avoid flash on refresh
						this.getFeatures().forEach(this.removeFeature, this);

				    	if (source) {
				    		//ol kml format uses kml document baseURI for relative URLs. since we are
				    		//using the proxy and preprocesing the text, baseURI is not properly set.
				    		source = source.replace(relativeHrefRegex, '<href>'+ baseUrl +'$1</href>');

				    		var networkLinks = this.getFormat().readNetworkLinks(source);

				    		var _this = this;
				    		for(var i=0; i<networkLinks.length; i++){
				    			 $.ajax({
								      url: "proxy?url=" + networkLinks[i].href,
								      dataType: 'text',
								      success: function(source, status){
								    	  if(source){

								    		  source = source.replace(relativeHrefRegex, '<href>'+ baseUrl +'$1</href>');

								    		  if(source.indexOf("<kml") == -1){
								    			   var header = "<kml xmlns=\"http://www.opengis.net/kml/2.2\" ";
													header += "xmlns:gx=\"http://www.google.com/kml/ext/2.2\" ";
													header += "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ";
													header += "xsi:schemaLocation=\"http://www.opengis.net/kml/2.2 http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd http://www.google.com/kml/ext/2.2 http://code.google.com/apis/kml/schema/kml22gx.xsd\">";
													header += source;
													header += "</kml>";

													source = header;
								    		   }

								    		  	// Replace BalloonStyle tags
												var start = source.indexOf('<BalloonStyle>');

												while (start != -1){
													var end = source.indexOf('</BalloonStyle>') + 15;
													var insertIndex = source.indexOf('</Style>', end) + 8;

													if (insertIndex != -1)
													{
														var balloonStyle = source.substring(start, end);

														var description = balloonStyle.replace('<BalloonStyle><text>', '<description>')
														.replace('</text></BalloonStyle>', '</description>');

														source = source.slice(0, insertIndex) + description + source.slice(insertIndex);
														source = source.replace(balloonStyle, "");
													}
													var nextStart = source.indexOf("<BalloonStyle>");
													if (nextStart < start) {
														start = -1;
													} else {
														start = nextStart;
													}
												}

												_this.addFeatures(_this.getFormat().readFeatures(
															 ol.xml.parse(source),
												        	 {featureProjection: 'EPSG:3857'}));
								    	  }
								      },
								      error: function(param1, status, error){},
								      scope: this
								   });
				    		}

				    		this.addFeatures(this.getFormat().readFeatures(
				    				 ol.xml.parse(source),
				                	 {featureProjection: 'EPSG:3857'}));
			            }
				    };

					var handler = loadFeatures.bind(this);

					$.ajax({
				      url: this.getUrl(),
				      dataType: 'text',
				      success: handler,
				      error: function(param1, status, error){}
				   });
				}
	        });

			// tells the mapController we can handle a defered reload
			// meaning we will handle removing features when new ones come in
			vectorSource.set("deferReload", true);

			return new ol.layer.Vector({
			  opacity: config.opacity || 1,
			  source: vectorSource
			});
		},

		buildBingLayer: function(url, layername, config) {
			var attrs = (config.attributes) ? JSON.parse(config.attributes) : {};
			return new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.BingMaps({
					key: Core.Config.getProperty("maps.bing.apikey"),
					imagerySet: attrs.type,
					maxZoom: 19
				})
			});
		},

		buildOSMLayer: function(url, layername, config) {
			return new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.OSM()
			});
		},

		buildXYZLayer: function(url, layername, config) {
			var attrs = (config.attributes) ? JSON.parse(config.attributes) : {};
			return new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.XYZ({
					url: url,
					maxZoom: attrs.maxZoom || 18
				})
			});
		},

		buildGeoJsonLayer: function(url, layername, config){
			if(layername){
				url = url + layername;
			}

			var vectorSource = new ol.source.Vector({
			  url: url,
			  format: new ol.format.GeoJSON()
	        });

			return new ol.layer.Vector({
			  opacity: config.opacity || 1,
			  source: vectorSource
			});

		},

		buildGpxLayer: function(url, layername, config){
			if(layername){
				url = url + layername;
			}

			var vectorSource = new ol.source.Vector({
			  url: "proxy?url=" + url,
			  format: new ol.format.GPX(),
			  projection: 'EPSG:3857'
	        });

			return new ol.layer.Vector({
			  opacity: config.opacity || 1,
			  source: vectorSource
			});

		},

		buildArcGisLayer: function(url, layername, config) {

			var layer = new ol.layer.Tile({
				opacity: config.opacity || 1,
				source: new ol.source.TileArcGISRest({
					url: url,
					params: {'LAYERS': 'show:' + layername }
				})
			});
			ArcGISFeatureRequest.addLayer(layer);

			return layer;

		}
	});
});
<|endoftext|>


<commit_before>/**
 *
 */

var _bpmServiceUrl= "/foxbpm-webapps-base/service";
var _bpmFilePath = "/foxbpm-webapps-base/foxbpm";
var _userId = "";
$.ajax({
	type : "get",
	dataType : "json",
	async:true,
	url : "/foxbpm-webapps-common/service/getLoginState",
	success:function(response){
		_userId = response.userId;
	},
	error:function(response){
		if(response.status == 401){
			window.location.href="/foxbpm-webapps-common/portal/login.html";
		}
	}
});
<commit_msg>提交修改common.js<commit_after>/**
 *
 */

var _bpmServiceUrl= "/foxbpm-webapps-base/service";
var _bpmFilePath = "/foxbpm-webapps-base/foxbpm";
var _userId = "";
$.ajax({
	type : "get",
	dataType : "json",
	async:false,
	url : "/foxbpm-webapps-common/service/getLoginState",
	success:function(response){
		_userId = response.userId;
	},
	error:function(response){
		if(response.status == 401){
			window.location.href="/foxbpm-webapps-common/portal/login.html";
		}
	}
});
<|endoftext|>


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

  angular
    .module('suborders')
    .controller('SubordersListController', SubordersListController);

  SubordersListController.$inject = ['SubordersService', 'Authentication'];

  function SubordersListController(SubordersService, Authentication) {
    var vm = this;

    vm.suborders = SubordersService.query();
    vm.user = Authentication.user;

    vm.onClick = onClick;

    function onClick(suborder){
      if(suborder.state === 'en espera'){
        $state.go('suborders.view', {
          suborderId: suborder._id
        });
      }
      else if(suborder.state === 'en proceso'){
        $state.go('suborders.feedback', {
          suborderId: suborder._id
        });
      }
    }

  }
})();
<commit_msg>se arreglo problema en el controlador de list suborders<commit_after>(function () {
  'use strict';

  angular
    .module('suborders')
    .controller('SubordersListController', SubordersListController);

  SubordersListController.$inject = ['$scope', '$state', 'SubordersService', 'Authentication'];

  function SubordersListController($scope, $state, SubordersService, Authentication) {
    var vm = this;

    vm.suborders = SubordersService.query();
    vm.user = Authentication.user;

    vm.onClick = onClick;

    function onClick(suborder){
      if(suborder.state === 'en espera'){
        $state.go('suborders.view', {
          suborderId: suborder._id
        });
      }
      else if(suborder.state === 'en proceso'){
        $state.go('suborders.feedback', {
          suborderId: suborder._id
        });
      }
    }

  }
})();
<|endoftext|>


<commit_before>import React from 'react';
import cx from 'classnames';
import Axis from './Axis';
import ORIENT from '../constants/orientation';

export default function AxisRight({
  scale,
  top,
  left,
  stroke,
  strokeWidth,
  strokeDasharray,
  rangePadding,
  label,
  labelOffset = 36,
  numTicks,
  tickFormat,
  tickStroke,
  tickTransform,
  tickLength = 8,
  tickLabelComponent = (
    <text
      textAnchor="start"
      fontFamily="Arial"
      fontSize={10}
      fill="black"
      dx="0.25em"
      dy="0.25em"
    />
  ),
  hideAxisLine,
  hideTicks,
  hideZero,
  className,
}) {
  return (
    <Axis
      className={cx('vx-axis-right', className)}
      orientation={ORIENT.right}
      top={top}
      left={left}
      scale={scale}
      stroke={stroke}
      strokeWidth={strokeWidth}
      strokeDasharray={strokeDasharray}
      rangePadding={rangePadding}
      labelComponent={
        typeof label === 'string' ?
        <text
          textAnchor="start"
          fontFamily="Arial"
          fontSize={10}
          fill="black"
        >
          {label}
        </text>
        : label
      }
      numTicks={numTicks}
      tickFormat={tickFormat}
      tickLength={tickLength}
      tickTransform={tickTransform}
      tickStroke={tickStroke}
      labelOffset={labelOffset}
      tickLabelComponent={tickLabelComponent}
      hideAxisLine={hideAxisLine}
      hideTicks={hideTicks}
      hideZero={hideZero}
    />
  );
}
<commit_msg>[AxisRight] fix default text anchor<commit_after>import React from 'react';
import cx from 'classnames';
import Axis from './Axis';
import ORIENT from '../constants/orientation';

export default function AxisRight({
  scale,
  top,
  left,
  stroke,
  strokeWidth,
  strokeDasharray,
  rangePadding,
  label,
  labelOffset = 36,
  numTicks,
  tickFormat,
  tickStroke,
  tickTransform,
  tickLength = 8,
  tickLabelComponent = (
    <text
      textAnchor="start"
      fontFamily="Arial"
      fontSize={10}
      fill="black"
      dx="0.25em"
      dy="0.25em"
    />
  ),
  hideAxisLine,
  hideTicks,
  hideZero,
  className,
}) {
  return (
    <Axis
      className={cx('vx-axis-right', className)}
      orientation={ORIENT.right}
      top={top}
      left={left}
      scale={scale}
      stroke={stroke}
      strokeWidth={strokeWidth}
      strokeDasharray={strokeDasharray}
      rangePadding={rangePadding}
      labelComponent={
        typeof label === 'string' ?
        <text
          textAnchor="middle"
          fontFamily="Arial"
          fontSize={10}
          fill="black"
        >
          {label}
        </text>
        : label
      }
      numTicks={numTicks}
      tickFormat={tickFormat}
      tickLength={tickLength}
      tickTransform={tickTransform}
      tickStroke={tickStroke}
      labelOffset={labelOffset}
      tickLabelComponent={tickLabelComponent}
      hideAxisLine={hideAxisLine}
      hideTicks={hideTicks}
      hideZero={hideZero}
    />
  );
}
<|endoftext|>


<commit_before>'use strict';

var path = require('path');

var autoprefixer = require('autoprefixer'),
    csswring = require('csswring'),
    mqpacker = require('css-mqpacker');

module.exports = [

    'build stylesheets',

    function() {
        return this.gulp

            .src(path.join(this.paths.css, '*.styl'))

            .pipe(this.plugins.stylus({
                paths: [
                    path.join(this.paths.css, 'imports'),
                    path.join(this.paths.assets, 'img'),
                    path.join(this.paths.cwd, 'node_modules'),
                    path.join(this.paths.cwd, 'node_modules/collection.stylus/src/stylus/imports/')
                ],
                'include css': true,
                url: {
                    name: 'inline-url',
                    limit: false
                },
                sourcemap: {
                    inline: true,
                    sourceRoot: '.',
                    basePath: path.relative(this.paths.cwd, this.paths.cssDest)
                }
            }))

            .pipe(this.plugins.sourcemaps.init({
                loadMaps: true
            }))

            .pipe(this.plugins.postcss([
                autoprefixer({
                    browsers: [
                        'last 2 versions',
                        '> 2%',
                        'Opera 11.1',
                        'Firefox ESR'
                    ]
                }),
                mqpacker
            ].concat(this.env === 'production' ?
                [csswring({
                    preserveHacks: true
                })] : []
            )))

            .pipe(this.plugins.sourcemaps.write('.', {
                includeContent: true,
                sourceRoot: '.'
            }))

            .pipe(this.gulp.dest(this.paths.cssDest))

            .on('error', this.log.error);

    }

];
<commit_msg>fix sourcemaps generation for stylus/css task<commit_after>'use strict';

var path = require('path');

var autoprefixer = require('autoprefixer'),
    csswring = require('csswring'),
    mqpacker = require('css-mqpacker');

module.exports = [

    'build stylesheets',

    function() {
        return this.gulp

            .src(path.join(this.paths.css, '*.styl'))

            .pipe(this.plugins.sourcemaps.init())

            .pipe(this.plugins.stylus({
                paths: [
                    path.join(this.paths.css, 'imports'),
                    path.join(this.paths.assets, 'img'),
                    path.join(this.paths.cwd, 'node_modules'),
                    path.join(this.paths.cwd, 'node_modules/collection.stylus/src/stylus/imports/')
                ],
                'include css': true,
                url: {
                    name: 'inline-url',
                    limit: false
                }
            }))

            .pipe(this.plugins.postcss([
                autoprefixer({
                    browsers: [
                        'last 2 versions',
                        '> 2%',
                        'Opera 11.1',
                        'Firefox ESR'
                    ]
                }),
                mqpacker
            ].concat(this.env === 'production' ?
                [csswring({
                    preserveHacks: true
                })] : []
            )))

            .pipe(this.plugins.sourcemaps.write('.', {
                includeContent: true,
                sourceRoot: '.'
            }))

            .pipe(this.gulp.dest(this.paths.cssDest))

            .on('error', this.log.error);

    }

];
<|endoftext|>


<commit_before>import resolve from 'rollup-plugin-node-resolve';
import uglify from 'rollup-plugin-uglify';
import babel from 'rollup-plugin-babel';

const banner = `
/**
 * Martian - Core JavaScript API for MindTouch
 *
 * Copyright (c) 2015 MindTouch Inc.
 * www.mindtouch.com  oss@mindtouch.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @preserve
 */
`;

export default {
    entry: './global.js',
    targets: [
        {
            dest: 'build/martian.min.js',
            format: 'iife',
            moduleName: 'MindTouch',
            banner
        }
    ],
    plugins: [
        resolve(),
        babel({
            babelrc: false,
            presets: [
                [ 'es2015', { modules: false } ]
            ],
            plugins: [ 'external-helpers' ]
        }),
        uglify()
    ],
    external: [ 'crypto' ]
};
<commit_msg>preserve license banner<commit_after>import resolve from 'rollup-plugin-node-resolve';
import uglify from 'rollup-plugin-uglify';
import babel from 'rollup-plugin-babel';

const banner = `
/**
 * Martian - Core JavaScript API for MindTouch
 *
 * Copyright (c) 2015 MindTouch Inc.
 * www.mindtouch.com  oss@mindtouch.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @preserve
 */
`;

export default {
    entry: './global.js',
    targets: [
        {
            dest: 'build/martian.min.js',
            format: 'iife',
            moduleName: 'MindTouch',
            banner
        }
    ],
    plugins: [
        resolve(),
        babel({
            babelrc: false,
            presets: [
                [ 'es2015', { modules: false } ]
            ],
            plugins: [ 'external-helpers' ]
        }),
        uglify({
            output: {
                comments: 'some'
            }
        })
    ],
    external: [ 'crypto' ]
};
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');

var HOME = process.env.HOME || process.env.USERPROFILE;

var PROFILE = [
	path.join(HOME, '.bashrc'),
	path.join(HOME, '.bash_profile')
].filter(fs.existsSync)[0] || path.join(HOME, '.bash_profile');

var BASH_COMPLETION_DIR = [
	'/usr/local/etc/bash_completion.d',
	'/etc/bash_completion.d',
	path.join(HOME, 'bash_completion.d'),
	path.join(HOME, '.bash_completion.d')
].filter(fs.existsSync)[0];

var TEMPLATE = fs.readFileSync(path.join(__dirname, 'template.sh'), 'utf-8');

var detectBin = function() {
	return (process.env._ || process.env.SUDO_COMMAND || '').split('/').pop();
};

module.exports = function(tab) {
	tab('completion')('--save', '-s')('--bin', '-b')('--dir', '-d')(function(opts) {
		var bin = opts.bin || detectBin();
		var completionDir = opts.dir || BASH_COMPLETION_DIR;

		if (!bin) {
			console.error('Could not detect bin name. Use --bin to specify it');
			process.exit(1);
		}

		var completion = TEMPLATE
			.replace(/\{cmd\}/g, bin)
			.replace(/\{completionDir\}/g, completionDir || '$BASH_COMPLETION_DIR')
			.replace(/\{profile\}/g, PROFILE.replace(HOME, '~'));

		if (opts.save) {
			if (!completionDir) {
				console.error('Could not locate bash_completion.d\nUse "'+bin+' completion" and do a manual install');
				process.exit(2);
			}
			try {
				fs.writeFileSync(path.join(completionDir, bin), completion);
			} catch (err) {
				if (err.code === 'EACCES') {
					console.error('Permission denied: could not write to '+path.join(completionDir, bin));
					process.exit(1);
				}
				throw err;
			}
			try {
				fs.writeSync(1, '# Completion for '+bin+' was installed.\n# To enable it now restart your terminal or\n\n. '+path.join(completionDir, bin)+'\n\n');
			} catch (err) {
				if (err.code !== 'EPIPE') throw err;
			}
			process.exit(0);
		}

		try {
			fs.writeSync(1, completion+'\n');
		} catch (err) {
			if (err.code !== 'EPIPE') throw err;
		}

		process.exit(0);
	});
};<commit_msg>more support for sudo bin<commit_after>var fs = require('fs');
var path = require('path');

var HOME = process.env.HOME || process.env.USERPROFILE;

var PROFILE = [
	path.join(HOME, '.bashrc'),
	path.join(HOME, '.bash_profile')
].filter(fs.existsSync)[0] || path.join(HOME, '.bash_profile');

var BASH_COMPLETION_DIR = [
	'/usr/local/etc/bash_completion.d',
	'/etc/bash_completion.d',
	path.join(HOME, 'bash_completion.d'),
	path.join(HOME, '.bash_completion.d')
].filter(fs.existsSync)[0];

var TEMPLATE = fs.readFileSync(path.join(__dirname, 'template.sh'), 'utf-8');

var detectBin = function() {
	return (process.env._ || process.env.SUDO_COMMAND || '').split('/').pop().split(' ')[0];
};

module.exports = function(tab) {
	tab('completion')('--save', '-s')('--bin', '-b')('--dir', '-d')(function(opts) {
		var bin = opts.bin || detectBin();
		var completionDir = opts.dir || BASH_COMPLETION_DIR;

		if (!bin) {
			console.error('Could not detect bin name. Use --bin to specify it');
			process.exit(1);
		}

		var completion = TEMPLATE
			.replace(/\{cmd\}/g, bin)
			.replace(/\{completionDir\}/g, completionDir || '$BASH_COMPLETION_DIR')
			.replace(/\{profile\}/g, PROFILE.replace(HOME, '~'));

		if (opts.save) {
			if (!completionDir) {
				console.error('Could not locate bash_completion.d\nUse "'+bin+' completion" and do a manual install');
				process.exit(2);
			}
			try {
				fs.writeFileSync(path.join(completionDir, bin), completion);
			} catch (err) {
				if (err.code === 'EACCES') {
					console.error('Permission denied: could not write to '+path.join(completionDir, bin));
					process.exit(1);
				}
				throw err;
			}
			try {
				fs.writeSync(1, '# Completion for '+bin+' was installed.\n# To enable it now restart your terminal or\n\n. '+path.join(completionDir, bin)+'\n\n');
			} catch (err) {
				if (err.code !== 'EPIPE') throw err;
			}
			process.exit(0);
		}

		try {
			fs.writeSync(1, completion+'\n');
		} catch (err) {
			if (err.code !== 'EPIPE') throw err;
		}

		process.exit(0);
	});
};<|endoftext|>


<commit_before>var libraryLoaded = false;
var callbacks = [];

module.exports = function(callback) {
    if (!libraryLoaded) {
        callbacks.push(callback);
    } else {
        callback.call();
    }
};

var cb = 'kwfUp-recaptchaCallback'.replace('-', '_');
window[cb] = function() {
    callbacks.forEach(function(callback) {
        callback.call();
    });
    libraryLoaded = true;
};
(function(cb){
    a='https://www.google.com/recaptcha/api.js?onload=' + cb + '&render=explicit';
    b=document;c='script';d=b.createElement(c);d.src=a;d.type='text/java'+c;d.async=true;
    a=b.getElementsByTagName(c)[0];a.parentNode.insertBefore(d,a);
})(cb);
<commit_msg>Load captcha library only when used<commit_after>var isRequested = false;
var isLoaded = false;
var callbacks = [];

module.exports = function(callback) {
    if (!isLoaded) {
        callbacks.push(callback);
    } else {
        callbacks.call();
    }

    if (!isRequested) {
        var cb = 'kwfUp-recaptchaCallback'.replace('-', '_');
        window[cb] = function() {
            isLoaded = true;
            callbacks.forEach(function(callback) {
                callback.call();
            });
        };
        (function(cb){
            var el = document.createElement('script');
            el.src = 'https://www.google.com/recaptcha/api.js?onload=' + cb + '&render=explicit';
            el.type = 'text/javascript';
            el.async = true;
            var targetEl = document.getElementsByTagName('script')[0];
            targetEl.parentNode.insertBefore(el, targetEl);
        })(cb);
        isRequested = true;
    }
};
<|endoftext|>


<commit_before>'use strict';
var React = require('react');

// Render a dropdown menu. All components within the dropdown get wrapped in
 tags, so the 'a' elements in:
//
// <DropdownMenu>
//   First
//   Second
// </DropdownMenu>
//
// ...get rendered as
//
First

//
Second

//
// Click handling method taken from:
// https://github.com/facebook/react/issues/579#issuecomment-60841923

var DropdownButton = module.exports.DropdownButton = React.createClass({
    propTypes: {
        title: React.PropTypes.string.isRequired, // Title of the trigger button
        disabled: React.PropTypes.bool // True to disable button
    },

    getInitialState: function() {
        return {
            open: false // True if dropdown is open
        }
    },

    componentDidMount: function() {
        // Add a click handler to the DOM document -- the entire page
        document.addEventListener('click', this.documentClickHandler);
    },

    componentWillUnmount: function() {
        // Remove the DOM document click handler now that the DropdownButton is going away.
        document.removeEventListener('click', this.documentClickHandler);
    },

    documentClickHandler: function() {
        // A click outside the DropdownButton closes the dropdown
        this.setState({open: false});
    },

    triggerClickHandler: function(e) {
        // After clicking the dropdown trigger button, don't allow the event to bubble to the rest of the DOM.
        e.nativeEvent.stopImmediatePropagation();
        this.setState({open: !this.state.open});
    },

    render: function() {
        return (

                    {this.props.title}

                {this.props.children}

        );
    }
});
<commit_msg>Button instead of link for Visualization<commit_after>'use strict';
var React = require('react');

// Render a dropdown menu. All components within the dropdown get wrapped in
 tags, so the 'a' elements in:
//
// <DropdownMenu>
//   First
//   Second
// </DropdownMenu>
//
// ...get rendered as
//
First

//
Second

//
// Click handling method taken from:
// https://github.com/facebook/react/issues/579#issuecomment-60841923

var DropdownButton = module.exports.DropdownButton = React.createClass({
    propTypes: {
        title: React.PropTypes.string.isRequired, // Title of the trigger button
        disabled: React.PropTypes.bool // True to disable button
    },

    getInitialState: function() {
        return {
            open: false // True if dropdown is open
        }
    },

    componentDidMount: function() {
        // Add a click handler to the DOM document -- the entire page
        document.addEventListener('click', this.documentClickHandler);
    },

    componentWillUnmount: function() {
        // Remove the DOM document click handler now that the DropdownButton is going away.
        document.removeEventListener('click', this.documentClickHandler);
    },

    documentClickHandler: function() {
        // A click outside the DropdownButton closes the dropdown
        this.setState({open: false});
    },

    triggerClickHandler: function(e) {
        // After clicking the dropdown trigger button, don't allow the event to bubble to the rest of the DOM.
        e.nativeEvent.stopImmediatePropagation();
        this.setState({open: !this.state.open});
    },

    render: function() {
        return (

                <button disabled={this.props.disabled} className="btn btn-info btn-sm dropdown-toggle" onClick={this.triggerClickHandler}>
                    {this.props.title}
                </button>
                {this.props.children}

        );
    }
});
<|endoftext|>


<commit_before>/*
 * The competition store keeps track of competitions that are in progress.
 * The store's purpose is to minimise DB I/O if the open competitions need
 * to be queried often.
 */
import { db, io } from './main';
import { loadCompetitionResults } from './api/competition';

export const competitions = {}; // Competition store, map from id to competition
const clients = {};      // Client sockets by socket ID (i.e. socket.id -> to socket mapping)

const competitionDurationHours = 24;

// Dictionary of userId,socket pairs which tell the socket to use
// when sending notification for specific user.
const notificationSubscribers = {};

// Send list of competitions to all connected sockets.
// Results need to be camelCased for correct object property names
// when restoring competitions from database upon startup.
// If clientSocket is null, broadcast to all connected clients.
function broadcastCompetitions(clientSocket = null) {
  if (!clientSocket)
    io.sockets.emit('competitionListUpdate', competitions);
  else
    clientSocket.emit('competitionListUpdate', competitions);
}

// Send list of competition result to all connected clients
function broadcastCompetitionResults(competitionId) {
  io.sockets.emit('competitionResultsUpdate', {
    competition: competitionId,
    results: competitions[competitionId].results,
  });
}

// Adds typing test object to the competition store
export function addCompetition(competition) {
  competition.duration = competitionDurationHours;
  competitions[competition.id] = competition;
  competitions[competition.id].results = {};

  broadcastCompetitions();
  // Keep competition open for 24 hours
  setTimeout(closeCompetition, competitionDurationHours * 60 * 60 * 1000, competition.id);
}

// Adds a result to competition. Result is expected to be an
// object containing necessary fields to describe the result.
// If `topResultNotifications` is true, creates notifications about the result
// for participants of the competition in question.
export async function addResult(competitionId, result, topResultNotifications = true) {
  let competition = competitions[competitionId];

  const userCurrentResult = competition.results[result.user.id];

  // If there's already a result that's better than this one, ignore the new result
  if (userCurrentResult && userCurrentResult.wpm > result.wpm)
    return;

  competition.results[result.user.id] = result;
  broadcastCompetitionResults(competitionId);

  if (!topResultNotifications)
    return;

  let sortedResults = sortResults(competition.results);
  let ranking = sortedResults.findIndex(r => r.user.id === result.user.id);

  // Create top result event for top3 results
  if (ranking < 3) {
    try {
      let res = await db.query('SELECT create_competition_top_result_event($1, $2, $3, $4)',
        [competitionId, result.user.id, result.wpm, ranking]);
      let eventId = res.rows[0].createCompetitionTopResultEvent;
      await createCompetitionNotifications(eventId, competitionId);
      await notifyCompetitionParticipants(competitionId, {
        eventId,
        competition: competitionId,
        type: 'top_result',
        wpm: result.wpm,
        ranking,
        user: result.user,
      });
    } catch(e) {
      console.log("Warning: something in addResult() didn't go as planned.");
      console.log(`competitionId: ${competitionId}, userId: ${result.user.id}`);
      console.log(`${e.name}: ${e.message}`);
    }
  }
}

// Notifies connected clients who are participants in a competition about
// a competition event
async function notifyCompetitionParticipants(competitionId, event) {
  let participants = await getParticipants(competitionId);
  for (let userId of participants) {
    if (notificationSubscribers[userId])
      notificationSubscribers[userId].emit('eventNotification', event);
  }
}

// Creates a notification for all participants in a competition about an event
async function createCompetitionNotifications(eventId, competitionId) {
  let participants = await getParticipants(competitionId);
  return Promise.all(participants.map(userId =>
    db.query('INSERT INTO notifications (usr, event) VALUES($1, $2)', [userId, eventId])));
}

// Returns an array of competition results, sorted by WPM in descending order.
// Argument is a result dictionary where userId is a key that maps
// to the user's (best) result in a competition.
function sortResults(results) {
  return Object.keys(results)
    .map(userId => results[userId])
    .sort((a, b) => b.wpm - a.wpm);
}

export function getCompetitionContent(competitionId) {
  return competitions[competitionId].content;
}

// Closes a competition:
//  - set status to finished
//  - create a competition 'finished' event
//  - create notification for all participants about competition being finished
async function closeCompetition(competitionId) {
  try {
    let result = await db.query('SELECT close_competition($1)', [competitionId]);
    let eventId = result.rows[0].closeCompetition;
    let participants = await getParticipants(competitionId);

    delete(competitions[competitionId]);
    broadcastCompetitions();
    await Promise.all(participants.map(userId =>
          db.query('INSERT INTO notifications(usr, event) VALUES($1, $2)',
            [userId, eventId])));
    } catch(e) {
      console.log('Warning: failed to close competition.\n' +
        `competitionId: ${competitionId}\n` +
        `${e.name}: ${e.message}`);
    }
}

// Get competition participants.
// Returns a promise that resolved with an array of user ids.
async function getParticipants(competitionId) {
  let res =
    await db.query('SELECT DISTINCT usr FROM results WHERE competition=$1',
      [competitionId]);

  return res.rows.map(r => r.usr);
}

// Helper to restore in-progress competitions from db when starting the server
// --- NOTE: if a competition would have been finished while the server was not running,
// and should thus have been closed but was not because the server indeed was not running,
// then such a competition will, in fact, enjoy eternal life.
// It's a bug, yes, but this restore functionality exists solely on making the developing of the
// application more pleasant and therefore this the bug doesn't bother us too much at this point.
// It could be fixed, but I'd rather take the time to write this comment than think about
// timestamps and setTimeouts.
export async function restoreCompetitions() {
  try {
    let runningCompetitions =
      (await db.query('SELECT id, created_at, created_by, finished, content, language, duration FROM competitions WHERE finished=false'))
        .rows;

    for (let c of runningCompetitions) {
      competitions[c.id] = c;

      // Load competition results
      c.results = {};
      let results = await loadCompetitionResults(c.id);
      for (let r of results)
        addResult(c.id, r, false);
      }
    } catch(e) {
      console.log("Warning: can't restore competitions from db");
      console.log(`${e.name}: ${e.message}`);
    }
}

// newClient(): stores client socket to client pool when they connect.
// On disconnect, remove clients from the pool.
export function newClient(socket) {
  clients[socket.id] = socket;
  socket.on('disconnect', () => {
    // Remove possible notification subscription
    for (let userId of Object.keys(notificationSubscribers)) {
      if (notificationSubscribers[userId] == socket) {
        delete(notificationSubscribers[userId]);
        break;
      }
    }
    delete (clients[socket.id]);
  });

  socket.on('notificationSubscribe', msg => {
    if (msg.userId)
      notificationSubscribers[msg.userId] = socket;
  });
}

export function getRunningCompetitions() {
  return Object.keys(competitions).map(key => {
    const { id, language, createdAt, createdBy, duration, finished, content } = competitions[key];
    return {
      id, language, createdAt, createdBy, duration, finished, content,
    };
  });
}
<commit_msg>Push notification id's together with event notifications sent over websocket<commit_after>/*
 * The competition store keeps track of competitions that are in progress.
 * The store's purpose is to minimise DB I/O if the open competitions need
 * to be queried often.
 */
import { db, io } from './main';
import { loadCompetitionResults } from './api/competition';

export const competitions = {}; // Competition store, map from id to competition
const clients = {};      // Client sockets by socket ID (i.e. socket.id -> to socket mapping)

const competitionDurationHours = 24;

// Dictionary of userId,socket pairs which tell the socket to use
// when sending notification for specific user.
const notificationSubscribers = {};

// Send list of competitions to all connected sockets.
// Results need to be camelCased for correct object property names
// when restoring competitions from database upon startup.
// If clientSocket is null, broadcast to all connected clients.
function broadcastCompetitions(clientSocket = null) {
  if (!clientSocket)
    io.sockets.emit('competitionListUpdate', competitions);
  else
    clientSocket.emit('competitionListUpdate', competitions);
}

// Send list of competition result to all connected clients
function broadcastCompetitionResults(competitionId) {
  io.sockets.emit('competitionResultsUpdate', {
    competition: competitionId,
    results: competitions[competitionId].results,
  });
}

// Adds typing test object to the competition store
export function addCompetition(competition) {
  competition.duration = competitionDurationHours;
  competitions[competition.id] = competition;
  competitions[competition.id].results = {};

  broadcastCompetitions();
  // Keep competition open for 24 hours
  setTimeout(closeCompetition, competitionDurationHours * 60 * 60 * 1000, competition.id);
}

// Adds a result to competition. Result is expected to be an
// object containing necessary fields to describe the result.
// If `topResultNotifications` is true, creates notifications about the result
// for participants of the competition in question.
export async function addResult(competitionId, result, topResultNotifications = true) {
  let competition = competitions[competitionId];

  const userCurrentResult = competition.results[result.user.id];

  // If there's already a result that's better than this one, ignore the new result
  if (userCurrentResult && userCurrentResult.wpm > result.wpm)
    return;

  competition.results[result.user.id] = result;
  broadcastCompetitionResults(competitionId);

  if (!topResultNotifications)
    return;

  let sortedResults = sortResults(competition.results);
  let ranking = sortedResults.findIndex(r => r.user.id === result.user.id);

  // Create top result event for top3 results
  if (ranking < 3) {
    try {
      let res = await db.query('SELECT create_competition_top_result_event($1, $2, $3, $4)',
        [competitionId, result.user.id, result.wpm, ranking]);
      let eventId = res.rows[0].createCompetitionTopResultEvent;
      let insertRes = await createCompetitionNotifications(eventId, competitionId);
      let userNotifications = {};

      insertRes.forEach(r => {
        let { userId, notificationId } = r.rows[0];
        userNotifications[userId] = notificationId;
      });

      await notifyCompetitionParticipants(
        competitionId, {
          eventId,
          competition: competitionId,
          type: 'top_result',
          wpm: result.wpm,
          ranking,
          user: result.user,
        },
        userNotifications);

    } catch(e) {
      console.log("Warning: something in addResult() didn't go as planned.");
      console.log(`competitionId: ${competitionId}, userId: ${result.user.id}`);
      console.log(`${e.name}: ${e.message}`);
    }
  }
}

// Notifies connected clients who are participants in a competition about
// a competition event.
//
// Arg `notificationUserMappings` maps user ids to notification ids ([{ userId: notificationId }]),
// telling which notification belongs to whom.
async function notifyCompetitionParticipants(competitionId, event, userNotificationMapping) {
  let participants = await getParticipants(competitionId);
  for (let userId of participants) {
    if (notificationSubscribers[userId]) {
      let notificationId = userNotificationMapping[userId];
      event['notificationId'] = notificationId;
      console.log(event);
      notificationSubscribers[userId].emit('eventNotification', event);
    }
  }
}

// Creates a notification for all participants in a competition about an event
async function createCompetitionNotifications(eventId, competitionId) {
  let participants = await getParticipants(competitionId);
  return Promise.all(participants.map(userId =>
     db.query('INSERT INTO notifications (usr, event) VALUES($1, $2) RETURNING id AS notification_id, usr AS user_id', [userId, eventId])));
}

// Returns an array of competition results, sorted by WPM in descending order.
// Argument is a result dictionary where userId is a key that maps
// to the user's (best) result in a competition.
function sortResults(results) {
  return Object.keys(results)
    .map(userId => results[userId])
    .sort((a, b) => b.wpm - a.wpm);
}

export function getCompetitionContent(competitionId) {
  return competitions[competitionId].content;
}

// Closes a competition:
//  - set status to finished
//  - create a competition 'finished' event
//  - create notification for all participants about competition being finished
async function closeCompetition(competitionId) {
  try {
    let result = await db.query('SELECT close_competition($1)', [competitionId]);
    let eventId = result.rows[0].closeCompetition;
    let participants = await getParticipants(competitionId);

    delete(competitions[competitionId]);
    broadcastCompetitions();
    await Promise.all(participants.map(userId =>
          db.query('INSERT INTO notifications(usr, event) VALUES($1, $2)',
            [userId, eventId])));
    } catch(e) {
      console.log('Warning: failed to close competition.\n' +
        `competitionId: ${competitionId}\n` +
        `${e.name}: ${e.message}`);
    }
}

// Get competition participants.
// Returns a promise that resolved with an array of user ids.
async function getParticipants(competitionId) {
  let res =
    await db.query('SELECT DISTINCT usr FROM results WHERE competition=$1',
      [competitionId]);

  return res.rows.map(r => r.usr);
}

// Helper to restore in-progress competitions from db when starting the server
// --- NOTE: if a competition would have been finished while the server was not running,
// and should thus have been closed but was not because the server indeed was not running,
// then such a competition will, in fact, enjoy eternal life.
// It's a bug, yes, but this restore functionality exists solely on making the developing of the
// application more pleasant and therefore this the bug doesn't bother us too much at this point.
// It could be fixed, but I'd rather take the time to write this comment than think about
// timestamps and setTimeouts.
export async function restoreCompetitions() {
  try {
    let runningCompetitions =
      (await db.query('SELECT id, created_at, created_by, finished, content, language, duration FROM competitions WHERE finished=false'))
        .rows;

    for (let c of runningCompetitions) {
      competitions[c.id] = c;

      // Load competition results
      c.results = {};
      let results = await loadCompetitionResults(c.id);
      for (let r of results)
        addResult(c.id, r, false);
      }
    } catch(e) {
      console.log("Warning: can't restore competitions from db");
      console.log(`${e.name}: ${e.message}`);
    }
}

// newClient(): stores client socket to client pool when they connect.
// On disconnect, remove clients from the pool.
export function newClient(socket) {
  clients[socket.id] = socket;
  socket.on('disconnect', () => {
    // Remove possible notification subscription
    for (let userId of Object.keys(notificationSubscribers)) {
      if (notificationSubscribers[userId] == socket) {
        delete(notificationSubscribers[userId]);
        break;
      }
    }
    delete (clients[socket.id]);
  });

  socket.on('notificationSubscribe', msg => {
    if (msg.userId)
      notificationSubscribers[msg.userId] = socket;
  });
}

export function getRunningCompetitions() {
  return Object.keys(competitions).map(key => {
    const { id, language, createdAt, createdBy, duration, finished, content } = competitions[key];
    return {
      id, language, createdAt, createdBy, duration, finished, content,
    };
  });
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';

import { colors, typography } from '../theme/variables';
import { toTitleCase } from '../helpers';

const Alert = styled.div`
  background: ${(props) => {
    const type = props.type;
    if (type === 'alert') {
      return colors.yellow;
    } else if (type === 'success') {
      return colors.green;
    } else if (type === 'error') {
      return colors.red;
    }
    return colors.lightblack;
  }};
  padding: 1em;
  margin: 1.5em;
  border-radius: 0.25em;
  border: inset solid 3px rgba(255, 255, 255, 0.5);
  font-family: ${typography.monospace};
  position: relative;

  .showHide {
    position: absolute;
    top: 1em;
    right: 1em;
    width: 2em;
    height: 2em;
    padding: 0;
    border: 2px solid ${colors.black};
    border-radius: 50%;
    background: transparent;
    span {
      display: block;
      position: absolute;
      width: 80%;
      height: 3px;
      background: ${colors.black};
      transition: all .1s ease-in-out;
      transform-origin: 50% 50%;
      left: 10%;
      top: 50%;
      margin-top: -1.5px;
    }

  }

  h4, .actionButton {
    height: auto;
    display: block;
    opacity: 1;
    transition: all .5s ease-in-out;
  }

  h4 {
    margin: 0;
  }

  .actionButton {
    margin: .5em 0 0;
    background: transparent;
    border: 2px solid ${colors.black};
    font-family: ${typography.monospace};
    padding: .4em 1em;
  }

  &.closed {
    h4, .actionButton {
      height: 0;
      display: none;
      opacity: 0;
    }
    .showHide {
      .vertical {
        transform: rotate(90deg);
      }
    }
  }
`;

class Banner extends Component {
  constructor() {
    super();
    this.showHide = this.showHide.bind(this);
    this.doAction = this.doAction.bind(this);

    this.state = {
      closed: false,
    };
  }

  showHide() {
    this.setState({
      closed: !this.state.closed,
    })
  }

  doAction() {
    const action = this.props.action;
    if (action === 'hide') {
      console.log('hide')
      this.showHide();
    }
    if (action === 'reload' || 'retry') {
      console.log('forceUpdate');
      return this.forceUpdate();
    }
    this.setState.action = 'Unknown action';
    return null;
  }

  render() {
    const action = this.props.action;
    const closed = this.state.closed ? 'closed' : null;
    const showHideButton = (<button className="showHide" onClick={this.showHide}></button>)
    const actionButton = (<button className="actionButton" onClick={action === 'hide' ? this.showHide : this.doAction}>{toTitleCase(this.props.action)}</button>);

    return (
      <Alert type={this.props.type} className={closed}>
        {this.state.closed ? `${toTitleCase(this.props.type)} Message` : null}
        {showHideButton}

{this.props.children}

        {actionButton}
      </Alert>
    );
  }
}

Banner.propTypes = {
  children: PropTypes.node.isRequired,
  type: PropTypes.string,
  action: PropTypes.string,
};

Banner.defaultProps = {
  type: 'info',
  action: 'hide',
};

export default Banner;
<commit_msg>Make banner useable<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
// import { goBack } from 'react-router-dom';
import styled from 'styled-components';

import { colors, typography } from '../theme/variables';
import { toTitleCase } from '../helpers';

const Alert = styled.div`
  background: ${(props) => {
    const type = props.type;
    if (type === 'alert') {
      return colors.yellow;
    } else if (type === 'success') {
      return colors.green;
    } else if (type === 'error') {
      return colors.red;
    }
    return colors.lightblack;
  }};
  padding: 1em 2em 1em 1em;
  margin: 0 0 1.5em;
  border-radius: 0.25em;
  border: inset solid 3px rgba(255, 255, 255, 0.5);
  font-family: ${typography.monospace};
  position: relative;

  .showHide {
    position: absolute;
    top: 1em;
    right: 1em;
    width: 2em;
    height: 2em;
    padding: 0;
    border: 2px solid ${colors.black};
    border-radius: 50%;
    background: transparent;
    span {
      display: block;
      position: absolute;
      width: 80%;
      height: 3px;
      background: ${colors.black};
      transition: all 0.1s ease-in-out;
      transform-origin: 50% 50%;
      left: 10%;
      top: 50%;
      margin-top: -1.5px;
    }
  }

  h4,
  .actionButton {
    height: auto;
    display: block;
    opacity: 1;
    transition: all 0.5s ease-in-out;
  }

  h4 {
    margin: 0;
  }

  .actionButton {
    margin: 0.5em 0 0;
    background: transparent;
    border: 2px solid ${colors.black};
    font-family: ${typography.monospace};
    padding: 0.4em 1em;
  }

  &.closed {
    h4,
    .actionButton {
      height: 0;
      display: none;
      opacity: 0;
    }
    .showHide {
      .vertical {
        transform: rotate(90deg);
      }
    }
  }
`;

class Banner extends Component {
  constructor() {
    super();
    this.showHide = this.showHide.bind(this);
    this.doAction = this.handleAction.bind(this);

    this.state = {
      closed: false,
      action: null,
    };
  }

  showHide() {
    this.setState({
      closed: !this.state.closed,
    });
  }

  handleAction() {
    const action = this.props.action;
    if (typeof action === 'function') {
      this.props.action();
    }
    if (action === 'hide') {
      console.log('hide');
      this.showHide();
    }
    if (action === 'reload') {
      console.log('forceUpdate');
      return this.forceUpdate();
    }
    if (action === 'back') {
      // TODO: make router go back
      console.log('Going back');
      return this.context.router.goBack();
    }
    this.setState.action = 'Unknown action';
    return null;
  }

  render() {
    const action = this.props.action;
    const closed = this.state.closed ? 'closed' : null;
    const showHideButton = (
      <button className="showHide" onClick={this.showHide}>

      </button>
    );
    const actionButton = (
      <button
        className="actionButton"
        onClick={action === 'hide' ? this.showHide : this.handleAction}
      >
        {toTitleCase(this.props.actionText)}
      </button>
    );

    return (
      <Alert type={this.props.type} className={closed}>
        {this.state.closed ? `${toTitleCase(this.props.type)} Message` : null}
        {showHideButton}

{this.props.children}

        {actionButton}
      </Alert>
    );
  }
}

Banner.propTypes = {
  children: PropTypes.node.isRequired,
  type: PropTypes.string,
  action: PropTypes.oneOfType([
    PropTypes.oneOf(['hide', 'reload', 'back']),
    PropTypes.func,
  ]).isRequired,
  actionText: PropTypes.string,
};

Banner.defaultProps = {
  type: 'info',
  actionText: 'Hide',
};

export default Banner;
<|endoftext|>


<commit_before>import React from 'react';
import { Col, Grid, Jumbotron, Button } from 'react-bootstrap';
import { inject, observer } from 'mobx-react';
import ItemList from './ItemList';
import Navigation from './Navigation';
import { CardColumns, Form, FormGroup, Input, Label, FormFeedback, ButtonGroup } from "reactstrap";
import Search from 'react-search';

class Browse extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      category: "",
      modal: false,
      cSelected: [],
      foundItems: []
    };
    this.toggle = this.toggle.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.loadItemsFromServer = this.loadItemsFromServer.bind(this);
    this.searchItems = this.searchItems.bind(this);
  }

  componentDidMount() {
    this.loadItemsFromServer();
  }

  loadItemsFromServer() {
    fetch('/item')
      .then(function(result) {return result.json();})
      .then(items => this.props.itemStore.setItems(items));
  }

  toggle() {
    this.setState({
      modal: !this.state.modal
    });
  }

  onCheckboxBtnClick(selected) {
    const index = this.state.cSelected.indexOf(selected);
    if (index < 0) {
      this.state.cSelected.push(selected);
    } else {
      this.state.cSelected.splice(index, 1);
    }
    this.setState({ cSelected: [...this.state.cSelected] });
  }

  handleSubmit(event) {
    event.preventDefault();
    fetch(`/item`)
    .then(function(result) {return result.json();})
    .then(data => this.setState({
    }));
  }

  searchItems(items, allItems) {
    allItems = this.props.itemStore.items;
    console.log(this.props.itemStore.items);
    allItems=allItems.filter(i => this.props.itemStore.items.category === i.category);
    this.props.itemStore.items = allItems;
  }

  render() {
    return(

          <Navigation/>

          <Grid>
            <Jumbotron style={{ backgroundColor: '#F0F1F5', boxPack: "center" }}>

                Browse the Hoods for the Goods!

              <Form>

                <FormGroup  style={{width: "50%"}}>
                  SEARCH
                  <Input state="success" placeholder="Search the Neighborhood"/>
                  <FormFeedback/>
                </FormGroup>

                <FormGroup tag="fieldset" row>
                  <legend className="col-form-legend col-sm-2">Limit Search</legend>
                  <Col sm={10}>

                    <ButtonGroup>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Power Tools")}
                        active={this.state.cSelected.includes("Power Tools")}>Power Tools
                      </Button>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Hobby")}
                        active={this.state.cSelected.includes("Hobby")}>Hobby
                      </Button>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Gardening")}
                        active={this.state.cSelected.includes("Gardening")}>Gardening
                      </Button>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Recreation")}
                        active={this.state.cSelected.includes("Recreation")}>Recreation
                      </Button>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Kitchen")}
                        active={this.state.cSelected.includes("Kitchen")}>Kitchen
                      </Button>

                    </ButtonGroup>

                  </Col>
                </FormGroup>

                <FormGroup check row className="d-flex align-items-start">
                  <Col sm={{ size: 20, offset: 2 }}>
                    <Button onClick={this.searchItems} className="btn btn-success btn-lg">Search
                    </Button>
                   </Col>
                </FormGroup>
              </Form>
            </Jumbotron>
          </Grid>

          <Grid>
            <Jumbotron style={{ backgroundColor: '#D1D5D8' }}>
              <CardColumns>
                <ItemList items={this.props.itemStore.items}/>
  //
              </CardColumns>
            </Jumbotron>
          </Grid>

    );
  }
}

Browse.propTypes = {
  children: React.PropTypes.object,
  userStore: React.PropTypes.object,
  itemStore: React.PropTypes.object,
  items: React.PropTypes.object,
  user: React.PropTypes.object
};

export default inject('itemStore', 'userStore')(observer(Browse));
<commit_msg>more browse stuff<commit_after>import React from 'react';
import { Col, Grid, Jumbotron, Button } from 'react-bootstrap';
import { inject, observer } from 'mobx-react';
import ItemList from './ItemList';
import Navigation from './Navigation';
import { CardColumns, Form, FormGroup, Input, Label, FormFeedback, ButtonGroup } from "reactstrap";

class Browse extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      category: "",
      modal: false,
      cSelected: [],
      foundItems: []
    };
    this.toggle = this.toggle.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.loadItemsFromServer = this.loadItemsFromServer.bind(this);
    this.searchItems = this.searchItems.bind(this);
  }

  componentDidMount() {
    this.loadItemsFromServer();
  }

  loadItemsFromServer() {
    fetch('/item')
      .then(function(result) {return result.json();})
      .then(items => this.props.itemStore.setItems(items));
  }

  toggle() {
    this.setState({
      modal: !this.state.modal
    });
  }

  onCheckboxBtnClick(selected) {
    const index = this.state.cSelected.indexOf(selected);
    if (index < 0) {
      this.state.cSelected.push(selected);
    } else {
      this.state.cSelected.splice(index, 1);
    }
    this.setState({ cSelected: [...this.state.cSelected] });
  }

  handleSubmit(event) {
    event.preventDefault();
    fetch(`/item`)
    .then(function(result) {return result.json();})
    .then(data => this.setState({
    }));
  }

  searchItems(items, allItems) {
    allItems = this.props.itemStore.items;
    console.log(this.props.itemStore.items);
    allItems=allItems.filter(i => this.props.itemStore.items.category === i.category);
    this.props.itemStore.items = allItems;
  }

  render() {
    return(

          <Navigation/>

          <Grid>
            <Jumbotron style={{ backgroundColor: '#F0F1F5', boxPack: "center" }}>

                Browse the Hoods for the Goods!

              <Form>

                <FormGroup  style={{width: "50%"}}>
                  SEARCH
                  <Input state="success" placeholder="Search the Neighborhood"/>
                  <FormFeedback/>
                </FormGroup>

                <FormGroup tag="fieldset" row>
                  <legend className="col-form-legend col-sm-2">Limit Search</legend>
                  <Col sm={10}>

                    <ButtonGroup>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Power Tools")}
                        active={this.state.cSelected.includes("Power Tools")}>Power Tools
                      </Button>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Hobby")}
                        active={this.state.cSelected.includes("Hobby")}>Hobby
                      </Button>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Gardening")}
                        active={this.state.cSelected.includes("Gardening")}>Gardening
                      </Button>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Recreation")}
                        active={this.state.cSelected.includes("Recreation")}>Recreation
                      </Button>

                      <Button color="primary" onClick={() =>
                        this.onCheckboxBtnClick("Kitchen")}
                        active={this.state.cSelected.includes("Kitchen")}>Kitchen
                      </Button>

                    </ButtonGroup>

                  </Col>
                </FormGroup>

                <FormGroup check row className="d-flex align-items-start">
                  <Col sm={{ size: 20, offset: 2 }}>
                    <Button onClick={this.searchItems} className="btn btn-success btn-lg">Search
                    </Button>
                   </Col>
                </FormGroup>
              </Form>
            </Jumbotron>
          </Grid>

          <Grid>
            <Jumbotron style={{ backgroundColor: '#D1D5D8' }}>
              <CardColumns>
                <ItemList items={this.props.itemStore.items}/>
  //
              </CardColumns>
            </Jumbotron>
          </Grid>

    );
  }
}

Browse.propTypes = {
  children: React.PropTypes.object,
  userStore: React.PropTypes.object,
  itemStore: React.PropTypes.object,
  items: React.PropTypes.object,
  user: React.PropTypes.object
};

export default inject('itemStore', 'userStore')(observer(Browse));
<|endoftext|>