<commit_before>import isObject from '../../utils/object/isObject';
import hasKey from '../../utils/object/hasKey';
import copyAttributes from '../../utils/object/copyAttributes';
import arrayFrom from '../../utils/array/arrayFrom';

const SEQUENCE_ATTRIBUTES = ['sequence', 'action'];
const KEYMAP_ATTRIBUTES = ['name', 'description', 'group'];

function createSequenceFromConfig(keyMapConfig) {
  return arrayFrom(keyMapConfig).map((sequenceOrKeyMapOptions) => {
    if (isObject(sequenceOrKeyMapOptions)) {
      /**
       * Support syntax:
       * [
       *   { sequence: 'a+b', action: 'keyup' },
       *   { sequence: 'c' }
       * ]
       */
      return copyAttributes(sequenceOrKeyMapOptions, {}, SEQUENCE_ATTRIBUTES);
    } else {
      /**
       * Support syntax:
       * 'a+b'
       */
      return { sequence: sequenceOrKeyMapOptions };
    }
  })
}

class ApplicationKeyMapBuilder {
  constructor(componentTree) {
    this._componentTree = componentTree;
  }

  build() {
    if (!this._componentTree.hasRoot()) {
      return {};
    }

    return this._build([this._componentTree.rootId], {});
  }

  _build(componentIds, keyMapSummary) {
    componentIds.forEach((componentId) => {
      const { childIds, keyMap } = this._componentTree.get(componentId);

      if (keyMap) {
        Object.keys(keyMap).forEach((actionName) => {
          const keyMapConfig = keyMap[actionName];

          keyMapSummary[actionName] = {};

          if (isObject(keyMapConfig)) {
            if (hasKey(keyMapConfig, 'sequences')) {
              /**
               * Support syntax:
               *  {
               *    sequences: [ {sequence: 'a+b', action: 'keyup' }],
               *    name: 'My keymap',
               *    description: 'Key to press for something special',
               *    group: 'Vanity'
               *  }
               */
              copyAttributes(
                keyMapConfig,
                keyMapSummary[actionName],
                KEYMAP_ATTRIBUTES
              );

              keyMapSummary[actionName].sequences =
                createSequenceFromConfig(keyMapConfig.sequences);
            } else {
              /**
               * Support syntax:
               * {
               *   sequence: 'a+b', action: 'keyup',
               *   name: 'My keymap',
               *   description: 'Key to press for something special',
               *   group: 'Vanity'
               * }
               */
              copyAttributes(keyMapConfig, keyMapSummary[actionName], KEYMAP_ATTRIBUTES);

              keyMapSummary[actionName].sequences = [
                copyAttributes(keyMapConfig, {}, SEQUENCE_ATTRIBUTES)
              ]
            }
          } else {
            keyMapSummary[actionName].sequences =
              createSequenceFromConfig(keyMapConfig)
          }
        });
      }

      this._build(childIds, keyMapSummary);
    });

    return keyMapSummary;
  }
}

export default ApplicationKeyMapBuilder;
<commit_msg>👷 Abstract out standardising action definitions into separate function<commit_after>import isObject from '../../utils/object/isObject';
import hasKey from '../../utils/object/hasKey';
import copyAttributes from '../../utils/object/copyAttributes';
import arrayFrom from '../../utils/array/arrayFrom';

const SEQUENCE_ATTRIBUTES = ['sequence', 'action'];
const KEYMAP_ATTRIBUTES = ['name', 'description', 'group'];

function createSequenceFromConfig(keyMapConfig) {
  return arrayFrom(keyMapConfig).map((sequenceOrKeyMapOptions) => {
    if (isObject(sequenceOrKeyMapOptions)) {
      /**
       * Support syntax:
       * [
       *   { sequence: 'a+b', action: 'keyup' },
       *   { sequence: 'c' }
       * ]
       */
      return copyAttributes(sequenceOrKeyMapOptions, {}, SEQUENCE_ATTRIBUTES);
    } else {
      /**
       * Support syntax:
       * 'a+b'
       */
      return { sequence: sequenceOrKeyMapOptions };
    }
  })
}

function normalizeActionDefinition(keyMap, actionName, keyMapSummary) {
  const keyMapConfig = keyMap[actionName];

  keyMapSummary[actionName] = {};

  if (isObject(keyMapConfig)) {
    if (hasKey(keyMapConfig, 'sequences')) {
      /**
       * Support syntax:
       *  {
       *    sequences: [ {sequence: 'a+b', action: 'keyup' }],
       *    name: 'My keymap',
       *    description: 'Key to press for something special',
       *    group: 'Vanity'
       *  }
       */
      copyAttributes(
        keyMapConfig,
        keyMapSummary[actionName],
        KEYMAP_ATTRIBUTES
      );

      keyMapSummary[actionName].sequences =
        createSequenceFromConfig(keyMapConfig.sequences);
    } else {
      /**
       * Support syntax:
       * {
       *   sequence: 'a+b', action: 'keyup',
       *   name: 'My keymap',
       *   description: 'Key to press for something special',
       *   group: 'Vanity'
       * }
       */
      copyAttributes(keyMapConfig, keyMapSummary[actionName], KEYMAP_ATTRIBUTES);

      keyMapSummary[actionName].sequences = [
        copyAttributes(keyMapConfig, {}, SEQUENCE_ATTRIBUTES)
      ];
    }
  } else {
    keyMapSummary[actionName].sequences =
      createSequenceFromConfig(keyMapConfig);
  }
}

class ApplicationKeyMapBuilder {
  constructor(componentTree) {
    this._componentTree = componentTree;
  }

  build() {
    if (!this._componentTree.hasRoot()) {
      return {};
    }

    return this._build([this._componentTree.rootId], {});
  }

  _build(componentIds, keyMapSummary) {
    componentIds.forEach((componentId) => {
      const { childIds, keyMap } = this._componentTree.get(componentId);

      if (keyMap) {
        Object.keys(keyMap).forEach((actionName) => {
          normalizeActionDefinition(keyMap, actionName, keyMapSummary);
        });
      }

      this._build(childIds, keyMapSummary);
    });

    return keyMapSummary;
  }
}

export default ApplicationKeyMapBuilder;
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );
var isPositiveInteger = require( '@stdlib/assert/is-positive-integer' ).isPrimitive;
var floor = require( '@stdlib/math/base/special/floor' );
var INT32_MAX = require( '@stdlib/constants/math/int32-max' );
var minstd = require( '@stdlib/random/base/minstd' ).factory;
var createTable = require( './create_table.js' );

// VARIABLES //

var NORMALIZATION_CONSTANT = INT32_MAX - 1;
var MAX_SEED = INT32_MAX - 1;

// MAIN //

/**
* Returns a linear congruential pseudorandom number generator (LCG) whose output is shuffled.
*
* @param {PositiveInteger} [seed] - pseudorandom number generator seed
* @throws {TypeError} must provide a positive integer
* @throws {RangeError} must provide a positive integer less than the maximum signed 32-bit integer
* @returns {Function} shuffled LCG
*
* @example
* var minstd = factory();
*
* var v = minstd();
* // returns <number>
*
* @example
* // Return a seeded LCG:
* var minstd = factory( 1234 );
*
* var v = minstd();
* // returns 1421600654
*/
function factory( seed ) {
	var table;
	var state;
	var rand;
	if ( arguments.length ) {
		if ( !isPositiveInteger( seed ) ) {
			throw new TypeError( 'invalid input argument. Must provide a positive integer. Value: `' + seed + '`.' );
		}
		if ( seed > MAX_SEED ) {
			throw new RangeError( 'invalid input argument. Must provide a positive integer less than the maximum signed 32-bit integer. Value: `' + seed + '`.' );
		}
		rand = minstd( seed );
	} else {
		rand = minstd();
	}
	table = createTable( rand );
	state = table[ 0 ];

	setReadOnly( minstdShuffle, 'NAME', 'minstd-shuffle' );
	setReadOnly( minstdShuffle, 'SEED', rand.SEED );
	setReadOnly( minstdShuffle, 'MIN', 1 );
	setReadOnly( minstdShuffle, 'MAX', INT32_MAX-1 );
	setReadOnly( minstdShuffle, 'normalized', normalized );

	setReadOnly( normalized, 'NAME', minstdShuffle.NAME );
	setReadOnly( normalized, 'SEED', minstdShuffle.SEED );
	setReadOnly( normalized, 'MIN', (minstdShuffle.MIN-1) / NORMALIZATION_CONSTANT );
	setReadOnly( normalized, 'MAX', (minstdShuffle.MAX-1) / NORMALIZATION_CONSTANT );

	return minstdShuffle;

	/**
	* Generates a pseudorandom integer on the interval \\( [1,2^{31}-1) \\).
	*
	* @private
	* @returns {PositiveInteger} pseudorandom integer
	*
	* @example
	* var v = minstd();
	* // returns <number>
	*/
	function minstdShuffle() {
		var i = floor( table.length * (state/INT32_MAX) );

		// Pull a state from the table and replace:
		state = table[ i ];
		table[ i ] = rand();

		return state;
	} // end FUNCTION minstdShuffle()

	/**
	* Generates a pseudorandom number on the interval \\( [0,1) \\).
	*
	* @private
	* @returns {number} pseudorandom number
	*
	* @example
	* var v = normalized()
	* // returns <number>
	*/
	function normalized() {
		return (minstdShuffle()-1) / NORMALIZATION_CONSTANT;
	} // end FUNCTION normalized()
} // end FUNCTION factory()

// EXPORTS //

module.exports = factory;
<commit_msg>Add type annotations<commit_after>'use strict';

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );
var isPositiveInteger = require( '@stdlib/assert/is-positive-integer' ).isPrimitive;
var floor = require( '@stdlib/math/base/special/floor' );
var INT32_MAX = require( '@stdlib/constants/math/int32-max' );
var minstd = require( '@stdlib/random/base/minstd' ).factory;
var createTable = require( './create_table.js' );

// VARIABLES //

var NORMALIZATION_CONSTANT = (INT32_MAX - 1)|0; // asm type annotation
var MAX_SEED = (INT32_MAX - 1)|0; // asm type annotation

// MAIN //

/**
* Returns a linear congruential pseudorandom number generator (LCG) whose output is shuffled.
*
* @param {PositiveInteger} [seed] - pseudorandom number generator seed
* @throws {TypeError} must provide a positive integer
* @throws {RangeError} must provide a positive integer less than the maximum signed 32-bit integer
* @returns {Function} shuffled LCG
*
* @example
* var minstd = factory();
*
* var v = minstd();
* // returns <number>
*
* @example
* // Return a seeded LCG:
* var minstd = factory( 1234 );
*
* var v = minstd();
* // returns 1421600654
*/
function factory( seed ) {
	var table;
	var state;
	var rand;
	if ( arguments.length ) {
		if ( !isPositiveInteger( seed ) ) {
			throw new TypeError( 'invalid input argument. Must provide a positive integer. Value: `' + seed + '`.' );
		}
		if ( seed > MAX_SEED ) {
			throw new RangeError( 'invalid input argument. Must provide a positive integer less than the maximum signed 32-bit integer. Value: `' + seed + '`.' );
		}
		rand = minstd( seed );
	} else {
		rand = minstd();
	}
	table = createTable( rand );
	state = table[ 0 ];

	setReadOnly( minstdShuffle, 'NAME', 'minstd-shuffle' );
	setReadOnly( minstdShuffle, 'SEED', rand.SEED );
	setReadOnly( minstdShuffle, 'MIN', 1 );
	setReadOnly( minstdShuffle, 'MAX', INT32_MAX-1 );
	setReadOnly( minstdShuffle, 'normalized', normalized );

	setReadOnly( normalized, 'NAME', minstdShuffle.NAME );
	setReadOnly( normalized, 'SEED', minstdShuffle.SEED );
	setReadOnly( normalized, 'MIN', (minstdShuffle.MIN-1) / NORMALIZATION_CONSTANT );
	setReadOnly( normalized, 'MAX', (minstdShuffle.MAX-1) / NORMALIZATION_CONSTANT );

	return minstdShuffle;

	/**
	* Generates a pseudorandom integer on the interval \\( [1,2^{31}-1) \\).
	*
	* @private
	* @returns {PositiveInteger} pseudorandom integer
	*
	* @example
	* var v = minstd();
	* // returns <number>
	*/
	function minstdShuffle() {
		var i = floor( table.length * (state/INT32_MAX) );

		// Pull a state from the table and replace:
		state = table[ i ];
		table[ i ] = rand();

		return state;
	} // end FUNCTION minstdShuffle()

	/**
	* Generates a pseudorandom number on the interval \\( [0,1) \\).
	*
	* @private
	* @returns {number} pseudorandom number
	*
	* @example
	* var v = normalized()
	* // returns <number>
	*/
	function normalized() {
		return (minstdShuffle()-1) / NORMALIZATION_CONSTANT;
	} // end FUNCTION normalized()
} // end FUNCTION factory()

// EXPORTS //

module.exports = factory;
<|endoftext|>


<commit_before>// Copyright, 2013-2014, by Tomas Korcak. <korczis@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

(function (global) {

    var deps = [
        'app'
    ];

    define(deps, function (App) {
        App.Router.map(function() {
            this.route("login", { path: "/login" });
            this.route("privacy", { path: "/privacy" });
            this.route("tos", { path: "/tos" });
        });

        return App;
    });
})(this);
<commit_msg>Contact route<commit_after>// Copyright, 2013-2014, by Tomas Korcak. <korczis@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

(function (global) {

    var deps = [
        'app'
    ];

    define(deps, function (App) {
        App.Router.map(function() {
            this.route('contact', { path: '/contact' });
            this.route('login', { path: '/login' });
            this.route('privacy', { path: '/privacy' });
            this.route('tos', { path: '/tos' });
        });

        return App;
    });
})(this);
<|endoftext|>


<commit_before>// JavaScript which enables display of this module's content
// to logged-out users belongs here.

// Create Google Map that displays the specified items. Uf mapOptions.center is not
// set to an object with lat and lng properties, the center is
// determined from the items. If there are no items... welcome to Philadelphia!

// id is a separate parameter because it's tough to set a property of an object
// in a nunjucks template, and we generate the id there. You are responsible for
// supplying a div with that id and a suitable width and height

var AposGoogleMap = function(items, id, mapOptions) {
  if (!window.google) {
    apos.log('google maps is not available in the build! Maybe you are running with offline: true in local.js, in which case this is normal.');
    return;
  }
  var self = this;

  // For points not geocoded server side
  var geocoder = new google.maps.Geocoder();

  var mapZoom;
  var mapCenter;

  self.filterBy = mapOptions.filterBy || 'all';

  function filter(filterBy) {
    self.filterBy = filterBy;
    forEachItem(function(item) {
      ifMappable(item, function(item) {
        if (item.infoBox) {
          item.infoBox.close();
        }
        item.marker.setVisible(false);
      });
    });
    forEachItem(function(item) {
      ifFiltered(item, function(item) {
        item.marker.setVisible(true);
      });
    });
    if (filterBy == 'all') {
      resetZoom();
      resetCenter();
    } else {
      autoZoom();
      autoCenter();
    }
  }

  function forEachItem(iterator) {
    for (var i in self.items) {
      iterator(self.items[i]);
    }
  }

  function ifMappable(item, callback) {
    if (item.coords) {
      return callback(item);
    }
  }

  function ifFiltered(item, callback) {
    ifMappable(item, function(item) {
      if (self.filterBy === 'all') {
        return callback(item);
      } else {
        var marker = item.marker;
        for (var t in marker.locTypes) {
          if (marker.locTypes[t] == self.filterBy) {
            return callback(item);
          }
        }
      }
    });
  }

  function autoCenter() {
    var valid = 0;
    var lat = 0.0;
    var lng = 0.0;
    forEachItem(function(item) {
      ifMappable(item, function(item) {
        ifFiltered(item, function(item) {
          lat += item.coords.lat;
          lng += item.coords.lng;
          valid++;
        });
      });
    });
    var mapCenter;
    if (valid) {
      mapCenter = new google.maps.LatLng(lat / valid, lng / valid);
    } else {
      mapCenter = new google.maps.LatLng(39.952335, -75.163789);
    }
    self.map.setCenter(mapCenter);
  }

  function autoZoom() {
    var bounds;
    // Auto-zoom
    bounds = new google.maps.LatLngBounds();
    var count = 0;
    forEachItem(function(item) {
      ifMappable(item, function(item) {
        ifFiltered(item, function(item) {
          count++;
          bounds.extend(new google.maps.LatLng(item.coords.lat, item.coords.lng));
        });
      });
    });
    if (count > 1) {
      self.map.fitBounds(bounds);
    } else if (count === 1) {
      self.map.setZoom(15);
    }
  }

  // Revert to whatever we had initially (for filtering by "all")
  function resetZoom() {
    if (mapZoom) {
      self.map.setZoom(mapZoom);
    } else {
      autoZoom();
    }
  }

  function resetCenter() {
    if (mapCenter) {
      self.map.setCenter(mapCenter);
    } else {
      autoCenter();
    }
  }

  self.items = items;
  self.mapOptions = mapOptions;

  // Why is this useful? (It's called once but just invokes its callback.)
  self.setup = function(callback) {
    callback();
  };

  // set up the actual map. This works asynchronously if needed to
  // geocode addresses browser-side

  self.googleMap = function() {

    // Geocode an item if needed
    function geocodeOne(i, callback) {
      var item = self.items[i];
      if ((!item.coords) && (item.address)) {
        geocoder.geocode( { 'address': self.items[i].address }, function(results, status) {
          if (status == google.maps.GeocoderStatus.OK) {
            item.coords = { lat: results[0].geometry.location.lat(), lng: results[0].geometry.location.lng() };
            return callback();
          } else {
            return callback();
          }
        });
      } else {
        return callback();
      }
    }

    // Simple async for loop to geocode them all, then invoke 'go'
    var i = 0;
    function geocodeNext() {
      if (i >= self.items.length) {
        return go();
      }
      return geocodeOne(i, function() {
        i++;
        return geocodeNext();
      });
    }

    geocodeNext();

    // Called once we're ready with all of our points
    function go() {
      var lat = 0.0;
      var lng = 0.0;
      if (self.mapOptions.center) {
        var center = self.mapOptions.center;
        mapCenter = new google.maps.LatLng(center.lat, center.lng);
      }

      mapZoom = self.mapOptions.zoom;
      var $mapEl = $('#' + id);
      // For easier targeting
      $mapEl.addClass('apos-map');
      var mapEl = $mapEl[0];

      // reasonable defaults for zoom and center until autoZoom or autoCenter can run
      var zoom = mapZoom || 12;

      var teaser = self.mapOptions.teaser;
      var map = new google.maps.Map(mapEl, {
        minZoom: teaser ? zoom : 0,
        maxZoom: teaser ? zoom : 100,
        zoom: zoom,
        panControl: !teaser,
        zoomControl: !teaser,
        scaleControl: !teaser,
        draggable: !teaser,
        streetViewControl: !teaser,
        center: mapCenter || new google.maps.LatLng(39.952335, -75.163789),
        mapTypeId: google.maps.MapTypeId.ROADMAP
      });

      if (self.mapOptions.teaser) {
        $(mapEl).css('cursor', 'pointer');
      }

      // teaser option: this display of the map is just a teaser to take
      // you to a more suitable page. Helps avoid overhead and scrolling
      // frustration when a map is full width on the homepage
      if (self.mapOptions.teaser) {
        google.maps.event.addListener(map, 'click', function() {
          // document.body.style.cursor = 'wait';
          window.location.href = self.mapOptions.teaser;
        });
      }

      self.map = map;

      var mapStyles = self.mapOptions.styles;

      if(mapStyles) map.setOptions({styles:mapStyles});

      map.setOptions({scrollwheel: false, mapTypeControl: false});

      // loop through the items getting passed in from our template
      // and create a marker / info box for each. To avoid convoluted
      // code just call a nested function in a loop and pass it 'i' so that
      // it's safe to write boring, simple event handlers there that use 'i'

      forEachItem(setUpItem);

      if (!mapZoom) {
        autoZoom();
      }
      if (!mapCenter) {
        autoCenter();
      }

      function setUpItem(item) {
        if (!item.coords) {
          // Ignore ungeocoded points
          return;
        }
        self.generateMarker(item, self.map);

        var selector = '.apos-location[data-location-id="' + item._id + '"]';
        $('.apos-location[data-location-id="' + item._id + '"]').click(function() {
          self.activateInfoBox(item);
          return false;
        });
        // attach a click listener to the marker that opens our info box
        if ((!mapOptions.teaser) && (!mapOptions.noBox)) {
          google.maps.event.addListener(item.marker, 'click', function() {
            self.activateInfoBox(item);
          });
        }
      }

      // Create a jQuery event that can be used to filter by
      // a particular tag or 'all'
      $(mapEl).on('filter', function(e, filterBy) {
        return filter(filterBy);
      });
    }
  };

  self.activateInfoBox = function(item) {
    if (!item.areas) {
      // No info boxes for items without rich content, such as those
      // supplied for a plain street address associated with an event
      return;
    }
    forEachItem(function(item) {
      if (item.infoBox) {
        item.infoBox.close();
      }
      if (item.marker) {
        item.marker.content.firstChild.className = item.marker.content.firstChild.className.replace(' active', '');
      }
    });
    if (!item.infoBox) {
      self.generateInfoBox(item, self.map);
    }
    item.infoBox.open(self.map, item.marker);
    item.marker.content.firstChild.className += " active";
  };

  // Find the locType mentioned first in the tags of the item, otherwise
  // the first one defined, otherwise 'general'

  self.getLocType = function(item) {
    // This information was pushed into browserland with apos.pushGlobalData
    var locTypes = apos.data.aposMap.locTypes;
    var locTypeTag = _.find(item.tags || [], function(tag) {
      return _.find(locTypes, function(locType) {
        return locType.value === tag;
      });
    });
    var locType;
    if (locTypeTag) {
      locType = _.find(locTypes, function(locType) {
        return locType.value === locTypeTag;
      });
    }
    if (!locType) {
      locType = locTypes[0];
    }
    if (!locType) {
      locType = { value: 'general', label: 'General' };
    }
    return locType;
  };

  // Return a CSS-friendly version of the locType name

  self.getCssClass = function(item) {
    return apos.cssName(self.getLocType(item).value);
  };

  self.generateMarker = function(item, map)
  {
    var markerHTML = document.createElement('DIV');
        markerHTML.innerHTML = '

';

    var coords;
    // If the address is already a coordinate pair ignore any geocoding result and use it directly
    if (item.address.match(/^[\-\+0-9\.\,\ ]+$/)) {
      var rawCoords = item.address.split(/,\s*/);
      coords = new google.maps.LatLng(parseFloat(rawCoords[0]), parseFloat(rawCoords[1]));
    } else {
      coords = new google.maps.LatLng(item.coords.lat, item.coords.lng);
    }

    var marker = new RichMarker({
      position: coords,
      draggable: false,
      visible: true,
      clickable: true,
      map: map,
      content: markerHTML
    });

    marker.locTypes = item.tags;
    item.marker = marker;
  };

  // IMPORTANT: if you want more properties to be visible here, make sure
  // you override the aposMapPruneLocations function to include them.
  // You can do that in your server-side extension of the map module.
  // See map/index.js for the original. Pruning is necessary to avoid
  // sending a zillion megabytes per page to the browser

  self.generateInfoBox = function(item, map) {
    var $box = apos.fromTemplate('.apos-map-location-info-box');

    var image = apos.getFirstImage(item, 'thumbnail');
    if (image) {
      var url = apos.filePath(image, { size: 'one-third' });
      $box.find('[data-image]').attr('src', url);
    } else {
      // Don't show a broken image
      $box.find('[data-image]').remove();
      $box.addClass('no-img');
    }
    $box.find('[data-loc-type]').text(item.locType);
    $box.find('[data-title]').text(item.title);
    // Do not show the "address" if it is actually a lat,long coordinate pair
    var $address = $box.find('[data-address]');
    if (!item.address.match(/^[\-\+0-9\.\,\ ]+$/)) {
      $address.text(item.address);
    } else {
      $address.hide();
    }
    $box.find('[data-descr]').text(item.descr);
    // $box.find('[data-hours]').text(item.hours);
    if (item.url) {
      $box.find('[data-url]').attr('href', item.url);
    }

    var boxOptions = {
      // Wants the actual DOM element, not jQuery
      content: $box[0],
      disableAutoPan: false,
      pixelOffset: new google.maps.Size(10,-137),
      boxStyle: {
        width: "280px"
       },
      closeBoxMargin: "0px 0px 0px 0px",
      closeBoxURL: "http://www.google.com/intl/en_us/mapfiles/close.gif",
      infoBoxClearance: new google.maps.Size(1, 1),
      pane: "floatPane",
      enableEventPropagation: false
    };

    item.infoBox = new InfoBox(boxOptions);
  };

  //call setup and feed it the google map load listener
  self.setup(function() {
    $(function() {
      self.googleMap();
    });
  });
};
<commit_msg>Better test for empty location<commit_after>// JavaScript which enables display of this module's content
// to logged-out users belongs here.

// Create Google Map that displays the specified items. Uf mapOptions.center is not
// set to an object with lat and lng properties, the center is
// determined from the items. If there are no items... welcome to Philadelphia!

// id is a separate parameter because it's tough to set a property of an object
// in a nunjucks template, and we generate the id there. You are responsible for
// supplying a div with that id and a suitable width and height

var AposGoogleMap = function(items, id, mapOptions) {
  if (!window.google) {
    apos.log('google maps is not available in the build! Maybe you are running with offline: true in local.js, in which case this is normal.');
    return;
  }
  var self = this;

  // For points not geocoded server side
  var geocoder = new google.maps.Geocoder();

  var mapZoom;
  var mapCenter;

  self.filterBy = mapOptions.filterBy || 'all';

  function filter(filterBy) {
    self.filterBy = filterBy;
    forEachItem(function(item) {
      ifMappable(item, function(item) {
        if (item.infoBox) {
          item.infoBox.close();
        }
        item.marker.setVisible(false);
      });
    });
    forEachItem(function(item) {
      ifFiltered(item, function(item) {
        item.marker.setVisible(true);
      });
    });
    if (filterBy == 'all') {
      resetZoom();
      resetCenter();
    } else {
      autoZoom();
      autoCenter();
    }
  }

  function forEachItem(iterator) {
    for (var i in self.items) {
      iterator(self.items[i]);
    }
  }

  function ifMappable(item, callback) {
    if (item.coords) {
      return callback(item);
    }
  }

  function ifFiltered(item, callback) {
    ifMappable(item, function(item) {
      if (self.filterBy === 'all') {
        return callback(item);
      } else {
        var marker = item.marker;
        for (var t in marker.locTypes) {
          if (marker.locTypes[t] == self.filterBy) {
            return callback(item);
          }
        }
      }
    });
  }

  function autoCenter() {
    var valid = 0;
    var lat = 0.0;
    var lng = 0.0;
    forEachItem(function(item) {
      ifMappable(item, function(item) {
        ifFiltered(item, function(item) {
          lat += item.coords.lat;
          lng += item.coords.lng;
          valid++;
        });
      });
    });
    var mapCenter;
    if (valid) {
      mapCenter = new google.maps.LatLng(lat / valid, lng / valid);
    } else {
      mapCenter = new google.maps.LatLng(39.952335, -75.163789);
    }
    self.map.setCenter(mapCenter);
  }

  function autoZoom() {
    var bounds;
    // Auto-zoom
    bounds = new google.maps.LatLngBounds();
    var count = 0;
    forEachItem(function(item) {
      ifMappable(item, function(item) {
        ifFiltered(item, function(item) {
          count++;
          bounds.extend(new google.maps.LatLng(item.coords.lat, item.coords.lng));
        });
      });
    });
    if (count > 1) {
      self.map.fitBounds(bounds);
    } else if (count === 1) {
      self.map.setZoom(15);
    }
  }

  // Revert to whatever we had initially (for filtering by "all")
  function resetZoom() {
    if (mapZoom) {
      self.map.setZoom(mapZoom);
    } else {
      autoZoom();
    }
  }

  function resetCenter() {
    if (mapCenter) {
      self.map.setCenter(mapCenter);
    } else {
      autoCenter();
    }
  }

  self.items = items;
  self.mapOptions = mapOptions;

  // Why is this useful? (It's called once but just invokes its callback.)
  self.setup = function(callback) {
    callback();
  };

  // set up the actual map. This works asynchronously if needed to
  // geocode addresses browser-side

  self.googleMap = function() {

    // Geocode an item if needed
    function geocodeOne(i, callback) {
      var item = self.items[i];
      if ((!item.coords) && (item.address)) {
        geocoder.geocode( { 'address': self.items[i].address }, function(results, status) {
          if (status == google.maps.GeocoderStatus.OK) {
            item.coords = { lat: results[0].geometry.location.lat(), lng: results[0].geometry.location.lng() };
            return callback();
          } else {
            return callback();
          }
        });
      } else {
        return callback();
      }
    }

    // Simple async for loop to geocode them all, then invoke 'go'
    var i = 0;
    function geocodeNext() {
      if (i >= self.items.length) {
        return go();
      }
      return geocodeOne(i, function() {
        i++;
        return geocodeNext();
      });
    }

    geocodeNext();

    // Called once we're ready with all of our points
    function go() {
      var lat = 0.0;
      var lng = 0.0;
      if (self.mapOptions.center) {
        var center = self.mapOptions.center;
        mapCenter = new google.maps.LatLng(center.lat, center.lng);
      }

      mapZoom = self.mapOptions.zoom;
      var $mapEl = $('#' + id);
      // For easier targeting
      $mapEl.addClass('apos-map');
      var mapEl = $mapEl[0];

      // reasonable defaults for zoom and center until autoZoom or autoCenter can run
      var zoom = mapZoom || 12;

      var teaser = self.mapOptions.teaser;
      var map = new google.maps.Map(mapEl, {
        minZoom: teaser ? zoom : 0,
        maxZoom: teaser ? zoom : 100,
        zoom: zoom,
        panControl: !teaser,
        zoomControl: !teaser,
        scaleControl: !teaser,
        draggable: !teaser,
        streetViewControl: !teaser,
        center: mapCenter || new google.maps.LatLng(39.952335, -75.163789),
        mapTypeId: google.maps.MapTypeId.ROADMAP
      });

      if (self.mapOptions.teaser) {
        $(mapEl).css('cursor', 'pointer');
      }

      // teaser option: this display of the map is just a teaser to take
      // you to a more suitable page. Helps avoid overhead and scrolling
      // frustration when a map is full width on the homepage
      if (self.mapOptions.teaser) {
        google.maps.event.addListener(map, 'click', function() {
          // document.body.style.cursor = 'wait';
          window.location.href = self.mapOptions.teaser;
        });
      }

      self.map = map;

      var mapStyles = self.mapOptions.styles;

      if(mapStyles) map.setOptions({styles:mapStyles});

      map.setOptions({scrollwheel: false, mapTypeControl: false});

      // loop through the items getting passed in from our template
      // and create a marker / info box for each. To avoid convoluted
      // code just call a nested function in a loop and pass it 'i' so that
      // it's safe to write boring, simple event handlers there that use 'i'

      forEachItem(setUpItem);

      if (!mapZoom) {
        autoZoom();
      }
      if (!mapCenter) {
        autoCenter();
      }

      function setUpItem(item) {
        if (!item.coords) {
          // Ignore ungeocoded points
          return;
        }
        self.generateMarker(item, self.map);

        var selector = '.apos-location[data-location-id="' + item._id + '"]';
        $('.apos-location[data-location-id="' + item._id + '"]').click(function() {
          self.activateInfoBox(item);
          return false;
        });
        // attach a click listener to the marker that opens our info box
        if ((!mapOptions.teaser) && (!mapOptions.noBox)) {
          google.maps.event.addListener(item.marker, 'click', function() {
            self.activateInfoBox(item);
          });
        }
      }

      // Create a jQuery event that can be used to filter by
      // a particular tag or 'all'
      $(mapEl).on('filter', function(e, filterBy) {
        return filter(filterBy);
      });
    }
  };

  self.activateInfoBox = function(item) {
    if (!item.descr) {
      // No info boxes for items without rich content, such as those
      // supplied for a plain street address associated with an event
      return;
    }
    forEachItem(function(item) {
      if (item.infoBox) {
        item.infoBox.close();
      }
      if (item.marker) {
        item.marker.content.firstChild.className = item.marker.content.firstChild.className.replace(' active', '');
      }
    });
    if (!item.infoBox) {
      self.generateInfoBox(item, self.map);
    }
    item.infoBox.open(self.map, item.marker);
    item.marker.content.firstChild.className += " active";
  };

  // Find the locType mentioned first in the tags of the item, otherwise
  // the first one defined, otherwise 'general'

  self.getLocType = function(item) {
    // This information was pushed into browserland with apos.pushGlobalData
    var locTypes = apos.data.aposMap.locTypes;
    var locTypeTag = _.find(item.tags || [], function(tag) {
      return _.find(locTypes, function(locType) {
        return locType.value === tag;
      });
    });
    var locType;
    if (locTypeTag) {
      locType = _.find(locTypes, function(locType) {
        return locType.value === locTypeTag;
      });
    }
    if (!locType) {
      locType = locTypes[0];
    }
    if (!locType) {
      locType = { value: 'general', label: 'General' };
    }
    return locType;
  };

  // Return a CSS-friendly version of the locType name

  self.getCssClass = function(item) {
    return apos.cssName(self.getLocType(item).value);
  };

  self.generateMarker = function(item, map)
  {
    var markerHTML = document.createElement('DIV');
        markerHTML.innerHTML = '

';

    var coords;
    // If the address is already a coordinate pair ignore any geocoding result and use it directly
    if (item.address.match(/^[\-\+0-9\.\,\ ]+$/)) {
      var rawCoords = item.address.split(/,\s*/);
      coords = new google.maps.LatLng(parseFloat(rawCoords[0]), parseFloat(rawCoords[1]));
    } else {
      coords = new google.maps.LatLng(item.coords.lat, item.coords.lng);
    }

    var marker = new RichMarker({
      position: coords,
      draggable: false,
      visible: true,
      clickable: true,
      map: map,
      content: markerHTML
    });

    marker.locTypes = item.tags;
    item.marker = marker;
  };

  // IMPORTANT: if you want more properties to be visible here, make sure
  // you override the aposMapPruneLocations function to include them.
  // You can do that in your server-side extension of the map module.
  // See map/index.js for the original. Pruning is necessary to avoid
  // sending a zillion megabytes per page to the browser

  self.generateInfoBox = function(item, map) {
    var $box = apos.fromTemplate('.apos-map-location-info-box');

    var image = apos.getFirstImage(item, 'thumbnail');
    if (image) {
      var url = apos.filePath(image, { size: 'one-third' });
      $box.find('[data-image]').attr('src', url);
    } else {
      // Don't show a broken image
      $box.find('[data-image]').remove();
      $box.addClass('no-img');
    }
    $box.find('[data-loc-type]').text(item.locType);
    $box.find('[data-title]').text(item.title);
    // Do not show the "address" if it is actually a lat,long coordinate pair
    var $address = $box.find('[data-address]');
    if (!item.address.match(/^[\-\+0-9\.\,\ ]+$/)) {
      $address.text(item.address);
    } else {
      $address.hide();
    }
    $box.find('[data-descr]').text(item.descr);
    // $box.find('[data-hours]').text(item.hours);
    if (item.url) {
      $box.find('[data-url]').attr('href', item.url);
    }

    var boxOptions = {
      // Wants the actual DOM element, not jQuery
      content: $box[0],
      disableAutoPan: false,
      pixelOffset: new google.maps.Size(10,-137),
      boxStyle: {
        width: "280px"
       },
      closeBoxMargin: "0px 0px 0px 0px",
      closeBoxURL: "http://www.google.com/intl/en_us/mapfiles/close.gif",
      infoBoxClearance: new google.maps.Size(1, 1),
      pane: "floatPane",
      enableEventPropagation: false
    };

    item.infoBox = new InfoBox(boxOptions);
  };

  //call setup and feed it the google map load listener
  self.setup(function() {
    $(function() {
      self.googleMap();
    });
  });
};
<|endoftext|>


<commit_before>define(
  'ephox.alloy.spec.ModalDialogSpec',

  [
    'ephox.alloy.spec.SpecSchema',
    'ephox.alloy.spec.UiSubstitutes',
    'ephox.boulder.api.FieldSchema',
    'ephox.highway.Merger',
    'ephox.perhaps.Option',
    'ephox.sugar.api.SelectorFind'
  ],

  function (SpecSchema, UiSubstitutes, FieldSchema, Merger, Option, SelectorFind) {
    var schema = [
      FieldSchema.strict('dom'),
      FieldSchema.strict('onEscape'),
      FieldSchema.defaulted('draggable', false)
    ];

    var make = function (spec) {
      var detail = SpecSchema.asStructOrDie('modal-dialog', schema, spec, [
        'title',
        'close',
        'draghandle',
        'body',
        'footer'
      ]);

      var extra = detail.draggable() ? {
        '<alloy.dialog.draghandle>': UiSubstitutes.single(
          Merger.deepMerge(
            detail.parts().draghandle(),
            detail.parts().draghandle().base,
            {
              uid: detail.partUids().draghandle,
              dragging: {
                mode: 'mouse',
                getTarget: function (handle) {
                  return SelectorFind.ancestor(handle, '[role="dialog"]').getOr(handle);
                }
              }
            }
          )
        )
      } : { };

      var placeholders = Merger.deepMerge(
        {
          '<alloy.dialog.title>': UiSubstitutes.single(
            Merger.deepMerge(
              detail.parts().title(),
              detail.parts().title().base,
              {
                uid: detail.partUids().title
              }
            )
          ),
          '<alloy.dialog.close>': UiSubstitutes.single(
            Merger.deepMerge(
              detail.parts().close(),
              detail.parts().close().base,
              {
                uid: detail.partUids().close
              }
            )
          ),
          '<alloy.dialog.body>': UiSubstitutes.single(
            Merger.deepMerge(
              detail.parts().body(),
              detail.parts().body().base,
              {
                uid: detail.partUids().body
              }
            )
          ),
          '<alloy.dialog.footer>': UiSubstitutes.single(
            Merger.deepMerge(
              detail.parts().footer(),
              detail.parts().footer().base,
              {
                uid: detail.partUids().footer
              }
            )
          )
        },
        extra
      );

      var components = UiSubstitutes.substitutePlaces(
        Option.some('modal-dialog'),
        detail,
        detail.components(),
        placeholders,
        { }
      );

      return Merger.deepMerge(
        spec,
        {
          uiType: 'custom',
          dom: detail.dom(),
          components: components,
          keying: {
            mode: 'cyclic',
            onEscape: detail.onEscape()
          }
        }
      );
    };

    return {
      make: make
    };
  }
);<commit_msg>VAN-44-wheel-hackery: showDialog and hideDialog<commit_after>define(
  'ephox.alloy.spec.ModalDialogSpec',

  [
    'ephox.alloy.behaviour.Behaviour',
    'ephox.alloy.dom.DomModification',
    'ephox.alloy.spec.SpecSchema',
    'ephox.alloy.spec.UiSubstitutes',
    'ephox.boulder.api.FieldPresence',
    'ephox.boulder.api.FieldSchema',
    'ephox.boulder.api.ValueSchema',
    'ephox.highway.Merger',
    'ephox.peanut.Fun',
    'ephox.perhaps.Option',
    'ephox.sugar.api.SelectorFind',
    'ephox.sugar.api.Traverse'
  ],

  function (Behaviour, DomModification, SpecSchema, UiSubstitutes, FieldPresence, FieldSchema, ValueSchema, Merger, Fun, Option, SelectorFind, Traverse) {
    var schema = [
      FieldSchema.strict('dom'),
      FieldSchema.strict('onEscape'),
      FieldSchema.strict('sink'),
      FieldSchema.defaulted('draggable', false)
    ];

    var make = function (spec) {
      var detail = SpecSchema.asStructOrDie('modal-dialog', schema, spec, [
        'title',
        'close',
        'draghandle',
        'body',
        'footer'
      ]);

      var extra = detail.draggable() ? {
        '<alloy.dialog.draghandle>': UiSubstitutes.single(
          Merger.deepMerge(
            detail.parts().draghandle(),
            detail.parts().draghandle().base,
            {
              uid: detail.partUids().draghandle,
              dragging: {
                mode: 'mouse',
                getTarget: function (handle) {
                  return SelectorFind.ancestor(handle, '[role="dialog"]').getOr(handle);
                }
              }
            }
          )
        )
      } : { };

      var placeholders = Merger.deepMerge(
        {
          '<alloy.dialog.title>': UiSubstitutes.single(
            Merger.deepMerge(
              detail.parts().title(),
              detail.parts().title().base,
              {
                uid: detail.partUids().title
              }
            )
          ),
          '<alloy.dialog.close>': UiSubstitutes.single(
            Merger.deepMerge(
              detail.parts().close(),
              detail.parts().close().base,
              {
                uid: detail.partUids().close
              }
            )
          ),
          '<alloy.dialog.body>': UiSubstitutes.single(
            Merger.deepMerge(
              detail.parts().body(),
              detail.parts().body().base,
              {
                uid: detail.partUids().body
              }
            )
          ),
          '<alloy.dialog.footer>': UiSubstitutes.single(
            Merger.deepMerge(
              detail.parts().footer(),
              detail.parts().footer().base,
              {
                uid: detail.partUids().footer
              }
            )
          )
        },
        extra
      );

      var components = UiSubstitutes.substitutePlaces(
        Option.some('modal-dialog'),
        detail,
        detail.components(),
        placeholders,
        { }
      );

      return Merger.deepMerge(
        spec,
        {
          uiType: 'custom',
          dom: detail.dom(),
          components: components,
          keying: {
            mode: 'cyclic',
            onEscape: detail.onEscape()
          },
          behaviours: [
            Behaviour.contract({
              name: Fun.constant('modal-dialog-spec'),
              exhibit: Fun.constant(DomModification.nu({})),
              handlers: Fun.constant({ }),
              apis: function () {
                return {
                  showDialog: function (dialog) {
                    var blocker = detail.sink().getSystem().build({
                      uiType: 'custom',
                      dom: {
                        tag: 'div',
                        styles: {
                          'position': 'fixed',
                          'left': '0px',
                          'top': '0px',
                          'right': '0px',
                          'bottom': '0px',
                          'background-color': 'rgba(20,20,20,0.5)',
                          'z-index': '100000000'
                        },
                        // FIX: Remove hard-coding
                        classes: [ 'ephox-gel-centered-dialog', 'ephox-gel-modal' ]
                      },
                      components: [
                        { built: dialog }
                      ]
                    });
                    detail.sink().getSystem().addToWorld(blocker);
                    detail.sink().apis().addContainer(blocker);
                    dialog.apis().focusIn();
                  },
                  hideDialog: function (dialog) {
                    console.log('hiding dialog');
                    Traverse.parent(dialog.element()).each(function (parent) {
                      dialog.getSystem().getByDom(parent).each(function (blocker) {
                        detail.sink().apis().removeContainer(blocker);
                        detail.sink().getSystem().removeFromWorld(blocker);
                      });
                    });
                  }
                };
              },
              schema: Fun.constant(FieldSchema.field('_', '_', FieldPresence.asOption(), ValueSchema.anyValue()))
            })

          ]
        }
      );
    };

    return {
      make: make
    };
  }
);<|endoftext|>


<commit_before>'use strict';

// Call this function when the page loads (the "ready" event)
$(document).ready(function() {
	initializePage();
})

/*
 * Function that is called when the document is ready.
 */
function initializePage() {
	//console.log(data);
	$("#search").autocomplete({
    source: ["Sarah", "Tonald", "Ken"],
    close: function (event, ui) {
    		if( window.location.href == "/index-A"){
    			window.location.replace("/convo-A/" + $(this).val() + "#recent");
    		}
    		else{
    			window.location.replace("/convo-B/" + $(this).val() + "#recent");
    		}

    	   }
	});
}<commit_msg>fixed search bar for A/B test<commit_after>'use strict';

// Call this function when the page loads (the "ready" event)
$(document).ready(function() {
	initializePage();
})

/*
 * Function that is called when the document is ready.
 */
function initializePage() {
	//console.log(data);
	$("#search").autocomplete({
    source: ["Sarah", "Tonald", "Ken"],
    close: function (event, ui) {
    		if( window.location.href.includes("/index-A")){
    			window.location.replace("/convo-A/" + $(this).val() + "#recent");
    		}
    		else{
    			window.location.replace("/convo-B/" + $(this).val() + "#recent");
    		}

    	   }
	});
}<|endoftext|>


<commit_before>var logData = [];
console.log = function(){
  args = Array.prototype.slice.call(arguments, 0);
  logData.push(args.join(' '));
};

var CDN = 'http://cdn.peerjs.com';
var FILE = 'peer.js';

var FULL_TEST_TIMEOUT = 30000;

function loadScript(v, cb) {
  if (!cb) {
    cb = noop;
  }
  var loadTimeout = setTimeout(function(){
    cb(new Error('Could not load PeerJS v' + v));
  }, 2000);
  var script = document.createElement('script');
  script.onload = function(){
    clearTimeout(loadTimeout);
    cb()
  };
  script.setAttribute("type", "text/javascript");
  script.setAttribute("src", CDN + "/" + v + "/" + FILE);
  document.getElementsByTagName("head")[0].appendChild(script);
}

function saveTestData(id, data, cb) {
  if (!cb) {
    cb = noop;
  }
  var out = {};
  out[ROLE] = data;
  out.testId = id;
  post('/save', out, cb);
}

function end(workerId, msg) {
  end = function(){};
  // Ensure end only runs once
  clearTimeout(testTimeout);
  if (TEST_ID) {
    console.log(msg);
    saveTestData(TEST_ID, {ended: msg, log: logData});
  }
  post('/end', {workerId: workerId, msg: msg}, noop);
}

// Load testing dependency
var queryData = getQueryData();

var WORKER_ID = queryData.WORKER_ID;
var TEST_ID = queryData.TEST_ID;
var ROLE = queryData.ROLE; // 'client' or 'host'
var PEERJS_VERSION = queryData.PEERJS_VERSION || '0';

if (!WORKER_ID) {
  // Shit
  throw new Error('No WORKER_ID');
}
if (!ROLE) {
  // Shit
  throw new Error('No ROLE specified');
}
if (!TEST_ID) {
  end(WORKER_ID, 'Missing TEST_ID');
  throw new Error('Missing TEST_ID');
}

loadScript(PEERJS_VERSION, function(err){
  if (err) {
    console.log('Error:', err.message);
    end(WORKER_ID, err.message);
  } else {
    console.log('Script loaded');
    saveTestData(TEST_ID, {
      browser: browserInfo(),
      peerJSVersion: PEERJS_VERSION
    });
    init();
  }
});

var testTimeout = setTimeout(function(){
  end(WORKER_ID, 'timeout');
}, FULL_TEST_TIMEOUT);

<commit_msg>Add timestamp to log msgs<commit_after>var logData = [];
console.log = function(){
  args = Array.prototype.slice.call(arguments, 0);
  logData.push(Date.now() + ' ' + args.join(' '));
};

var CDN = 'http://cdn.peerjs.com';
var FILE = 'peer.js';

var FULL_TEST_TIMEOUT = 30000;

function loadScript(v, cb) {
  if (!cb) {
    cb = noop;
  }
  var loadTimeout = setTimeout(function(){
    cb(new Error('Could not load PeerJS v' + v));
  }, 2000);
  var script = document.createElement('script');
  script.onload = function(){
    clearTimeout(loadTimeout);
    cb()
  };
  script.setAttribute("type", "text/javascript");
  script.setAttribute("src", CDN + "/" + v + "/" + FILE);
  document.getElementsByTagName("head")[0].appendChild(script);
}

function saveTestData(id, data, cb) {
  if (!cb) {
    cb = noop;
  }
  var out = {};
  out[ROLE] = data;
  out.testId = id;
  post('/save', out, cb);
}

function end(workerId, msg) {
  end = function(){};
  // Ensure end only runs once
  clearTimeout(testTimeout);
  if (TEST_ID) {
    console.log(msg);
    saveTestData(TEST_ID, {ended: msg, log: logData});
  }
  post('/end', {workerId: workerId, msg: msg}, noop);
}

// Load testing dependency
var queryData = getQueryData();

var WORKER_ID = queryData.WORKER_ID;
var TEST_ID = queryData.TEST_ID;
var ROLE = queryData.ROLE; // 'client' or 'host'
var PEERJS_VERSION = queryData.PEERJS_VERSION || '0';

if (!WORKER_ID) {
  // Shit
  throw new Error('No WORKER_ID');
}
if (!ROLE) {
  // Shit
  throw new Error('No ROLE specified');
}
if (!TEST_ID) {
  end(WORKER_ID, 'Missing TEST_ID');
  throw new Error('Missing TEST_ID');
}

loadScript(PEERJS_VERSION, function(err){
  if (err) {
    console.log('Error:', err.message);
    end(WORKER_ID, err.message);
  } else {
    console.log('Script loaded');
    saveTestData(TEST_ID, {
      browser: browserInfo(),
      peerJSVersion: PEERJS_VERSION
    });
    init();
  }
});

var testTimeout = setTimeout(function(){
  end(WORKER_ID, 'timeout');
}, FULL_TEST_TIMEOUT);

<|endoftext|>


<commit_before>/*global YUI */
YUI.add('dualjustify', function (Y, NAME) {

    "use strict";

    /*static variable*/
    var DOUBLE_BYTE = 'd',
        SINGLE_BYTE = 's',
        TAG = 't',
        DUALJUSTIFY_SELECTOR = '.dualjustify',
        JUSTIFY_SPAN = 'justify-span',
        JUSTIFY_HYPHEN = 'justify-hyphen',
        NOJUSTIFY = 'justify-noadjust',

        /*private variable*/

        refBlock,  // last node that needs dualjustify. We store this node because we use it for testing char width
        widthNode, // a span that we used for inserting each single character and measure its width
        widthMap = {},  // store all the single byte character's width
        origHtml = {};  // cached all the nodes' original html in case of recovery the DOM.

    /**
     * A function for determing if this is CJK (Chinese/Japanese/Korean) chars
     * @param character {String} The character that we want to test
     * @void
     */
    function isDoubleByte(character) {
        // A temporary hack for determing if this is double-byte chars
        // it looks like not a trivial as it may seen.
        // first of all, double byte chars are spread all over the UTF encoding space
        // second, if you dig further, even double byte character !== CJK chars, and we only want CJK which occupy full font width
        // before I found a way (and if possible, a fast way), I simply assume all char code less than 10000 are non-CJK.
        // surprisingly, this rule works pretty well on Chinese blogs when I tested it.
        return character.charCodeAt(0) > 10000;
    }

    /**
     * A function that calculates the char width, and store the value in widthMap.
     * @param character {String} The character that we want to get width
     * @void
     */
    function getCharWidth(character) {

        if (!widthNode) {
            refBlock.append('');
            widthNode = refBlock.get('lastChild');
        }

        if (widthMap[character] === undefined) {
            if (character === ' ') {
                widthMap[character] = getCharWidth('e e') - 2 * getCharWidth('e');
            } else {
                widthNode.set('text', character);
                widthMap[character] = widthNode.get('offsetWidth');
            }
        }

        return widthMap[character];
    }

    /**
     * Parse a Node's innerHTML and parse into an array. The array would be look like
     *  [
     *       { type: TAG, text: ''},
     *       { type: SINGLE_BYTE, text: 'abc' },
     *       { type: DOUBLE_BYTE, text: '中文'},
     *       { type: TAG, text: ''}
     *  ]
     * @param node {Y.Node} a node which we want to parse its innerHTML
     * @return {Array} parsed result
     */
    function parseInnerHtml(node) {
        var output = [], outerhtml, innerhtml, tag, text, currentInDoubleByte, i, max, character, isDouble,
            currentStr = '', nodeName = node.get('nodeName').toLowerCase();

        if (nodeName === 'br') {
            // if node is BR tag, include it directly
            output.push({
                type: TAG,
                text: node.get('outerHTML')
            });
        } else if (node.get('childNodes').size() === 0 || node.test('.' + JUSTIFY_SPAN)) {
            // base case: this node contains pure text, parse string into array
            text = node.get('text');
            if (text.length > 0) {
                // initial value
                currentInDoubleByte = isDoubleByte(text.charAt(0));

                for (i = 0, max = text.length; i < max; i += 1) {
                    character = text.charAt(i);
                    isDouble = isDoubleByte(character);

                    if (!isDouble) {
                        getCharWidth(character);
                    }

                    // if new char uses the same lang w/ current string
                    if (isDouble === currentInDoubleByte) {
                        // append it to current string
                        currentStr += character;
                    } else {
                    // if not, save the previous string into text array
                        output.push({
                            type: currentInDoubleByte ? DOUBLE_BYTE : SINGLE_BYTE,
                            text: currentStr
                        });
                        currentStr = character;
                        currentInDoubleByte = isDouble;
                    }

                }
                // last one
                output.push({
                    type: currentInDoubleByte ? DOUBLE_BYTE : SINGLE_BYTE,
                    text: currentStr
                });
            }

        } else {
            // this is a node which contains more than one child node

            // concat all childnodes results
            node.get('childNodes').each(function (child) {
                output = output.concat(parseInnerHtml(child));
            });

            outerhtml = node.get('outerHTML');
            innerhtml = node.get('innerHTML');

            // if we have outer html
            if (outerhtml.length > innerhtml.length) {

                tag = outerhtml.split(innerhtml +  '</' + nodeName + '>');
                if (tag && tag[0]) {
                    output.unshift({ type: TAG, text: tag[0]});
                    output.push({type: TAG, text: '</' + nodeName + '>'});
                }
            }
        }

        return output;

    }

    /**
     * Transfrom parseInnerHtml results into justified html. The input array is expected to be look like
     *  [
     *       { type: TAG, text: ''},
     *       { type: SINGLE_BYTE, text: 'abc' },
     *       { type: DOUBLE_BYTE, text: '中文'},
     *       { type: TAG, text: ''}
     *  ]
     * and the output would be in html, which you can directly use it to replace current node's innerhtml and see justifed result
     * @param elements {Array} an array which represents the content of current node's outer html
     * @param node {Y.Node} node that needs dual-justify
     * @return {String} html
     */
    function generateJustifyHtml(elements, node) {

        if (!node || !elements || !Array.isArray(elements)) {
            return;
        }

        var containerWidth = parseInt(node.getComputedStyle('width').replace('px', ''), 10),
            currentLineChars = 0,
            outputHtml = '',
            fontsize = parseInt(node.getComputedStyle('fontSize').replace('px', ''), 10),
            charPerLine = Math.floor(containerWidth / fontsize);

        // looping over all html elements and generating output html
        Y.Array.each(elements, function (content, index) {

            if ((index === 0 || index === elements.length - 1) && content.type === TAG) {
                //skip the outer wrapper
                return;
            }

            var textWidth, units, cutpos, classes, textAlign, i, max, spaceleft;

            currentLineChars = currentLineChars % charPerLine;
            if (content.type === TAG) {
                // if the tag is br, it will go to next line, so reset the currentLineChars
                if (content.text === '
' || content.text === '
') {
                    currentLineChars = 0;
                }
                // otherwise, just assume it is harmless and include it
                outputHtml += content.text;
            } else if (content.type === DOUBLE_BYTE) {
                outputHtml += content.text;
                currentLineChars += content.text.length;
            } else {
                // current element is single byte
                content.text = content.text.trim();
                textAlign = 'center';
                while (content.text.length > 0) {
                    textWidth = 0;
                    units = 0;
                    // new string width
                    classes = JUSTIFY_SPAN;
                    spaceleft = (charPerLine - currentLineChars) * fontsize;  //left space in current row; in pixels
                    for (i = 0, max = content.text.length; i < max; i += 1) {
                        textWidth += widthMap[content.text.charAt(i)];
                        if (spaceleft < textWidth) {
                            // hit line end
                            // we need to cut string here, and the rest will go to next line
                            units = charPerLine - currentLineChars;
                            if (/[a-zA-Z]/.test(content.text.charAt(i - 1)) && /[a-zA-Z]/.test(content.text.charAt(i))) {
                                classes += ' ' + JUSTIFY_HYPHEN;
                                textAlign = 'right';
                            }
                            break;
                        }
                    }
                    units = units || Math.ceil(textWidth / fontsize);
                    cutpos = i;
                    while (cutpos < content.text.length && /\s/.test(content.text.charAt(cutpos))) {
                        cutpos += 1;
                    }
                    outputHtml += '' + content.text.slice(0, cutpos) + '';
                    content.text = content.text.substring(cutpos);
                    currentLineChars = (currentLineChars + units) % charPerLine;
                    textAlign = content.text.length > 0 ? 'left' : 'center';
                }

            }

        });

        return outputHtml;

    }

    /**
     * A function which transform a node to be dual-justify
     *
     * An example usage would be:
     * YUI().use('event-resize', 'dualjustify', function(Y){
     *   var options = {
     *       // CSS selector for the article body's paragraph
     *       selector: '.content p'
     *   };
     *   Y.on('domready', Y.Justify.DualJustify, null, options);
     *   Y.on('resize', Y.Justify.DualJustify, null, options);
     * });
     */
    Y.namespace('Justify').DualJustify = function (options) {

        var timestart = Date.now(), timeend,
            selector = options && options.selector ? options.selector : DUALJUSTIFY_SELECTOR,
            blocks = Y.all(selector);

        refBlock = blocks.size() > 0 ? blocks.item(blocks.size() - 1) : null;

        blocks.each(function (node, index) {
            if (node.test('.' + NOJUSTIFY)) {
                return;
            }

            var text,
                elements,
                justifySpans = node.all('.' + JUSTIFY_SPAN),
                nodeID = node.generateID();

            if (origHtml[nodeID]) {
                node.setHTML(origHtml[nodeID]);
            }

            text = node.get('text').trim();

            if (text.length * 0.5 > text.replace(/[0-9a-zA-Z]/g, '').length || node.one('iframe,object,img,i,embed,table,ol,ul,li,.' + NOJUSTIFY)) {
                // 1. over half of the text is english, bypass this
                // 2. if there are any iframe/object... which is not inline text, we will skip
                node.addClass(NOJUSTIFY);
                return;
            }

            if (!origHtml[nodeID]) {
                origHtml[nodeID] = node.get('innerHTML');
            }

            elements = parseInnerHtml(node);
            node.setHTML(generateJustifyHtml(elements, node)).setStyle('word-break', 'break-all');

        });
        if (widthNode) {
            widthNode.remove(true);
        }
        timeend = Date.now();
        Y.log('total execution: ' + (timeend - timestart), 'info', 'dualjustify');
    };

}, "0.0.1", { requires: [ 'node' ]});
<commit_msg>use exact Japanese/Chinese/Korean utf encoding index to determine language<commit_after>/*global YUI */
YUI.add('dualjustify', function (Y, NAME) {

    "use strict";

    /*static variable*/
    var CJK_STRING = 'd',
        NONCJK_STRING = 's',
        TAG = 't',
        DUALJUSTIFY_SELECTOR = '.dualjustify',
        JUSTIFY_SPAN = 'justify-span',
        JUSTIFY_HYPHEN = 'justify-hyphen',
        NOJUSTIFY = 'justify-noadjust',

        /*private variable*/

        refBlock,  // last node that needs dualjustify. We store this node because we use it for testing char width
        widthNode, // a span that we used for inserting each single character and measure its width
        widthMap = {},  // store all the single byte character's width
        origHtml = {};  // cached all the nodes' original html in case of recovery the DOM.

    /**
     * A function for determing if this is CJK (Chinese/Japanese/Korean) chars
     * @param character {String} The character that we want to test
     * @void
     */
    function isCJK(character) {
        // it is not as trivial as it may seen.
        // CJK chars are spread all over the UTF encoding space. Not all of them are grouped together
        // Japanese characters starts at 11904, then comes Chinese chars. Chinese chars ends around 40902
        // Korean characters starts at 44032, ends at 55203.
        // There are some chars that falls in this range and doesn't belong to CJK (e.g. 12331 is  〫)
        // but they are rarely used so I ignore them
        // ref: http://www.public.asu.edu/~rjansen/glyph_encoding.html
        var charcode = character.charCodeAt(0);
        return (charcode >= 11904 && charcode <= 40902) || (charcode >= 44032 && charcode <= 55203) ;
    }

    /**
     * A function that calculates the char width, and store the value in widthMap.
     * @param character {String} The character that we want to get width
     * @void
     */
    function getCharWidth(character) {

        if (!widthNode) {
            refBlock.append('');
            widthNode = refBlock.get('lastChild');
        }

        if (widthMap[character] === undefined) {
            if (character === ' ') {
                widthMap[character] = getCharWidth('e e') - 2 * getCharWidth('e');
            } else {
                widthNode.set('text', character);
                widthMap[character] = widthNode.get('offsetWidth');
            }
        }

        return widthMap[character];
    }

    /**
     * Parse a Node's innerHTML and parse into an array. The array would be look like
     *  [
     *       { type: TAG, text: ''},
     *       { type: NONCJK_STRING, text: 'abc' },
     *       { type: CJK_STRING, text: '中文'},
     *       { type: TAG, text: ''}
     *  ]
     * @param node {Y.Node} a node which we want to parse its innerHTML
     * @return {Array} parsed result
     */
    function parseInnerHtml(node) {
        var output = [], outerhtml, innerhtml, tag, text, currentStringInCJK, i, max, character, currentCharInCJK,
            currentStr = '', nodeName = node.get('nodeName').toLowerCase();

        if (nodeName === 'br') {
            // if node is BR tag, include it directly
            output.push({
                type: TAG,
                text: node.get('outerHTML')
            });
        } else if (node.get('childNodes').size() === 0 || node.test('.' + JUSTIFY_SPAN)) {
            // base case: this node contains pure text, parse string into array
            text = node.get('text');
            if (text.length > 0) {
                // initial value
                currentStringInCJK = isCJK(text.charAt(0));

                for (i = 0, max = text.length; i < max; i += 1) {
                    character = text.charAt(i);
                    currentCharInCJK = isCJK(character);

                    if (!currentCharInCJK) {
                        getCharWidth(character);
                    }

                    // if new char uses the same lang w/ current string
                    if (currentCharInCJK === currentStringInCJK) {
                        // append it to current string
                        currentStr += character;
                    } else {
                    // if not, save the previous string into text array
                        output.push({
                            type: currentStringInCJK ? CJK_STRING : NONCJK_STRING,
                            text: currentStr
                        });
                        currentStr = character;
                        currentStringInCJK = currentCharInCJK;
                    }

                }
                // last one
                output.push({
                    type: currentStringInCJK ? CJK_STRING : NONCJK_STRING,
                    text: currentStr
                });
            }

        } else {
            // this is a node which contains more than one child node

            // concat all childnodes results
            node.get('childNodes').each(function (child) {
                output = output.concat(parseInnerHtml(child));
            });

            outerhtml = node.get('outerHTML');
            innerhtml = node.get('innerHTML');

            // if we have outer html
            if (outerhtml.length > innerhtml.length) {

                tag = outerhtml.split(innerhtml +  '</' + nodeName + '>');
                if (tag && tag[0]) {
                    output.unshift({ type: TAG, text: tag[0]});
                    output.push({type: TAG, text: '</' + nodeName + '>'});
                }
            }
        }

        return output;

    }

    /**
     * Transfrom parseInnerHtml results into justified html. The input array is expected to be look like
     *  [
     *       { type: TAG, text: ''},
     *       { type: NONCJK_STRING, text: 'abc' },
     *       { type: CJK_STRING, text: '中文'},
     *       { type: TAG, text: ''}
     *  ]
     * and the output would be in html, which you can directly use it to replace current node's innerhtml and see justifed result
     * @param elements {Array} an array which represents the content of current node's outer html
     * @param node {Y.Node} node that needs dual-justify
     * @return {String} html
     */
    function generateJustifyHtml(elements, node) {

        if (!node || !elements || !Array.isArray(elements)) {
            return;
        }

        var containerWidth = parseInt(node.getComputedStyle('width').replace('px', ''), 10),
            currentLineChars = 0,
            outputHtml = '',
            fontsize = parseInt(node.getComputedStyle('fontSize').replace('px', ''), 10),
            charPerLine = Math.floor(containerWidth / fontsize);

        // looping over all html elements and generating output html
        Y.Array.each(elements, function (content, index) {

            if ((index === 0 || index === elements.length - 1) && content.type === TAG) {
                //skip the outer wrapper
                return;
            }

            var textWidth, units, cutpos, classes, textAlign, i, max, spaceleft;

            currentLineChars = currentLineChars % charPerLine;
            if (content.type === TAG) {
                // if the tag is br, it will go to next line, so reset the currentLineChars
                if (content.text === '
' || content.text === '
') {
                    currentLineChars = 0;
                }
                // otherwise, just assume it is harmless and include it
                outputHtml += content.text;
            } else if (content.type === CJK_STRING) {
                outputHtml += content.text;
                currentLineChars += content.text.length;
            } else {
                // current element is non-CJK string
                content.text = content.text.trim();
                textAlign = 'center';
                while (content.text.length > 0) {
                    textWidth = 0;
                    units = 0;
                    // new string width
                    classes = JUSTIFY_SPAN;
                    spaceleft = (charPerLine - currentLineChars) * fontsize;  //left space in current row; in pixels
                    for (i = 0, max = content.text.length; i < max; i += 1) {
                        textWidth += widthMap[content.text.charAt(i)];
                        if (spaceleft < textWidth) {
                            // hit line end
                            // we need to cut string here, and the rest will go to next line
                            units = charPerLine - currentLineChars;
                            if (/[a-zA-Z]/.test(content.text.charAt(i - 1)) && /[a-zA-Z]/.test(content.text.charAt(i))) {
                                classes += ' ' + JUSTIFY_HYPHEN;
                                textAlign = 'right';
                            }
                            break;
                        }
                    }
                    units = units || Math.ceil(textWidth / fontsize);
                    cutpos = i;
                    while (cutpos < content.text.length && /\s/.test(content.text.charAt(cutpos))) {
                        cutpos += 1;
                    }
                    outputHtml += '' + content.text.slice(0, cutpos) + '';
                    content.text = content.text.substring(cutpos);
                    currentLineChars = (currentLineChars + units) % charPerLine;
                    textAlign = content.text.length > 0 ? 'left' : 'center';
                }

            }

        });

        return outputHtml;

    }

    /**
     * A function which transform a node to be dual-justify
     *
     * An example usage would be:
     * YUI().use('event-resize', 'dualjustify', function(Y){
     *   var options = {
     *       // CSS selector for the article body's paragraph
     *       selector: '.content p'
     *   };
     *   Y.on('domready', Y.Justify.DualJustify, null, options);
     *   Y.on('resize', Y.Justify.DualJustify, null, options);
     * });
     */
    Y.namespace('Justify').DualJustify = function (options) {

        var timestart = Date.now(), timeend,
            selector = options && options.selector ? options.selector : DUALJUSTIFY_SELECTOR,
            blocks = Y.all(selector);

        refBlock = blocks.size() > 0 ? blocks.item(blocks.size() - 1) : null;

        blocks.each(function (node, index) {
            if (node.test('.' + NOJUSTIFY)) {
                return;
            }

            var text,
                elements,
                justifySpans = node.all('.' + JUSTIFY_SPAN),
                nodeID = node.generateID();

            if (origHtml[nodeID]) {
                node.setHTML(origHtml[nodeID]);
            }

            text = node.get('text').trim();

            if (text.length * 0.5 > text.replace(/[0-9a-zA-Z]/g, '').length || node.one('iframe,object,img,i,embed,table,ol,ul,li,.' + NOJUSTIFY)) {
                // 1. over half of the text is english, bypass this
                // 2. if there are any iframe/object... which is not inline text, we will skip
                node.addClass(NOJUSTIFY);
                return;
            }

            if (!origHtml[nodeID]) {
                origHtml[nodeID] = node.get('innerHTML');
            }

            elements = parseInnerHtml(node);
            node.setHTML(generateJustifyHtml(elements, node)).setStyle('word-break', 'break-all');

        });
        if (widthNode) {
            widthNode.remove(true);
        }
        timeend = Date.now();
        Y.log('total execution: ' + (timeend - timestart), 'info', 'dualjustify');
    };

}, "0.0.1", { requires: [ 'node' ]});
<|endoftext|>


<commit_before>$(document).ready(function() {

  // bind pay button
  $('#pay').on('click', function(event){
   event.preventDefault;
   alert('pay caught')
  });

  $('#charge').on('click', function(event){
   event.preventDefault;
   alert('charge caught')

  });

});

// make a payment
var makePayment = function(){
  var action = $('.create-transaction').attr('action');
  var method = $('.create-transaction').attr('method');
  var toPerson = $('.to-field').val()
  var amount = $('.amount-field').val()
  var description = $('.description-field').val()
};

// make a charge
var makeCharge = function(){
  var action = $('.create-transaction').attr('action');
  var method = $('.create-transaction').attr('method');
  var toPerson = $('.to-field').val()
  var amount = $('.amount-field').val()
  var description = $('.description-field').val()
};
<commit_msg>intial request ajax packaging<commit_after>$(document).ready(function() {

  // bind pay button
  $('#pay').on('click', function(event){
   event.preventDefault;
   alert('pay caught')
  });

  $('#charge').on('click', function(event){
   event.preventDefault;
   alert('charge caught')

  });

});

// make a payment
var makePayment = function(){
  var action = $('.create-transaction').attr('action');
  var method = $('.create-transaction').attr('method');
  var toPerson = $('.to-field').val()
  var amount = $('.amount-field').val()
  var description = $('.description-field').val()

  var request = $.ajax({
      url: action,
      type: method,
      data: {to: toPerson,
             amount: amount,
             description: description,
            },
      dataType: 'JSON'
    });

};

// make a charge
var makeCharge = function(){
  var action = $('.create-transaction').attr('action');
  var method = $('.create-transaction').attr('method');
  var toPerson = $('.to-field').val()
  var amount = $('.amount-field').val()
  var description = $('.description-field').val()
};
<|endoftext|>


<commit_before>$(document).ready(function() {
  app.hookupMap();
  app.hookupSteps();
});

var app = app || {};
app.state = {
  channel: 'sms',
  geom: undefined,
  publisher_id: undefined,
  contact: undefined,
};

app.hookupMap = function() {
  var options = {
    zoom: 12,
    center: [47.604432, -122.336014],
    tileLayer: { detectRetina: true },
    scrollWheelZoom: false,
  };
  var mapId = $('meta[name=mapId]').attr('content');
  var map = app.map = L.mapbox.map('map', mapId, options);
};

app.hookupSteps = function() {
  $('.startButton').on('click', function() {
    app.scrollToElement($('#step1'));
  });

  $('.publisher:not(.soon)').on('click', function(event) {
    $('.publisher').removeClass('selected');

    var $publisher = $(event.currentTarget);
    app.state.publisher_id = $publisher.data('publisher-id');
    $publisher.addClass('selected');

    // Update the confirmation section with the name
    $('.confirmationType').html($publisher.data('publisher-title'));

    app.scrollToElement($('#step2'));
  });

  $('.mapButton').on('click', function() {
    app.scrollToElement($('#step3'));
  });

  $('.smsButton').on('click', function(event) {
    $(event.target).addClass('selected');
    $('.extraInfo').slideDown();
  });

  var finishSubscribe = function(e) {
    // TODO: animate the done checkmark at the same time
    e.preventDefault();
    app.state.contact = $('.phoneNumber').val();

    app.submitSubscription(function() {
      $('#confirmation').slideDown();
      app.scrollToElement($('#confirmation'));
    });
  };
  $('.subscribeButton').on('click', finishSubscribe);
  $('#subscribeForm').on('submit', finishSubscribe);

  $('.resetButton').on('click', function(event) {
    app.resetState();
    app.scrollToElement($('#step1'));

    // Hide after we've scrolled up.
    setTimeout(function() {
      $('#confirmation').hide();
    }, 800);
  });

  var prevMarker, prevCircle;
  var geolocate = function(e) {
    e.preventDefault();
    var address = $('#geolocate').val();
    app.geocode(address, function(latlng) {
      app.map.setView(latlng, 14);
      updateGeometry(latlng);

      if (prevMarker) app.map.removeLayer(prevMarker);
      if (prevCircle) app.map.removeLayer(prevCircle);

      prevMarker = L.marker(latlng).addTo(app.map);
      prevCircle = L.circle(latlng, 1000).addTo(app.map);
    });
  };

  var BOUNDING_DISTANCE_IN_KM = 1;
  var updateGeometry = function(latlng) {
    var center = new LatLon(latlng[0], latlng[1]);
    var bbox = center.boundingBox(BOUNDING_DISTANCE_IN_KM);
    app.state.geom = JSON.stringify({
      type: 'Polygon',
      coordinates: [bbox],
    });
  };

  $('#geolocateForm').on('submit', geolocate);
  $('.geolocateButton').on('click', geolocate);
};

app.scrollToElement = function(el) {
  $('html,body').animate({
    scrollTop: el.offset().top
  }, 800);
};

app.geocode = function(city, callback, context) {
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + encodeURIComponent(city);

  $.getJSON(url, function(response) {
    if (response.error || response.results.length === 0) {
      console.log('Unable to geocode city. Womp Womp.', response.error);
    }

    // Get the coordinates for the center of the city
    var location = response.results[0].geometry.location;
    var latlng = [location.lat, location.lng];
    callback.call(context || this, latlng);
  });
};

app.resetState = function() {
  app.state.publisher_id = undefined;
  $('.publisher').removeClass('selected');

  // Let's leave the location and phone number in place, for easy re-subscribe
};

app.submitSubscription = function(callback) {
  $.post('/subscriptions', { subscription: app.state }, callback);
};
<commit_msg>Halve the default radius<commit_after>$(document).ready(function() {
  app.hookupMap();
  app.hookupSteps();
});

var app = app || {};
app.state = {
  channel: 'sms',
  geom: undefined,
  publisher_id: undefined,
  contact: undefined,
};

app.hookupMap = function() {
  var options = {
    zoom: 13,
    center: [47.604432, -122.336014],
    tileLayer: { detectRetina: true },
    scrollWheelZoom: false,
  };
  var mapId = $('meta[name=mapId]').attr('content');
  var map = app.map = L.mapbox.map('map', mapId, options);
};

app.hookupSteps = function() {
  $('.startButton').on('click', function() {
    app.scrollToElement($('#step1'));
  });

  $('.publisher:not(.soon)').on('click', function(event) {
    $('.publisher').removeClass('selected');

    var $publisher = $(event.currentTarget);
    app.state.publisher_id = $publisher.data('publisher-id');
    $publisher.addClass('selected');

    // Update the confirmation section with the name
    $('.confirmationType').html($publisher.data('publisher-title'));

    app.scrollToElement($('#step2'));
  });

  $('.mapButton').on('click', function() {
    app.scrollToElement($('#step3'));
  });

  $('.smsButton').on('click', function(event) {
    $(event.target).addClass('selected');
    $('.extraInfo').slideDown();
  });

  var finishSubscribe = function(e) {
    // TODO: animate the done checkmark at the same time
    e.preventDefault();
    app.state.contact = $('.phoneNumber').val();

    app.submitSubscription(function() {
      $('#confirmation').slideDown();
      app.scrollToElement($('#confirmation'));
    });
  };
  $('.subscribeButton').on('click', finishSubscribe);
  $('#subscribeForm').on('submit', finishSubscribe);

  $('.resetButton').on('click', function(event) {
    app.resetState();
    app.scrollToElement($('#step1'));

    // Hide after we've scrolled up.
    setTimeout(function() {
      $('#confirmation').hide();
    }, 800);
  });

  var prevMarker, prevCircle;
  var geolocate = function(e) {
    e.preventDefault();
    var address = $('#geolocate').val();
    app.geocode(address, function(latlng) {
      app.map.setView(latlng, 15);
      updateGeometry(latlng);

      if (prevMarker) app.map.removeLayer(prevMarker);
      if (prevCircle) app.map.removeLayer(prevCircle);

      prevMarker = L.marker(latlng).addTo(app.map);
      prevCircle = L.circle(latlng, 500).addTo(app.map);
    });
  };

  var BOUNDING_DISTANCE_IN_KM = 0.5;
  var updateGeometry = function(latlng) {
    var center = new LatLon(latlng[0], latlng[1]);
    var bbox = center.boundingBox(BOUNDING_DISTANCE_IN_KM);
    app.state.geom = JSON.stringify({
      type: 'Polygon',
      coordinates: [bbox],
    });
  };

  $('#geolocateForm').on('submit', geolocate);
  $('.geolocateButton').on('click', geolocate);
};

app.scrollToElement = function(el) {
  $('html,body').animate({
    scrollTop: el.offset().top
  }, 800);
};

app.geocode = function(city, callback, context) {
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + encodeURIComponent(city);

  $.getJSON(url, function(response) {
    if (response.error || response.results.length === 0) {
      console.log('Unable to geocode city. Womp Womp.', response.error);
    }

    // Get the coordinates for the center of the city
    var location = response.results[0].geometry.location;
    var latlng = [location.lat, location.lng];
    callback.call(context || this, latlng);
  });
};

app.resetState = function() {
  app.state.publisher_id = undefined;
  $('.publisher').removeClass('selected');

  // Let's leave the location and phone number in place, for easy re-subscribe
};

app.submitSubscription = function(callback) {
  $.post('/subscriptions', { subscription: app.state }, callback);
};
<|endoftext|>


<commit_before>
var EscSidebar = function() {
    var toggleMinus = '/images/minus.jpg';
    var togglePlus = '/images/plus.jpg';

    return {

        makeCollapsible : function(target, subHead) {
            //By Default put the Menu in collapsed state
            $('.children').parent().children('ul').slideUp('fast');

            //Expand or Contract one particular Nested ul
            $('img', subHead).addClass('clickable').click(function() {
                var toggleSrc = $(this).attr('src');
                if ( toggleSrc == toggleMinus ) {
                    $(this).attr('src', togglePlus).parent().children('ul').slideUp('fast');
                } else{
                    $(this).attr('src', toggleMinus).parent().children('ul').slideDown('fast');
                };
            });

            // Click on an app to get stuff in the content pane
            $(target + ' li li').click(function(appclick) {
                var thisEnv = $(this).parents("li:first").attr("id").replace('_env', '');
                var thisApp = $(this).attr("id").replace('_app', '');
                var url = "/environments/" + thisEnv + "/" + thisApp;
                EscEditor.editPropertiesFor(thisEnv, thisApp);
            });
        },

        contract : function(target) {
            target.attr('src', toggleMinus).children('ul').slideUp('fast');
            $('img', target).attr('src', togglePlus);
        },

        expand : function(target) {
            target.children('ul').slideDown('fast');
            $('img', target).attr('src', toggleMinus);
        },

        getListofEnvsAndApps : function(target) {
            $.getJSON("/environments", function(envData) {
                var envList = "
";
                $.each(envData, function(i, thisEnv) {
                    envList += ('
 ' + thisEnv);
                    var url = "/environments/" + thisEnv;
                    $.getJSON(url, function(appData) {
                        appList = '<ul class="children"';
                        $.each(appData, function(i, thisApp) {
                            appList += "
" + thisApp + "
";
                        });
                        appList += "
";
                        $(target + ' #' + thisEnv + '_env').append(appList);
                    });
                    envList += ('
');
                });
                envList += '
<form id="new_env_form" action="javascript:void(0);"><input type="text" id="new_env_name" name="new_env_name"/></form>
';
                envList += "
";
                $(target).html(envList);
                $(target + " #new_env_form").submit(EscSidebar.createNewEnv);
            });
        },

        validateEnvName : function(name) {
            if ((name == "default") || (name == "")) {
                return false;
            } else {
                return true;
            }
        },

        createNewEnv : function() {
            var newName = $('#new_env_name').val();
            if (EscSidebar.validateEnvName(newName)) {
                $('#new_env_name').val("");
                $.ajax({
                    type: "POST",
                    url: "/environments/" + newName,
                    data: {},
                    success: function(data, textStatus) {
                        EscSidebar.getListofEnvsAndApps('#sidebar');
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown) {
                        alert("Error creating new environment '" + newName);
                    },
                });
            } else {
                alert("Not going to create new environment called " + newName);
            }
        },

// End of namespace
    };
}();

$(document).ready(function() {
    // Get nested list of environments/apps
    $('#show_env').click(function() {
        var subHead = $('.children').parent();
        EscSidebar.makeCollapsible('#sidebar', subHead);
    })

    //Expand All Code
    $('.expand').live("click", function() {
        $(this).children('ul').slideDown('fast');
        $('img', $(this)).attr('src', toggleMinus);
    });

    //Contract All Code
    $('.contract').live("click", function() {
        $(this).attr('src', toggleMinus).children('ul').slideUp('fast');
        $('img', $(this)).attr('src', togglePlus);
    });

    EscSidebar.getListofEnvsAndApps('#sidebar');
});

<commit_msg>Fix cascading event bug, fix expand/collapse<commit_after>
var EscSidebar = function() {
    var toggleMinus = '/images/minus.jpg';
    var togglePlus = '/images/plus.jpg';

    return {

        makeCollapsible : function(target, subHead) {
            //By Default put the Menu in collapsed state
            $('.children').parent().children('ul').slideUp('fast');
        },

        getListofEnvsAndApps : function(target) {
            $.getJSON("/environments", function(envData) {
                var envList = "
";
                $.each(envData, function(i, thisEnv) {
                    envList += ('
 ' + thisEnv);
                    var url = "/environments/" + thisEnv;
                    $.getJSON(url, function(appData) {
                        appList = '<ul class="children"';
                        $.each(appData, function(i, thisApp) {
                            appList += "
" + thisApp + "
";
                        });
                        appList += "
";
                        $(target + ' #' + thisEnv + '_env').append(appList);
                    });
                    envList += ('
');
                });
                envList += '
<form id="new_env_form" action="javascript:void(0);"><input type="text" id="new_env_name" name="new_env_name"/></form>
';
                envList += "
";
                $(target).html(envList);
                $(target + " #new_env_form").submit(EscSidebar.createNewEnv);
            });
        },

        validateEnvName : function(name) {
            if ((name == "default") || (name == "")) {
                return false;
            } else {
                return true;
            }
        },

        createNewEnv : function() {
            var newName = $('#new_env_name').val();
            if (EscSidebar.validateEnvName(newName)) {
                $('#new_env_name').val("");
                $.ajax({
                    type: "POST",
                    url: "/environments/" + newName,
                    data: {},
                    success: function(data, textStatus) {
                        EscSidebar.getListofEnvsAndApps('#sidebar');
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown) {
                        alert("Error creating new environment '" + newName);
                    },
                });
            } else {
                alert("Not going to create new environment called " + newName);
            }
        },

// End of namespace
    };
}();

$(document).ready(function() {
    // Get nested list of environments/apps
    //$('#show_env').click(function() {
    //    var subHead = $('.children').parent();
    //    EscSidebar.makeCollapsible('#sidebar', subHead);
    //})

    //Expand All Code
    $('.expand').live("click", function() {
        $('.children').slideDown('fast');
        $('img', $('.children').parent()).attr('src', toggleMinus);
    });

    //Contract All Code
    $('.contract').live("click", function() {
        $('.children').parent().attr('src', toggleMinus).children('ul').slideUp('fast');
        $('img', $('.children').parent()).attr('src', togglePlus);
    });

    // Click on an app to get stuff in the content pane
    $('#sidebar li li').live("click", function() {
        var thisEnv = $(this).parents("li:first").attr("id").replace('_env', '');
        var thisApp = $(this).attr("id").replace('_app', '');
        EscEditor.editPropertiesFor(thisEnv, thisApp);
    });

    var toggleMinus = '/images/minus.jpg';
    var togglePlus = '/images/plus.jpg';

    //Expand or Contract one particular Nested ul
    $('.expander').live("click", function() {
        var toggleSrc = $(this).attr('src');
        if ( toggleSrc == toggleMinus ) {
            $(this).attr('src', togglePlus).parent().children('ul').slideUp('fast');
        } else{
            $(this).attr('src', toggleMinus).parent().children('ul').slideDown('fast');
        };
    });

    EscSidebar.getListofEnvsAndApps('#sidebar');
});

<|endoftext|>


<commit_before>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// License: GNU General Public License v3. See license.txt

/* toolbar settings */
wn.provide('erpnext.toolbar');

erpnext.toolbar.setup = function() {
	// profile
	var $user = $('#toolbar-user');
	$user.append('
 '
		+wn._("My Settings")+'...
');
	$user.append('

');
	$user.append('
\
		 '+wn._('Documentation')+'
');
	$user.append('
\
		 '+wn._('Forum')+'
');

	if(wn.boot.expires_on) {
		$user.append('
\
			 '+wn._('Live Chat')+'
');
	}

	erpnext.toolbar.set_new_comments();

	$("#toolbar-tools").append('
\
		 Latest Updates
');
}

erpnext.toolbar.set_new_comments = function(new_comments) {
	return;
	var navbar_nc = $('.navbar-new-comments');
	if(cint(new_comments)) {
		navbar_nc.addClass('navbar-new-comments-true')
		navbar_nc.text(new_comments);
	} else {
		navbar_nc.removeClass('navbar-new-comments-true');
		navbar_nc.text(0);
	}
}<commit_msg>[minor] removed erpnext namespaces/branding<commit_after>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// License: GNU General Public License v3. See license.txt

/* toolbar settings */
wn.provide('erpnext.toolbar');

erpnext.toolbar.setup = function() {
	// profile
	var $user = $('#toolbar-user');
	$user.append('
 '
		+wn._("My Settings")+'...
');
	$user.append('

');
	$user.append('
\
		 '+wn._('Documentation')+'
');
	$user.append('
\
		 '+wn._('Forum')+'
');

	if(wn.boot.expires_on) {
		$user.append('
\
			 '+wn._('Live Chat')+'
');
	}

	$("#toolbar-tools").append('
\
		 Latest Updates
');
}<|endoftext|>


<commit_before>/** @jsx React.DOM */

define(
  [
    'react',
    'backbone'
  ],
  function (React, Backbone) {

    return React.createClass({

      propTypes: {
        project: React.PropTypes.instanceOf(Backbone.Model).isRequired
      },

      render: function () {
        return (
          <option value={this.props.project.id}>
            {this.props.project.get('name')}
          </option>
        );
      }
    });

  });
<commit_msg>Display default project as a "Do not add to project" option<commit_after>/** @jsx React.DOM */

define(
  [
    'react',
    'backbone'
  ],
  function (React, Backbone) {

    return React.createClass({

      propTypes: {
        project: React.PropTypes.instanceOf(Backbone.Model).isRequired
      },

      render: function () {
        var projectName = this.props.project.get('name');
        if(projectName === "Default") projectName = "Do not add to a project";
        return (
          <option value={this.props.project.id}>
            {projectName}
          </option>
        );
      }
    });

  });
<|endoftext|>