<commit_before>import * as $ from './utilities';

const REGEXP_ACTIONS = /^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;

function getPointer({ pageX, pageY }, endOnly) {
  const end = {
    endX: pageX,
    endY: pageY,
  };

  if (endOnly) {
    return end;
  }

  return $.extend({
    startX: pageX,
    startY: pageY,
  }, end);
}

export default {
  resize() {
    const self = this;
    const options = self.options;
    const container = self.container;
    const containerData = self.containerData;
    const minContainerWidth = Number(options.minContainerWidth) || 200;
    const minContainerHeight = Number(options.minContainerHeight) || 100;

    if (self.disabled || containerData.width === minContainerWidth ||
      containerData.height === minContainerHeight) {
      return;
    }

    const ratio = container.offsetWidth / containerData.width;
    let canvasData;
    let cropBoxData;

    // Resize when width changed or height changed
    if (ratio !== 1 || container.offsetHeight !== containerData.height) {
      if (options.restore) {
        canvasData = self.getCanvasData();
        cropBoxData = self.getCropBoxData();
      }

      self.render();

      if (options.restore) {
        self.setCanvasData($.each(canvasData, (n, i) => {
          canvasData[i] = n * ratio;
        }));
        self.setCropBoxData($.each(cropBoxData, (n, i) => {
          cropBoxData[i] = n * ratio;
        }));
      }
    }
  },

  dblclick() {
    const self = this;

    if (self.disabled || self.options.dragMode === 'none') {
      return;
    }

    self.setDragMode($.hasClass(self.dragBox, 'cropper-crop') ? 'move' : 'crop');
  },

  wheel(event) {
    const self = this;
    const e = $.getEvent(event);
    const ratio = Number(self.options.wheelZoomRatio) || 0.1;
    let delta = 1;

    if (self.disabled) {
      return;
    }

    e.preventDefault();

    // Limit wheel speed to prevent zoom too fast (#21)
    if (self.wheeling) {
      return;
    }

    self.wheeling = true;

    setTimeout(() => {
      self.wheeling = false;
    }, 50);

    if (e.deltaY) {
      delta = e.deltaY > 0 ? 1 : -1;
    } else if (e.wheelDelta) {
      delta = -e.wheelDelta / 120;
    } else if (e.detail) {
      delta = e.detail > 0 ? 1 : -1;
    }

    self.zoom(-delta * ratio, e);
  },

  cropStart(event) {
    const self = this;

    if (self.disabled) {
      return;
    }

    const options = self.options;
    const pointers = self.pointers;
    const e = $.getEvent(event);
    let action;

    if (e.changedTouches) {
      // Handle touch event
      $.each(e.changedTouches, (touch) => {
        pointers[touch.identifier] = getPointer(touch);
      });
    } else {
      // Handle mouse event and pointer event
      pointers[e.pointerId || 0] = getPointer(e);
    }

    if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {
      action = 'zoom';
    } else {
      action = $.getData(e.target, 'action');
    }

    if (REGEXP_ACTIONS.test(action)) {
      if ($.dispatchEvent(self.element, 'cropstart', {
        originalEvent: e,
        action,
      }) === false) {
        return;
      }

      e.preventDefault();

      self.action = action;
      self.cropping = false;

      if (action === 'crop') {
        self.cropping = true;
        $.addClass(self.dragBox, 'cropper-modal');
      }
    }
  },

  cropMove(event) {
    const self = this;
    const action = self.action;

    if (self.disabled || !action) {
      return;
    }

    const pointers = self.pointers;
    const e = $.getEvent(event);

    e.preventDefault();

    if ($.dispatchEvent(self.element, 'cropmove', {
      originalEvent: e,
      action,
    }) === false) {
      return;
    }

    if (e.changedTouches) {
      $.each(e.changedTouches, (touch) => {
        $.extend(pointers[touch.identifier], getPointer(touch, true));
      });
    } else {
      $.extend(pointers[e.pointerId || 0], getPointer(e, true));
    }

    self.change(e);
  },

  cropEnd(event) {
    const self = this;

    if (self.disabled) {
      return;
    }

    const action = self.action;
    const pointers = self.pointers;
    const e = $.getEvent(event);

    if (e.changedTouches) {
      $.each(e.changedTouches, (touch) => {
        delete pointers[touch.identifier];
      });
    } else {
      delete pointers[e.pointerId || 0];
    }

    if (action) {
      e.preventDefault();

      if (!Object.keys(pointers).length) {
        self.action = '';
      }

      if (self.cropping) {
        self.cropping = false;
        $.toggleClass(self.dragBox, 'cropper-modal', self.cropped && this.options.modal);
      }

      $.dispatchEvent(self.element, 'cropend', {
        originalEvent: e,
        action,
      });
    }
  },
};
<commit_msg>Improve code style<commit_after>import * as $ from './utilities';

const REGEXP_ACTIONS = /^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;

function getPointer({ pageX, pageY }, endOnly) {
  const end = {
    endX: pageX,
    endY: pageY,
  };

  if (endOnly) {
    return end;
  }

  return $.extend({
    startX: pageX,
    startY: pageY,
  }, end);
}

export default {
  resize() {
    const self = this;
    const options = self.options;
    const container = self.container;
    const containerData = self.containerData;
    const minContainerWidth = Number(options.minContainerWidth) || 200;
    const minContainerHeight = Number(options.minContainerHeight) || 100;

    if (self.disabled || containerData.width === minContainerWidth ||
      containerData.height === minContainerHeight) {
      return;
    }

    const ratio = container.offsetWidth / containerData.width;
    let canvasData;
    let cropBoxData;

    // Resize when width changed or height changed
    if (ratio !== 1 || container.offsetHeight !== containerData.height) {
      if (options.restore) {
        canvasData = self.getCanvasData();
        cropBoxData = self.getCropBoxData();
      }

      self.render();

      if (options.restore) {
        self.setCanvasData($.each(canvasData, (n, i) => {
          canvasData[i] = n * ratio;
        }));
        self.setCropBoxData($.each(cropBoxData, (n, i) => {
          cropBoxData[i] = n * ratio;
        }));
      }
    }
  },

  dblclick() {
    const self = this;

    if (self.disabled || self.options.dragMode === 'none') {
      return;
    }

    self.setDragMode($.hasClass(self.dragBox, 'cropper-crop') ? 'move' : 'crop');
  },

  wheel(event) {
    const self = this;
    const e = $.getEvent(event);
    const ratio = Number(self.options.wheelZoomRatio) || 0.1;
    let delta = 1;

    if (self.disabled) {
      return;
    }

    e.preventDefault();

    // Limit wheel speed to prevent zoom too fast (#21)
    if (self.wheeling) {
      return;
    }

    self.wheeling = true;

    setTimeout(() => {
      self.wheeling = false;
    }, 50);

    if (e.deltaY) {
      delta = e.deltaY > 0 ? 1 : -1;
    } else if (e.wheelDelta) {
      delta = -e.wheelDelta / 120;
    } else if (e.detail) {
      delta = e.detail > 0 ? 1 : -1;
    }

    self.zoom(-delta * ratio, e);
  },

  cropStart(event) {
    const self = this;

    if (self.disabled) {
      return;
    }

    const options = self.options;
    const pointers = self.pointers;
    const e = $.getEvent(event);
    let action;

    if (e.changedTouches) {
      // Handle touch event
      $.each(e.changedTouches, (touch) => {
        pointers[touch.identifier] = getPointer(touch);
      });
    } else {
      // Handle mouse event and pointer event
      pointers[e.pointerId || 0] = getPointer(e);
    }

    if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {
      action = 'zoom';
    } else {
      action = $.getData(e.target, 'action');
    }

    if (!REGEXP_ACTIONS.test(action)) {
      return;
    }

    if ($.dispatchEvent(self.element, 'cropstart', {
      originalEvent: e,
      action,
    }) === false) {
      return;
    }

    e.preventDefault();

    self.action = action;
    self.cropping = false;

    if (action === 'crop') {
      self.cropping = true;
      $.addClass(self.dragBox, 'cropper-modal');
    }
  },

  cropMove(event) {
    const self = this;
    const action = self.action;

    if (self.disabled || !action) {
      return;
    }

    const pointers = self.pointers;
    const e = $.getEvent(event);

    e.preventDefault();

    if ($.dispatchEvent(self.element, 'cropmove', {
      originalEvent: e,
      action,
    }) === false) {
      return;
    }

    if (e.changedTouches) {
      $.each(e.changedTouches, (touch) => {
        $.extend(pointers[touch.identifier], getPointer(touch, true));
      });
    } else {
      $.extend(pointers[e.pointerId || 0], getPointer(e, true));
    }

    self.change(e);
  },

  cropEnd(event) {
    const self = this;

    if (self.disabled) {
      return;
    }

    const action = self.action;
    const pointers = self.pointers;
    const e = $.getEvent(event);

    if (e.changedTouches) {
      $.each(e.changedTouches, (touch) => {
        delete pointers[touch.identifier];
      });
    } else {
      delete pointers[e.pointerId || 0];
    }

    if (!action) {
      return;
    }

    e.preventDefault();

    if (!Object.keys(pointers).length) {
      self.action = '';
    }

    if (self.cropping) {
      self.cropping = false;
      $.toggleClass(self.dragBox, 'cropper-modal', self.cropped && this.options.modal);
    }

    $.dispatchEvent(self.element, 'cropend', {
      originalEvent: e,
      action,
    });
  },
};
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import withIntl from '../lib/withIntl';
import Header from './Header';
import Body from './Body';
import Footer from './Footer';
import { addCreateCollectiveMutation } from '../graphql/mutations';
import CreateCollectiveForm from './CreateCollectiveForm';
import CreateCollectiveCover from './CreateCollectiveCover';
import SignInForm from './SignInForm';
import { get } from 'lodash';
import { FormattedMessage, defineMessages } from 'react-intl';

class CreateCollective extends React.Component {

  static propTypes = {
    host: PropTypes.object
  }

  constructor(props) {
    super(props);
    this.state = { collective: { type: 'COLLECTIVE' }, result: {} };
    this.createCollective = this.createCollective.bind(this);
    this.error = this.error.bind(this);
    this.resetError = this.resetError.bind(this);
    this.messages = defineMessages({
      "host.apply.title": { id: "host.apply.title", defaultMessage: "Apply to create a new {hostname} collective" },
      "collective.create.title": { id: "collective.create.title", defaultMessage: "Create an Open Collective" },
      "collective.create.description": { id: "collective.create.description", defaultMessage: "The place for your community to collect money and share your finance in full transparency." }
    });

    this.host = props.host || {
      type: 'COLLECTIVE',
      settings: {
        apply: {
          title: this.props.intl.formatMessage(this.messages["collective.create.title"]),
          description: this.props.intl.formatMessage(this.messages["collective.create.description"])
        }
      }
    };

  }

  error(msg) {
    this.setState({ result: { error: msg }})
  }

  resetError() {
    this.error();
  }

  async createCollective(CollectiveInputType) {
    if (!CollectiveInputType.tos || (get(this.host, 'settings.tos') && !CollectiveInputType.hostTos)) {
      this.setState( { result: { error: "Please accept the terms of service" }})
      return;
    }
    this.setState( { status: 'loading' });
    CollectiveInputType.type = 'COLLECTIVE';
    CollectiveInputType.HostCollectiveId = this.host.id;
    if (CollectiveInputType.tags) {
      CollectiveInputType.tags = CollectiveInputType.tags.split(',').map(t => t.trim());
    }
    CollectiveInputType.tags = [...CollectiveInputType.tags || [], ...this.host.tags || []] || [];
    if (CollectiveInputType.category) {
      CollectiveInputType.tags.push(CollectiveInputType.category);
    }
    CollectiveInputType.data = CollectiveInputType.data || {};
    CollectiveInputType.data.members = CollectiveInputType.members;
    CollectiveInputType.data.meetupSlug = CollectiveInputType.meetup;
    delete CollectiveInputType.category;
    delete CollectiveInputType.tos;
    delete CollectiveInputType.hostTos;
    console.log(">>> creating collective ", CollectiveInputType);
    try {
      const res = await this.props.createCollective(CollectiveInputType);
      const collective = res.data.createCollective;
      let successUrl;
      if (CollectiveInputType.HostCollectiveId) {
        successUrl = `${window.location.protocol}//${window.location.host}/${collective.slug}?status=collectiveCreated&CollectiveId=${collective.id}&collectiveSlug=${collective.slug}`;
      } else {
        successUrl = `${window.location.protocol}//${window.location.host}/${collective.slug}/connect/stripe`;
      }
      this.setState({ status: 'idle', result: { success: `Collective created successfully` }});
      window.location.replace(successUrl);
    } catch (err) {
      console.error(">>> createCollective error: ", JSON.stringify(err));
      const errorMsg = (err.graphQLErrors && err.graphQLErrors[0]) ? err.graphQLErrors[0].message : err.message;
      this.setState( { status: 'idle', result: { error: errorMsg }})
      throw new Error(errorMsg);
    }
  }

  render() {
    const { LoggedInUser, intl } = this.props;

    const canApply = get(this.host, 'settings.apply');
    const title = get(this.host, 'settings.apply.title') || intl.formatMessage(this.messages["host.apply.title"], { hostname: this.host.name });
    const description = get(this.host, 'settings.apply.description') || intl.formatMessage(this.messages["host.apply.description"], { hostname: this.host.name });

    return (

          <CreateCollectiveCover host={this.host} />

            { !canApply &&

<FormattedMessage id="collectives.create.error.HostNotOpenToApplications" defaultMessage="This host is not open to applications" />

            }

            { canApply && !LoggedInUser &&

<FormattedMessage id="collectives.create.signin" defaultMessage="Sign in or create an Open Collective account" />

                <SignInForm next={`/${this.host.slug}/apply`} />

            }

            { canApply && LoggedInUser &&

                <CreateCollectiveForm
                  host={this.host}
                  collective={this.state.collective}
                  onSubmit={this.createCollective}
                  onChange={this.resetError}
                  />

{this.state.result.success}

{this.state.result.error}

            }

    );
  }

}

export default withIntl(addCreateCollectiveMutation(CreateCollective));
<commit_msg>redirect to /edit#host<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import withIntl from '../lib/withIntl';
import Header from './Header';
import Body from './Body';
import Footer from './Footer';
import { addCreateCollectiveMutation } from '../graphql/mutations';
import CreateCollectiveForm from './CreateCollectiveForm';
import CreateCollectiveCover from './CreateCollectiveCover';
import SignInForm from './SignInForm';
import { get } from 'lodash';
import { FormattedMessage, defineMessages } from 'react-intl';

class CreateCollective extends React.Component {

  static propTypes = {
    host: PropTypes.object
  }

  constructor(props) {
    super(props);
    this.state = { collective: { type: 'COLLECTIVE' }, result: {} };
    this.createCollective = this.createCollective.bind(this);
    this.error = this.error.bind(this);
    this.resetError = this.resetError.bind(this);
    this.messages = defineMessages({
      "host.apply.title": { id: "host.apply.title", defaultMessage: "Apply to create a new {hostname} collective" },
      "collective.create.title": { id: "collective.create.title", defaultMessage: "Create an Open Collective" },
      "collective.create.description": { id: "collective.create.description", defaultMessage: "The place for your community to collect money and share your finance in full transparency." }
    });

    this.host = props.host || {
      type: 'COLLECTIVE',
      settings: {
        apply: {
          title: this.props.intl.formatMessage(this.messages["collective.create.title"]),
          description: this.props.intl.formatMessage(this.messages["collective.create.description"])
        }
      }
    };

  }

  error(msg) {
    this.setState({ result: { error: msg }})
  }

  resetError() {
    this.error();
  }

  async createCollective(CollectiveInputType) {
    if (!CollectiveInputType.tos || (get(this.host, 'settings.tos') && !CollectiveInputType.hostTos)) {
      this.setState( { result: { error: "Please accept the terms of service" }})
      return;
    }
    this.setState( { status: 'loading' });
    CollectiveInputType.type = 'COLLECTIVE';
    CollectiveInputType.HostCollectiveId = this.host.id;
    if (CollectiveInputType.tags) {
      CollectiveInputType.tags = CollectiveInputType.tags.split(',').map(t => t.trim());
    }
    CollectiveInputType.tags = [...CollectiveInputType.tags || [], ...this.host.tags || []] || [];
    if (CollectiveInputType.category) {
      CollectiveInputType.tags.push(CollectiveInputType.category);
    }
    CollectiveInputType.data = CollectiveInputType.data || {};
    CollectiveInputType.data.members = CollectiveInputType.members;
    CollectiveInputType.data.meetupSlug = CollectiveInputType.meetup;
    delete CollectiveInputType.category;
    delete CollectiveInputType.tos;
    delete CollectiveInputType.hostTos;
    console.log(">>> creating collective ", CollectiveInputType);
    try {
      const res = await this.props.createCollective(CollectiveInputType);
      const collective = res.data.createCollective;
      let successUrl;
      if (CollectiveInputType.HostCollectiveId) {
        successUrl = `${window.location.protocol}//${window.location.host}/${collective.slug}?status=collectiveCreated&CollectiveId=${collective.id}&collectiveSlug=${collective.slug}`;
      } else {
        successUrl = `${window.location.protocol}//${window.location.host}/${collective.slug}/edit#host`;
      }
      this.setState({ status: 'idle', result: { success: `Collective created successfully` }});
      window.location.replace(successUrl);
    } catch (err) {
      console.error(">>> createCollective error: ", JSON.stringify(err));
      const errorMsg = (err.graphQLErrors && err.graphQLErrors[0]) ? err.graphQLErrors[0].message : err.message;
      this.setState( { status: 'idle', result: { error: errorMsg }})
      throw new Error(errorMsg);
    }
  }

  render() {
    const { LoggedInUser, intl } = this.props;

    const canApply = get(this.host, 'settings.apply');
    const title = get(this.host, 'settings.apply.title') || intl.formatMessage(this.messages["host.apply.title"], { hostname: this.host.name });
    const description = get(this.host, 'settings.apply.description') || intl.formatMessage(this.messages["host.apply.description"], { hostname: this.host.name });

    return (

          <CreateCollectiveCover host={this.host} />

            { !canApply &&

<FormattedMessage id="collectives.create.error.HostNotOpenToApplications" defaultMessage="This host is not open to applications" />

            }

            { canApply && !LoggedInUser &&

<FormattedMessage id="collectives.create.signin" defaultMessage="Sign in or create an Open Collective account" />

                <SignInForm next={`/${this.host.slug}/apply`} />

            }

            { canApply && LoggedInUser &&

                <CreateCollectiveForm
                  host={this.host}
                  collective={this.state.collective}
                  onSubmit={this.createCollective}
                  onChange={this.resetError}
                  />

{this.state.result.success}

{this.state.result.error}

            }

    );
  }

}

export default withIntl(addCreateCollectiveMutation(CreateCollective));
<|endoftext|>


<commit_before>import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actions from '../../actions';

import DeviceForm from './presenter';

// takes as input the state from a reducer
function mapStateToProps(state) {

  return {
    deviceForm: state.deviceForm
  };
}

function mapDispatchToProps(dispatch) {
  return {
    deviceFormFieldChange: bindActionCreators(actions.deviceFormFieldChange, dispatch),
    deviceFormMultiFieldChange: bindActionCreators(actions.deviceFormMultiFieldChange, dispatch),
    deviceFormSubmit: bindActionCreators(actions.deviceFormSubmit, dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(DeviceForm);
<commit_msg>expose deviceFormSubmitBlockchain to presenter<commit_after>import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actions from '../../actions';

import DeviceForm from './presenter';

// takes as input the state from a reducer
function mapStateToProps(state) {

  return {
    deviceForm: state.deviceForm
  };
}

function mapDispatchToProps(dispatch) {
  return {
    deviceFormFieldChange: bindActionCreators(actions.deviceFormFieldChange, dispatch),
    deviceFormMultiFieldChange: bindActionCreators(actions.deviceFormMultiFieldChange, dispatch),
    deviceFormSubmit: bindActionCreators(actions.deviceFormSubmit, dispatch),
    deviceFormSubmitBlockchain: bindActionCreators(actions.deviceFormSubmitBlockchain, dispatch),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(DeviceForm);
<|endoftext|>


<commit_before>/**
 * This is inserted into the content to handle the letter push
 *
 * @author Aaron Saray (http://aaronsaray.com)
 */
(function(){
	/**
	 * The selectors used to find the URL
	 * @type {array}
	 */
    var selectors = [
		'div.selectedEntry a.title',			// title bar for active entry, collapsed or expanded
		'.selectedEntry a.visitWebsiteButton',	// the button square button on list view
		'a.visitWebsiteButton',					// the floating one for card view
		'.entry.selected a.title'				// title bar for active entry in React-based collapsed list view
    ];

	/**
	 * Main feedlybackgroundtab constructor
	 */
	var FBT = function() {

		/**
		 * The default key code which is ;
		 * @type {number}
		 * @private
		 */
		var _triggerKeyCode = 59;

		/**
		 * Used to create the default key code from local storage
		 * Also modifies the help popup
		 */
		this.init = function() {
			chrome.storage.sync.get('shortcutKey', function(settings) {
				if (settings.shortcutKey) {
					_triggerKeyCode = settings.shortcutKey.charCodeAt(0);
				}
			});
		};

		/**
		 * handler for key press - must be not in textarea or input and must be not altered
		 * Then it sends extension request
		 * @param e
		 */
		this.keyPressHandler = function(e) {
			var tag = e.target.tagName.toLowerCase();
			if (tag != 'input' && tag != 'textarea') {
				if ((!e.altKey && !e.ctrlKey) && e.keyCode == _triggerKeyCode) {
                    var url;
                    for (var x in selectors) {
                        url = document.querySelector(selectors[x]);
                        if (url) {
                            break;
                        }
                    }
					if (url) {
						chrome.extension.sendMessage({url: url.href});
					}
                    else {
                        console.log("Could not find any selectors from: " + selectors.join());
                    }
				}
			}
		}
	};

	if (window == top) {
		var fbt = new FBT();
		fbt.init();
		window.addEventListener('keypress', fbt.keyPressHandler, false);
	}
})();
<commit_msg>Add new selector for Article view<commit_after>/**
 * This is inserted into the content to handle the letter push
 *
 * @author Aaron Saray (http://aaronsaray.com)
 */
(function(){
	/**
	 * The selectors used to find the URL
	 * @type {array}
	 */
    var selectors = [
		'div.selectedEntry a.title',			// title bar for active entry, collapsed or expanded
		'.selectedEntry a.visitWebsiteButton',	// the button square button on list view
		'.list-entries .selected a.visitWebsiteButton',	// the button square button on list view
		'a.visitWebsiteButton',					// the floating one for card view
		'.entry.selected a.title'				// title bar for active entry in React-based collapsed list view
    ];

	/**
	 * Main feedlybackgroundtab constructor
	 */
	var FBT = function() {

		/**
		 * The default key code which is ;
		 * @type {number}
		 * @private
		 */
		var _triggerKeyCode = 59;

		/**
		 * Used to create the default key code from local storage
		 * Also modifies the help popup
		 */
		this.init = function() {
			chrome.storage.sync.get('shortcutKey', function(settings) {
				if (settings.shortcutKey) {
					_triggerKeyCode = settings.shortcutKey.charCodeAt(0);
				}
			});
		};

		/**
		 * handler for key press - must be not in textarea or input and must be not altered
		 * Then it sends extension request
		 * @param e
		 */
		this.keyPressHandler = function(e) {
			var tag = e.target.tagName.toLowerCase();
			if (tag != 'input' && tag != 'textarea') {
				if ((!e.altKey && !e.ctrlKey) && e.keyCode == _triggerKeyCode) {
                    var url;
                    for (var x in selectors) {
                        url = document.querySelector(selectors[x]);
                        if (url) {
                            break;
                        }
                    }
					if (url) {
						chrome.extension.sendMessage({url: url.href});
					}
                    else {
                        console.log("Could not find any selectors from: " + selectors.join());
                    }
				}
			}
		}
	};

	if (window == top) {
		var fbt = new FBT();
		fbt.init();
		window.addEventListener('keypress', fbt.keyPressHandler, false);
	}
})();
<|endoftext|>


<commit_before>/************************************************************
  util
*************************************************************/
const Util = (() => {
  const NAME = 'Cosmos.lib.Util';

  class Util {

    // static

    static get name() {
      return NAME;
    }

    /**
     * event on selector
     *
     * @param  {String}   selector   querySelector
     * @param  {String}   type       event type
     * @param  {Function} listener   event listener
     * @param  {Boolean}  useCapture
     * @return {number|null}
     */
    static eventOnSelector(selector, type, listener, useCapture = false) {
      let elements = document.querySelectorAll(selector);
      if (elements.length == 0) {
        return null;
      }
      for (let element of elements) {
        element.addEventListener(type, listener, useCapture);
      }
      return elements.length;
    }

    /**
     * find ancestor by selector
     *
     * @param  {Element} element
     * @param  {String}  selector
     * @return {Element|null}
     */
    static findAncestor(element, selector) {
      do {
        if(element == document.querySelector('html')) return null;
        element = element.parentElement;
      } while(!element.matches(selector));
      return element;
    }

    /**
     * wrap elements by div.wrapper
     * @param  {String} target  querySelector
     * @param  {String} wrapper wrapper's class name
     * @return {void}
     */
    static wrap(target, wrapper) {
      var elements = document.querySelectorAll(target);
      var div = document.createElement('div');
      div.classList.add(wrapper);

      for (let el of elements) {
        let parent = el.parentNode;
        let sibling = el.nextSibling;

        div.appendChild(el);

        if (sibling) {
          parent.insertBefore(div, sibling);
        } else {
          parent.appendChild(div);
        }
      }
    }

    /**
     * wrap all elements inside to div.wrapper
     * @param  {String}  target  querySelector
     * @param  {String}  wrapper wrapper's class name
     * @return {void}
     */
    static wrapAll(target, wrapper) {
      var elements = document.querySelectorAll(target);
      var div = document.createElement('div');
      div.classList.add(wrapper);
      var parent = elements[0].parentNode;
      var sibling = elements[0].nextSibling;

      elements.forEach((elm) => {
        div.appendChild(elm);
      });

      if (sibling) {
        parent.insertBefore(div, sibling);
      } else {
        parent.appendChild(div);
      }
    }

    /**
     * location.search to Object.
     *
     * @return {Object}
     */
    static locationSearchToObject() {
      let queries = window.location.search.substring(1).split('&');
      let obj = {};

      queries.forEach((value) => {
        let q = value.split('=');
        if (!q[1]) return;
        obj[decodeURIComponent(q[0])] = decodeURIComponent(q[1]);
      });

      return obj;
    }
  }

  return Util;
})();

export default Util;
<commit_msg>Add static method "isContains()" in Util.js<commit_after>/************************************************************
  util
*************************************************************/
const Util = (() => {
  const NAME = 'Cosmos.lib.Util';

  class Util {

    // static

    static get name() {
      return NAME;
    }

    /**
     * event on selector
     *
     * @param  {String}   selector   querySelector
     * @param  {String}   type       event type
     * @param  {Function} listener   event listener
     * @param  {Boolean}  useCapture
     * @return {number|null}
     */
    static eventOnSelector(selector, type, listener, useCapture = false) {
      let elements = document.querySelectorAll(selector);
      if (elements.length == 0) {
        return null;
      }
      for (let element of elements) {
        element.addEventListener(type, listener, useCapture);
      }
      return elements.length;
    }

    /**
     * find ancestor by selector
     *
     * @param  {Element} element
     * @param  {String}  selector
     * @return {Element|null}
     */
    static findAncestor(element, selector) {
      do {
        if(element == document.querySelector('html')) return null;
        element = element.parentElement;
      } while(!element.matches(selector));
      return element;
    }

    /**
     * wrap elements by div.wrapper
     * @param  {String} target  querySelector
     * @param  {String} wrapper wrapper's class name
     * @return {void}
     */
    static wrap(target, wrapper) {
      var elements = document.querySelectorAll(target);
      var div = document.createElement('div');
      div.classList.add(wrapper);

      for (let el of elements) {
        let parent = el.parentNode;
        let sibling = el.nextSibling;

        div.appendChild(el);

        if (sibling) {
          parent.insertBefore(div, sibling);
        } else {
          parent.appendChild(div);
        }
      }
    }

    /**
     * wrap all elements inside to div.wrapper
     * @param  {String}  target  querySelector
     * @param  {String}  wrapper wrapper's class name
     * @return {void}
     */
    static wrapAll(target, wrapper) {
      var elements = document.querySelectorAll(target);
      var div = document.createElement('div');
      div.classList.add(wrapper);
      var parent = elements[0].parentNode;
      var sibling = elements[0].nextSibling;

      elements.forEach((elm) => {
        div.appendChild(elm);
      });

      if (sibling) {
        parent.insertBefore(div, sibling);
      } else {
        parent.appendChild(div);
      }
    }

    /**
     * location.search to Object.
     *
     * @return {Object}
     */
    static locationSearchToObject() {
      let queries = window.location.search.substring(1).split('&');
      let obj = {};

      queries.forEach((value) => {
        let q = value.split('=');
        if (!q[1]) return;
        obj[decodeURIComponent(q[0])] = decodeURIComponent(q[1]);
      });

      return obj;
    }

    /**
     * returns true if 'big' contains 'small'.
     *
     * @param  {mixed}  big
     * @param  {mixed}  small
     * @return {Boolean}
     */
    static isContains(big, small) {
      if (typeof big !== typeof small) return false;

      if (Array.isArray(big) && Array.isArray(small)) {
        let correct = 0;
        big.forEach((v) => {
          if (small.includes(v)) correct++;
        });
        return correct == small.length;
      } else if (typeof small === 'object' && Object(small) === small) {
        for (let p in small) {
          if (!(p in big && this.isContains(big[p], small[p]))) return false;
        }
        return true;
      } else {
        return big === small;
      }
    }
  }

  return Util;
})();

export default Util;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import momentPropTypes from 'react-moment-proptypes';
import sls from 'single-line-string';

import { configureMapSQL } from '../../constants/sql_queries';
import { basemapURL, cartoUser, crashDataFieldNames } from '../../constants/app_config';
import { boroughs, configureLayerSource, crashDataChanged } from '../../constants/api';

import ZoomControls from './ZoomControls';
import CustomFilter from './customFilter';

class LeafletMap extends Component {
  constructor() {
    super();
    this.mapBounds = [[40.91553, -73.70002], [40.49613, -74.25559]];
    this.fitBoundsOptions = {
      paddingTopLeft: [60, 100],
      paddingBottomRight: [300, 120],
      maxZoom: 18
    };
    this.polyOverlayStyle = {
      color: '#17838f',
      fillColor: '#17838f'
    };
    this.mapDiv = undefined; // div Leaflet mounts to
    this.map = undefined; // instance of L.map
    this.filterPolygons = undefined; // L.geoJson for selecting an geo area to filter by
    this.filterAreaTooltip = undefined; // div for tooltip for filter area polygons
    this.mapStatsDisclaimer = undefined;
    this.customDraw = undefined;
    this.cartoLayer = undefined;
    this.cartoSubLayer = undefined;
    this.cartodbSQL = undefined;
    this.cartoSubLayerTooltip = undefined;
    this.cartoInfowindow = undefined;
    this.handleZoomIn = this.handleZoomIn.bind(this);
    this.handleZoomOut = this.handleZoomOut.bind(this);
  }

  componentDidMount() {
    const { lngLats } = this.props;
    this.cartodbSQL = new cartodb.SQL({ user: cartoUser });
    this.initMap();
    this.initCustomFilter();

    if (lngLats && lngLats.length) {
      // reverse the coordinates lng, lat -> lat, lng because Leaflet
      const reversed = lngLats.reduce((acc, cur) => {
        acc.push([cur[1], cur[0]]);
        return acc;
      }, []);
      const poly = L.polygon(reversed, this.polyOverlayStyle);
      this.customDraw.drawLayer.addLayer(poly);
    }
  }

  componentWillReceiveProps(nextProps) {
    const { geo, geojson, identifier, lngLats, drawEnabeled } = nextProps;

    if (identifier && identifier !== this.props.identifier) {
      // user filtered by a specific geography, so hide the GeoJSON boundary overlay
      this.map.removeLayer(this.filterPolygons);
    }

    if (crashDataChanged(this.props, nextProps)) {
      // if boundary filters were changed by user, update map data
      this.updateCartoSubLayer(nextProps);
    }

    if (geo === 'Citywide' && this.props.geo !== 'Citywide') {
      this.showMapStatsDisclaimer();
      this.hideFilterAreaPolygons();
    } else if (geo !== 'Citywide') {
      this.hideMapStatsDisclaimer();
    }

    if (geo !== this.props.geo && geo !== 'Citywide' && geo !== 'Custom') {
      // cancel custom draw in case it was enabled
      this.customFilterCancelDraw();
      // clear custom area layer if it was drawn
      this.customDraw.drawLayer.clearLayers();
      // make an API call for GeoJSON of boundary polygons
      this.props.fetchGeoPolygons(geo);
    }

    if (geojson.features.length) {
      if (
        (geojson.geoName !== this.props.geojson.geoName) ||
        (geo !== 'Citywide' && this.props.geo === 'Citywide') ||
        (geo !== 'Custom' && this.props.geo === 'Custom')
      ) {
        // show the user polygons for filter by area / boundary
        this.renderFilterPolygons(geo, geojson);
      }
    }

    if (geo === 'Custom' && this.props.geo !== 'Custom') {
      // clear existing drawn layer
      this.customDraw.drawLayer.clearLayers();
      // enable Leaflet Draw
      this.customFilterEnableDraw();
    }

    if (!drawEnabeled && this.props.drawEnabeled) {
      this.customFilterCancelDraw();
    } else if (drawEnabeled && !this.props.drawEnabeled) {
      this.customDraw.drawLayer.clearLayers();
      this.customFilterEnableDraw();
    }

    if (lngLats && lngLats.length) {
      // clear the custom overlay after user finishes drawing
      // this.customDraw.drawLayer.clearLayers();
    }
  }

  shouldComponentUpdate() {
    // let Leaflet have control over this part of the DOM, not React.
    return false;
  }

  handleZoomIn() {
    this.map.zoomIn();
  }

  handleZoomOut() {
    this.map.zoomOut();
  }

  initMap() {
    const self = this;
    const { zoom, lat, lng, onMapMoved } = this.props;
    this.map = L.map(this.mapDiv, {
      doubleClickZoom: false,
      center: [lat, lng],
      zoom,
      maxZoom: 18,
      zoomControl: false,
      scrollWheelZoom: false
    });

    L.tileLayer(basemapURL, {
      maxZoom: 18,
      attribution: '© OpenStreetMap'
    }).addTo(self.map);

    // update the URL query params with map zoom & center
    this.map.on('moveend', onMapMoved);

    this.initCartoLayer();
  }

  initCartoLayer() {
    const self = this;
    const layerSource = configureLayerSource(configureMapSQL(this.props));
    // const layerSource = 'https://chekpeds.carto.com/api/v2/viz/acf2c4f6-e987-11e6-bfb2-0e233c30368f/viz.json';
    const options = {
      https: true,
      infowindow: true,
      legends: false,
    };

    // `cartodb` is a global var, refers to CARTO.JS: https://carto.com/docs/carto-engine/carto-js/
    // this creates the crash data tile layer & utf grid for Leaflet
    cartodb.createLayer(self.map, layerSource, options)
      .addTo(self.map)
      .on('done', (layer) => {
        self.cartoLayer = layer;
        layer.on('error', (error) => { throw error; });
        layer.on('loading', () => self.props.dataLoading(true));
        layer.on('load', () => self.props.dataLoading(false));
        // store a reference to the Carto SubLayer so we can act upon it later,
        // mainly to update the SQL query based on filters applied by the user
        self.cartoSubLayer = layer.getSubLayer(0);
        // add tooltips to sublayer
        self.initCartoSubLayerTooltips();
        // add infowindows to sublayer
        self.initCartoSubLayerInfowindows();
      })
      .on('error', (error) => { throw error; });
  }

  initCartoSubLayerTooltips() {
    const self = this;
    const template = sls`

Total Crashes: {{total_crashes}}

Total Persons Injured: {{persons_injured}}

Total Persons Killed: {{persons_killed}}

(Click for details)

    `;

    // add the tooltip to the crashes sublayer
    self.cartoSubLayerTooltip = this.cartoLayer.leafletMap.viz.addOverlay({
      type: 'tooltip',
      layer: self.cartoSubLayer,
      template,
      position: 'bottom|right',
      fields: [
        { total_crashes: 'Total Crashes' },
        { persons_injured: 'persons injured' },
        { persons_killed: 'persons killed' }
      ]
    });
  }

  initCartoSubLayerInfowindows() {
    const self = this;
    const infowindowTemplate = document.getElementById('infowindow_template').innerHTML;
    self.cartoInfowindow = cdb.vis.Vis.addInfowindow(
      self.map,
      self.cartoSubLayer,
      crashDataFieldNames,
      {
        infowindowTemplate,
        templateType: 'mustache',
      }
    );
  }

  hideCartoTooltips() {
    this.cartoSubLayerTooltip.hide();
  }

  hideCartoInfowindow() {
    this.cartoInfowindow._closeInfowindow();
  }

  fitMapBounds(sql) {
    // sets the map center & zoom to bounding box of crash data
    // returned by the current SQL query
    const self = this;
    self.cartodbSQL.getBounds(sql)
      .done((bounds) => {
        self.map.fitBounds(bounds, self.fitBoundsOptions);
      });
  }

  updateCartoSubLayer(nextProps) {
    // create the new sql query string
    const sql = configureMapSQL(nextProps);
    // hide any visible tooltips
    this.hideFilterAreaTooltip();
    this.hideCartoTooltips();
    // hide any open infowindow
    this.hideCartoInfowindow();
    // fit the map extent to the queried data
    this.fitMapBounds(sql);
    // set the cartoSubLayer to be interactive
    this.cartoSubLayer.setInteraction(true);
    // update the cartoLayer SQL using new props, such as start and end dates
    this.cartoSubLayer.setSQL(sql);
  }

  initCustomFilter() {
    const { filterByAreaCustom } = this.props;
    this.customDraw = new CustomFilter();
    this.customDraw.mapInstance = this.map;
    this.customDraw.layerCreatedCallback = filterByAreaCustom;
    this.customDraw.onLayerCreated();
    this.customDraw.initCustomFilterLayer();
    this.customDraw.initDrawPolygon();
  }

  customFilterEnableDraw() {
    this.cartoSubLayer.setInteraction(false);
    this.hideFilterAreaPolygons();
    this.hideFilterAreaTooltip();
    this.hideCartoTooltips();
    this.hideCartoInfowindow();
    this.customDraw.startDraw();
  }

  customFilterCancelDraw() {
    this.customDraw.cancelDraw();
    this.cartoSubLayer.setInteraction(true);
  }

  showMapStatsDisclaimer() {
    this.mapStatsDisclaimer.style.display = '';
  }

  hideMapStatsDisclaimer() {
    this.mapStatsDisclaimer.style.display = 'none';
  }

  revealFilterAreaTooltip(geo, event) {
    const { containerPoint, target } = event;
    const { x, y } = containerPoint;
    const identifier = target.feature.properties.identifier;
    const p = this.filterAreaTooltip.children[0];
    this.filterAreaTooltip.style.cssText = `display: initial; top: ${(y - 25)}px; left: ${(x + 10)}px;`;
    p.textContent = geo === 'Borough' ? boroughs[identifier] : identifier;
  }

  hideFilterAreaTooltip() {
    this.filterAreaTooltip.style.display = 'none';
  }

  hideFilterAreaPolygons() {
    // clear any existing geojson polygons that may be visible
    if (this.filterPolygons) {
      this.map.removeLayer(this.filterPolygons);
    }
  }

  renderFilterPolygons(geo, geojson) {
    const self = this;

    // functions to pass to L.geoJson.options.onEachFeature
    function handleMouseover(e) {
      const layer = e.target;

      layer.setStyle({
        fillColor: '#105b63',
        fillOpacity: 1
      });

      self.revealFilterAreaTooltip(geo, e);

      if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
        layer.bringToFront();
      }
    }

    function handleMouseout(e) {
      self.filterPolygons.resetStyle(e.target);
      self.hideFilterAreaTooltip();
    }

    function handleMousemove(e) {
      self.revealFilterAreaTooltip(geo, e);
    }

    function handleClick(e) {
      const target = e.target;
      const identifier = target.feature.properties.identifier;
      self.cartoSubLayer.setInteraction(true);
      self.props.filterByAreaIdentifier(identifier);
    }

    function onEachFeature(feature, layer) {
      layer.on({
        mouseover: handleMouseover,
        mouseout: handleMouseout,
        mousemove: handleMousemove,
        click: handleClick,
      });
    }

    // sets the default polygon style for filterPolygons
    function style() {
      return {
        fillColor: '#17838f',
        weight: 1,
        opacity: 1,
        color: '#fff',
        fillOpacity: 0.7
      };
    }

    if (geo !== 'Custom') {
      // for filter area polygons, fit map bounds to all of NYC
      this.map.fitBounds(this.mapBounds, this.mapBoundsOptions);
    }

    // hide Carto sublayer tooltips / info windows & prevent interaction
    this.hideCartoTooltips();
    this.hideCartoInfowindow();
    this.cartoSubLayer.setInteraction(false);

    // clear any existing geojson polygons that may be visible
    this.hideFilterAreaPolygons();

    // set the geojson layer, store it, and add it to the map
    this.filterPolygons = L.geoJson(geojson, {
      onEachFeature,
      style,
    }).addTo(this.map);
  }

  render() {
    return (

        <ZoomControls
          handleZoomIn={this.handleZoomIn}
          handleZoomOut={this.handleZoomOut}
        />

 { this.mapStatsDisclaimer = _; }} className="map-stats-disclaimer">

            Note: Map data may differ from stats below
            due to lack of location information provided by the NYPD.

 { this.filterAreaTooltip = _; }} className="filter-area-tooltip">

 { this.mapDiv = _; }} id="map" />

    );
  }
}

LeafletMap.defaultProps = {
  zoom: 12,
  lat: 40.687,
  lng: -73.982,
  identifier: '',
  lngLats: [],
  geojson: {},
};

LeafletMap.propTypes = {
  onMapMoved: PropTypes.func.isRequired,
  dataLoading: PropTypes.func.isRequired,
  filterByAreaIdentifier: PropTypes.func.isRequired,
  filterByAreaCustom: PropTypes.func.isRequired,
  fetchGeoPolygons: PropTypes.func.isRequired,
  zoom: PropTypes.number,
  lat: PropTypes.number,
  lng: PropTypes.number,
  geo: PropTypes.string.isRequired,
  geojson: PropTypes.shape({
    type: PropTypes.string,
    features: PropTypes.array,
    geoName: PropTypes.string,
  }),
  identifier: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number
  ]),
  lngLats: PropTypes.arrayOf(
    PropTypes.arrayOf(PropTypes.number)
  ),
  drawEnabeled: PropTypes.bool.isRequired,
  startDate: momentPropTypes.momentObj.isRequired,
  endDate: momentPropTypes.momentObj.isRequired,
  filterType: PropTypes.shape({
    fatality: PropTypes.shape({
      cyclist: PropTypes.bool.isRequired,
      motorist: PropTypes.bool.isRequired,
      pedestrian: PropTypes.bool.isRequired,
    }).isRequired,
    injury: PropTypes.shape({
      cyclist: PropTypes.bool.isRequired,
      motorist: PropTypes.bool.isRequired,
      pedestrian: PropTypes.bool.isRequired,
    }).isRequired,
    noInjuryFatality: PropTypes.bool.isRequired
  }).isRequired,
};

export default LeafletMap;
<commit_msg>clean up add/remove custom draw layer logic #45<commit_after>import React, { Component, PropTypes } from 'react';
import momentPropTypes from 'react-moment-proptypes';
import sls from 'single-line-string';

import { configureMapSQL } from '../../constants/sql_queries';
import { basemapURL, cartoUser, crashDataFieldNames } from '../../constants/app_config';
import { boroughs, configureLayerSource, crashDataChanged } from '../../constants/api';

import ZoomControls from './ZoomControls';
import CustomFilter from './customFilter';

class LeafletMap extends Component {
  constructor() {
    super();
    this.mapBounds = [[40.91553, -73.70002], [40.49613, -74.25559]];
    this.fitBoundsOptions = {
      paddingTopLeft: [60, 100],
      paddingBottomRight: [300, 120],
      maxZoom: 18
    };
    this.polyOverlayStyle = {
      color: '#17838f',
      fillColor: '#17838f'
    };
    this.mapDiv = undefined; // div Leaflet mounts to
    this.map = undefined; // instance of L.map
    this.filterPolygons = undefined; // L.geoJson for selecting an geo area to filter by
    this.filterAreaTooltip = undefined; // div for tooltip for filter area polygons
    this.mapStatsDisclaimer = undefined;
    this.customDraw = undefined;
    this.cartoLayer = undefined;
    this.cartoSubLayer = undefined;
    this.cartodbSQL = undefined;
    this.cartoSubLayerTooltip = undefined;
    this.cartoInfowindow = undefined;
    this.handleZoomIn = this.handleZoomIn.bind(this);
    this.handleZoomOut = this.handleZoomOut.bind(this);
  }

  componentDidMount() {
    const { lngLats } = this.props;
    this.cartodbSQL = new cartodb.SQL({ user: cartoUser });
    this.initMap();
    this.initCustomFilter();

    // if app loaded with an existing polygon from query params, add it to the drawlayer feat group
    if (lngLats && lngLats.length) {
      // reverse the coordinates [lng, lat] -> [lat, lng], because Leaflet :)
      const reversed = lngLats.reduce((acc, cur) => {
        acc.push([cur[1], cur[0]]);
        return acc;
      }, []);
      const poly = L.polygon(reversed, this.polyOverlayStyle);
      this.customDraw.drawLayer.addLayer(poly);
    }
  }

  componentWillReceiveProps(nextProps) {
    const { geo, geojson, identifier, drawEnabeled } = nextProps;

    if (identifier && identifier !== this.props.identifier) {
      // user filtered by a specific geography, so hide the GeoJSON boundary overlay
      this.map.removeLayer(this.filterPolygons);
    }

    if (crashDataChanged(this.props, nextProps)) {
      // if boundary filters were changed by user, update map data
      this.updateCartoSubLayer(nextProps);
    }

    if (geo === 'Citywide' && this.props.geo !== 'Citywide') {
      this.showMapStatsDisclaimer();
      this.hideFilterAreaPolygons();
      this.customFilterClearPoly();
    } else if (geo !== 'Citywide') {
      this.hideMapStatsDisclaimer();
    }

    if (geo !== this.props.geo && geo !== 'Citywide' && geo !== 'Custom') {
      // cancel custom draw in case it was enabled
      this.customFilterCancelDraw();
      // clear custom area layer if it was drawn
      this.customFilterClearPoly();
      // make an API call for GeoJSON of boundary polygons
      this.props.fetchGeoPolygons(geo);
    }

    if (geojson.features.length) {
      if (
        (geojson.geoName !== this.props.geojson.geoName) ||
        (geo !== 'Citywide' && this.props.geo === 'Citywide') ||
        (geo !== 'Custom' && this.props.geo === 'Custom')
      ) {
        // show the user polygons for filter by area / boundary
        this.renderFilterPolygons(geo, geojson);
      }
    }

    if (geo === 'Custom' && this.props.geo !== 'Custom') {
      this.customFilterClearPoly();
      this.customFilterEnableDraw();
    }

    if (!drawEnabeled && this.props.drawEnabeled) {
      this.customFilterCancelDraw();
    } else if (drawEnabeled && !this.props.drawEnabeled) {
      // clear any existing custom drawn polygon before drawing another one
      this.customFilterClearPoly();
      this.customFilterEnableDraw();
    }
  }

  shouldComponentUpdate() {
    // let Leaflet have control over this part of the DOM, not React.
    return false;
  }

  handleZoomIn() {
    this.map.zoomIn();
  }

  handleZoomOut() {
    this.map.zoomOut();
  }

  initMap() {
    const self = this;
    const { zoom, lat, lng, onMapMoved } = this.props;
    this.map = L.map(this.mapDiv, {
      doubleClickZoom: false,
      center: [lat, lng],
      zoom,
      maxZoom: 18,
      zoomControl: false,
      scrollWheelZoom: false
    });

    L.tileLayer(basemapURL, {
      maxZoom: 18,
      attribution: '© OpenStreetMap'
    }).addTo(self.map);

    // update the URL query params with map zoom & center
    this.map.on('moveend', onMapMoved);

    this.initCartoLayer();
  }

  initCartoLayer() {
    const self = this;
    const layerSource = configureLayerSource(configureMapSQL(this.props));
    // const layerSource = 'https://chekpeds.carto.com/api/v2/viz/acf2c4f6-e987-11e6-bfb2-0e233c30368f/viz.json';
    const options = {
      https: true,
      infowindow: true,
      legends: false,
    };

    // `cartodb` is a global var, refers to CARTO.JS: https://carto.com/docs/carto-engine/carto-js/
    // this creates the crash data tile layer & utf grid for Leaflet
    cartodb.createLayer(self.map, layerSource, options)
      .addTo(self.map)
      .on('done', (layer) => {
        self.cartoLayer = layer;
        layer.on('error', (error) => { throw error; });
        layer.on('loading', () => self.props.dataLoading(true));
        layer.on('load', () => self.props.dataLoading(false));
        // store a reference to the Carto SubLayer so we can act upon it later,
        // mainly to update the SQL query based on filters applied by the user
        self.cartoSubLayer = layer.getSubLayer(0);
        // add tooltips to sublayer
        self.initCartoSubLayerTooltips();
        // add infowindows to sublayer
        self.initCartoSubLayerInfowindows();
      })
      .on('error', (error) => { throw error; });
  }

  initCartoSubLayerTooltips() {
    const self = this;
    const template = sls`

Total Crashes: {{total_crashes}}

Total Persons Injured: {{persons_injured}}

Total Persons Killed: {{persons_killed}}

(Click for details)

    `;

    // add the tooltip to the crashes sublayer
    self.cartoSubLayerTooltip = this.cartoLayer.leafletMap.viz.addOverlay({
      type: 'tooltip',
      layer: self.cartoSubLayer,
      template,
      position: 'bottom|right',
      fields: [
        { total_crashes: 'Total Crashes' },
        { persons_injured: 'persons injured' },
        { persons_killed: 'persons killed' }
      ]
    });
  }

  initCartoSubLayerInfowindows() {
    const self = this;
    const infowindowTemplate = document.getElementById('infowindow_template').innerHTML;
    self.cartoInfowindow = cdb.vis.Vis.addInfowindow(
      self.map,
      self.cartoSubLayer,
      crashDataFieldNames,
      {
        infowindowTemplate,
        templateType: 'mustache',
      }
    );
  }

  hideCartoTooltips() {
    this.cartoSubLayerTooltip.hide();
  }

  hideCartoInfowindow() {
    this.cartoInfowindow._closeInfowindow();
  }

  fitMapBounds(sql) {
    // sets the map center & zoom to bounding box of crash data
    // returned by the current SQL query
    const self = this;
    self.cartodbSQL.getBounds(sql)
      .done((bounds) => {
        self.map.fitBounds(bounds, self.fitBoundsOptions);
      });
  }

  updateCartoSubLayer(nextProps) {
    // create the new sql query string
    const sql = configureMapSQL(nextProps);
    // hide any visible tooltips
    this.hideFilterAreaTooltip();
    this.hideCartoTooltips();
    // hide any open infowindow
    this.hideCartoInfowindow();
    // fit the map extent to the queried data
    this.fitMapBounds(sql);
    // set the cartoSubLayer to be interactive
    this.cartoSubLayer.setInteraction(true);
    // update the cartoLayer SQL using new props, such as start and end dates
    this.cartoSubLayer.setSQL(sql);
  }

  initCustomFilter() {
    const { filterByAreaCustom } = this.props;
    this.customDraw = new CustomFilter();
    this.customDraw.mapInstance = this.map;
    this.customDraw.layerCreatedCallback = filterByAreaCustom;
    this.customDraw.onLayerCreated();
    this.customDraw.initCustomFilterLayer();
    this.customDraw.initDrawPolygon();
  }

  customFilterEnableDraw() {
    this.cartoSubLayer.setInteraction(false);
    this.hideFilterAreaPolygons();
    this.hideFilterAreaTooltip();
    this.hideCartoTooltips();
    this.hideCartoInfowindow();
    this.customDraw.startDraw();
  }

  customFilterCancelDraw() {
    this.customDraw.cancelDraw();
    this.cartoSubLayer.setInteraction(true);
  }

  customFilterClearPoly() {
    this.customDraw.drawLayer.clearLayers();
  }

  showMapStatsDisclaimer() {
    this.mapStatsDisclaimer.style.display = '';
  }

  hideMapStatsDisclaimer() {
    this.mapStatsDisclaimer.style.display = 'none';
  }

  revealFilterAreaTooltip(geo, event) {
    const { containerPoint, target } = event;
    const { x, y } = containerPoint;
    const identifier = target.feature.properties.identifier;
    const p = this.filterAreaTooltip.children[0];
    this.filterAreaTooltip.style.cssText = `display: initial; top: ${(y - 25)}px; left: ${(x + 10)}px;`;
    p.textContent = geo === 'Borough' ? boroughs[identifier] : identifier;
  }

  hideFilterAreaTooltip() {
    this.filterAreaTooltip.style.display = 'none';
  }

  hideFilterAreaPolygons() {
    // clear any existing geojson polygons that may be visible
    if (this.filterPolygons) {
      this.map.removeLayer(this.filterPolygons);
    }
  }

  renderFilterPolygons(geo, geojson) {
    const self = this;

    // functions to pass to L.geoJson.options.onEachFeature
    function handleMouseover(e) {
      const layer = e.target;

      layer.setStyle({
        fillColor: '#105b63',
        fillOpacity: 1
      });

      self.revealFilterAreaTooltip(geo, e);

      if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
        layer.bringToFront();
      }
    }

    function handleMouseout(e) {
      self.filterPolygons.resetStyle(e.target);
      self.hideFilterAreaTooltip();
    }

    function handleMousemove(e) {
      self.revealFilterAreaTooltip(geo, e);
    }

    function handleClick(e) {
      const target = e.target;
      const identifier = target.feature.properties.identifier;
      self.cartoSubLayer.setInteraction(true);
      self.props.filterByAreaIdentifier(identifier);
    }

    function onEachFeature(feature, layer) {
      layer.on({
        mouseover: handleMouseover,
        mouseout: handleMouseout,
        mousemove: handleMousemove,
        click: handleClick,
      });
    }

    // sets the default polygon style for filterPolygons
    function style() {
      return {
        fillColor: '#17838f',
        weight: 1,
        opacity: 1,
        color: '#fff',
        fillOpacity: 0.7
      };
    }

    if (geo !== 'Custom') {
      // for filter area polygons, fit map bounds to all of NYC
      this.map.fitBounds(this.mapBounds, this.mapBoundsOptions);
    }

    // hide Carto sublayer tooltips / info windows & prevent interaction
    this.hideCartoTooltips();
    this.hideCartoInfowindow();
    this.cartoSubLayer.setInteraction(false);

    // clear any existing geojson polygons that may be visible
    this.hideFilterAreaPolygons();

    // set the geojson layer, store it, and add it to the map
    this.filterPolygons = L.geoJson(geojson, {
      onEachFeature,
      style,
    }).addTo(this.map);
  }

  render() {
    return (

        <ZoomControls
          handleZoomIn={this.handleZoomIn}
          handleZoomOut={this.handleZoomOut}
        />

 { this.mapStatsDisclaimer = _; }} className="map-stats-disclaimer">

            Note: Map data may differ from stats below
            due to lack of location information provided by the NYPD.

 { this.filterAreaTooltip = _; }} className="filter-area-tooltip">

 { this.mapDiv = _; }} id="map" />

    );
  }
}

LeafletMap.defaultProps = {
  zoom: 12,
  lat: 40.687,
  lng: -73.982,
  identifier: '',
  lngLats: [],
  geojson: {},
};

LeafletMap.propTypes = {
  onMapMoved: PropTypes.func.isRequired,
  dataLoading: PropTypes.func.isRequired,
  filterByAreaIdentifier: PropTypes.func.isRequired,
  filterByAreaCustom: PropTypes.func.isRequired,
  fetchGeoPolygons: PropTypes.func.isRequired,
  zoom: PropTypes.number,
  lat: PropTypes.number,
  lng: PropTypes.number,
  geo: PropTypes.string.isRequired,
  geojson: PropTypes.shape({
    type: PropTypes.string,
    features: PropTypes.array,
    geoName: PropTypes.string,
  }),
  identifier: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number
  ]),
  lngLats: PropTypes.arrayOf(
    PropTypes.arrayOf(PropTypes.number)
  ),
  drawEnabeled: PropTypes.bool.isRequired,
  startDate: momentPropTypes.momentObj.isRequired,
  endDate: momentPropTypes.momentObj.isRequired,
  filterType: PropTypes.shape({
    fatality: PropTypes.shape({
      cyclist: PropTypes.bool.isRequired,
      motorist: PropTypes.bool.isRequired,
      pedestrian: PropTypes.bool.isRequired,
    }).isRequired,
    injury: PropTypes.shape({
      cyclist: PropTypes.bool.isRequired,
      motorist: PropTypes.bool.isRequired,
      pedestrian: PropTypes.bool.isRequired,
    }).isRequired,
    noInjuryFatality: PropTypes.bool.isRequired
  }).isRequired,
};

export default LeafletMap;
<|endoftext|>


<commit_before>import React, {
    Component,
    PropTypes,
} from 'react-native';
import { connect } from 'react-redux/native';
import { changeRoute, fetchProductsIfNeeded } from '../actions/PostersActions';
import * as routes from '../routes/PostersRoutes';

export class PostersNavigator extends Component {
    showProductList() {
        const { route } = routes.productListRoute(this.props);
        this.props.changeRoute(route);
    }

    showProductDetail(productId) {
        const { route, routeDatas } = routes.productDetailRoute(this.props, productId);
        this.props.changeRoute(route, routeDatas);
    }

    bindActions(props) {
        const { route, routeDatas } = props;

        switch (route.name) {
        case routes.PRODUCT_LIST:
            routeDatas.fetchProductsIfNeeded = () => {
                this.props.fetchProductsIfNeeded();
            };
            routeDatas.showProductPage = (product) => {
                this.showProductDetail(product);
            };
            break;
        case routes.PRODUCT_DETAIL:
            routeDatas.onBack = () => {
                this.showProductList();
            };
            break;
        default:
            break;
        }

        return { route, routeDatas };
    }

    render() {
        const { route, routeDatas } = this.bindActions(this.props);

        return (
            <route.component {...routeDatas} />
        );
    }
}

PostersNavigator.propTypes = {
    route: PropTypes.object.isRequired,
    routeDatas: PropTypes.object.isRequired,
    changeRoute: PropTypes.func.isRequired,
    fetchProductsIfNeeded: PropTypes.func.isRequired,
};

export default connect((state) => state, {
    changeRoute,
    fetchProductsIfNeeded,
})(PostersNavigator);
<commit_msg>Use native Navigator<commit_after>import React, {
    Component,
    PropTypes,
    Navigator,
    BackAndroid,
} from 'react-native';
import { connect } from 'react-redux/native';
import { changeRoute, fetchProductsIfNeeded } from '../actions/PostersActions';
import * as routes from '../routes/PostersRoutes';

export class PostersNavigator extends Component {
    constructor(props) {
        super(props);
        this.bindEventListnener();

        const { route, routeDatas } = this.bindActions(this.props);
        this.route = route;
        this.routeDatas = routeDatas;
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps === this.props || !this.navigator) {
            return;
        }

        const { route, routeDatas } = this.bindActions(nextProps);

        if (this.route !== route) {
            this.route = route;
            this.routeDatas = routeDatas;
            this.navigator.push(route);
            return;
        }

        if (this.routeDatas !== routeDatas) {
            this.routeDatas = routeDatas;
            this.navigator.replace(this.route);
        }
    }

    bindEventListnener() {
        BackAndroid.addEventListener('hardwareBackPress', () => {
            if (!this.navigator) {
                return false;
            }

            if (this.route.name === routes.PRODUCT_LIST) {
                return false;
            }

            this.navigator.pop();
            return true;
        });
    }

    showProductList() {
        const { route } = routes.productListRoute(this.props);
        this.props.changeRoute(route);
    }

    showProductDetail(productId) {
        const { route, routeDatas } = routes.productDetailRoute(this.props, productId);
        this.props.changeRoute(route, routeDatas);
    }

    bindActions(props) {
        const { route, routeDatas } = props;

        switch (route.name) {
        case routes.PRODUCT_LIST:
            routeDatas.fetchProductsIfNeeded = () => {
                this.props.fetchProductsIfNeeded();
            };
            routeDatas.showProductPage = (product) => {
                this.showProductDetail(product);
            };
            break;
        case routes.PRODUCT_DETAIL:
            if (!this.navigator) {
                routeDatas.onBack = () => {
                    this.showProductList();
                };
            } else {
                routeDatas.onBack = () => {
                    this.navigator.pop();
                };
            }
            break;
        default:
            break;
        }

        return { route, routeDatas };
    }

    renderScene(route, navigator) {
        if (!this.navigator) {
            this.navigator = navigator;
        }

        const { routeDatas } = this;

        return (
            <route.component {...routeDatas} />
        );
    }

    render() {
        const { route } = this;

        return (
            <Navigator
                initialRoute={route}
                configureScene={() => Navigator.SceneConfigs.FadeAndroid}
                renderScene={this.renderScene.bind(this)}
            />
        );
    }
}

PostersNavigator.propTypes = {
    route: PropTypes.object.isRequired,
    routeDatas: PropTypes.object.isRequired,
    changeRoute: PropTypes.func.isRequired,
    fetchProductsIfNeeded: PropTypes.func.isRequired,
};

export default connect((state) => state, {
    changeRoute,
    fetchProductsIfNeeded,
})(PostersNavigator);
<|endoftext|>


<commit_before>/*======================================================
************   Messages   ************
======================================================*/
var Messages = function (container, params) {
    var defaults = {
        autoLayout: true,
        newMessagesFirst: false,
        messageTemplate:
            '{{#if day}}' +
            '
{{day}} {{#if time}}, {{time}}{{/if}}
' +
            '{{/if}}' +
            '
' +
                '{{#if name}}
{{name}}
{{/if}}' +
                '
{{text}}
' +
                '{{#if avatar}}

{{/if}}' +
                '{{#if label}}
{{label}}
{{/if}}' +
            '
'
    };
    params = params || {};
    for (var def in defaults) {
        if (typeof params[def] === 'undefined' || params[def] === null) {
            params[def] = defaults[def];
        }
    }

    // Instance
    var m = this;

    // Params
    m.params = params;

    // Container
    m.container = $(container);
    if (m.container.length === 0) return;

    // Autolayout
    if (m.params.autoLayout) m.container.addClass('messages-auto-layout');

    // Is In Page
    m.pageContainer = m.container.parents('.page').eq(0);
    m.pageContent = m.pageContainer.find('.page-content');

    // Compiled template
    m.template = Template7.compile(m.params.messageTemplate);

    // Auto Layout
    m.layout = function () {
        if (!m.container.hasClass('messages-auto-layout')) m.container.addClass('messages-auto-layout');
        m.container.find('.message').each(function () {
            var message = $(this);
            if (message.find('.message-text img').length > 0) message.addClass('message-pic');
            if (message.find('.message-avatar').length > 0) message.addClass('message-with-avatar');
        });
        m.container.find('.message').each(function () {
            var message = $(this);
            var isSent = message.hasClass('message-sent');
            var next = message.next('.message-' + (isSent ? 'sent' : 'received'));
            var prev = message.prev('.message-' + (isSent ? 'sent' : 'received'));
            if (next.length === 0) {
                message.addClass('message-last message-with-tail');
            }
            else message.removeClass('message-last message-with-tail');

            if (prev.length === 0) {
                message.addClass('message-first');
            }
            else message.removeClass('message-first');

            if (prev.length > 0 && prev.find('.message-name').length > 0 && message.find('.message-name').length > 0) {
                if (prev.find('.message-name').text() !== message.find('.message-name').text()) {
                    prev.addClass('message-last message-with-tail');
                    message.addClass('message-first');
                }
            }
        });

    };

    // Add Message
    m.appendMessage = function (props, animate) {
        return m.addMessage(props, 'append', animate);
    };
    m.prependMessage = function (props, animate) {
        return m.addMessage(props, 'prepend', animate);
    };
    m.addMessage = function (props, method, animate) {
        return m.addMessages([props], method, animate);
    };
    m.addMessages = function (newMessages, method, animate) {
        if (typeof animate === 'undefined') {
            animate = true;
        }
        if (typeof method === 'undefined') {
            method = m.params.newMessagesFirst ? 'prepend' : 'append';
        }
        var newMessagesHTML = '', i;
        for (i = 0; i < newMessages.length; i++) {
            var props = newMessages[i] || {};
            props.type = props.type || 'sent';
            if (!props.text) continue;
            props.hasImage = props.text.indexOf('= 0;
            if (animate) props.position = method === 'append' ? 'bottom' : 'top';

            newMessagesHTML += m.template(props);
        }
        m.container[method](newMessagesHTML);

        if (m.params.autoLayout) m.layout();
        if ((method === 'append' && !m.params.newMessagesFirst) || (method === 'prepend' && m.params.newMessagesFirst)) {
            m.scrollMessages(animate ? undefined : 0);
        }
        var messages = m.container.find('.message');
        if (newMessages.length === 1) {
            return method === 'append' ? messages[messages.length - 1] : messages[0];
        }
        else {
            var messagesToReturn = [];
            if (method === 'append') {
                for (i = messages.length - newMessages.length; i < messages.length; i++) {
                    messagesToReturn.push(messages[i]);
                }
            }
            else {
                for (i = 0; i < newMessages.length; i++) {
                    messagesToReturn.push(messages[i]);
                }
            }
            return messagesToReturn;
        }

    };
    m.removeMessage = function (messageToRemove) {
        messageToRemove = $(messageToRemove);
        if (messageToRemove.length === 0) {
            return false;
        }
        else {
            messageToRemove.remove();
            if (m.params.autoLayout) m.layout();
            return true;
        }
    };
    m.removeMessages = function (messagesToRemove) {
        m.removeMessage(messagesToRemove);
    };
    m.clean = function () {
        m.container.html('');
    };

    // Scroll
    m.scrollMessages = function (duration) {
        if (typeof duration === 'undefined') duration = 400;
        var currentScroll = m.pageContent[0].scrollTop;
        var newScroll = m.params.newMessagesFirst ? 0 : m.pageContent[0].scrollHeight - m.pageContent[0].offsetHeight;
        if (newScroll === currentScroll) return;
        m.pageContent.scrollTop(newScroll, duration);
    };

    // Init Destroy
    m.init = function () {
        if (m.params.messages) {
            m.addMessages(m.params.messages, undefined, false);
        }
        else {
            if (m.params.autoLayout) m.layout();
            m.scrollMessages(0);
        }

    };
    m.destroy = function () {
        m = null;
    };

    // Init
    m.init();

    m.container[0].f7Messages = m;
    return m;
};
app.messages = function (container, params) {
    return new Messages (container, params);
};
app.initPageMessages = function (pageContainer) {
    pageContainer = $(pageContainer);
    var messages = pageContainer.find('.messages');
    if (messages.length === 0) return;
    if (!messages.hasClass('messages-init')) {
        return;
    }
    var m = app.messages(messages, messages.dataset());

    // Destroy on page remove
    function pageBeforeRemove() {
        m.destroy();
        pageContainer.off('pageBeforeRemove', pageBeforeRemove);
    }
    if (pageContainer.hasClass('page')) {
        pageContainer.on('pageBeforeRemove', pageBeforeRemove);
    }
};
<commit_msg>Shorten names<commit_after>/*======================================================
************   Messages   ************
======================================================*/
var Messages = function (container, params) {
    var defaults = {
        autoLayout: true,
        newMessagesFirst: false,
        messageTemplate:
            '{{#if day}}' +
            '
{{day}} {{#if time}}, {{time}}{{/if}}
' +
            '{{/if}}' +
            '
' +
                '{{#if name}}
{{name}}
{{/if}}' +
                '
{{text}}
' +
                '{{#if avatar}}

{{/if}}' +
                '{{#if label}}
{{label}}
{{/if}}' +
            '
'
    };
    params = params || {};
    for (var def in defaults) {
        if (typeof params[def] === 'undefined' || params[def] === null) {
            params[def] = defaults[def];
        }
    }

    // Instance
    var m = this;

    // Params
    m.params = params;

    // Container
    m.container = $(container);
    if (m.container.length === 0) return;

    // Autolayout
    if (m.params.autoLayout) m.container.addClass('messages-auto-layout');

    // Is In Page
    m.pageContainer = m.container.parents('.page').eq(0);
    m.pageContent = m.pageContainer.find('.page-content');

    // Compiled template
    m.template = Template7.compile(m.params.messageTemplate);

    // Auto Layout
    m.layout = function () {
        if (!m.container.hasClass('messages-auto-layout')) m.container.addClass('messages-auto-layout');
        m.container.find('.message').each(function () {
            var message = $(this);
            if (message.find('.message-text img').length > 0) message.addClass('message-pic');
            if (message.find('.message-avatar').length > 0) message.addClass('message-with-avatar');
        });
        m.container.find('.message').each(function () {
            var message = $(this);
            var isSent = message.hasClass('message-sent');
            var next = message.next('.message-' + (isSent ? 'sent' : 'received'));
            var prev = message.prev('.message-' + (isSent ? 'sent' : 'received'));
            if (next.length === 0) {
                message.addClass('message-last message-with-tail');
            }
            else message.removeClass('message-last message-with-tail');

            if (prev.length === 0) {
                message.addClass('message-first');
            }
            else message.removeClass('message-first');

            if (prev.length > 0 && prev.find('.message-name').length > 0 && message.find('.message-name').length > 0) {
                if (prev.find('.message-name').text() !== message.find('.message-name').text()) {
                    prev.addClass('message-last message-with-tail');
                    message.addClass('message-first');
                }
            }
        });

    };

    // Add Message
    m.appendMessage = function (props, animate) {
        return m.addMessage(props, 'append', animate);
    };
    m.prependMessage = function (props, animate) {
        return m.addMessage(props, 'prepend', animate);
    };
    m.addMessage = function (props, method, animate) {
        return m.addMessages([props], method, animate);
    };
    m.addMessages = function (newMessages, method, animate) {
        if (typeof animate === 'undefined') {
            animate = true;
        }
        if (typeof method === 'undefined') {
            method = m.params.newMessagesFirst ? 'prepend' : 'append';
        }
        var newMessagesHTML = '', i;
        for (i = 0; i < newMessages.length; i++) {
            var props = newMessages[i] || {};
            props.type = props.type || 'sent';
            if (!props.text) continue;
            props.hasImage = props.text.indexOf('= 0;
            if (animate) props.position = method === 'append' ? 'bottom' : 'top';

            newMessagesHTML += m.template(props);
        }
        m.container[method](newMessagesHTML);

        if (m.params.autoLayout) m.layout();
        if ((method === 'append' && !m.params.newMessagesFirst) || (method === 'prepend' && m.params.newMessagesFirst)) {
            m.scrollMessages(animate ? undefined : 0);
        }
        var messages = m.container.find('.message');
        if (newMessages.length === 1) {
            return method === 'append' ? messages[messages.length - 1] : messages[0];
        }
        else {
            var messagesToReturn = [];
            if (method === 'append') {
                for (i = messages.length - newMessages.length; i < messages.length; i++) {
                    messagesToReturn.push(messages[i]);
                }
            }
            else {
                for (i = 0; i < newMessages.length; i++) {
                    messagesToReturn.push(messages[i]);
                }
            }
            return messagesToReturn;
        }

    };
    m.removeMessage = function (message) {
        message = $(message);
        if (message.length === 0) {
            return false;
        }
        else {
            message.remove();
            if (m.params.autoLayout) m.layout();
            return true;
        }
    };
    m.removeMessages = function (messages) {
        m.removeMessage(messages);
    };
    m.clean = function () {
        m.container.html('');
    };

    // Scroll
    m.scrollMessages = function (duration) {
        if (typeof duration === 'undefined') duration = 400;
        var currentScroll = m.pageContent[0].scrollTop;
        var newScroll = m.params.newMessagesFirst ? 0 : m.pageContent[0].scrollHeight - m.pageContent[0].offsetHeight;
        if (newScroll === currentScroll) return;
        m.pageContent.scrollTop(newScroll, duration);
    };

    // Init Destroy
    m.init = function () {
        if (m.params.messages) {
            m.addMessages(m.params.messages, undefined, false);
        }
        else {
            if (m.params.autoLayout) m.layout();
            m.scrollMessages(0);
        }

    };
    m.destroy = function () {
        m = null;
    };

    // Init
    m.init();

    m.container[0].f7Messages = m;
    return m;
};
app.messages = function (container, params) {
    return new Messages (container, params);
};
app.initPageMessages = function (pageContainer) {
    pageContainer = $(pageContainer);
    var messages = pageContainer.find('.messages');
    if (messages.length === 0) return;
    if (!messages.hasClass('messages-init')) {
        return;
    }
    var m = app.messages(messages, messages.dataset());

    // Destroy on page remove
    function pageBeforeRemove() {
        m.destroy();
        pageContainer.off('pageBeforeRemove', pageBeforeRemove);
    }
    if (pageContainer.hasClass('page')) {
        pageContainer.on('pageBeforeRemove', pageBeforeRemove);
    }
};
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';

import styles from './index.style';

class PullSelect extends Component {
  constructor(props) {
    super(props);

    this.state = {
      selected: null,
    }

    this.onSelect = this.onSelect.bind(this);
    this.onPress = this.onPress.bind(this);
  }

  onSelect(selected) {
    this.setState({
      selected,
    });
    this.props.onChangeText(selected);
  }

  onPress() {
    this.props.navigation.navigate(this.props.screen, {
      onSelect: this.onSelect,
    });
  }

  render() {
    let { placeholder } = this.props;
    if (this.state.selected) {
      placeholder = this.state.selected.title;
    }

    return (
      <TouchableHighlight onPress={ this.onPress } underlayColor="#eee" >
        <View style={ styles.buttonWrapper }>
          <Text style={ styles.buttonText }>{ placeholder }</Text>
          <View style={ styles.buttonIcon }>
            <Icon name="chevron-right" size={ 20 } color="#ccc" />
          </View>
        </View>
      </TouchableHighlight>
    )
  }
}

PullSelect.propTypes = {
  placeholder: PropTypes.string.isRequired,
  screen: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  onChangeText: PropTypes.func.isRequired,
}

PullSelect.defaultProps = {
  onChangeText: () => {},
}

export default PullSelect;
<commit_msg>Support value change<commit_after>import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
import { NavigationActions } from 'react-navigation';
import Icon from 'react-native-vector-icons/MaterialIcons';

import styles from './index.style';

class PullSelect extends Component {
  constructor(props) {
    super(props);

    this.state = {
      selected: null,
    }

    this.onSelect = this.onSelect.bind(this);
    this.onPress = this.onPress.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.selected !== this.props.selected) {
      this.setState({
        selected: nextProps.selected,
      });
    }
  }

  onSelect(selected) {
    this.setState({
      selected,
    });
    this.props.onChangeText(selected);
  }

  onPress() {
    if ('undefined' !== typeof this.props.action) {
      const action = NavigationActions.navigate({
        ...this.props.action,
        params: {
          ...this.props.action.params,
          onSelect: this.onSelect,
        },
      })
      return this.props.screenProps.redirect(action);
    }

    this.props.navigation.navigate(this.props.screen, {
      onSelect: this.onSelect,
    });
  }

  render() {
    let { placeholder } = this.props;
    if (this.state.selected) {
      placeholder = this.state.selected.title;
    }

    return (
      <TouchableHighlight onPress={ this.onPress } underlayColor="#eee" >
        <View style={ styles.buttonWrapper }>
          <Text style={ styles.buttonText }>{ placeholder }</Text>
          <View style={ styles.buttonIcon }>
            <Icon name="chevron-right" size={ 20 } color="#ccc" />
          </View>
        </View>
      </TouchableHighlight>
    )
  }
}

PullSelect.propTypes = {
  placeholder: PropTypes.string.isRequired,
  screen: PropTypes.string,
  action: PropTypes.object,
  title: PropTypes.string.isRequired,
  selected: PropTypes.object,
  onChangeText: PropTypes.func.isRequired,
}

PullSelect.defaultProps = {
  onChangeText: () => {},
}

export default PullSelect;
<|endoftext|>


<commit_before>function encodeParams (params) {
  let newParams = Object.assign({}, params)

  Object.keys(params).forEach(key => {
    newParams[key] = encodeURIComponent(newParams[key])
  })

  return newParams
}

export const twitter = {
  action: 'Tweet',
  makeUrl: (params) => {
    let {url, text, via} = encodeParams(params)
    let viaParam = params.via ? `&via=${via}` : ''
    return `https://twitter.com/share?url=${url}&text=${text}${viaParam}`
  },
}

export const facebook = {
  action: 'Share',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.facebook.com/sharer/sharer.php?u=${url}`
  },
}

export const googleplus = {
  action: 'Share',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://plus.google.com/share?url=${url}`
  },
}

export const reddit = {
  action: 'Share',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.reddit.com/submit?url=${url}`
  },
}

export const tumblr = {
  action: 'Post',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.tumblr.com/share/link?url=${url}`
  },
}

export const linkedin = {
  action: 'Share',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.linkedin.com/shareArticle?mini=true&url=${url}`
  },
}

export const pinterest = {
  action: 'Pin it',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.pinterest.com/pin/create/button/?url=${url}`
  },
}

export const slack = {
  action: 'Slack it',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `http://slackbutton.herokuapp.com/post/new/?url=${url}`
  },
}

export const vk = {
  action: 'Share',
  makeUrl: (params) => {
    let {url, title, text} = encodeParams(params)
    return `http://vk.com/share.php?url=${url}&title=${title}&description=${text}`
  },
}

export const email = {
  action: 'Email',
  makeUrl: (params) => {
    let {url, title, text} = encodeParams(params)
    return `mailto:?subject=${title}&body=${text}%0${url}`
  },
}
<commit_msg>Fix %20<commit_after>function encodeParams (params) {
  let newParams = Object.assign({}, params)

  Object.keys(params).forEach(key => {
    newParams[key] = encodeURIComponent(newParams[key])
  })

  return newParams
}

export const twitter = {
  action: 'Tweet',
  makeUrl: (params) => {
    let {url, text, via} = encodeParams(params)
    let viaParam = params.via ? `&via=${via}` : ''
    return `https://twitter.com/share?url=${url}&text=${text}${viaParam}`
  },
}

export const facebook = {
  action: 'Share',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.facebook.com/sharer/sharer.php?u=${url}`
  },
}

export const googleplus = {
  action: 'Share',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://plus.google.com/share?url=${url}`
  },
}

export const reddit = {
  action: 'Share',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.reddit.com/submit?url=${url}`
  },
}

export const tumblr = {
  action: 'Post',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.tumblr.com/share/link?url=${url}`
  },
}

export const linkedin = {
  action: 'Share',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.linkedin.com/shareArticle?mini=true&url=${url}`
  },
}

export const pinterest = {
  action: 'Pin it',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `https://www.pinterest.com/pin/create/button/?url=${url}`
  },
}

export const slack = {
  action: 'Slack it',
  makeUrl: (params) => {
    let {url} = encodeParams(params)
    return `http://slackbutton.herokuapp.com/post/new/?url=${url}`
  },
}

export const vk = {
  action: 'Share',
  makeUrl: (params) => {
    let {url, title, text} = encodeParams(params)
    return `http://vk.com/share.php?url=${url}&title=${title}&description=${text}`
  },
}

export const email = {
  action: 'Email',
  makeUrl: (params) => {
    let {url, title, text} = encodeParams(params)
    return `mailto:?subject=${title}&body=${text}%20${url}`
  },
}
<|endoftext|>