<commit_before>var App = (function () {

	var self = {};

	// UI containers
	var $map = null;
	var $console = null;
	var $search = null;

	// UI buttons
	var $searchBtn = null;
	var $clearBtn = null;
	var $exportBtn = null;

	// Google Maps API
	var map = null;
	var markers = [];
	var geocoder = null;
	var infoWindow = null;

	/**
	 * Triggered when the DOM is ready.
	 *
	 * @param {Object} ev Dom ready event object
	 */
	var onAppReady = function (ev) {
		console.log('App ready');

		// Cache UI containers
		$map = $('#map');
		$console = $('#console');
		$search = $('#search');

		// Cache UI buttons
		$clearBtn = $console.find('.btn-clear');
		$searchBtn = $console.find('.btn-search');
		$exportBtn = $console.find('.btn-export');

		// UI buttons events
		$clearBtn.on('click', onClearMap);
		$exportBtn.on('click', onExportMap);

		// Form events
		$console.on('submit', onConsoleSubmit);
		$search.on('submit', onSearchSubmit);

		var mapOptions = {
			center: new google.maps.LatLng(46.8032826, -71.242796),
			zoom: 17,
			mapTypeId: google.maps.MapTypeId.ROADMAP
		};

		map = new google.maps.Map($map.get(0), mapOptions);

		geocoder = new google.maps.Geocoder();

		google.maps.event.addListener(map, 'center_changed', onMapChange);
		google.maps.event.addListener(map, 'zoom_changed', onMapChange);

		google.maps.event.addListenerOnce(map, 'idle', function () {
			google.maps.event.trigger(map, 'zoom_changed');
		});
	};

	/**
	 * Triggered when the export button is clicked.
	 *
	 * @param {Object} ev Submit event object
	 */
	var onExportMap = function (ev) {
		var $button = $(this);
		var exportList = [];

		// Loop and build array with lines for each intersection
		// {node_id},{connecting_node_id},{connecting_node_id},etc
		$.each(markers, function(i, marker) {
			exportList.push(marker.data.id + ',' + marker.data.adjacentNodes.join(','))
		});

		// base64 list
		var exportData = btoa(exportList.join("\r\n"));

		// Change href of button
		$button.attr('href', 'data:application/octet-stream;charset=utf-8;base64,' + exportData);
	};

	/**
	 * Clears map from all markers
	 *
	 * @param {Object} ev Submit event object
	 */
	var onClearMap = function (ev) {
		ev.preventDefault();

		$.each(markers, function (i, marker) {
			marker.setMap(null);
		});

		markers = [];

		$clearBtn.attr('disabled', true);
		$exportBtn.attr('disabled', true);
	};

	/**
	 * Triggered when the display button is clicked.
	 *
	 * @param {Object} ev Submit event object
	 */
	var onConsoleSubmit = function (ev) {
		ev.preventDefault();

		$clearBtn.trigger('click');

		var $form = $(this);

		$.ajax({
			url: $form.attr('action'),
			type: $form.attr('method'),
			data: {
				bbox:[$form.find('#minlon').val(), $form.find('#minlat').val(), $form.find('#maxlon').val(), $form.find('#maxlat').val()].join(',')
			},
			dataType: 'json',
			dataFilter: cleanUpNodes,
			success: function (data) {
				$clearBtn.attr('disabled', false);
				$exportBtn.attr('disabled', false);

				onDataReceived(data);
			},
			complete: function () {
				$form.find(':input').attr('disabled', false);
			},
			error: function (request, error, message) {
				console.log(request, error, message);
			}
		});

		$form.find(':input').attr('disabled', true);
	};

	/**
	 * Filters data and transforms XML into JSON.
	 *
	 * @param {String} data Raw XML text
	 * @returns {String} Stringified JSON data
	 */
	var cleanUpNodes = function(data) {
		// Parse XML file
		var xml = $.parseXML(data);

		// XPath filters to remove unnecessary information
		var filters = [
			'@k="building"',
			'@k="building:part"',
			'@k="amenity"',
			'@k="historic"',
			'@k="natural"',
			'@k="waterway"',
			'@k="leisure"',
			'@k="bridge"',
			'@k="railway"',
			'@k="service"',

			'@v="footway"',
			'@v="industrial"',
			'@v="recreation_ground"',
			'@v="dyke"',
			'@v="cycleway"',
			'@v="pedestrian"',
			'@v="track"',
			'@v="grass"',
			'@v="cemetery"',
		];

		console.log("XML has " + xml.evaluate('count(//*)', xml, null, XPathResult.NUMBER_TYPE, null).numberValue + " elements.");

		var badNodes = xml.evaluate('/osm/way[tag[' + filters.join(' or ') + ']] | //tag | /osm/relation | /osm/bounds', xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)

		// Clean up to speed up, mothertrucker!
		deleted = 0;
		for (var i = 0 ; i < badNodes.snapshotLength; i++) {
			var node = badNodes.snapshotItem(i);
			node.parentNode.removeChild(node);
		}

		console.log("After clean up, XML has " + xml.evaluate('count(//*)', xml, null, XPathResult.NUMBER_TYPE, null).numberValue + " elements.");

		// Node references, convert to integers
		var refNodes = xml.evaluate('/osm/way/nd/@ref', xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
		var refs = xmlMapToArray(refNodes, parseInt);

		console.log("Found " + refs.length + " node references.");

		var sortedRefs = refs.sort();
		var repeatedRefs = [];

		for (var i = 0; i < refs.length - 1; i++) {
			if (sortedRefs[i + 1] == sortedRefs[i]) {
				repeatedRefs.push(sortedRefs[i]);
			}
		}

		console.log("Found " + repeatedRefs.length + " intersections.");

		var intersections = [];

		for (var i in repeatedRefs) {
			var nodeId = repeatedRefs[i];
			var node = xml.evaluate('/osm/node[@id="' + nodeId + '"]', xml, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);

			var adjacentNodes = xml.evaluate('/osm/way/nd[@ref="' + nodeId + '"]/following-sibling::nd[1]/@ref | /osm/way/nd[@ref="' + nodeId + '"]/preceding-sibling::nd[1]/@ref', xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
			var adjacents = xmlMapToArray(adjacentNodes, parseInt);

			var intersection = {
				id: nodeId,
				lat: parseFloat(node.singleNodeValue.attributes.getNamedItem('lat').value),
				lng: parseFloat(node.singleNodeValue.attributes.getNamedItem('lon').value),
				adjacentNodes: adjacents
			};

			intersections.push(intersection);
		}

		return JSON.stringify(intersections);
	}

	var xmlMapToArray = function(xml, func) {
		var arr = [];

		for (var i = 0 ; i < xml.snapshotLength; i++) {
			arr.push(func(xml.snapshotItem(i).nodeValue));
		}

		return arr;
	}

	/**
	 * Triggered when data is received from the AJAX call.
	 *
	 * @param {Object} data Data containing nodes and adjacent nodes
	 */
	var onDataReceived = function (data) {
		// Loop through intersections to display markers
		$.each(data, function (i, intersection) {
			// Create marker
			var marker = new google.maps.Marker({
				position: new google.maps.LatLng(intersection.lat, intersection.lng),
				map: map,
				title: intersection.id.toString(),
				data: intersection,
				icon:createMarker()
			});

			google.maps.event.addListener(marker, 'click', onMarkerClick);

			// Store marker in array
			markers.push(marker);
		});
	};

	/**
	 * Triggered when the map is changed (zoom or pan).
	 *
	 * @param {Object} ev Map change event object
	 */
	var onMapChange = function (ev) {
		// Get bounds
		var bounds = map.getBounds();

		// Change bounds
		$console.find('input#maxlon').val(map.getBounds().getNorthEast().lng());
		$console.find('input#minlon').val(map.getBounds().getSouthWest().lng());
		$console.find('input#maxlat').val(map.getBounds().getNorthEast().lat());
		$console.find('input#minlat').val(map.getBounds().getSouthWest().lat());
	};

	/**
	 * Triggered when the search form is submitted.
	 *
	 * @param {Object} ev Submit event object
	 */
	var onSearchSubmit = function (ev) {
		ev.preventDefault();

		var $form = $(this);
		var query = $form.find('input:first').val();

		$form.find(':input').attr('disabled', true);

		geocoder.geocode({'address': query}, function (results, status) {
			$form.find(':input').attr('disabled', false);

			if (status == google.maps.GeocoderStatus.OK) {
				map.setCenter(results[0].geometry.location);
			}
		});
	};

	/**
	 * Returns all the markers in a way.
	 *
	 * @param {Number} waydId ID of way
	 * @param {Array} excludeNodeId List of excluded node IDs
	 * @returns
	 */
	var findMarkersInWay = function(wayId, excludeNodeId) {
		var markersInWay = [];

		for (var i = 0; i < markers.length; i++) {
			var marker = markers[i];

			if (marker.data.id === excludeNodeId) {
				continue;
			}

			if ($.inArray(wayId, marker.data.ways) !== -1) {
				markersInWay.push(marker);
			}
		}

		return markersInWay;
	};

	/**
	 * Returns a specific marker based on it's node ID.
	 *
	 * @param {Number} nodeId
	 * @returns {Number|Null} Google Maps marker or nothing
	 */
	var findMarkerById = function(nodeId) {
		for (var i = 0; i < markers.length; i++) {
			if (markers[i].data.id === nodeId) {
				return markers[i];
			}
		}

		return null;
	};

	/**
	 * Resets all the markers to their original look.
	 */
	var resetMarkers = function() {
		for (var i = 0; i < markers.length; i++) {
			markers[i].setIcon(createMarker());
		}
	};

	/**
	 * Triggered when a marker is clicked
	 *
	 * @param {Object} ev Click event object
	 * @param {Object} marker Marker that was clicked
	 */
	var onMarkerClick = function (ev, marker) {
		resetMarkers();

		var currentMarker = this;

		/*
		var markersInWay = [];

		for (var i = 0; i < this.data.ways.length; i++) {
			$.merge(markersInWay, findMarkersInWay(this.data.ways[i], currentMarker.data.id));
		}

		for (var i = 0; i < markersInWay.length; i++) {
			markersInWay[i].setIcon(createMarker('FFFFFF'));
		}
		*/

		for (var i = 0; i < this.data.adjacentNodes.length; i++) {
			var adjacentMarker = findMarkerById(this.data.adjacentNodes[i]);

			if (adjacentMarker) {
				adjacentMarker.setIcon(createMarker('000000'));
			}
		}

		// Identify current clicked marker
		currentMarker.setIcon(createMarker('00FF00'));

		if (infoWindow) {
			infoWindow.close();
			infoWindow = null;
		}

		infoWindow = new google.maps.InfoWindow({
			content: currentMarker.data.id.toString()
		});

		infoWindow.open(map, currentMarker);
	};

	/**
	 * Creates a marker
	 *
	 * @param {String} color Hexadecimal color
	 * @returns {MarkerImage} Google Maps marker
	 */
	var createMarker = function(color) {
		return new google.maps.MarkerImage(
			"http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + (color || 'FE7569'),
			new google.maps.Size(21, 34),
			new google.maps.Point(0,0),
			new google.maps.Point(10, 34)
		);
	};

	/**
	 * App constructor
	 */
	var construct = (function () {
		google.maps.event.addDomListener(window, 'load', onAppReady);
	})();

	return self;
})();

$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};<commit_msg>comments and formatting<commit_after>var App = (function () {

	var self = {};

	// UI containers
	var $map = null;
	var $console = null;
	var $search = null;

	// UI buttons
	var $searchBtn = null;
	var $clearBtn = null;
	var $exportBtn = null;

	// Google Maps API
	var map = null;
	var markers = [];
	var geocoder = null;
	var infoWindow = null;

	/**
	 * Triggered when the DOM is ready.
	 *
	 * @param {Object} ev Dom ready event object
	 */
	var onAppReady = function (ev) {
		console.log('App ready');

		// Cache UI containers
		$map = $('#map');
		$console = $('#console');
		$search = $('#search');

		// Cache UI buttons
		$clearBtn = $console.find('.btn-clear');
		$searchBtn = $console.find('.btn-search');
		$exportBtn = $console.find('.btn-export');

		// UI buttons events
		$clearBtn.on('click', onClearMap);
		$exportBtn.on('click', onExportMap);

		// Form events
		$console.on('submit', onConsoleSubmit);
		$search.on('submit', onSearchSubmit);

		var mapOptions = {
			center: new google.maps.LatLng(46.8032826, -71.242796),
			zoom: 17,
			mapTypeId: google.maps.MapTypeId.ROADMAP
		};

		map = new google.maps.Map($map.get(0), mapOptions);

		geocoder = new google.maps.Geocoder();

		google.maps.event.addListener(map, 'center_changed', onMapChange);
		google.maps.event.addListener(map, 'zoom_changed', onMapChange);

		google.maps.event.addListenerOnce(map, 'idle', function () {
			google.maps.event.trigger(map, 'zoom_changed');
		});
	};

	/**
	 * Triggered when the export button is clicked.
	 *
	 * @param {Object} ev Submit event object
	 */
	var onExportMap = function (ev) {
		var $button = $(this);
		var exportList = [];

		// Loop and build array with lines for each intersection
		// {node_id},{connecting_node_id},{connecting_node_id},etc
		$.each(markers, function (i, marker) {
			exportList.push(marker.data.id + ',' + marker.data.adjacentNodes.join(','))
		});

		// base64 list
		var exportData = btoa(exportList.join("\r\n"));

		// Change href of button
		$button.attr('href', 'data:application/octet-stream;charset=utf-8;base64,' + exportData);
	};

	/**
	 * Clears map from all markers
	 *
	 * @param {Object} ev Submit event object
	 */
	var onClearMap = function (ev) {
		ev.preventDefault();

		$.each(markers, function (i, marker) {
			marker.setMap(null);
		});

		markers = [];

		$clearBtn.attr('disabled', true);
		$exportBtn.attr('disabled', true);
	};

	/**
	 * Triggered when the display button is clicked.
	 *
	 * @param {Object} ev Submit event object
	 */
	var onConsoleSubmit = function (ev) {
		ev.preventDefault();

		$clearBtn.trigger('click');

		var $form = $(this);

		$.ajax({
			url: $form.attr('action'),
			type: $form.attr('method'),
			data: {
				bbox: [$form.find('#minlon').val(), $form.find('#minlat').val(), $form.find('#maxlon').val(), $form.find('#maxlat').val()].join(',')
			},
			dataType: 'json',
			dataFilter: cleanUpNodes,
			success: function (data) {
				$clearBtn.attr('disabled', false);
				$exportBtn.attr('disabled', false);

				onDataReceived(data);
			},
			complete: function () {
				$form.find(':input').attr('disabled', false);
			},
			error: function (request, error, message) {
				console.log(request, error, message);
			}
		});

		$form.find(':input').attr('disabled', true);
	};

	/**
	 * Filters data and transforms XML into JSON.
	 *
	 * @param {String} data Raw XML text
	 * @returns {String} Stringified JSON data
	 */
	var cleanUpNodes = function (data) {
		// Parse XML file
		var xml = $.parseXML(data);

		// XPath filters to remove unnecessary information
		var filters = [
			'@k="building"',
			'@k="building:part"',
			'@k="amenity"',
			'@k="historic"',
			'@k="natural"',
			'@k="waterway"',
			'@k="leisure"',
			'@k="bridge"',
			'@k="railway"',
			'@k="service"',

			'@v="footway"',
			'@v="industrial"',
			'@v="recreation_ground"',
			'@v="dyke"',
			'@v="cycleway"',
			'@v="pedestrian"',
			'@v="track"',
			'@v="grass"',
			'@v="cemetery"'
		];

		console.log("XML has " + xml.evaluate('count(//*)', xml, null, XPathResult.NUMBER_TYPE, null).numberValue + " elements.");

		var badNodes = xml.evaluate('/osm/way[tag[' + filters.join(' or ') + ']] | //tag | /osm/relation | /osm/bounds', xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

		// Clean up to speed up, mothertrucker!
		var deleted = 0;
		for (var i = 0; i < badNodes.snapshotLength; i++) {
			var node = badNodes.snapshotItem(i);
			node.parentNode.removeChild(node);
		}

		console.log("After clean up, XML has " + xml.evaluate('count(//*)', xml, null, XPathResult.NUMBER_TYPE, null).numberValue + " elements.");

		// Node references, convert to integers
		var refNodes = xml.evaluate('/osm/way/nd/@ref', xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
		var refs = xmlMapToArray(refNodes, parseInt);

		console.log("Found " + refs.length + " node references.");

		var sortedRefs = refs.sort();
		var repeatedRefs = [];

		for (var j = 0; j < refs.length - 1; j++) {
			if (sortedRefs[j + 1] == sortedRefs[j]) {
				repeatedRefs.push(sortedRefs[j]);
			}
		}

		console.log("Found " + repeatedRefs.length + " intersections.");

		var intersections = [];

		for (var k in repeatedRefs) {
			var nodeId = repeatedRefs[k];
			var currentNode = xml.evaluate('/osm/node[@id="' + nodeId + '"]', xml, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);

			var adjacentNodes = xml.evaluate('/osm/way/nd[@ref="' + nodeId + '"]/following-sibling::nd[1]/@ref | /osm/way/nd[@ref="' + nodeId + '"]/preceding-sibling::nd[1]/@ref', xml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
			var adjacents = xmlMapToArray(adjacentNodes, parseInt);

			var intersection = {
				id: nodeId,
				lat: parseFloat(currentNode.singleNodeValue.attributes.getNamedItem('lat').value),
				lng: parseFloat(currentNode.singleNodeValue.attributes.getNamedItem('lon').value),
				adjacentNodes: adjacents
			};

			intersections.push(intersection);
		}

		return JSON.stringify(intersections);
	};

	var xmlMapToArray = function (xml, func) {
		var arr = [];

		for (var i = 0; i < xml.snapshotLength; i++) {
			arr.push(func(xml.snapshotItem(i).nodeValue));
		}

		return arr;
	};

	/**
	 * Triggered when data is received from the AJAX call.
	 *
	 * @param {Object} data Data containing nodes and adjacent nodes
	 */
	var onDataReceived = function (data) {
		// Loop through intersections to display markers
		$.each(data, function (i, intersection) {
			// Create marker
			var marker = new google.maps.Marker({
				position: new google.maps.LatLng(intersection.lat, intersection.lng),
				map: map,
				title: intersection.id.toString(),
				data: intersection,
				icon: createMarker()
			});

			google.maps.event.addListener(marker, 'click', onMarkerClick);

			// Store marker in array
			markers.push(marker);
		});
	};

	/**
	 * Triggered when the map is changed (zoom or pan).
	 *
	 * @param {Object} ev Map change event object
	 */
	var onMapChange = function (ev) {
		// Get bounds
		var bounds = map.getBounds();

		// Change bounds
		$console.find('input#maxlon').val(map.getBounds().getNorthEast().lng());
		$console.find('input#minlon').val(map.getBounds().getSouthWest().lng());
		$console.find('input#maxlat').val(map.getBounds().getNorthEast().lat());
		$console.find('input#minlat').val(map.getBounds().getSouthWest().lat());
	};

	/**
	 * Triggered when the search form is submitted.
	 *
	 * @param {Object} ev Submit event object
	 */
	var onSearchSubmit = function (ev) {
		ev.preventDefault();

		var $form = $(this);
		var query = $form.find('input:first').val();

		$form.find(':input').attr('disabled', true);

		geocoder.geocode({'address': query}, function (results, status) {
			$form.find(':input').attr('disabled', false);

			if (status == google.maps.GeocoderStatus.OK) {
				map.setCenter(results[0].geometry.location);
			}
		});
	};

	/**
	 * Returns all the markers in a way.
	 *
	 * @param {Number} wayId ID of way
	 * @param {Array} excludeNodeId List of excluded node IDs
	 * @returns {Array} Marker in way
	 */
	var findMarkersInWay = function (wayId, excludeNodeId) {
		var markersInWay = [];

		for (var i = 0; i < markers.length; i++) {
			var marker = markers[i];

			if (marker.data.id === excludeNodeId) {
				continue;
			}

			if ($.inArray(wayId, marker.data.ways) !== -1) {
				markersInWay.push(marker);
			}
		}

		return markersInWay;
	};

	/**
	 * Returns a specific marker based on it's node ID.
	 *
	 * @param {Number} nodeId
	 * @returns {Number|Null} Google Maps marker or nothing
	 */
	var findMarkerById = function (nodeId) {
		for (var i = 0; i < markers.length; i++) {
			if (markers[i].data.id === nodeId) {
				return markers[i];
			}
		}

		return null;
	};

	/**
	 * Resets all the markers to their original look.
	 */
	var resetMarkers = function () {
		for (var i = 0; i < markers.length; i++) {
			markers[i].setIcon(createMarker());
		}
	};

	/**
	 * Triggered when a marker is clicked
	 *
	 * @param {Object} ev Click event object
	 * @param {Object} marker Marker that was clicked
	 */
	var onMarkerClick = function (ev, marker) {
		resetMarkers();

		var currentMarker = this;

		/*
		 var markersInWay = [];

		 for (var i = 0; i < this.data.ways.length; i++) {
		 $.merge(markersInWay, findMarkersInWay(this.data.ways[i], currentMarker.data.id));
		 }

		 for (var i = 0; i < markersInWay.length; i++) {
		 markersInWay[i].setIcon(createMarker('FFFFFF'));
		 }
		 */

		for (var i = 0; i < this.data.adjacentNodes.length; i++) {
			var adjacentMarker = findMarkerById(this.data.adjacentNodes[i]);

			if (adjacentMarker) {
				adjacentMarker.setIcon(createMarker('000000'));
			}
		}

		// Identify current clicked marker
		currentMarker.setIcon(createMarker('00FF00'));

		if (infoWindow) {
			infoWindow.close();
			infoWindow = null;
		}

		infoWindow = new google.maps.InfoWindow({
			content: currentMarker.data.id.toString()
		});

		infoWindow.open(map, currentMarker);
	};

	/**
	 * Creates a marker
	 *
	 * @param {String} color Hexadecimal color
	 * @returns {Object} Google Maps marker
	 */
	var createMarker = function (color) {
		return new google.maps.MarkerImage(
			"http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + (color || 'FE7569'),
			new google.maps.Size(21, 34),
			new google.maps.Point(0, 0),
			new google.maps.Point(10, 34)
		);
	};

	/**
	 * App constructor
	 */
	var construct = (function () {
		google.maps.event.addDomListener(window, 'load', onAppReady);
	})();

	return self;
})();

/**
 * Transforms form data into an object via serializeArray
 * Solution by Tobias Cohen (http://stackoverflow.com/a/1186309/176772)
 * @returns {Object}
 */
$.fn.serializeObject = function () {
	var o = {};
	var a = this.serializeArray();
	$.each(a, function () {
		if (o[this.name] !== undefined) {
			if (!o[this.name].push) {
				o[this.name] = [o[this.name]];
			}
			o[this.name].push(this.value || '');
		} else {
			o[this.name] = this.value || '';
		}
	});
	return o;
};<|endoftext|>


<commit_before>(function(root, factory){

  if (typeof define === 'function' && define.amd) {
    define(['exports', 'underscore', 'backbone'], function(exports, _, Backbone){
      return factory(root, _, Backbone);
    });

  } else {
    factory(root, root._, root.Backbone);
  }

})(this, function(root, _, Backbone){

  //
  // Backbone View Hierachy extension
  //
  _.extend(Backbone.View.prototype, {

    // getter method
    getChildren: function() {
      if (!this.children) {
        this.children = [];
      }
      return this.children;
    },

    // find a child by id from the hierachy
    // Breadth First Search
    findChild: function(id) {
      var result = _.find(this.getChildren(), function(child){
        return child.el.id == id;
      });

      if (!result) {
        var container = _.find(this.getChildren(), function(child){
          return child.find(id);
        });
        result = container.find(id);
      }

      return result;
    },

    addChild: function(child) {
      this.getChildren().push(child);
    },

    removeChild: function(child) {
      var index = this.getChildren().indexOf(child);
      if (index != -1) this.getChildren().splice(index, 1);
    },

    disposeChildren: function() {
      _.each(this.children, function(child) {
        if (child.dispose) {
          child.dispose();
        }
      });

      this.children = null;
    },

    //
    dispose: function() {

      this.disposeChildren();

      this.remove();
    },

    remove: function() {

      this.disposeChildren();

      this.__super__.remove.apply(this, arguments);
    }

  });

  // legacy api
  var legacy = {

    addSubview: function(view){
      this.addChild(view);
    },

    //find a subview
    //Breadth First Search
    find: function(id){
      return this.findChild(id);
    }

  }

  _.extend(Backbone.View.prototype, legacy);

});
<commit_msg>fix a call bug<commit_after>(function(root, factory){

  if (typeof define === 'function' && define.amd) {
    define(['exports', 'underscore', 'backbone'], function(exports, _, Backbone){
      return factory(root, _, Backbone);
    });

  } else {
    factory(root, root._, root.Backbone);
  }

})(this, function(root, _, Backbone){

  var origin_remove = Backbone.View.prototype.remove;
  //
  // Backbone View Hierachy extension
  //
  _.extend(Backbone.View.prototype, {

    // getter method
    getChildren: function() {
      if (!this.children) {
        this.children = [];
      }
      return this.children;
    },

    // find a child by id from the hierachy
    // Breadth First Search
    findChild: function(id) {
      var result = _.find(this.getChildren(), function(child){
        return child.el.id == id;
      });

      if (!result) {
        var container = _.find(this.getChildren(), function(child){
          return child.find(id);
        });
        result = container.find(id);
      }

      return result;
    },

    addChild: function(child) {
      this.getChildren().push(child);
    },

    removeChild: function(child) {
      var index = this.getChildren().indexOf(child);
      if (index != -1) this.getChildren().splice(index, 1);
    },

    disposeChildren: function() {
      _.each(this.children, function(child) {
        if (child.dispose) {
          child.dispose();
        }
      });

      this.children = null;
    },

    //
    dispose: function() {

      this.disposeChildren();

      this.remove();
    },

    remove: function() {

      this.disposeChildren();

      origin_remove.apply(this, arguments);
    }

  });

  // legacy api
  var legacy = {

    addSubview: function(view){
      this.addChild(view);
    },

    //find a subview
    //Breadth First Search
    find: function(id){
      return this.findChild(id);
    }

  }

  _.extend(Backbone.View.prototype, legacy);

});
<|endoftext|>


<commit_before>/**
 * Javascript for autocomplete feature of shop_search
 *
 * @author Mark Guinn <mark@adaircreative.com>
 * @date 9.23.13
 * @package shop_search
 */
(function ($, window, document, undefined) {
	'use strict';
	if (typeof(window.ShopSearch) == 'undefined') window.ShopSearch = {};

	ShopSearch.init = function() {
		// Search sorter
		$('#sort').change(function(){
			var $this = $(this);
			var url = $this.data('url');
			url = url.replace('NEWSORTVALUE', encodeURIComponent($this.val()));
			$(document.body).trigger('searchstate', url);
		});

		// initialize the other components
		if (ShopSearch.Suggest)        ShopSearch.Suggest.init();
		if (ShopSearch.CheckboxFacets) ShopSearch.CheckboxFacets.init();
		if (ShopSearch.RangeFacets)    ShopSearch.RangeFacets.init();
	};

	// We define this global event which, by default, just reloads
	// the page. This allows one to unregister all handlers on this
	// event and register one of it's own which may, for example,
	// use Ajax or otherwise check the form state.
	// We define it outside the onready handler so that it can easily
	// be unregistered from inside any other onready handler.
	$(document.body).on('searchstate', function(e, url){
		if (url && url.substr(0, 1) != '/' && url.substr(0, 5) != 'http:') {
			// There is a bug/feature in IE that causes document.location.href = '...'
			// not to respect the base href with relative urls. This takes care of it.
			var base = 	$('base');
			if (base.length > 0) {
				var baseHref = base.prop('href');
				if (baseHref.substr(-1) != '/') baseHref += '/';
				url = baseURI + url;
			}
		}

		if (url) document.location.href = url;
	});

	// initialize search onready
	$(document).ready(ShopSearch.init);

}(jQuery, this, this.document));
<commit_msg>3rd try at fixing ie bug<commit_after>/**
 * Javascript for autocomplete feature of shop_search
 *
 * @author Mark Guinn <mark@adaircreative.com>
 * @date 9.23.13
 * @package shop_search
 */
(function ($, window, document, undefined) {
	'use strict';
	if (typeof(window.ShopSearch) == 'undefined') window.ShopSearch = {};

	ShopSearch.init = function() {
		// Search sorter
		$('#sort').change(function(){
			var $this = $(this);
			var url = $this.data('url');
			url = url.replace('NEWSORTVALUE', encodeURIComponent($this.val()));
			$(document.body).trigger('searchstate', url);
		});

		// initialize the other components
		if (ShopSearch.Suggest)        ShopSearch.Suggest.init();
		if (ShopSearch.CheckboxFacets) ShopSearch.CheckboxFacets.init();
		if (ShopSearch.RangeFacets)    ShopSearch.RangeFacets.init();
	};

	// We define this global event which, by default, just reloads
	// the page. This allows one to unregister all handlers on this
	// event and register one of it's own which may, for example,
	// use Ajax or otherwise check the form state.
	// We define it outside the onready handler so that it can easily
	// be unregistered from inside any other onready handler.
	$(document.body).on('searchstate', function(e, url){
		if (url && url.substr(0, 1) != '/' && url.substr(0, 5) != 'http:') {
			// There is a bug/feature in IE that causes document.location.href = '...'
			// not to respect the base href with relative urls. This takes care of it.
			var base = 	$('base');
			if (base.length > 0) {
				var baseHref = base.prop('href');
				if (baseHref.substr(-1) != '/') baseHref += '/';
				url = baseHref + url;
			}
		}

		if (url) document.location.href = url;
	});

	// initialize search onready
	$(document).ready(ShopSearch.init);

}(jQuery, this, this.document));
<|endoftext|>


<commit_before>$(function() {
  $('.navbar').load('../navbar.html');
  $('#landing').load('../landing.html');

});

$(window).load(function() {
	console.log("ham");
  $(".navbar").on("click", "a", function(event){
  	event.preventDefault();
  	$("#landing").hide();
  	$("#canvas").remove();
  	var myAudioTag1 = document.getElementById("audiotag1");

  	var myAudioTag2 = document.getElementById("audiotag2");
  	if(myAudioTag1){myAudioTag1.pause();}
  	if(myAudioTag1){myAudioTag1.pause();}

  	var link = $(this).attr("href");
  	var request = $.ajax({url: link});
  	request.done(function(response){
	  	if(link == "http://peterhinners.github.io"){
	  		$(".main-content").html("");
	  		$("#landing").show();

	  	} else {
	  		$(".main-content").html(response);
	  	}
  	});
	});

	$(".main-content").on("click", "a", function(event){
		var link = $(this).attr("href");
		if(link == "http://catbreak.herokuapp.com"){
			// } else if(link == "http://peterhinners.github.io/blog/TomJerry.html"){
			// event.preventDefault();
			// $(".navbar").hide();
			// // $(".main-content").html("");
			// var request = $.ajax({url: link});
			//   request.done(function(response){
			//   	// $(".navbar").hide();
			//   	$(".main-content").html(response);
			//   });
			}	else {
			event.preventDefault();
			$(".main-content").html("");
			var request = $.ajax({url: link});
			  request.done(function(response){
			  	$(".main-content").html(response);
			  });
		}

	});

});

window.addEventListener("keydown", function(e) {
    // space and arrow keys
    if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
        e.preventDefault();
    }
}, false);

<commit_msg>sound<commit_after>$(function() {
  $('.navbar').load('../navbar.html');
  $('#landing').load('../landing.html');

});

$(window).load(function() {
	console.log("tom");
  $(".navbar").on("click", "a", function(event){
  	event.preventDefault();
  	$("#landing").hide();
  	$("#canvas").remove();
  	$('audio').each(function(){ this.pause() });

  	var link = $(this).attr("href");
  	var request = $.ajax({url: link});
  	request.done(function(response){
	  	if(link == "http://peterhinners.github.io"){
	  		$(".main-content").html("");
	  		$("#landing").show();

	  	} else {
	  		$(".main-content").html(response);
	  	}
  	});
	});

	$(".main-content").on("click", "a", function(event){
		var link = $(this).attr("href");
		if(link == "http://catbreak.herokuapp.com"){
			// } else if(link == "http://peterhinners.github.io/blog/TomJerry.html"){
			// event.preventDefault();
			// $(".navbar").hide();
			// // $(".main-content").html("");
			// var request = $.ajax({url: link});
			//   request.done(function(response){
			//   	// $(".navbar").hide();
			//   	$(".main-content").html(response);
			//   });
			}	else {
			event.preventDefault();
			$(".main-content").html("");
			var request = $.ajax({url: link});
			  request.done(function(response){
			  	$(".main-content").html(response);
			  });
		}

	});

});

window.addEventListener("keydown", function(e) {
    // space and arrow keys
    if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
        e.preventDefault();
    }
}, false);

<|endoftext|>


<commit_before>//datastruktur på liste over hytter: hytteliste[hytte[karfunksjon(koordinater), område, hyttenavn, bildested, beskrivelse, ID, høydemeter, fasiliteter]].
var hytter = [
  [L.latLng(60.535, 5.924722), "bergsdalen", "Høgabu", "img/hogabu.jpg", "Høgabu er en av turlagets eldste hytter. Den ligger fint til i siden over Tjøranadalsvatnet, med flott utsyn mot Trollaskardet mot sørøst og Bukkafjellet bak. Kneisende opp ved hytten, ligger Bergsbukken. I Tjørnadalsvatnet kan man få fisk av varierende størrelse, iblant kan den være av bra størrelse.", 0, 900, "
Selvbetjent hele året.

26 senger.
", 'Hundebur Vedovn'],
  [L.latLng(60.536944, 6.079167), "bergsdalen", "Alexander Grieghytten", "img/alexgrieg.jpg", "Området rundt Alexander Grieghytten har en rekke turmuligheter. Hamlagrøhornet og Krampane nord for hytten, og Kjerringafjellet og Storliknausen sør for hytten, er alle flotte toppturmål. Førstnevnte er mye benyttet som utflukt for de de yngste turgåerne (barneskolealder). Vinterstid er det lett og fint skiterreng fra Hamlagrøosen og sørover mot Fugladalen.", 1, 600, "
Ubetjent hele året.

Må låne nøkkel.

34 sengeplasser.
", 'Hundebur Vedovn Dusj'],
  [L.latLng(60.500833, 5.960278), "bergsdalen", "Gullhorgabu", "img/gullhorgabu.jpg", "Gullhorgabu ligger ca. 3 timers gange fra bilvei i Bergsdalen. Stien går på den kulturhistoriske vegen Militærvegen, som ble bygget som transportvei mellom Voss og Samnanger i slutten av 1920-årene, gjør i dag tjeneste som god tursti. Det er en fin tur både for store og små. Her kan man stedvis gå ved siden av hverandre, gjennom et variert, spennende og samtidig lettgått terreng.  Beregn lenger tid går du med barn.", 2, 840, "
Selvbetjent hele året.

34 sengeplasser.
", "Vedovn Hundebur"],
  [L.latLng(60.473889, 6.075278), "bergsdalen", "Vending", "img/vending.jpg", "Rundt Vending er det et ypperlig turterreng både sommer og vinter. Fuglafjell. Geitafjell og Skrott er flotte dagsturmål fra hytten. Ta med fiskestang, og går du med barn, ligg over en dag og kos dere i området. En av de fineste turene man kan gå i dette området, er fra Hamlagrø, overnatting på Vending og derfra til Botnen.", 3, 820, "
Selvbetjent hele året.

30 sengeplasser.
", "Vedovn Hundebur"],
  [L.latLng(60.514602, 6.201278), "bergsdalen", "Kiellandbu", "img/kiellandbu.jpg", "Kiellandbu ligger flott til på Løkjedalsnuten med imponerende utsikt mot Botnen, Fyksesund og mot Folgefonna på andre siden av Hardangerfjorden. Om sommeren nås hytten fra Hamlagrø, Hodnaberg i Bergsdalen, og fra Botnen i Fyksesund på t-merkede stier.", 4, 913, "
Ubetjent hele året.

5 sengeplasser.
", "Vedovn"],
  [L.latLng(60.465862, 6.125688), "bergsdalen", "Breidablik", "img/breidablik.jpg", "Hytteanlegget Breidablik ligger ligger høyt over Fitjadalen i Kvam og er et av landets mest egenartede hytteanlegg med en fantastisk beliggenhet og et stort rundskue. Fra Vending kan man ta dagstur opp til hytten, mens fra Hamlagrøosen er det greit å ta med seg litt god mat og overnatte. Fra Breidablik kan man ta en kveldstur til Skrott. Uansett er dette stedet verdt en overnatting, og topptur til Skrott setter en ekstra spiss på turen.", 5, 1160, "
Ubetjent.

Stengt fra 19. sep til 1. juli.

10 sengeplasser.
", "Vedovn Hundebur"],
  [L.latLng(60.090833, 6.343056), "folgefonna", "Breidablikk", "img/breidablik.jpg", "beskrivelse", 6, 1322, "Ubetjent hele året. 4 sengeplasser. "],
  [L.latLng(60.076667, 6.347778), "folgefonna", "Fonnabu", "img/breidablik.jpg", "beskrivelse", 7, 1450, "Selvbetjent hele året, 27 sengeplasser"]
];

//struktur på startsteder: startSteder[startsted[kartfunksjon(koordinater), beskrivelse, ID]]
//hver hytte har ett designert startsted dersom man kun har valgt 1 hytte i hyttevelgeren
var startSteder = [
  [L.latLng(60.571399, 5.949952), "Lid i bergsdalen.", 0],
  [L.latLng(60.534741, 6.074092), "Parkeringsplass ved Bergsdalsvegen.", 1],
  [L.latLng(60.544582, 6.030346), "Småbrekkene i bergsdalen.", 2],
  [L.latLng(60.534741, 6.074092), "Fra hovedveien.", 3],
  [L.latLng(60.560545, 6.208481), "Hodnaberg i enden av hamlagrøvatnet.", 4],
  [L.latLng(60.534741, 6.074092), "Parkeringsplass ved Bergdalsvegen.", 5]
];
<commit_msg>endret rekkefølge på hytter<commit_after>//datastruktur på liste over hytter: hytteliste[hytte[karfunksjon(koordinater), område, hyttenavn, bildested, beskrivelse, ID, høydemeter, fasiliteter]].
var hytter = [
  [L.latLng(60.535, 5.924722), "bergsdalen", "Høgabu", "img/hogabu.jpg", "Høgabu er en av turlagets eldste hytter. Den ligger fint til i siden over Tjøranadalsvatnet, med flott utsyn mot Trollaskardet mot sørøst og Bukkafjellet bak. Kneisende opp ved hytten, ligger Bergsbukken. I Tjørnadalsvatnet kan man få fisk av varierende størrelse, iblant kan den være av bra størrelse.", 0, 900, "
Selvbetjent hele året.

26 senger.
", 'Hundebur Vedovn'],
  [L.latLng(60.500833, 5.960278), "bergsdalen", "Gullhorgabu", "img/gullhorgabu.jpg", "Gullhorgabu ligger ca. 3 timers gange fra bilvei i Bergsdalen. Stien går på den kulturhistoriske vegen Militærvegen, som ble bygget som transportvei mellom Voss og Samnanger i slutten av 1920-årene, gjør i dag tjeneste som god tursti. Det er en fin tur både for store og små. Her kan man stedvis gå ved siden av hverandre, gjennom et variert, spennende og samtidig lettgått terreng.  Beregn lenger tid går du med barn.", 2, 840, "
Selvbetjent hele året.

34 sengeplasser.
", "Vedovn Hundebur"],
  [L.latLng(60.473889, 6.075278), "bergsdalen", "Vending", "img/vending.jpg", "Rundt Vending er det et ypperlig turterreng både sommer og vinter. Fuglafjell. Geitafjell og Skrott er flotte dagsturmål fra hytten. Ta med fiskestang, og går du med barn, ligg over en dag og kos dere i området. En av de fineste turene man kan gå i dette området, er fra Hamlagrø, overnatting på Vending og derfra til Botnen.", 3, 820, "
Selvbetjent hele året.

30 sengeplasser.
", "Vedovn Hundebur"],
  [L.latLng(60.465862, 6.125688), "bergsdalen", "Breidablik", "img/breidablik.jpg", "Hytteanlegget Breidablik ligger ligger høyt over Fitjadalen i Kvam og er et av landets mest egenartede hytteanlegg med en fantastisk beliggenhet og et stort rundskue. Fra Vending kan man ta dagstur opp til hytten, mens fra Hamlagrøosen er det greit å ta med seg litt god mat og overnatte. Fra Breidablik kan man ta en kveldstur til Skrott. Uansett er dette stedet verdt en overnatting, og topptur til Skrott setter en ekstra spiss på turen.", 5, 1160, "
Ubetjent.

Stengt fra 19. sep til 1. juli.

10 sengeplasser.
", "Vedovn Hundebur"],
  [L.latLng(60.514602, 6.201278), "bergsdalen", "Kiellandbu", "img/kiellandbu.jpg", "Kiellandbu ligger flott til på Løkjedalsnuten med imponerende utsikt mot Botnen, Fyksesund og mot Folgefonna på andre siden av Hardangerfjorden. Om sommeren nås hytten fra Hamlagrø, Hodnaberg i Bergsdalen, og fra Botnen i Fyksesund på t-merkede stier.", 4, 913, "
Ubetjent hele året.

5 sengeplasser.
", "Vedovn"],
  [L.latLng(60.536944, 6.079167), "bergsdalen", "Alexander Grieghytten", "img/alexgrieg.jpg", "Området rundt Alexander Grieghytten har en rekke turmuligheter. Hamlagrøhornet og Krampane nord for hytten, og Kjerringafjellet og Storliknausen sør for hytten, er alle flotte toppturmål. Førstnevnte er mye benyttet som utflukt for de de yngste turgåerne (barneskolealder). Vinterstid er det lett og fint skiterreng fra Hamlagrøosen og sørover mot Fugladalen.", 1, 600, "
Ubetjent hele året.

Må låne nøkkel.

34 sengeplasser.
", 'Hundebur Vedovn Dusj'],
  [L.latLng(60.090833, 6.343056), "folgefonna", "Breidablikk", "img/breidablik.jpg", "beskrivelse", 6, 1322, "Ubetjent hele året. 4 sengeplasser. "],
  [L.latLng(60.076667, 6.347778), "folgefonna", "Fonnabu", "img/breidablik.jpg", "beskrivelse", 7, 1450, "Selvbetjent hele året, 27 sengeplasser"]
];

//struktur på startsteder: startSteder[startsted[kartfunksjon(koordinater), beskrivelse, ID]]
//hver hytte har ett designert startsted dersom man kun har valgt 1 hytte i hyttevelgeren
var startSteder = [
  [L.latLng(60.571399, 5.949952), "Lid i bergsdalen.", 0],
  [L.latLng(60.534741, 6.074092), "Parkeringsplass ved Bergsdalsvegen.", 1],
  [L.latLng(60.544582, 6.030346), "Småbrekkene i bergsdalen.", 2],
  [L.latLng(60.534741, 6.074092), "Fra hovedveien.", 3],
  [L.latLng(60.560545, 6.208481), "Hodnaberg i enden av hamlagrøvatnet.", 4],
  [L.latLng(60.534741, 6.074092), "Parkeringsplass ved Bergdalsvegen.", 5]
];
<|endoftext|>


<commit_before>// 'use strict';

var schedule = require('node-schedule');
// var c = require('./channels.js');
// var d = require('./daily.js');
// var date = require('./date.js');
// var slack = require('./slack.js');
var glob = require('glob');
var path = require('path');

module.exports = scheduler;

function scheduler() {

  var events = [];

  glob(path.resolve('./schedules/**/*.js'), function(err, files) {
    files.forEach(function (file) {
      console.log(file);
      events = events.concat(require(file));
    });
  });

  console.log(events);

  events = events.concat(require('../schedules/_juniors_daily.js'));
  // events = events.concat(require('../schedules/_seniors_daily.js'));
  // events = events.concat(require('../schedules/_students_daily.js'));
  //
  // events = events.concat(require('../schedules/W1D4.js');
  // events = events.concat(require('../schedules/W7D4.js');

  events.forEach(function (event) {
    schedule.scheduleJob(event.when, function () {
      if(Array.isArray(event.who)) {
        event.who.forEach(function (channel) {
          channel.send(event.message);
        });
      } else {
        event.who.send(event.message);
      }
    }.bind(this));
  });
}
<commit_msg>debugging<commit_after>// 'use strict';

var schedule = require('node-schedule');
// var c = require('./channels.js');
// var d = require('./daily.js');
// var date = require('./date.js');
// var slack = require('./slack.js');
var glob = require('glob');
var path = require('path');

module.exports = scheduler;

function scheduler() {

  var events = [];

  glob(path.resolve('./schedules/**/*.js'), function(err, files) {
    files.forEach(function (file) {
      console.log(file);
      events = events.concat(require(file));
    });

    events.forEach(function (event) {
      schedule.scheduleJob(event.when, function () {
        if(Array.isArray(event.who)) {
          event.who.forEach(function (channel) {
            channel.send(event.message);
          });
        } else {
          event.who.send(event.message);
        }
      }.bind(this));
    });
    console.log(events);
  });

  // events = events.concat(require('../schedules/_juniors_daily.js'));
  // events = events.concat(require('../schedules/_seniors_daily.js'));
  // events = events.concat(require('../schedules/_students_daily.js'));
  //
  // events = events.concat(require('../schedules/W1D4.js');
  // events = events.concat(require('../schedules/W7D4.js');

}
<|endoftext|>


<commit_before>$(function() {
    // Default drilldowns and cuts
    var drilldowns = ["department", "unit", "fund"];
    var cuts = {"time.year": "2013|time.year:2014"};

    // Get url parameters (this could be easily parsed, but we use purl)
    // Purl is available here: https://github.com/allmarkedup/jQuery-URL-Parser
    var parameters = $.url().param();

    // Start collecting breadcrumbs. We begin with Departments (base url)
    var path = $.url().attr('path');
    var crumbs = [{path:path, title:'Departments'}];

    // While the first drilldown is in the url parameters
    // we move it to the cuts instead
    while(drilldowns[0] in parameters) {
	var drill = drilldowns.shift();
	cuts[drill] = parameters[drill];
	// Add crumb to our crumbs.
	// The path is computed from the preceeding crumb but we add
	// the new url parameter for this particular crumb
	crumbs.push({path:[crumbs[crumbs.length-1].path,
			   (crumbs.length > 1) ? '&' : '?',
			   drill, '=', parameters[drill]].join(''),
		     title:parameters[drill]
		    });
    }

    // Create the links for our crumbs
    var breadcrumbs = [];
    for (var idx in crumbs) {
	breadcrumbs.push(''+
			 crumbs[idx].title+'');
    }
    // Add our breadcrumbs to the page (to an element with id #breadcrumbs
    $('#breadcrumbs').html(breadcrumbs.join(' > '));

    // Create the state from the (possibly modified) drilldowns and cuts
    var state = {
	"drilldowns": drilldowns,
	"cuts": cuts
    };

    var context = {
	dataset: "mayor_s_proposed_policy_budget_fy2013-15",
	siteUrl: "http://openspending.org",
        drilldown: function(node) { // Gets called on node click
	    // If the node has children we can drill more
	    if (node.data.node.children.length) {
		// We create a new location by adding a url parameter
		// The we have to check if we need to add ? or &
		// (it depends on if there are any url parameters present).
		// The url parameter is of the form dimension=name
		var new_location = [window.location.href,
				    window.location.search ? '&' : '?',
				    drilldowns[0], '=', node.name];
		// Go to the new location
		window.location.href = new_location.join('');
	    }
	    // If the node doesn't have children we notify the user
	    else
	    {
		// This can be made more beautiful
		alert("Sorry, we can't dig deeper");
	    }
	}};
    // Create the Treemap
    window.wdg_widget = new OpenSpending.Treemap($('#treewidget13-15'), context, state);
});<commit_msg>fix bad character code<commit_after>$(function() {
    // Default drilldowns and cuts
    var drilldowns = ["department", "unit", "fund"];
    var cuts = {"time.year": "2013|time.year:2014"};

    // Get url parameters (this could be easily parsed, but we use purl)
    // Purl is available here: https://github.com/allmarkedup/jQuery-URL-Parser
    var parameters = $.url().param();

    // Start collecting breadcrumbs. We begin with Departments (base url)
    var path = $.url().attr('path');
    var crumbs = [{path:path, title:'Departments'}];

    // While the first drilldown is in the url parameters
    // we move it to the cuts instead
    while(drilldowns[0] in parameters) {
	var drill = drilldowns.shift();
	cuts[drill] = parameters[drill];
	// Add crumb to our crumbs.
	// The path is computed from the preceeding crumb but we add
	// the new url parameter for this particular crumb
	crumbs.push({path:[crumbs[crumbs.length-1].path,
			   (crumbs.length > 1) ? '&' : '?',
			   drill, '=', parameters[drill]].join(''),
		     title:parameters[drill]
		    });
    }

    // Create the links for our crumbs
    var breadcrumbs = [];
    for (var idx in crumbs) {
	breadcrumbs.push(''+
			 crumbs[idx].title+'');
    }
    // Add our breadcrumbs to the page (to an element with id #breadcrumbs
    $('#breadcrumbs').html(breadcrumbs.join(' > '));

    // Create the state from the (possibly modified) drilldowns and cuts
    var state = {
	"drilldowns": drilldowns,
	"cuts": cuts
    };

    var context = {
	dataset: "mayor_s_proposed_policy_budget_fy2013-15",
	siteUrl: "http://openspending.org",
        drilldown: function(node) { // Gets called on node click
	    // If the node has children we can drill more
	    if (node.data.node.children.length) {
		// We create a new location by adding a url parameter
		// The we have to check if we need to add ? or &
		// (it depends on if there are any url parameters present).
		// The url parameter is of the form dimension=name
		var new_location = [window.location.href,
				    window.location.search ? '&' : '?',
				    drilldowns[0], '=',
				    encodeURIComponent(node.name)];
		// Go to the new location
		window.location.href = new_location.join('');
	    }
	    // If the node doesn't have children we notify the user
	    else
	    {
		// This can be made more beautiful
		alert("Sorry, we can't dig deeper");
	    }
	}};
    // Create the Treemap
    window.wdg_widget = new OpenSpending.Treemap($('#treewidget13-15'), context, state);
});<|endoftext|>


<commit_before>var Utils          = require("./utils")
  , DAOFactory   = require("./dao-factory")
  , DataTypes      = require('./data-types')
  , DAOFactoryManager   = require("./dao-factory-manager")
  , Migrator       = require("./migrator")
  , QueryInterface = require("./query-interface")

if(parseFloat(process.version.replace('v', '')) < 0.6) {
  console.log("DEPRECATION WARNING: Support for Node.JS < v0.6 will be canceled in the next minor release.")
}

module.exports = (function() {
  var Sequelize = function(database, username, password, options) {
    this.options = Utils._.extend({
      dialect: 'mysql',
      host: 'localhost',
      port: 3306,
      protocol: 'tcp',
      define: {},
      query: {},
      sync: {},
      logging: console.log,
      omitNull: false
    }, options || {})

    if(this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    this.config = {
      database: database,
      username: username,
      password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
      host    : this.options.host,
      port    : this.options.port,
      pool    : this.options.pool,
      protocol: this.options.protocol
    }

    var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")

    this.daoFactoryManager = new DAOFactoryManager(this)
    this.connectorManager  = new ConnectorManager(this, this.config)

    this.importCache = {}
  }

  Sequelize.Utils = Utils

  for (var dataType in DataTypes) {
    Sequelize[dataType] = DataTypes[dataType]
  }

  Sequelize.prototype.getQueryInterface = function() {
    this.queryInterface = this.queryInterface || new QueryInterface(this)
    return this.queryInterface
  }

  Sequelize.prototype.getMigrator = function(options, force) {
    if(force) {
      this.migrator = new Migrator(this, options)
    } else {
      this.migrator = this.migrator || new Migrator(this, options)
    }

    return this.migrator
  }

  Sequelize.prototype.define = function(daoName, attributes, options) {
    options = options || {}
    if(this.options.define) {
      options = Sequelize.Utils.merge(options, this.options.define)
    }
    options.omitNull = this.options.omitNull

    var factory = new DAOFactory(daoName, attributes, options)
    this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
    return factory
  }

  Sequelize.prototype.isDefined = function(daoName) {
    var daos = this.daoFactoryManager.daos
    return (daos.filter(function(dao) { return dao.name === daoName }).length !== 0)
  }

  Sequelize.prototype.import = function(path) {
    if (!this.importCache[path]) {
      var defineCall = require(path)
      this.importCache[path] = defineCall(this, DataTypes)
    }

    return this.importCache[path]
  }

  Sequelize.prototype.migrate = function(options) {
    this.getMigrator().migrate(options)
  }

  Sequelize.prototype.query = function(sql, callee, options) {
    options = Utils._.extend(Utils._.clone(this.options.query), options || {})
    options = Utils._.extend(options, {
      logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log
    })

    return this.connectorManager.query(sql, callee, options)
  }

  Sequelize.prototype.sync = function(options) {
    options = options || {}

    if(this.options.sync) {
      options = Sequelize.Utils.merge(options, this.options.sync)
    }

    var chainer = new Utils.QueryChainer()

    this.daoFactoryManager.daos.forEach(function(dao) {
      chainer.add(dao.sync(options))
    })

    return chainer.run()
  }

  Sequelize.prototype.drop = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer

      self.daoFactoryManager.daos.forEach(function(dao) { chainer.add(dao.drop()) })

      chainer
        .run()
        .success(function() { emitter.emit('success', null) })
        .error(function(err) { emitter.emit('error', err) })
    }).run()
  }

  return Sequelize
})()
<commit_msg>formatting + docs<commit_after>var Utils             = require("./utils")
  , DAOFactory        = require("./dao-factory")
  , DataTypes         = require('./data-types')
  , DAOFactoryManager = require("./dao-factory-manager")
  , Migrator          = require("./migrator")
  , QueryInterface    = require("./query-interface")

if(parseFloat(process.version.replace('v', '')) < 0.6) {
  console.log("DEPRECATION WARNING: Support for Node.JS < v0.6 will be canceled in the next minor release.")
}

module.exports = (function() {
  /**
    Main constructor of the project.

    Params:

      - `database`
      - `username`
      - `password`, optional, default: null
      - `options`, optional, default: {}

    Examples:

        mymodule.write('foo')
        mymodule.write('foo', { stream: process.stderr })

  */
  var Sequelize = function(database, username, password, options) {
    this.options = Utils._.extend({
      dialect: 'mysql',
      host: 'localhost',
      port: 3306,
      protocol: 'tcp',
      define: {},
      query: {},
      sync: {},
      logging: console.log,
      omitNull: false
    }, options || {})

    if(this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    this.config = {
      database: database,
      username: username,
      password: (( (["", null, false].indexOf(password) > -1) || (typeof password == 'undefined')) ? null : password),
      host    : this.options.host,
      port    : this.options.port,
      pool    : this.options.pool,
      protocol: this.options.protocol
    }

    var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")

    this.daoFactoryManager = new DAOFactoryManager(this)
    this.connectorManager  = new ConnectorManager(this, this.config)

    this.importCache = {}
  }

  /**
    Reference to Utils
  */
  Sequelize.Utils = Utils

  for (var dataType in DataTypes) {
    Sequelize[dataType] = DataTypes[dataType]
  }

  Sequelize.prototype.getQueryInterface = function() {
    this.queryInterface = this.queryInterface || new QueryInterface(this)
    return this.queryInterface
  }

  Sequelize.prototype.getMigrator = function(options, force) {
    if(force) {
      this.migrator = new Migrator(this, options)
    } else {
      this.migrator = this.migrator || new Migrator(this, options)
    }

    return this.migrator
  }

  Sequelize.prototype.define = function(daoName, attributes, options) {
    options = options || {}
    if(this.options.define) {
      options = Sequelize.Utils.merge(options, this.options.define)
    }
    options.omitNull = this.options.omitNull

    var factory = new DAOFactory(daoName, attributes, options)
    this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
    return factory
  }

  Sequelize.prototype.isDefined = function(daoName) {
    var daos = this.daoFactoryManager.daos
    return (daos.filter(function(dao) { return dao.name === daoName }).length !== 0)
  }

  Sequelize.prototype.import = function(path) {
    if (!this.importCache[path]) {
      var defineCall = require(path)
      this.importCache[path] = defineCall(this, DataTypes)
    }

    return this.importCache[path]
  }

  Sequelize.prototype.migrate = function(options) {
    this.getMigrator().migrate(options)
  }

  Sequelize.prototype.query = function(sql, callee, options) {
    options = Utils._.extend(Utils._.clone(this.options.query), options || {})
    options = Utils._.extend(options, {
      logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log
    })

    return this.connectorManager.query(sql, callee, options)
  }

  Sequelize.prototype.sync = function(options) {
    options = options || {}

    if(this.options.sync) {
      options = Sequelize.Utils.merge(options, this.options.sync)
    }

    var chainer = new Utils.QueryChainer()

    this.daoFactoryManager.daos.forEach(function(dao) {
      chainer.add(dao.sync(options))
    })

    return chainer.run()
  }

  Sequelize.prototype.drop = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer

      self.daoFactoryManager.daos.forEach(function(dao) { chainer.add(dao.drop()) })

      chainer
        .run()
        .success(function() { emitter.emit('success', null) })
        .error(function(err) { emitter.emit('error', err) })
    }).run()
  }

  return Sequelize
})()
<|endoftext|>


<commit_before>$(function() {
    // Default drilldowns and cuts and year
	var defaultYear = 2013;
    var drilldowns = ["fund", "department", "unit"];

	//Default year cuts
    var cuts = {"time.year": "2013"};
	//Default header text
	var headerText = "Mayor's Proposed Spending"

    // Get url parameters (this could be easily parsed, but we use purl)
    // Purl is available here: https://github.com/allmarkedup/jQuery-URL-Parser
    var parameters = $.url().param();

	//check for 'reference_years' in URL parameters
	//argument must be formatted with four digit years, eg. 2012
	//separate multiple years with a '+' symbol, but you need only supply one
	//the cuts used by default would correspond to: reference_years=2013+2014

	if(parameters['reference_years'])
	{
		//initialize the string to be formatted, and separate the years from the argument

		var cutString = "";
		headerText = "";
		var years = parameters['reference_years'].split(" ");
		var i = 0;

		//loop through the list of years

		$.each(years, function(index, value){
			//the first argument has nothing preceding it
			if(i!=0)
			{
				//after the first, prepend the new cut and the '|' to perform and addition
				cutString += "|time.year:";
				headerText += "-";
			}
			//add the year
			cutString += value;
			headerText += value;
			i++;
		});
		//apply the formatted cuts string
		cuts = {"time.year": cutString};
		headerText += " Mayor's Proposed Spending";
	}

	$('#year-header').html(headerText);
    // Start collecting breadcrumbs. We begin with Funds (base url)
    var path = $.url().attr('path');
    var crumbs = [{path:path, title:'Funds'}];

    // While the first drilldown is in the url parameters
    // we move it to the cuts instead
    while(drilldowns[0] in parameters) {
	var drill = drilldowns.shift();
	cuts[drill] = parameters[drill];
	// Add crumb to our crumbs.
	// The path is computed from the preceeding crumb but we add
	// the new url parameter for this particular crumb
	crumbs.push({path:[crumbs[crumbs.length-1].path,
			   (crumbs.length > 1) ? '&' : '?',
			   drill, '=', parameters[drill]].join(''),
		     title:parameters[drill]
		    });
    }

    // Create the links for our crumbs
    var breadcrumbs = [];
    for (var idx in crumbs) {
	breadcrumbs.push(''+
			 crumbs[idx].title+'');
    }
    // Add our breadcrumbs to the page (to an element with id #breadcrumbs
    $('#breadcrumbs').html(breadcrumbs.join(' > '));

    // Create the state from the (possibly modified) drilldowns and cuts
    var state = {
	"drilldowns": drilldowns,
	"cuts": cuts
    };

    var context = {
	dataset: "mayor_s_proposed_policy_budget_fy2013-15",
	siteUrl: "http://openspending.org",
        drilldown: function(node) { // Gets called on node click
	    // If the node has children we can drill more
	    if (node.data.node.children.length) {
		// We create a new location by adding a url parameter
		// Then we have to check if we need to add ? or &
		// (it depends on if there are any url parameters present).
		// The url parameter is of the form dimension=name
		var new_location = [window.location.href,
				    window.location.search ? '&' : '?',
				    drilldowns[0], '=',
				    encodeURIComponent(node.name)];
		// Go to the new location
		window.location.href = new_location.join('');
	    }
	    // If the node doesn't have children we notify the user
	    else
	    {
		// This can be made more beautiful
		alert("That's as low as we go.");
	    }
	}};
    // Create the Treemap
    // HACK: Override $jit.Trans.Expo.easeOut transition when it's loaded to eliminate animation
    window.wdg_widget = new OpenSpending.Treemap($('#treewidget13-15'), context, state);
});<commit_msg>change dash to ampersand<commit_after>$(function() {
    // Default drilldowns and cuts and year
	var defaultYear = 2013;
    var drilldowns = ["fund", "department", "unit"];

	//Default year cuts
    var cuts = {"time.year": "2013"};
	//Default header text
	var headerText = "Mayor's Proposed Spending"

    // Get url parameters (this could be easily parsed, but we use purl)
    // Purl is available here: https://github.com/allmarkedup/jQuery-URL-Parser
    var parameters = $.url().param();

	//check for 'reference_years' in URL parameters
	//argument must be formatted with four digit years, eg. 2012
	//separate multiple years with a '+' symbol, but you need only supply one
	//the cuts used by default would correspond to: reference_years=2013+2014

	if(parameters['reference_years'])
	{
		//initialize the string to be formatted, and separate the years from the argument

		var cutString = "";
		headerText = "";
		var years = parameters['reference_years'].split(" ");
		var i = 0;

		//loop through the list of years

		$.each(years, function(index, value){
			//the first argument has nothing preceding it
			if(i!=0)
			{
				//after the first, prepend the new cut and the '|' to perform and addition
				cutString += "|time.year:";
				headerText += "&";
			}
			//add the year
			cutString += value;
			headerText += value;
			i++;
		});
		//apply the formatted cuts string
		cuts = {"time.year": cutString};
		headerText += " Mayor's Proposed Spending";
	}

	$('#year-header').html(headerText);
    // Start collecting breadcrumbs. We begin with Funds (base url)
    var path = $.url().attr('path');
    var crumbs = [{path:path, title:'Funds'}];

    // While the first drilldown is in the url parameters
    // we move it to the cuts instead
    while(drilldowns[0] in parameters) {
	var drill = drilldowns.shift();
	cuts[drill] = parameters[drill];
	// Add crumb to our crumbs.
	// The path is computed from the preceeding crumb but we add
	// the new url parameter for this particular crumb
	crumbs.push({path:[crumbs[crumbs.length-1].path,
			   (crumbs.length > 1) ? '&' : '?',
			   drill, '=', parameters[drill]].join(''),
		     title:parameters[drill]
		    });
    }

    // Create the links for our crumbs
    var breadcrumbs = [];
    for (var idx in crumbs) {
	breadcrumbs.push(''+
			 crumbs[idx].title+'');
    }
    // Add our breadcrumbs to the page (to an element with id #breadcrumbs
    $('#breadcrumbs').html(breadcrumbs.join(' > '));

    // Create the state from the (possibly modified) drilldowns and cuts
    var state = {
	"drilldowns": drilldowns,
	"cuts": cuts
    };

    var context = {
	dataset: "mayor_s_proposed_policy_budget_fy2013-15",
	siteUrl: "http://openspending.org",
        drilldown: function(node) { // Gets called on node click
	    // If the node has children we can drill more
	    if (node.data.node.children.length) {
		// We create a new location by adding a url parameter
		// Then we have to check if we need to add ? or &
		// (it depends on if there are any url parameters present).
		// The url parameter is of the form dimension=name
		var new_location = [window.location.href,
				    window.location.search ? '&' : '?',
				    drilldowns[0], '=',
				    encodeURIComponent(node.name)];
		// Go to the new location
		window.location.href = new_location.join('');
	    }
	    // If the node doesn't have children we notify the user
	    else
	    {
		// This can be made more beautiful
		alert("That's as low as we go.");
	    }
	}};
    // Create the Treemap
    // HACK: Override $jit.Trans.Expo.easeOut transition when it's loaded to eliminate animation
    window.wdg_widget = new OpenSpending.Treemap($('#treewidget13-15'), context, state);
});<|endoftext|>


<commit_before>/* global Log, Config */
'use strict';

const EventEmitter = require('events').EventEmitter;
const Aws = require('aws-sdk');

const DEFAULT_INTERVAL = 60000;

class S3Parser {
  constructor() {
    this.properties = {};
  }

  update(data) {
    let properties = {};

    try {
      properties = JSON.parse(data.toString());
    } catch (e) {
      // We should have an error condition but tbd
    }
    this.properties = properties;
  }
}

class S3 extends EventEmitter {
  constructor(opts) {
    super();
    const options = opts || {};

    if (!options.hasOwnProperty('bucket') || !options.hasOwnProperty('path')) {
      throw new Error('Bucket or path not supplied');
    }

    if (!options.hasOwnProperty('name')) {
      this.name = `s3-${options.bucket}-${options.path}`;
    }

    this.interval = DEFAULT_INTERVAL;
    this._parser = options.parser || new S3Parser(options);
    this._ok = false;
    this._updated = null;

    /**
     * Initialize the s3 client
     */
    this.service = new Aws.S3({
      region: Config.get('index:region')
    });

    this.configure(options);
    this.etag = null;
    this.properties = {};
  }

  /**
   * Check the status of the polling interval loop
   *
   * @return {Boolean} True if the pooling loop is running
   */
  get _running() {
    return !!this._timer;
  }

  /**
   * The source type
   *
   * @returns {string}
   * @private
   */
  get _type() {
    return this.constructor.type;
  }

  /**
   * @param {Object} params
   * @returns {Boolean}
   */
  configure(params) {
    let changed = false;

    changed = S3.setIfChanged(this, 'bucket', params.bucket || Config.get('index:bucket')) || changed;
    changed = S3.setIfChanged(this, 'interval', params.interval || DEFAULT_INTERVAL) || false;
    changed = S3.setIfChanged(this, 'path', params.path) || changed;

    return changed;
  }

  initialize() {
    if (this._timer) {
      return this;
    }

    Log.info(`Initializing ${this._type} source ${this.name}`, {
      source: this.name,
      type: this._type
    });

    // Initialize state to 'RUNNING'
    this._timer = true;
    const _this = this;

    this.emit('startup');

    (function poll() {
      const timer = Date.now();

      Log.debug(`Polling source ${_this.name} for updates`, {
        source: _this.name,
        type: _this._type
      });

      _this._fetch((err, data) => {
        // If `shutdown()` was called _during_ a fetch operation, `clearTimeout()`
        // will have no effect. Instead, we overload `_interval` as a state to detect
        // if a shutdown has been initiated. False-y -> don't set another timeout.
        if (_this._running) {
          _this._timer = setTimeout(poll, _this.interval);
        }
        if (err) {
          return _this._error(err);
        }

        Log.debug(`Polled source ${_this.name} in ${(Date.now() - timer)}ms`, {
          source: _this.name,
          type: _this._type
        });

        if (data) {
          return _this._update(data);
        }
        _this.emit('no-update');

        Log.debug(`Source ${_this.name} is up to date`, {
          source: _this.name,
          type: _this._type
        });
      });

      // Start the polling loop
    }());

    return this;
  }

  /**
   * Stop the polling interval loop
   *
   * @return {S3} Receiver
   */
  shutdown() {
    if (!this._running) {
      return this;
    }

    this._ok = false;

    Log.info(`Shutting down ${this._type} source ${this.name}`, {
      source: this.name,
      type: this._type
    });

    clearTimeout(this._timer);
    delete this._timer;

    this.emit('shutdown');
    return this;
  }

  /**
   * Return an object describing the source-instance's current status
   *
   * @return {Object}
   */
  status() {
    return {
      ok: this._ok,
      updated: this._updated,
      interval: this._timer,
      running: this._running
    };
  }

  /**
   * Handle errors from underlying source facilities
   *
   * @emits {Error} error If any listeners have been registered
   *
   * @param  {Error} err An instance of Error
   * @return {S3} Reference to instance
   * @private
   */
  _error(err) {
    this._ok = false;
    Log.error(err, {
      source: this.name,
      type: this._type
    });

    // Only emit an error event if there are listeners.
    if (this.listeners('error').length > 0) {
      this.emit('error', err);
    }

    return this;
  }

  /**
   *
   * @param {Function} callback
   * @private
   */
  _fetch(callback) {
    const _this = this;

    this.service.getObject({
      Bucket: this.bucket,
      Key: this.path,
      IfNoneMatch: this.etag
    }, (err, data) => {
      if (err) {
        if (err.code === 'NotModified') {
          return callback(null, false);
        }
        if (err.code === 'NoSuchKey') {
          return callback(null, false);
        }

        Log.error(err, {
          source: _this.name,
          type: _this.type
        });
        return callback(err);
      }

      _this.etag = data.ETag;
      callback(null, data.Body);
    });
  }

  /**
   * Called by implementations to update source data
   *
   * @param {Buffer}  data  Updated data from an implementation-specific source
   * @return {S3} Reference to instance
   * @private
   */
  _update(data) {
    this._parser.update(data);
    this.properties = this._parser.properties;
    this._updated = new Date();
    this._ok = true;

    Log.info('Updated source ' + this.name, {
      source: this.name,
      type: this._type
    });
    this.emit('update', this);

    return this;
  }

  /**
   * Helper method to detect parameter changes
   *
   * @param {Object}  scope The target object on which to set a parameter
   * @param {String}  key   The property of `scope` to set
   * @param {*}   value The value to compare and set
   * @return {Boolean} True if the parameter has been updated
   * @static
   */
  static setIfChanged(scope, key, value) {
    if (scope[key] === value) {
      return false;
    }

    scope[key] = value; // eslint-disable-line no-param-reassign
    return true;
  }
}

S3.type = 's3';

module.exports = S3;
<commit_msg>Default value should be the value of changed rather than explicitly false.<commit_after>/* global Log, Config */
'use strict';

const EventEmitter = require('events').EventEmitter;
const Aws = require('aws-sdk');

const DEFAULT_INTERVAL = 60000;

class S3Parser {
  constructor() {
    this.properties = {};
  }

  update(data) {
    let properties = {};

    try {
      properties = JSON.parse(data.toString());
    } catch (e) {
      // We should have an error condition but tbd
    }
    this.properties = properties;
  }
}

class S3 extends EventEmitter {
  constructor(opts) {
    super();
    const options = opts || {};

    if (!options.hasOwnProperty('bucket') || !options.hasOwnProperty('path')) {
      throw new Error('Bucket or path not supplied');
    }

    if (!options.hasOwnProperty('name')) {
      this.name = `s3-${options.bucket}-${options.path}`;
    }

    this.interval = DEFAULT_INTERVAL;
    this._parser = options.parser || new S3Parser(options);
    this._ok = false;
    this._updated = null;

    /**
     * Initialize the s3 client
     */
    this.service = new Aws.S3({
      region: Config.get('index:region')
    });

    this.configure(options);
    this.etag = null;
    this.properties = {};
  }

  /**
   * Check the status of the polling interval loop
   *
   * @return {Boolean} True if the pooling loop is running
   */
  get _running() {
    return !!this._timer;
  }

  /**
   * The source type
   *
   * @returns {string}
   * @private
   */
  get _type() {
    return this.constructor.type;
  }

  /**
   * @param {Object} params
   * @returns {Boolean}
   */
  configure(params) {
    let changed = false;

    changed = S3.setIfChanged(this, 'bucket', params.bucket || Config.get('index:bucket')) || changed;
    changed = S3.setIfChanged(this, 'interval', params.interval || DEFAULT_INTERVAL) || changed;
    changed = S3.setIfChanged(this, 'path', params.path) || changed;

    return changed;
  }

  initialize() {
    if (this._timer) {
      return this;
    }

    Log.info(`Initializing ${this._type} source ${this.name}`, {
      source: this.name,
      type: this._type
    });

    // Initialize state to 'RUNNING'
    this._timer = true;
    const _this = this;

    this.emit('startup');

    (function poll() {
      const timer = Date.now();

      Log.debug(`Polling source ${_this.name} for updates`, {
        source: _this.name,
        type: _this._type
      });

      _this._fetch((err, data) => {
        // If `shutdown()` was called _during_ a fetch operation, `clearTimeout()`
        // will have no effect. Instead, we overload `_interval` as a state to detect
        // if a shutdown has been initiated. False-y -> don't set another timeout.
        if (_this._running) {
          _this._timer = setTimeout(poll, _this.interval);
        }
        if (err) {
          return _this._error(err);
        }

        Log.debug(`Polled source ${_this.name} in ${(Date.now() - timer)}ms`, {
          source: _this.name,
          type: _this._type
        });

        if (data) {
          return _this._update(data);
        }
        _this.emit('no-update');

        Log.debug(`Source ${_this.name} is up to date`, {
          source: _this.name,
          type: _this._type
        });
      });

      // Start the polling loop
    }());

    return this;
  }

  /**
   * Stop the polling interval loop
   *
   * @return {S3} Receiver
   */
  shutdown() {
    if (!this._running) {
      return this;
    }

    this._ok = false;

    Log.info(`Shutting down ${this._type} source ${this.name}`, {
      source: this.name,
      type: this._type
    });

    clearTimeout(this._timer);
    delete this._timer;

    this.emit('shutdown');
    return this;
  }

  /**
   * Return an object describing the source-instance's current status
   *
   * @return {Object}
   */
  status() {
    return {
      ok: this._ok,
      updated: this._updated,
      interval: this._timer,
      running: this._running
    };
  }

  /**
   * Handle errors from underlying source facilities
   *
   * @emits {Error} error If any listeners have been registered
   *
   * @param  {Error} err An instance of Error
   * @return {S3} Reference to instance
   * @private
   */
  _error(err) {
    this._ok = false;
    Log.error(err, {
      source: this.name,
      type: this._type
    });

    // Only emit an error event if there are listeners.
    if (this.listeners('error').length > 0) {
      this.emit('error', err);
    }

    return this;
  }

  /**
   *
   * @param {Function} callback
   * @private
   */
  _fetch(callback) {
    const _this = this;

    this.service.getObject({
      Bucket: this.bucket,
      Key: this.path,
      IfNoneMatch: this.etag
    }, (err, data) => {
      if (err) {
        if (err.code === 'NotModified') {
          return callback(null, false);
        }
        if (err.code === 'NoSuchKey') {
          return callback(null, false);
        }

        Log.error(err, {
          source: _this.name,
          type: _this.type
        });
        return callback(err);
      }

      _this.etag = data.ETag;
      callback(null, data.Body);
    });
  }

  /**
   * Called by implementations to update source data
   *
   * @param {Buffer}  data  Updated data from an implementation-specific source
   * @return {S3} Reference to instance
   * @private
   */
  _update(data) {
    this._parser.update(data);
    this.properties = this._parser.properties;
    this._updated = new Date();
    this._ok = true;

    Log.info('Updated source ' + this.name, {
      source: this.name,
      type: this._type
    });
    this.emit('update', this);

    return this;
  }

  /**
   * Helper method to detect parameter changes
   *
   * @param {Object}  scope The target object on which to set a parameter
   * @param {String}  key   The property of `scope` to set
   * @param {*}   value The value to compare and set
   * @return {Boolean} True if the parameter has been updated
   * @static
   */
  static setIfChanged(scope, key, value) {
    if (scope[key] === value) {
      return false;
    }

    scope[key] = value; // eslint-disable-line no-param-reassign
    return true;
  }
}

S3.type = 's3';

module.exports = S3;
<|endoftext|>


<commit_before>var playElem = document.getElementById('play');
var pauseElem = document.getElementById('pause');
var nextElem = document.getElementById('next');
var prevElem = document.getElementById('prev');
playElem.onclick = function() {
    playMusic(document.querySelector('input[name="musicSelect"]:checked').value);
}
pauseElem.onclick = function() {
    pauseMusic();
}
nextElem.onclick = function() {
    nextMusic();
}
prevElem.onclick = function() {
    prevMusic();
}

var audioMusic = {};
var musicDefaultVolume = 1;
// Distinct sounds' volumes
var srcVolumes = {
    'http://sound.mp3': 0.2
};

function initMusic() {
    for (var trigger in musicUrls) {
        if (!musicUrls[trigger]) continue;
        var len = musicUrls[trigger].length;
        if (!len) continue;
        var currentAudio = audioMusic[trigger] = new Audio();
        var currentTrack = currentAudio.trackno = Math.floor(Math.random() * len);
        currentAudio.src = musicUrls[trigger][currentTrack];
        currentAudio.trigger = trigger;
        currentAudio.volume = srcVolumes[currentAudio.src] || musicDefaultVolume;
        currentAudio.len = len;
        currentAudio.onended = function() {
            this.trackno = this.trackno < len - 2 ? ++this.trackno : 0;
            this.src = musicUrls[this.trigger][this.trackno];
            audioMusic.currentMusic = this;
            this.play();
        }
    }
}

function playMusic(trigger) {
    if (!musicUrls[trigger]) {
        console.warn('There is no ' + trigger);
        return;
    }
    if (audioMusic.currentMusic) audioMusic.currentMusic.pause();
    audioMusic.currentMusic = audioMusic[trigger];
    audioMusic.currentMusic.play();
}

function pauseMusic() {
    if (!audioMusic.currentMusic) {
        return;
    }
    audioMusic.currentMusic.pause();
}

function nextMusic() {
    var cm = audioMusic.currentMusic;
    if (!cm) return;
    cm.trackno = cm.trackno < cm.len - 2 ? ++cm.trackno : 0;
    cm.src = musicUrls[cm.trigger][cm.trackno];
    cm.play();
}

function prevMusic() {
    var cm = audioMusic.currentMusic;
    if (!cm) return;
    cm.trackno = cm.trackno > 0 ? --cm.trackno : cm.len - 1;
    cm.src = musicUrls[cm.trigger][cm.trackno];
    cm.play();
}

var musicUrls = {
    aggressive: [
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Black%20Vortex.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/The%20Complex.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Go%20Cart.mp3'
    ],
    dark: [
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Reign%20Supreme.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Drums%20of%20the%20Deep.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/The%20Builder.mp3'
    ],
    intense: [
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Undaunted.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Unity.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Dark%20Fog.mp3'
    ]
};

// var music = {
//     faturenet: [
//     {
//         artist: 'Waverine',
//         title: 'Launch',
//         album: 'Vintage',
//         year: 2014,
//         genre: 'electro pop rock new age piano brass',
//         url: 'http://archive.org/download/FNet058/01.Launch.mp3'
//     },
//     {
//         artist: 'Incentive',
//         title: 'Because He Loves You',
//         album: 'Eschatology',
//         year: 2013,
//         genre: 'noise drone post-industrial idm technoid',
//         url: 'http://archive.org/download/FNet054/02.Incentive-Because_He_Loves_You.mp3'
//     },
//     ],
// };

initMusic();<commit_msg>Fix incs, decs<commit_after>var playElem = document.getElementById('play');
var pauseElem = document.getElementById('pause');
var nextElem = document.getElementById('next');
var prevElem = document.getElementById('prev');
playElem.onclick = function() {
    playMusic(document.querySelector('input[name="musicSelect"]:checked').value);
}
pauseElem.onclick = function() {
    pauseMusic();
}
nextElem.onclick = function() {
    nextMusic();
}
prevElem.onclick = function() {
    prevMusic();
}

var audioMusic = {};
var musicDefaultVolume = 1;
// Distinct sounds' volumes
var srcVolumes = {
    'http://sound.mp3': 0.2
};

function initMusic() {
    for (var trigger in musicUrls) {
        if (!musicUrls[trigger]) continue;
        var len = musicUrls[trigger].length;
        if (!len) continue;
        var currentAudio = audioMusic[trigger] = new Audio();
        var currentTrack = currentAudio.trackno = Math.floor(Math.random() * len);
        currentAudio.src = musicUrls[trigger][currentTrack];
        currentAudio.trigger = trigger;
        currentAudio.volume = srcVolumes[currentAudio.src] || musicDefaultVolume;
        currentAudio.len = len;
        currentAudio.onended = function() {
            this.trackno = this.trackno < len - 2 ? this.trackno++ : 0;
            this.src = musicUrls[this.trigger][this.trackno];
            audioMusic.currentMusic = this;
            this.play();
        }
    }
}

function playMusic(trigger) {
    if (!musicUrls[trigger]) {
        console.warn('There is no ' + trigger);
        return;
    }
    if (audioMusic.currentMusic) audioMusic.currentMusic.pause();
    audioMusic.currentMusic = audioMusic[trigger];
    audioMusic.currentMusic.play();
}

function pauseMusic() {
    if (!audioMusic.currentMusic) {
        return;
    }
    audioMusic.currentMusic.pause();
}

function nextMusic() {
    var cm = audioMusic.currentMusic;
    if (!cm) return;
    cm.trackno = cm.trackno < cm.len - 2 ? cm.trackno++ : 0;
    cm.src = musicUrls[cm.trigger][cm.trackno];
    cm.play();
}

function prevMusic() {
    var cm = audioMusic.currentMusic;
    if (!cm) return;
    cm.trackno = cm.trackno > 0 ? cm.trackno-- : cm.len - 1;
    cm.src = musicUrls[cm.trigger][cm.trackno];
    cm.play();
}

var musicUrls = {
    aggressive: [
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Black%20Vortex.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/The%20Complex.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Go%20Cart.mp3'
    ],
    dark: [
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Reign%20Supreme.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Drums%20of%20the%20Deep.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/The%20Builder.mp3'
    ],
    intense: [
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Undaunted.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Unity.mp3',
        'http://incompetech.com/music/royalty-free/mp3-royaltyfree/Dark%20Fog.mp3'
    ]
};

// var music = {
//     faturenet: [
//     {
//         artist: 'Waverine',
//         title: 'Launch',
//         album: 'Vintage',
//         year: 2014,
//         genre: 'electro pop rock new age piano brass',
//         url: 'http://archive.org/download/FNet058/01.Launch.mp3'
//     },
//     {
//         artist: 'Incentive',
//         title: 'Because He Loves You',
//         album: 'Eschatology',
//         year: 2013,
//         genre: 'noise drone post-industrial idm technoid',
//         url: 'http://archive.org/download/FNet054/02.Incentive-Because_He_Loves_You.mp3'
//     },
//     ],
// };

initMusic();<|endoftext|>


<commit_before>import * as baton from './baton'
import {url} from './url'
import Slack from 'node-slack'

export const api = new Slack(process.env.BATON_HOOK_URL)
export const username = 'baton'

export const msg = (req: Object, text: String, icon_emoji: String) => {
  return api.respond(req.body, () => { return {text, username, icon_emoji} })
}

export const err = (req: Object, text: String) => {
  return api.respond(req.body, () => Object.assign(req.body, {text, username, icon_emoji: 'boom'}))
}

// formats a collection of batons into "pretty" text for use in slack
export const items = (req: Object, batons: Array) => {
  return api.respond(req.body, () => {
    let text = ''

    batons.forEach(btn => {
      text += btn.label ?
               `* _${btn.label}_ <${btn.link}>` :
               `* <${btn.link}>`

      if (btn.tags) {
        text += ' | ' + btn.tags.map(t => ` ${t}`)
      }

      text += '\n'
    })

    return {text, icon_emoji: 'bookmark'}
  })
}

// formats baton into "pretty" text for use in slack
export const item = (req: Object, baton: Object) => items(req, [baton])

// parses a slack command into a slack-flavored instance of a baton object
export const cmd = (req: Object) => {
  const command = req.body.command || req.body.text

  if (!command) {
    return err(req, 'Missing required command or text field in slack request')
  }

  const matches = {
    link  : command.match(url),
    label : command.match(/(?:pass|drop|find|browse) (.*?)[\.!\?](?:\s|$)/), // use sentence following command (WIP)
    tags  : command.match(/(\[[^[\]]*\])/),
  }

  if (!matches.link) {
    return err(req, 'A valid URL is required!')
  }

  const label = null // TODO - matches.label[0]
  const link  = matches.link[0]
  const tags  = matches.tags ? JSON.parse(matches.tags[0]) : []

  return Object.assign({link, tags}, req.body)
}

// NOTE - use api.send(msg) to utilize incoming webhook
<commit_msg>:bug: debug<commit_after>import * as baton from './baton'
import {url} from './url'
import Slack from 'node-slack'

export const api = new Slack(process.env.BATON_HOOK_URL)
export const username = 'baton'

export const msg = (req: Object, text: String, icon_emoji: String) => {
  return api.respond(req.body, () => { return {text, username, icon_emoji} })
}

export const err = (req: Object, text: String) => {
  return api.respond(req.body, () => Object.assign(req.body, {text, username, icon_emoji: 'boom'}))
}

// formats a collection of batons into "pretty" text for use in slack
export const items = (req: Object, batons: Array) => {
  return api.respond(req.body, () => {
    let text = ''

    batons.forEach(btn => {
      text += btn.label ?
               `* _${btn.label}_ <${btn.link}>` :
               `* <${btn.link}>`

      if (btn.tags) {
        text += ' | ' + btn.tags.map(t => ` ${t}`)
      }

      text += '\n'
    })

    return {text, icon_emoji: 'bookmark'}
  })
}

// formats baton into "pretty" text for use in slack
export const item = (req: Object, baton: Object) => items(req, [baton])

// parses a slack command into a slack-flavored instance of a baton object
export const cmd = (req: Object) => {
  const command = req.body.command || req.body.text

  if (!command) {
    return err(req, 'Missing required command or text field in slack request')
  }

  const matches = {
    link  : command.match(url),
    label : command.match(/(?:pass|drop|find|browse) (.*?)[\.!\?](?:\s|$)/), // use sentence following command (WIP)
    tags  : command.match(/(\[[^[\]]*\])/),
  }

  if (!matches.link) {
    return err(req, 'A valid URL is required!')
  }

  const label = null // TODO - matches.label[0]
  const link  = matches.link[0]
  const tags  = matches.tags ? JSON.parse(matches.tags[0]) : []

  console.log('[baton:DEBUG] slack command, found link', link)

  return Object.assign({link, tags}, req.body)
}

// NOTE - use api.send(msg) to utilize incoming webhook
<|endoftext|>


<commit_before>var helpers = require('helpers');

/* Don't be fooled about max_usage. While we do set it initially, it's nothing
   but a flag - the real usage counter is in redis in counter:<aid> and the only
   way to read-access that is by calling Alias.prototype.use() (or set use to true
   when callind findById())

   use() in turn will use redis' DECR operation on the counter.

   This makes sure, considering that DECR is atomic, that even under high load
   no mail can slip in :-)
 */
var Alias = function(aid) {
  this.aid = aid || null,
  this.valid_from = null,
  this.valid_until = null,
  this.max_usage = null
}
helpers.extend(Alias.prototype, (function(){
  function dv(ad, m) {
    var d = null;
    if (ad.hasOwnProperty(m)) {
      d = new Date(Date.parse(ad[m]));
      if (!d) {
        throw new Error(m + ' provided but invalid');
      }
    }
    return d;
  }

  function validate(ad) {
    if (ad.days){
      var d = new Date();
      d.setDate(d.getDate() + parseInt(ad.days, 10));
      this.valid_until = d;
    }else{
      this.valid_from = dv(ad, 'valid-from');
      this.valid_until = dv(ad, 'valid-until');
    }
    this.target = null;
    this.max_usage = parseInt(ad['max-usage'], 10);
    if (isNaN(this.max_usage)) this.max_usage = null;

    if (ad.hasOwnProperty('target')) {
      // we require a "raw" email address, so no comments or anything
      // also, I'm not even trying to be clever here. If it looks like
      // not-at@notdot.notdot, it's probably a valid email address
      if (ad.target.match(/^[^@]+@[^.@]+\.[^@.][^@]+$/))
        this.target = ad.target
    }
    if (!this.target) {
      throw new Error("Target missing or invalid");
    }
  }
  return {
    setInfo: function(ad) {
      validate.apply(this, arguments);
    },
    use: function(callback){
      var pdate = null;

      // node currently can't parse the output of its JSON serialized dates :-)
      function fixJSONDate(s){
        return s.replace(/\.[0-9]+Z$/, ' UTC').replace(/(\d)T(\d)/, "$1 $2");
      }

      if (!this.aid){
        callback(true);
        return;
      }

      if (this.valid_from && Date.parse(fixJSONDate(this.valid_from)) > new Date()){
        callback(false);
        return; // no further checks
      }
      if (this.valid_until && Date.parse(fixJSONDate(this.valid_until)) < new Date()){
        callback(false);
        return; // no further checks
      }

      if (!this.max_usage){
        callback(true);
      }else{
        require('redis').client(function(client){
          client.decr('counter:' + this.aid, function(err, data){
            if (err){ callback(false); }
            var c = parseInt(data, 10);
            callback(!isNaN(c) && (c >= 0));
          });
        });
      }
    }
  };
}()));

(function() {
  var ids = 0;
  var UUID = require("uuid").UUID;

  var AliasProvider = (function(){
    function getNextId(callback) {
      var len = 3, c = "";
      require('redis').client(function(client){
        client.get('admin:keylength', function(err, data){
          len = data || len;
          var uuidg = function uuidg() {
            len++;
            var id = UUID.uuid(len);
            client.setnx('aliases:' + id, 'reserved', function(err, data) {
              if (err) throw new Error(err);
              if (parseInt(data, 10) == 0) {
                client.set('admin:keylength', len, function(err, data){
                  if (err) throw new Error(err);
                  uuidg();
                });
              }
              callback(id);
            });
          }();
        });
      });
    }
    return {
      findById: function(id, use, callback) {
        if (callback) {
          require('redis').client(function(client){
            client.get('aliases:' + id, function(err, data) {
              if (err || !data) {
                callback(null);
                return;
              }
              helpers.extend(ad = new Alias(), JSON.parse(""+data));

              if (use){
                ad.use(function(valid){
                  callback(valid ? ad : null);
                })
              }else{
                callback(ad);
              }
            });
          });
        }
      },
      getNew: function() {
        return new Alias();
      },
      save: function(alias, callback) {
        if (!alias.aid) {
          alias.updatekey = UUID.uuid();
          getNextId(function(id) {
            alias.aid = id;
            require('redis').client(function(client){
              client.set('aliases:' + id, JSON.stringify(alias), function(err, data) {
                if (err) throw new Error(err);
                if (alias.max_usage){
                  client.set('counter:' + id, alias.max_usage,  function(err, data){
                    if (err) throw new Error(err);
                    callback(alias.aid);
                  })
                }else{
                  callback(alias.aid);
                }
              });
            });
          });
        }
      }
    }
  }());

  exports.AliasProvider = AliasProvider;
})();
<commit_msg>fixed alias use() for new redis connection method<commit_after>var helpers = require('helpers');

/* Don't be fooled about max_usage. While we do set it initially, it's nothing
   but a flag - the real usage counter is in redis in counter:<aid> and the only
   way to read-access that is by calling Alias.prototype.use() (or set use to true
   when callind findById())

   use() in turn will use redis' DECR operation on the counter.

   This makes sure, considering that DECR is atomic, that even under high load
   no mail can slip in :-)
 */
var Alias = function(aid) {
  this.aid = aid || null,
  this.valid_from = null,
  this.valid_until = null,
  this.max_usage = null
}
helpers.extend(Alias.prototype, (function(){
  function dv(ad, m) {
    var d = null;
    if (ad.hasOwnProperty(m)) {
      d = new Date(Date.parse(ad[m]));
      if (!d) {
        throw new Error(m + ' provided but invalid');
      }
    }
    return d;
  }

  function validate(ad) {
    if (ad.days){
      var d = new Date();
      d.setDate(d.getDate() + parseInt(ad.days, 10));
      this.valid_until = d;
    }else{
      this.valid_from = dv(ad, 'valid-from');
      this.valid_until = dv(ad, 'valid-until');
    }
    this.target = null;
    this.max_usage = parseInt(ad['max-usage'], 10);
    if (isNaN(this.max_usage)) this.max_usage = null;

    if (ad.hasOwnProperty('target')) {
      // we require a "raw" email address, so no comments or anything
      // also, I'm not even trying to be clever here. If it looks like
      // not-at@notdot.notdot, it's probably a valid email address
      if (ad.target.match(/^[^@]+@[^.@]+\.[^@.][^@]+$/))
        this.target = ad.target
    }
    if (!this.target) {
      throw new Error("Target missing or invalid");
    }
  }
  return {
    setInfo: function(ad) {
      validate.apply(this, arguments);
    },
    use: function(callback){
      var pdate = null;

      // node currently can't parse the output of its JSON serialized dates :-)
      function fixJSONDate(s){
        return s.replace(/\.[0-9]+Z$/, ' UTC').replace(/(\d)T(\d)/, "$1 $2");
      }

      if (!this.aid){
        callback(true);
        return;
      }

      if (this.valid_from && Date.parse(fixJSONDate(this.valid_from)) > new Date()){
        callback(false);
        return; // no further checks
      }
      if (this.valid_until && Date.parse(fixJSONDate(this.valid_until)) < new Date()){
        callback(false);
        return; // no further checks
      }

      if (!this.max_usage){
        callback(true);
      }else{
        var alias = this;
        require('redis').client(function(client){
          client.decr('counter:' + alias.aid, function(err, data){
            if (err){ callback(false); }
            var c = parseInt(""+data, 10);
            callback(!isNaN(c) && (c >= 0));
          });
        });
      }
    }
  };
}()));

(function() {
  var ids = 0;
  var UUID = require("uuid").UUID;

  var AliasProvider = (function(){
    function getNextId(callback) {
      var len = 3, c = "";
      require('redis').client(function(client){
        client.get('admin:keylength', function(err, data){
          len = data || len;
          var uuidg = function uuidg() {
            len++;
            var id = UUID.uuid(len);
            client.setnx('aliases:' + id, 'reserved', function(err, data) {
              if (err) throw new Error(err);
              if (parseInt(""+data, 10) == 0) {
                client.set('admin:keylength', len, function(err, data){
                  if (err) throw new Error(err);
                  uuidg();
                });
              }
              callback(id);
            });
          }();
        });
      });
    }
    return {
      findById: function(id, use, callback) {
        if (callback) {
          require('redis').client(function(client){
            client.get('aliases:' + id, function(err, data) {
              if (err || !data) {
                sys.error(err.message);
                callback(null);
                return;
              }
              helpers.extend(ad = new Alias(), JSON.parse(""+data));

              if (use){
                ad.use(function(valid){
                  callback(valid ? ad : null);
                })
              }else{
                callback(ad);
              }
            });
          });
        }
      },
      getNew: function() {
        return new Alias();
      },
      save: function(alias, callback) {
        if (!alias.aid) {
          alias.updatekey = UUID.uuid();
          getNextId(function(id) {
            alias.aid = id;
            require('redis').client(function(client){
              client.set('aliases:' + id, JSON.stringify(alias), function(err, data) {
                if (err) throw new Error(err);
                if (alias.max_usage){
                  client.set('counter:' + id, alias.max_usage,  function(err, data){
                    if (err) throw new Error(err);
                    callback(alias.aid);
                  })
                }else{
                  callback(alias.aid);
                }
              });
            });
          });
        }
      }
    }
  }());

  exports.AliasProvider = AliasProvider;
})();
<|endoftext|>