<commit_before>
var init = {
  lat: 39.908715,
  lng: 116.3973889,
  zoom: function() {
    var match = location.href.match(/zoom=(\d+)/);
    return match != null ? parseInt(match[1]) : 14
  },
  center: function() {
    var match = location.href.match(/center=([\d.]+),([\d.]+)/);
    if(match != null) {
      return new google.maps.LatLng(
        parseFloat(match[1]), parseFloat(match[2]));
    } else {
      return new google.maps.LatLng(init.lat, init.lng);
    }
  }
};

var map, geocoder, marker, clippedArea;
var mapDiv, addInput;
var selectedNote = null;

var uiState = {
  "loading" : {
    "load-icon" : true
  },
  "selection" : {
    "notes" : true,
    "save-button" : true
  },
  "success" : {
    "success-message" : true
  },
  "error" : {
    "error-message" : true
  },
}

function initialize() {
  var options = {
    center: init.center(),
    zoom: init.zoom()
  };

  mapDiv = document.getElementById("map-canvas");
  addInput = document.getElementById("address");

  map = new google.maps.Map(mapDiv, options);
  geocoder = new google.maps.Geocoder();
  marker = new google.maps.Marker({
    map: map,
    draggable: true,
    animation: google.maps.Animation.DROP,
    position: init.center()
  });

  initClippedArea();

  $('#address').keydown(function(event) {
    if(event.keyCode == 13) {
      search();
    }
  });
}

function initClippedArea() {
  clippedArea = new google.maps.Rectangle();

  google.maps.event.addListenerOnce(map, 'idle', function() {
    clippedArea.setOptions(rectOptions());
  });

  google.maps.event.addListener(map, 'zoom_changed', function() {
    clippedArea.setOptions(rectOptions());
  });

  google.maps.event.addListener(map, 'center_changed', function() {
    clippedArea.setOptions(rectOptions());
  });

  google.maps.event.addListener(clippedArea, 'mousemove', function(event) {
    var lat = event.latLng.lat();
    var lng = event.latLng.lng();
    //map.setOptions({ draggableCursor: 'crosshair' });
  });
}

function rectOptions() {
  return {
    strokeOpacity: 0,
    fillColor: '#FF0000',
    fillOpacity: 0.15,
    map: map,
    bounds: clippedBounds()
  }
}

function clippedBounds() {
  var proj = map.getProjection();
  var center = proj.fromLatLngToPoint(map.getCenter());

  var scale = Math.pow(2, map.getZoom());
  var tl = new google.maps.Point(
    center.x - 300/scale,
    center.y - 225/scale);
  var br = new google.maps.Point(
    center.x + 300/scale,
    center.y + 225/scale);

  return new google.maps.LatLngBounds(
    proj.fromPointToLatLng(tl),
    proj.fromPointToLatLng(br)
  );
}

function updateUI(state) {
  var allElements = ["notes", "save-button", "load-icon",
    "success-message", "error-message"];
  var meta = uiState[state];
  for(var i = 0; i < allElements.length; i++) {
    var id = allElements[i];
    if(meta[id] === true) {
      $("#" + id).show();
    } else {
      $("#" + id).hide();
    }
  }
}

function getBackURL() {
  var center = map.getCenter();
  var zoom = map.getZoom();
  var lat = center.lat();
  var lng = center.lng();
  return window.location.origin +
    "?center=" + lat + "," + lng + "&zoom=" + zoom;
}

function updateMap(center) {
  map.setCenter(center);
  marker.setAnimation(google.maps.Animation.DROP);
  marker.setPosition(map.getCenter());
}

function search() {
  var address = addInput.value;
  if(address.trim() !== "") {
    geocoder.geocode( { "address": address }, function(data, status) {
      if(status == google.maps.GeocoderStatus.OK) {
        var loc = data[0].geometry.location;
        updateMap(loc);
      } else {
        alert("Location not found!");
      }
    });
  }
}

function popupSaveDialog() {
  updateUI("loading");

  $("#myModal").modal("show");

  $.ajax({
    url: "/api/notes",
    dataType: "json"
  })
  .done(function(data) {
    var notes = data.notes;
    var htmlCodes = [];

    for(var i = 0; i < notes.length; i++) {
      var note = notes[i];
      htmlCodes.push('' +
        note.title + '');
    }

    $("#note-list").html(htmlCodes.join(""));
    updateUI("selection");
  })
  .fail(function(xhr) {
    if(xhr.status == 401) {
      window.location.href = "oauth/request_token?back_url=" +
        encodeURIComponent(getBackURL());
    } else {
      updateUI("error");
    }
  });
}

function selectNote(note) {
  if(selectedNote != note) {
    if(selectedNote != null) {
      selectedNote.classList.remove("active");
    }
    selectedNote = note;
    selectedNote.classList.add("active");
  }
}

function resetSelectedNote() {
  if(selectedNote != null) {
    selectedNote.classList.remove("active");
    selectedNote = null;
  }
}

function save() {
  var center = map.getCenter();
  var data = {
    zoom: map.getZoom(),
    lat: center.lat(),
    lng: center.lng()
  }

  var mp = marker.getPosition();
  if(typeof mp !== "undefined") {
    data["mlat"] = mp.lat();
    data["mlng"] = mp.lng();
  }

  updateUI("loading");

  if(selectedNote == null) {
    var noteName = $("#new-note-name").val();
    data.note_name = noteName;

    $.ajax({
      type: "POST",
      dataType: "json",
      url: "/api/notes",
      data: data
    })
    .done(function(data) {
      updateUI("success");
      $("#note-link").attr("href", data.note_url);
    })
    .fail(function(xhr) {
      updateUI("error");
    });
  } else {
    $.ajax({
      type: "PUT",
      dataType: "json",
      url: "/api/notes/" + selectedNote.id,
      data: data
    })
    .done(function(data){
      updateUI("success");
      $("#note-link").attr("href", data.note_url);
    })
    .fail(function(xhr){
      updateUI("error");
    });
  }
}

function moveMarker() {
  updateMap(map.getCenter());
}

function shareLink(link) {
  window.open(link.href, '',
    'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');
}
<commit_msg>trim method of string is not supported in IE<commit_after>
var init = {
  lat: 39.908715,
  lng: 116.3973889,
  zoom: function() {
    var match = location.href.match(/zoom=(\d+)/);
    return match != null ? parseInt(match[1]) : 14
  },
  center: function() {
    var match = location.href.match(/center=([\d.]+),([\d.]+)/);
    if(match != null) {
      return new google.maps.LatLng(
        parseFloat(match[1]), parseFloat(match[2]));
    } else {
      return new google.maps.LatLng(init.lat, init.lng);
    }
  }
};

var map, geocoder, marker, clippedArea;
var mapDiv, addInput;
var selectedNote = null;

var uiState = {
  "loading" : {
    "load-icon" : true
  },
  "selection" : {
    "notes" : true,
    "save-button" : true
  },
  "success" : {
    "success-message" : true
  },
  "error" : {
    "error-message" : true
  },
}

function initialize() {
  var options = {
    center: init.center(),
    zoom: init.zoom()
  };

  mapDiv = document.getElementById("map-canvas");
  addInput = document.getElementById("address");

  map = new google.maps.Map(mapDiv, options);
  geocoder = new google.maps.Geocoder();
  marker = new google.maps.Marker({
    map: map,
    draggable: true,
    animation: google.maps.Animation.DROP,
    position: init.center()
  });

  initClippedArea();

  $('#address').keydown(function(event) {
    if(event.keyCode == 13) {
      search();
    }
  });
}

function initClippedArea() {
  clippedArea = new google.maps.Rectangle();

  google.maps.event.addListenerOnce(map, 'idle', function() {
    clippedArea.setOptions(rectOptions());
  });

  google.maps.event.addListener(map, 'zoom_changed', function() {
    clippedArea.setOptions(rectOptions());
  });

  google.maps.event.addListener(map, 'center_changed', function() {
    clippedArea.setOptions(rectOptions());
  });

  google.maps.event.addListener(clippedArea, 'mousemove', function(event) {
    var lat = event.latLng.lat();
    var lng = event.latLng.lng();
    //map.setOptions({ draggableCursor: 'crosshair' });
  });
}

function rectOptions() {
  return {
    strokeOpacity: 0,
    fillColor: '#FF0000',
    fillOpacity: 0.15,
    map: map,
    bounds: clippedBounds()
  }
}

function clippedBounds() {
  var proj = map.getProjection();
  var center = proj.fromLatLngToPoint(map.getCenter());

  var scale = Math.pow(2, map.getZoom());
  var tl = new google.maps.Point(
    center.x - 300/scale,
    center.y - 225/scale);
  var br = new google.maps.Point(
    center.x + 300/scale,
    center.y + 225/scale);

  return new google.maps.LatLngBounds(
    proj.fromPointToLatLng(tl),
    proj.fromPointToLatLng(br)
  );
}

function updateUI(state) {
  var allElements = ["notes", "save-button", "load-icon",
    "success-message", "error-message"];
  var meta = uiState[state];
  for(var i = 0; i < allElements.length; i++) {
    var id = allElements[i];
    if(meta[id] === true) {
      $("#" + id).show();
    } else {
      $("#" + id).hide();
    }
  }
}

function getBackURL() {
  var center = map.getCenter();
  var zoom = map.getZoom();
  var lat = center.lat();
  var lng = center.lng();
  return window.location.origin +
    "?center=" + lat + "," + lng + "&zoom=" + zoom;
}

function updateMap(center) {
  map.setCenter(center);
  marker.setAnimation(google.maps.Animation.DROP);
  marker.setPosition(map.getCenter());
}

function search() {
  var address = addInput.value;
  address = address.replace(/^\s+|\s+$/g, '');
  if(address !== "") {
    geocoder.geocode( { "address": address }, function(data, status) {
      if(status == google.maps.GeocoderStatus.OK) {
        var loc = data[0].geometry.location;
        updateMap(loc);
      } else {
        alert("Location not found!");
      }
    });
  }
}

function popupSaveDialog() {
  updateUI("loading");

  $("#myModal").modal("show");

  $.ajax({
    url: "/api/notes",
    dataType: "json"
  })
  .done(function(data) {
    var notes = data.notes;
    var htmlCodes = [];

    for(var i = 0; i < notes.length; i++) {
      var note = notes[i];
      htmlCodes.push('' +
        note.title + '');
    }

    $("#note-list").html(htmlCodes.join(""));
    updateUI("selection");
  })
  .fail(function(xhr) {
    if(xhr.status == 401) {
      window.location.href = "oauth/request_token?back_url=" +
        encodeURIComponent(getBackURL());
    } else {
      updateUI("error");
    }
  });
}

function selectNote(note) {
  if(selectedNote != note) {
    if(selectedNote != null) {
      selectedNote.classList.remove("active");
    }
    selectedNote = note;
    selectedNote.classList.add("active");
  }
}

function resetSelectedNote() {
  if(selectedNote != null) {
    selectedNote.classList.remove("active");
    selectedNote = null;
  }
}

function save() {
  var center = map.getCenter();
  var data = {
    zoom: map.getZoom(),
    lat: center.lat(),
    lng: center.lng()
  }

  var mp = marker.getPosition();
  if(typeof mp !== "undefined") {
    data["mlat"] = mp.lat();
    data["mlng"] = mp.lng();
  }

  updateUI("loading");

  if(selectedNote == null) {
    var noteName = $("#new-note-name").val();
    data.note_name = noteName;

    $.ajax({
      type: "POST",
      dataType: "json",
      url: "/api/notes",
      data: data
    })
    .done(function(data) {
      updateUI("success");
      $("#note-link").attr("href", data.note_url);
    })
    .fail(function(xhr) {
      updateUI("error");
    });
  } else {
    $.ajax({
      type: "PUT",
      dataType: "json",
      url: "/api/notes/" + selectedNote.id,
      data: data
    })
    .done(function(data){
      updateUI("success");
      $("#note-link").attr("href", data.note_url);
    })
    .fail(function(xhr){
      updateUI("error");
    });
  }
}

function moveMarker() {
  updateMap(map.getCenter());
}

function shareLink(link) {
  window.open(link.href, '',
    'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');
}
<|endoftext|>


<commit_before>setTimeout(function () {
    if(document.querySelector('.schemes')){
        document.querySelector('.schemes').innerHTML = '
This project is not affiliated with or endorsed by the RATP.
' +
          '
Support is available here. ' +
          '
' +
          '
Source code is available here. ' +
          '
' +
          '
Official RATP SOAP API status is available on this dashboard.
' +
          'BEWARE : Work in progress, use at your own risk.'
    }
}, 300);
<commit_msg>Remove WIP information<commit_after>setTimeout(function () {
    if(document.querySelector('.schemes')){
        document.querySelector('.schemes').innerHTML = '
This project is not affiliated with or endorsed by the RATP.
' +
          '
Support is available here. ' +
          '
' +
          '
Source code is available here. ' +
          '
' +
          '
Official RATP SOAP API status is available on this dashboard.
'
    }
}, 300);
<|endoftext|>


<commit_before>window.overview = {};

var tomorrow = window.utils.formatDate_api(moment().add(1, "days").toDate())

$(document).ready(function() {
	$("#overview").on("tabOpened", function() {
		$(".overview-date").text(window.utils.formatDate_english(new Date()));
		window.api.get('overview/announcements/get/', function(data) {
			$("#announcementList").text("");
			var response = data.feedback.reverse();
			for (var feed in response) {
				if (moment(response[feed].time).add(response[feed].days, "days").isAfter(moment())) {
					var $feedLi = $("

");
						$feedLi.append(response[feed].msg);
					$("#announcementList").append($feedLi);
				}
			}
			if ($("#announcementList").children().size() > 0) {
				$("#announcementAlert").css("display", "block");
			} else {
				$("#announcementAlert").css("display", "none");
			}
		});
		window.api.get('/planner/events/getDay/' + tomorrow, function(result) {
			HWphrase("howmuchhw", result.events.length);
		});
	});
});

function HWphrase(id, hw) {
	if(hw < 1){
		document.getElementById(id).innerHTML = ' Looks like tonight will be an easy night! You have no homework due tomorrow!';
	} else if (hw < 3) {
		document.getElementById(id).innerHTML = ' It seems like you will have some free time tonight! You have ' + hw + ' assignments due tomorrow.';
	} else if (hw < 5){
		document.getElementById(id).innerHTML = ' Tonight might be rough. You have ' + hw + ' assignments due tomorrow.';
	} else {
		document.getElementById(id).innerHTML = ' You might be up late doing homework tonight. You have ' + hw + ' assignments due tomorrow.';
	}
}

function hello(){
	alert("Hi!");
}
<commit_msg>Close #17<commit_after>window.overview = {};

var tomorrow = window.utils.formatDate_api(moment().add(1, "days").toDate())

$(document).ready(function() {
	$("#overview").on("tabOpened", function() {
		$(".overview-date").text(window.utils.formatDate_english(new Date()));
		window.api.get('overview/announcements/get/', function(data) {
			$("#announcementList").text("");
			var response = data.feedback.reverse();
			for (var feed in response) {
				if (moment(response[feed].time).add(response[feed].days, "days").isAfter(moment())) {
					var $feedLi = $("

");
						$feedLi.append(response[feed].msg);
					$("#announcementList").append($feedLi);
				}
			}
			if ($("#announcementList").children().size() > 0) {
				$("#announcementAlert").css("display", "block");
			} else {
				$("#announcementAlert").css("display", "none");
			}
		});
		window.api.get('/planner/events/getDay/' + tomorrow, function(result) {
			HWphrase("howmuchhw", result.events.length);
		});
	});
});

function HWphrase(id, hw) {
	if(hw < 1){
		document.getElementById(id).innerHTML = ' Looks like tonight will be an easy night! You have no homework due tomorrow!';
	} else if (hw < 3) {
		if (hw === 1){
			document.getElementById(id).innerHTML = ' It seems like you will have some free time tonight! You have ' + hw + ' assignment due tomorrow.';
		} else {
			document.getElementById(id).innerHTML = ' It seems like you will have some free time tonight! You have ' + hw + ' assignments due tomorrow.';
		}
	} else if (hw < 5){
		document.getElementById(id).innerHTML = ' Tonight might be rough. You have ' + hw + ' assignments due tomorrow.';
	} else {
		document.getElementById(id).innerHTML = ' You might be up late doing homework tonight. You have ' + hw + ' assignments due tomorrow.';
	}
}

function hello(){
	alert("Hi!");
}
<|endoftext|>


<commit_before>'use strict';

var map;
function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: -34.397, lng: 150.644},
    zoom: 21,
  });

  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
      var pos = {
        lat: position.coords.latitude,
        lng: position.coords.longitude
      };

        // var myCar = new google.maps.Circle({
        //   strokeColor: 'blue',
        //   strokeOpacity: 0.8,
        //   strokeWeight: 2,
        //   fillColor: 'blue',
        //   fillOpacity: 0.5,
        //   map: map,
        //   center: {lat: position.coords.latitude, lng: position.coords.longitude},
        //   radius: 1,
        //   draggable: true
        // });
        // var otherCar = new google.maps.Circle({
        //   strokeColor: 'red',
        //   strokeOpacity: 0.8,
        //   strokeWeight: 2,
        //   fillColor: 'red',
        //   fillOpacity: 0.5,
        //   map: map,
        //   center: {lat: position.coords.latitude, lng: position.coords.longitude + 0.00003},
        //   radius: 1,
        //   draggable: true
        // });
        // var arrow = new google.maps.Marker({
        //   center: {lat: position.coords.latitude, lng: position.coords.longitude + 0.00003},
        //   map: map,
        //   path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW
        // })

      map.setCenter(pos);
    }, function() {
      handleLocationError(true, infoWindow, map.getCenter());
    });
  } else {
    // Browser doesn't support Geolocation
    handleLocationError(false, infoWindow, map.getCenter());
  }

  map.addListener('click', function(e) {
    placeMarkerAndPanTo(e.latLng, map);
  });

  function placeMarkerAndPanTo(latLng, map) {
    var marker = new google.maps.Marker({
      position: latLng,
      map: map
    });
    //this code (latLng.lat().toFixed(3) gets the latitude for wherever the user placed the marker)
    console.log(latLng.lat().toFixed(3));
    map.panTo(latLng);
  }
}
<commit_msg>write functions to collect longitude too<commit_after>'use strict';

var map;
function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: -34.397, lng: 150.644},
    zoom: 21,
  });

  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
      var pos = {
        lat: position.coords.latitude,
        lng: position.coords.longitude
      };

        // var myCar = new google.maps.Circle({
        //   strokeColor: 'blue',
        //   strokeOpacity: 0.8,
        //   strokeWeight: 2,
        //   fillColor: 'blue',
        //   fillOpacity: 0.5,
        //   map: map,
        //   center: {lat: position.coords.latitude, lng: position.coords.longitude},
        //   radius: 1,
        //   draggable: true
        // });
        // var otherCar = new google.maps.Circle({
        //   strokeColor: 'red',
        //   strokeOpacity: 0.8,
        //   strokeWeight: 2,
        //   fillColor: 'red',
        //   fillOpacity: 0.5,
        //   map: map,
        //   center: {lat: position.coords.latitude, lng: position.coords.longitude + 0.00003},
        //   radius: 1,
        //   draggable: true
        // });
        // var arrow = new google.maps.Marker({
        //   center: {lat: position.coords.latitude, lng: position.coords.longitude + 0.00003},
        //   map: map,
        //   path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW
        // })

      map.setCenter(pos);
    }, function() {
      handleLocationError(true, map.getCenter());
    });
  } else {
    // Browser doesn't support Geolocation
    handleLocationError(false, map.getCenter());
  }

  map.addListener('click', function(e) {
    placeMarker(e.latLng, map);
  });

  function placeMarker(latLng, map) {
    var marker = new google.maps.Marker({
      position: latLng,
      map: map
    });
    //this code (latLng.lat().toFixed(3) gets the latitude for wherever the user placed the marker)
    console.log(latLng.lat().toFixed(6));
    console.log(latLng.lng().toFixed(6));
  }
}
<|endoftext|>


<commit_before><<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
// Prevent QUnit from running when the DOM load event fires
QUnit.config.autostart = false;
if (window.sessionStorage != null) {
    window.sessionStorage.clear();
}
=======
sessionStorage.clear();
>>>>>>> updated test-runner and changed title of battery tests
=======
=======
>>>>>>> updated test-runner and changed title of battery tests
sessionStorage.clear();
=======
// Prevent QUnit from running when the DOM load event fires
QUnit.config.autostart = false;
=======
>>>>>>> updated test-runner and changed title of battery tests
if (window.sessionStorage != null) {
    window.sessionStorage.clear();
}
>>>>>>> on wp7 sessionStorage does not exist until just before device ready
>>>>>>> on wp7 sessionStorage does not exist until just before device ready

// Timeout is 2 seconds to allow physical devices enough
// time to query the response. This is important for some
// Android devices.
var Tests = function() {};
Tests.TEST_TIMEOUT = 5000;
<commit_msg>another rebase fix<commit_after>QUnit.config.autostart = false;
if (window.sessionStorage != null) {
    window.sessionStorage.clear();
}

// Timeout is 2 seconds to allow physical devices enough
// time to query the response. This is important for some
// Android devices.
var Tests = function() {};
Tests.TEST_TIMEOUT = 5000;
<|endoftext|>


<commit_before>define(function (require) {
  return function MapFactory(Private) {
    var _ = require('lodash');
    var $ = require('jquery');
    var L = require('leaflet');
    require('./../lib/leaflet.mouseposition/L.Control.MousePosition.css');
    require('./../lib/leaflet.mouseposition/L.Control.MousePosition');
    require('./../lib/leaflet.setview/L.Control.SetView');
    var syncMaps = require('./sync_maps');

    var markerIcon = L.icon({
      iconUrl: require('./images/marker-icon.png'),
      iconRetinaUrl: require('./images/marker-icon-2x.png'),
      iconSize: [25, 41]
    });

    var defaultMapZoom = 2;
    var defaultMapCenter = [15, 5];
    var defaultMarkerType = 'Scaled Circle Markers';

    var mapTiles = {
      url: 'http://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
      options: {
        attribution: 'Map data © OpenStreetMap contributors'
      }
    };
    var markerTypes = {
      'Scaled Circle Markers': Private(require('./marker_types/scaled_circles')),
      'Shaded Circle Markers': Private(require('./marker_types/shaded_circles')),
      'Shaded Geohash Grid': Private(require('./marker_types/geohash_grid')),
      'Heatmap': Private(require('./marker_types/heatmap')),
    };

    /**
     * Tile Map Maps
     *
     * @class Map
     * @constructor
     * @param container {HTML Element} Element to render map into
     * @param params {Object} Parameters used to build a map
     */
    function TileMapMap(container, params) {
      this._container = container;

      // keep a reference to all of the optional params
      this._callbacks = _.get(params, 'callbacks');
      this._setMarkerType(params.mapType);
      const centerArray = _.get(params, 'center') || defaultMapCenter;
      this._mapCenter = L.latLng(centerArray[0], centerArray[1]);
      this._mapZoom = _.get(params, 'zoom') || defaultMapZoom;
      this._valueFormatter = params.valueFormatter || _.identity;
      this._tooltipFormatter = params.tooltipFormatter || _.identity;
      this._setAttr(params.attr);
      this._isEditable = params.editable || false;

      var mapOptions = {
        minZoom: 1,
        maxZoom: 18,
        noWrap: true,
        maxBounds: L.latLngBounds([-90, -220], [90, 220]),
        scrollWheelZoom: _.get(params.attr, 'scrollWheelZoom', true),
        fadeAnimation: false,
      };

      this._createMap(mapOptions);
    }

    TileMapMap.prototype._addDrawControl = function () {
      if (this._boundingControl) return;

      //create Markers feature group and add saved markers
      this._drawnItems = new L.FeatureGroup();
      var self = this;
      this._attr.markers.forEach(function(point) {
        self._drawnItems.addLayer(
          L.marker(
            point,
            {icon: markerIcon}));
      });
      this.map.addLayer(this._drawnItems);
      this._layerControl.addOverlay(this._drawnItems, "Markers");

      //https://github.com/Leaflet/Leaflet.draw
      const drawOptions = {
        draw: {
          circle: false,
          marker: {
            icon: markerIcon
          },
          polygon: false,
          polyline: false,
          rectangle: {
            shapeOptions: {
              stroke: false,
              color: '#000'
            }
          }
        },
        edit: {
          featureGroup: this._drawnItems,
          edit: false
        }
      }
      //Do not show marker and remove buttons when visualization is displayed in dashboard, i.e. not editable
      if(!this._isEditable) {
        drawOptions.draw.marker = false;
        drawOptions.edit.remove = false;
      }

      this._boundingControl = new L.Control.Draw(drawOptions);
      this.map.addControl(this._boundingControl);
    };

    TileMapMap.prototype._addSetViewControl = function () {
      if (this._setViewControl) return;

      this._setViewControl = new L.Control.SetView();
      this.map.addControl(this._setViewControl);
    };

    /**
     * Adds label div to each map when data is split
     *
     * @method addTitle
     * @param mapLabel {String}
     * @return {undefined}
     */
    TileMapMap.prototype.addTitle = function (mapLabel) {
      if (this._label) return;

      var label = this._label = L.control();

      label.onAdd = function () {
        this._div = L.DomUtil.create('div', 'tilemap-info tilemap-label');
        this.update();
        return this._div;
      };
      label.update = function () {
        this._div.innerHTML = '
' + _.escape(mapLabel) + '
';
      };

      // label.addTo(this.map);
      this.map.addControl(label);
    };

    /**
     * remove css class for desat filters on map tiles
     *
     * @method saturateTiles
     * @return undefined
     */
    TileMapMap.prototype.saturateTiles = function () {
      if (!this._attr.isDesaturated) {
        $('img.leaflet-tile-loaded').addClass('filters-off');
      }
    };

    TileMapMap.prototype.updateSize = function () {
      this.map.invalidateSize({
        debounceMoveend: true
      });
    };

    TileMapMap.prototype.destroy = function () {
      if (this._label) this._label.removeFrom(this.map);
      if (this._fitControl) this._fitControl.removeFrom(this.map);
      if (this._boundingControl) this._boundingControl.removeFrom(this.map);
      if (this._markers) this._markers.destroy();
      syncMaps.remove(this.map);
      this.map.remove();
      this.map = undefined;
    };

    /**
     * Switch type of data overlay for map:
     * creates featurelayer from mapData (geoJson)
     *
     * @method _addMarkers
     */
    TileMapMap.prototype.addMarkers = function (chartData, newParams) {
      if(newParams) {
        this._setMarkerType(newParams.mapType);
        this._setAttr(newParams);
      }
      if(chartData) {
        this._chartData = chartData;
        this._geoJson = _.get(chartData, 'geoJson');
      }
      if (this._markers) this._markers.destroy();

      this._markers = this._createMarkers({
        tooltipFormatter: this._tooltipFormatter,
        valueFormatter: this._valueFormatter,
        attr: this._attr
      });

      if (this._geoJson.features.length > 1) {
        this._markers.addLegend();
      }
    };

    /**
     * Create the marker instance using the given options
     *
     * @method _createMarkers
     * @param options {Object} options to give to marker class
     * @return {Object} marker layer
     */
    TileMapMap.prototype._createMarkers = function (options) {
      var MarkerType = markerTypes[this._markerType];
      return new MarkerType(this.map, this._geoJson, options);
    };

    TileMapMap.prototype._setMarkerType = function (markerType) {
      this._markerType = markerTypes[markerType] ? markerType : defaultMarkerType;
    };

    TileMapMap.prototype._setAttr = function (attr) {
      this._attr = attr || {};

      //update map options based on new attributes
      if(this.map) {
        if(this._attr.scrollWheelZoom) {
          this.map.scrollWheelZoom.enable();
        } else {
          this.map.scrollWheelZoom.disable();
        }
      }
    };

    TileMapMap.prototype._attachEvents = function () {
      var self = this;
      var saturateTiles = self.saturateTiles.bind(self);

      this._tileLayer.on('tileload', saturateTiles);

      this.map.on('unload', function () {
        self._tileLayer.off('tileload', saturateTiles);
      });

      this.map.on('moveend', _.debounce(function setZoomCenter(ev) {
        if (!self.map) return;
        if (self._hasSameLocation()) return;

        // update internal center and zoom references
        self._mapCenter = self.map.getCenter();
        self._mapZoom = self.map.getZoom();
        self.addMarkers();

        self._callbacks.mapMoveEnd({
          chart: self._chartData,
          map: self.map,
          center: self._mapCenter,
          zoom: self._mapZoom,
        });
      }, 500, false));

      this.map.on('setview:fitBounds', function (e) {
        self._fitBounds();
      });

      this.map.on('draw:created', function (e) {
        switch (e.layerType) {
          case "marker":
            self._drawnItems.addLayer(e.layer);
            self._callbacks.createMarker({
              e: e,
              chart: self._chartData,
              latlng: e.layer._latlng
            });
            break;
          case "rectangle":
            var bounds = e.layer.getBounds();
            self._callbacks.rectangle({
              e: e,
              chart: self._chartData,
              bounds: {
                top_left: {
                  lat: bounds.getNorthWest().lat,
                  lon: bounds.getNorthWest().lng
                },
                bottom_right: {
                  lat: bounds.getSouthEast().lat,
                  lon: bounds.getSouthEast().lng
                }
              }
            });
            break;
          default:
            console.log("draw:created, unexpected layerType: " + drawType);
        }
      });

      this.map.on('draw:deleted', function (e) {
        self._callbacks.deleteMarkers({
          chart: self._chartData,
          deletedLayers: e.layers,
        });
      });

      this.map.on('zoomend', _.debounce(function () {
        if (!self.map) return;
        if (self._hasSameLocation()) return;

        self._mapCenter = self.map.getCenter();
        self._mapZoom = self.map.getZoom();
        if (!self._callbacks) return;

        self._callbacks.mapZoomEnd({
          chart: self._chartData,
          map: self.map,
          center: self._mapCenter,
          zoom: self._mapZoom,
        });
      }, 500, false));
    };

    TileMapMap.prototype._hasSameLocation = function () {
      const oldLat = this._mapCenter.lat.toFixed(5);
      const oldLon = this._mapCenter.lng.toFixed(5);
      const newLat = this.map.getCenter().lat.toFixed(5);
      const newLon = this.map.getCenter().lng.toFixed(5);
      let isSame = false;
      if (oldLat === newLat
        && oldLon === newLon
        && this.map.getZoom() === this._mapZoom) {
        isSame = true;
      }
      return isSame;
    }

    TileMapMap.prototype._createMap = function (mapOptions) {
      if (this.map) this.destroy();

      // add map tiles layer, using the mapTiles object settings
      if (this._attr.wms && this._attr.wms.enabled) {
        this._tileLayer = L.tileLayer.wms(this._attr.wms.url, this._attr.wms.options);
      } else {
        this._tileLayer = L.tileLayer(mapTiles.url, mapTiles.options);
      }

      // append tile layers, center and zoom to the map options
      mapOptions.layers = this._tileLayer;
      mapOptions.center = this._mapCenter;
      mapOptions.zoom = this._mapZoom;

      this.map = L.map(this._container, mapOptions);
      this._layerControl = L.control.layers();
      this._layerControl.addTo(this.map);
      L.control.mousePosition().addTo(this.map);
      this._addSetViewControl();
      this._addDrawControl();
      this._attachEvents();
      syncMaps.add(this.map);
    };

    /**
     * zoom map to fit all features in featureLayer
     *
     * @method _fitBounds
     * @param map {Leaflet Object}
     * @return {boolean}
     */
    TileMapMap.prototype._fitBounds = function () {
      this.map.fitBounds(this._getDataRectangles());
    };

    /**
     * Get the Rectangles representing the geohash grid
     *
     * @return {LatLngRectangles[]}
     */
    TileMapMap.prototype._getDataRectangles = function () {
      if (!this._geoJson) return [];
      return _.pluck(this._geoJson.features, 'properties.rectangle');
    };

    return TileMapMap;
  };
});
<commit_msg>fixes #1<commit_after>define(function (require) {
  return function MapFactory(Private) {
    var _ = require('lodash');
    var $ = require('jquery');
    var L = require('leaflet');
    require('./../lib/leaflet.mouseposition/L.Control.MousePosition.css');
    require('./../lib/leaflet.mouseposition/L.Control.MousePosition');
    require('./../lib/leaflet.setview/L.Control.SetView');
    var syncMaps = require('./sync_maps');

    var markerIcon = L.icon({
      iconUrl: require('./images/marker-icon.png'),
      iconRetinaUrl: require('./images/marker-icon-2x.png'),
      iconSize: [25, 41]
    });

    var defaultMapZoom = 2;
    var defaultMapCenter = [15, 5];
    var defaultMarkerType = 'Scaled Circle Markers';

    var mapTiles = {
      url: 'http://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
      options: {
        attribution: 'Map data © OpenStreetMap contributors'
      }
    };
    var markerTypes = {
      'Scaled Circle Markers': Private(require('./marker_types/scaled_circles')),
      'Shaded Circle Markers': Private(require('./marker_types/shaded_circles')),
      'Shaded Geohash Grid': Private(require('./marker_types/geohash_grid')),
      'Heatmap': Private(require('./marker_types/heatmap')),
    };

    /**
     * Tile Map Maps
     *
     * @class Map
     * @constructor
     * @param container {HTML Element} Element to render map into
     * @param params {Object} Parameters used to build a map
     */
    function TileMapMap(container, params) {
      this._container = container;

      // keep a reference to all of the optional params
      this._callbacks = _.get(params, 'callbacks');
      this._setMarkerType(params.mapType);
      const centerArray = _.get(params, 'center') || defaultMapCenter;
      this._mapCenter = L.latLng(centerArray[0], centerArray[1]);
      this._mapZoom = _.get(params, 'zoom') || defaultMapZoom;
      this._valueFormatter = params.valueFormatter || _.identity;
      this._tooltipFormatter = params.tooltipFormatter || _.identity;
      this._setAttr(params.attr);
      this._isEditable = params.editable || false;

      var mapOptions = {
        minZoom: 1,
        maxZoom: 18,
        noWrap: true,
        maxBounds: L.latLngBounds([-90, -220], [90, 220]),
        scrollWheelZoom: _.get(params.attr, 'scrollWheelZoom', true),
        fadeAnimation: false,
      };

      this._createMap(mapOptions);
    }

    TileMapMap.prototype._addDrawControl = function () {
      if (this._boundingControl) return;

      //create Markers feature group and add saved markers
      this._drawnItems = new L.FeatureGroup();
      var self = this;
      this._attr.markers.forEach(function(point) {
        self._drawnItems.addLayer(
          L.marker(
            point,
            {icon: markerIcon}));
      });
      this.map.addLayer(this._drawnItems);
      this._layerControl.addOverlay(this._drawnItems, "Markers");

      //https://github.com/Leaflet/Leaflet.draw
      const drawOptions = {
        draw: {
          circle: false,
          marker: {
            icon: markerIcon
          },
          polygon: false,
          polyline: false,
          rectangle: {
            shapeOptions: {
              stroke: false,
              color: '#000'
            }
          }
        },
        edit: {
          featureGroup: this._drawnItems,
          edit: false
        }
      }
      //Do not show marker and remove buttons when visualization is displayed in dashboard, i.e. not editable
      if(!this._isEditable) {
        drawOptions.draw.marker = false;
        drawOptions.edit.remove = false;
      }

      this._boundingControl = new L.Control.Draw(drawOptions);
      this.map.addControl(this._boundingControl);
    };

    TileMapMap.prototype._addSetViewControl = function () {
      if (this._setViewControl) return;

      this._setViewControl = new L.Control.SetView();
      this.map.addControl(this._setViewControl);
    };

    /**
     * Adds label div to each map when data is split
     *
     * @method addTitle
     * @param mapLabel {String}
     * @return {undefined}
     */
    TileMapMap.prototype.addTitle = function (mapLabel) {
      if (this._label) return;

      var label = this._label = L.control();

      label.onAdd = function () {
        this._div = L.DomUtil.create('div', 'tilemap-info tilemap-label');
        this.update();
        return this._div;
      };
      label.update = function () {
        this._div.innerHTML = '
' + _.escape(mapLabel) + '
';
      };

      // label.addTo(this.map);
      this.map.addControl(label);
    };

    /**
     * remove css class for desat filters on map tiles
     *
     * @method saturateTiles
     * @return undefined
     */
    TileMapMap.prototype.saturateTiles = function () {
      if (!this._attr.isDesaturated) {
        $('img.leaflet-tile-loaded').addClass('filters-off');
      }
    };

    TileMapMap.prototype.updateSize = function () {
      this.map.invalidateSize({
        debounceMoveend: true
      });
    };

    TileMapMap.prototype.destroy = function () {
      if (this._label) this._label.removeFrom(this.map);
      if (this._fitControl) this._fitControl.removeFrom(this.map);
      if (this._boundingControl) this._boundingControl.removeFrom(this.map);
      if (this._markers) this._markers.destroy();
      syncMaps.remove(this.map);
      this.map.remove();
      this.map = undefined;
    };

    /**
     * Switch type of data overlay for map:
     * creates featurelayer from mapData (geoJson)
     *
     * @method _addMarkers
     */
    TileMapMap.prototype.addMarkers = function (chartData, newParams) {
      if(newParams) {
        this._setMarkerType(newParams.mapType);
        this._setAttr(newParams);
      }
      if(chartData) {
        this._chartData = chartData;
        this._geoJson = _.get(chartData, 'geoJson');
      }
      if (this._markers) this._markers.destroy();

      this._markers = this._createMarkers({
        tooltipFormatter: this._tooltipFormatter,
        valueFormatter: this._valueFormatter,
        attr: this._attr
      });

      if (this._geoJson.features.length > 1) {
        this._markers.addLegend();
      }
    };

    /**
     * Create the marker instance using the given options
     *
     * @method _createMarkers
     * @param options {Object} options to give to marker class
     * @return {Object} marker layer
     */
    TileMapMap.prototype._createMarkers = function (options) {
      var MarkerType = markerTypes[this._markerType];
      return new MarkerType(this.map, this._geoJson, options);
    };

    TileMapMap.prototype._setMarkerType = function (markerType) {
      this._markerType = markerTypes[markerType] ? markerType : defaultMarkerType;
    };

    TileMapMap.prototype._setAttr = function (attr) {
      this._attr = attr || {};

      //update map options based on new attributes
      if(this.map) {
        if(this._attr.scrollWheelZoom) {
          this.map.scrollWheelZoom.enable();
        } else {
          this.map.scrollWheelZoom.disable();
        }
      }
    };

    TileMapMap.prototype._attachEvents = function () {
      var self = this;
      var saturateTiles = self.saturateTiles.bind(self);

      this._tileLayer.on('tileload', saturateTiles);

      this.map.on('unload', function () {
        self._tileLayer.off('tileload', saturateTiles);
      });

      this.map.on('moveend', _.debounce(function setZoomCenter(ev) {
        if (!self.map) return;
        if (self._hasSameLocation()) return;

        // update internal center and zoom references
        self._mapCenter = self.map.getCenter();
        self._mapZoom = self.map.getZoom();
        self.addMarkers();

        self._callbacks.mapMoveEnd({
          chart: self._chartData,
          map: self.map,
          center: self._mapCenter,
          zoom: self._mapZoom,
        });
      }, 500, false));

      this.map.on('setview:fitBounds', function (e) {
        self._fitBounds();
      });

      this.map.on('draw:created', function (e) {
        switch (e.layerType) {
          case "marker":
            self._drawnItems.addLayer(e.layer);
            self._callbacks.createMarker({
              e: e,
              chart: self._chartData,
              latlng: e.layer._latlng
            });
            break;
          case "rectangle":
            var bounds = e.layer.getBounds();
            self._callbacks.rectangle({
              e: e,
              chart: self._chartData,
              bounds: {
                top_left: {
                  lat: bounds.getNorthWest().lat,
                  lon: bounds.getNorthWest().lng
                },
                bottom_right: {
                  lat: bounds.getSouthEast().lat,
                  lon: bounds.getSouthEast().lng
                }
              }
            });
            break;
          default:
            console.log("draw:created, unexpected layerType: " + drawType);
        }
      });

      this.map.on('draw:deleted', function (e) {
        self._callbacks.deleteMarkers({
          chart: self._chartData,
          deletedLayers: e.layers,
        });
      });

      this.map.on('zoomend', _.debounce(function () {
        if (!self.map) return;
        if (self._hasSameLocation()) return;

        self._mapCenter = self.map.getCenter();
        self._mapZoom = self.map.getZoom();
        if (!self._callbacks) return;

        self._callbacks.mapZoomEnd({
          chart: self._chartData,
          map: self.map,
          center: self._mapCenter,
          zoom: self._mapZoom,
        });
      }, 500, false));
    };

    TileMapMap.prototype._hasSameLocation = function () {
      const oldLat = this._mapCenter.lat.toFixed(5);
      const oldLon = this._mapCenter.lng.toFixed(5);
      const newLat = this.map.getCenter().lat.toFixed(5);
      const newLon = this.map.getCenter().lng.toFixed(5);
      let isSame = false;
      if (oldLat === newLat
        && oldLon === newLon
        && this.map.getZoom() === this._mapZoom) {
        isSame = true;
      }
      return isSame;
    }

    TileMapMap.prototype._createMap = function (mapOptions) {
      if (this.map) this.destroy();

      // add map tiles layer, using the mapTiles object settings
      if (this._attr.wms && this._attr.wms.enabled) {
        this._tileLayer = L.tileLayer.wms(this._attr.wms.url, this._attr.wms.options);
      } else {
        this._tileLayer = L.tileLayer(mapTiles.url, mapTiles.options);
      }

      // append tile layers, center and zoom to the map options
      mapOptions.layers = this._tileLayer;
      mapOptions.center = this._mapCenter;
      mapOptions.zoom = this._mapZoom;

      this.map = L.map(this._container, mapOptions);
      this._layerControl = L.control.layers();
      this._layerControl.addTo(this.map);
      L.control.mousePosition().addTo(this.map);
      this._addSetViewControl();
      this._addDrawControl();
      this._attachEvents();
      syncMaps.add(this.map);
    };

    /**
     * zoom map to fit all features in featureLayer
     *
     * @method _fitBounds
     * @param map {Leaflet Object}
     * @return {boolean}
     */
    TileMapMap.prototype._fitBounds = function () {
      var bounds = this._getDataRectangles();
      if(bounds.length > 0) {
        this.map.fitBounds(bounds);
      }
    };

    /**
     * Get the Rectangles representing the geohash grid
     *
     * @return {LatLngRectangles[]}
     */
    TileMapMap.prototype._getDataRectangles = function () {
      if (!this._geoJson) return [];
      return _.pluck(this._geoJson.features, 'properties.rectangle');
    };

    return TileMapMap;
  };
});
<|endoftext|>


<commit_before>(function() {

var queryStringParam = /^\?(.*)/;
var namedParam    = /:([\w\d]+)/g;
var splatParam    = /\*([\w\d]+)/g;
var escapeRegExp  = /[-[\]{}()+?.,\\^$|#\s]/g;
var queryStrip = /(\?.*)$/;
var fragmentStrip = /^([^\?]*)/;
Backbone.Router.arrayValueSplit = '|';

var _getFragment = Backbone.History.prototype.getFragment;

_.extend(Backbone.History.prototype, {
  getFragment : function(fragment, forcePushState, excludeQueryString) {
    fragment = _getFragment.apply(this, arguments);
    if (excludeQueryString) {
      fragment = fragment.replace(queryStrip, '');
    }
    return fragment;
  },

  // this will not perform custom query param serialization specific to the router
  // but will return a map of key/value pairs (the value is a string or array)
  getQueryParameters : function(fragment, forcePushState) {
    fragment = _getFragment.apply(this, arguments);
    // if no query string exists, this will still be the original fragment
    var queryString = fragment.replace(fragmentStrip, '');
    var match = queryString.match(queryStringParam);
    if (match) {
      queryString = match[1];
      var rtn = {}
      iterateQueryString(queryString, function(name, value) {
        if (!rtn[name]) {
          rtn[name] = value;
        } else if (_.isString(rtn[name])) {
          rtn[name] = [rtn[name], value];
        } else {
          rtn[name].push(value);
        }
      });
      return rtn;
    } else {
      // no values
      return {};
    }
  }
});

_.extend(Backbone.Router.prototype, {
  getFragment : function(fragment, forcePushState, excludeQueryString) {
    fragment = _getFragment.apply(this, arguments);
    if (excludeQueryString) {
      fragment = fragment.replace(queryStrip, '');
    }
    return fragment;
  },

  _routeToRegExp : function(route) {
    route = route.replace(escapeRegExp, "\\$&");
    var match = route.match(namedParam, "([^\/?]*)");
    var paramCount = 0;
    var isWildCard = false;

    if (match) {
      route = route.replace(namedParam, "([^\/?]*)");
      paramCount = match.length;
    }
    match = route.match(splatParam);
    if (match) {
      route = route.replace(splatParam, "([^\?]*)");
      isWildcard = true;
    } else {
      // query parameters should not be used with wildcard
      route += '([\?]{1}.*)?';
    }

    var rtn = new RegExp('^' + route + '$');
    rtn.paramCount = paramCount;
    rtn.isWildCard = isWildCard;
    return rtn;
  },

  /**
   * Given a route, and a URL fragment that it matches, return the array of
   * extracted parameters.
   */
  _extractParameters : function(route, fragment) {
    var params = route.exec(fragment).slice(1);

    // do we have an additional query string?
    var match = params.length && params[params.length-1] && params[params.length-1].match(queryStringParam);
    if (match) {
      var queryString = match[1];
      var data = {};
      if (queryString) {
        var self = this;
        iterateQueryString(queryString, function(name, value) {
          self._setParamValue(name, value, data);
        });
      }
      params[params.length-1] = data;
    }

    // decode params
    for (var i=0; i<route.paramCount; i++) {
      if (_.isString(params[i])) {
        params[i] = decodeURIComponent(params[i]);
      }
    }

    return params;
  },

  /**
   * Set the parameter value on the data hash
   */
  _setParamValue : function(key, value, data) {
    // use '.' to define hash separators
    var parts = key.split('.');
    var _data = data;
    for (var i=0; i<parts.length; i++) {
      var part = parts[i];
      if (i === parts.length-1) {
        // set the value
        _data[part] = this._decodeParamValue(value, _data[part]);
      } else {
        _data = _data[part] = _data[part] || {};
      }
    }
  },

  /**
   * Decode an individual parameter value (or list of values)
   * @param value the complete value
   * @param currentValue the currently known value (or list of values)
   */
  _decodeParamValue : function(value, currentValue) {
    // '|' will indicate an array.  Array with 1 value is a=|b - multiple values can be a=b|c
    if (value.indexOf(Backbone.Router.arrayValueSplit) >= 0) {
      var values = value.split(Backbone.Router.arrayValueSplit);
      // clean it up
      for (var i=values.length-1; i>=0; i--) {
        if (!values[i]) {
          values.splice(i, 1);
        } else {
          values[i] = decodeURIComponent(values[i])
        }
      }
      return values;
    }
    if (!currentValue) {
      return decodeURIComponent(value);
    } else if (_.isArray(currentValue)) {
      currentValue.push(value);
      return currentValue;
    } else {
      return [currentValue, value];
    }
  },

  /**
   * Return the route fragment with queryParameters serialized to query parameter string
   */
  toFragment: function(route, queryParameters) {
    if (queryParameters) {
      if (!_.isString(queryParameters)) {
        queryParameters = this._toQueryString(queryParameters);
      }
      route += '?' + queryParameters;
    }
    return route;
  },

  /**
   * Serialize the val hash to query parameters and return it.  Use the namePrefix to prefix all param names (for recursion)
   */
  _toQueryString: function(val, namePrefix) {
    function encodeSplit(val) { return val.replace(Backbone.Router.arrayValueSplit, encodeURIComponent(Backbone.Router.arrayValueSplit)); }

    if (!val) return '';
    namePrefix = namePrefix || '';
    var rtn = '';
    for (var name in val) {
      var _val = val[name];
      if (_.isString(_val) || _.isNumber(_val) || _.isBoolean(_val) || _.isDate(_val)) {
        // primitave type
        _val = this._toQueryParam(_val);
        if (_.isBoolean(_val) || _val) {
          rtn += (rtn ? '&' : '') + this._toQueryParamName(name, namePrefix) + '=' + encodeSplit(encodeURIComponent(_val));
        }
      } else if (_.isArray(_val)) {
        // arrrays use Backbone.Router.arrayValueSplit separator
        var str = '';
        for (var i in _val) {
          var param = this._toQueryParam(_val[i]);
          if (_.isBoolean(param) || param) {
            str += Backbone.Router.arrayValueSplit + encodeSplit(param);
          }
        }
        if (str) {
          rtn += (rtn ? '&' : '') + this._toQueryParamName(name, namePrefix) + '=' + str;
        }
      } else {
        // dig into hash
        var result = this._toQueryString(_val, this._toQueryParamName(name, namePrefix, true));
        if (result) {
          rtn += (rtn ? '&' : '') + result;
        }
      }
    }
    return rtn;
  },

  /**
   * return the actual parameter name
   * @param name the parameter name
   * @param namePrefix the prefix to the name
   * @param createPrefix true if we're creating a name prefix, false if we're creating the name
   */
  _toQueryParamName: function(name, prefix, isPrefix) {
    return (prefix + name + (isPrefix ? '.' : ''));
  },

  /**
   * Return the string representation of the param used for the query string
   */
  _toQueryParam: function (param) {
    if (_.isNull(param) || _.isUndefined(param)) {
      return null;
    }
    if (_.isDate(param)) {
      return param.getDate().getTime();
    }
    return param;
  }
});

function iterateQueryString(queryString, callback) {
  var keyValues = queryString.split('&');
  var self = this;
  _.each(keyValues, function(keyValue) {
    var arr = keyValue.split('=');
    if (arr.length > 1 && arr[1]) {
      callback(arr[0], arr[1]);
    }
  });
}

})();
<commit_msg>slight refactoring<commit_after>(function() {

var queryStringParam = /^\?(.*)/;
var namedParam    = /:([\w\d]+)/g;
var splatParam    = /\*([\w\d]+)/g;
var escapeRegExp  = /[-[\]{}()+?.,\\^$|#\s]/g;
var queryStrip = /(\?.*)$/;
var fragmentStrip = /^([^\?]*)/;
Backbone.Router.arrayValueSplit = '|';

var _getFragment = Backbone.History.prototype.getFragment;

_.extend(Backbone.History.prototype, {
  getFragment : function(fragment, forcePushState, excludeQueryString) {
    fragment = _getFragment.apply(this, arguments);
    if (excludeQueryString) {
      fragment = fragment.replace(queryStrip, '');
    }
    return fragment;
  },

  // this will not perform custom query param serialization specific to the router
  // but will return a map of key/value pairs (the value is a string or array)
  getQueryParameters : function(fragment, forcePushState) {
    fragment = _getFragment.apply(this, arguments);
    // if no query string exists, this will still be the original fragment
    var queryString = fragment.replace(fragmentStrip, '');
    var match = queryString.match(queryStringParam);
    if (match) {
      queryString = match[1];
      var rtn = {}
      iterateQueryString(queryString, function(name, value) {
        if (!rtn[name]) {
          rtn[name] = value;
        } else if (_.isString(rtn[name])) {
          rtn[name] = [rtn[name], value];
        } else {
          rtn[name].push(value);
        }
      });
      return rtn;
    } else {
      // no values
      return {};
    }
  }
});

_.extend(Backbone.Router.prototype, {
  getFragment : function(fragment, forcePushState, excludeQueryString) {
    fragment = _getFragment.apply(this, arguments);
    if (excludeQueryString) {
      fragment = fragment.replace(queryStrip, '');
    }
    return fragment;
  },

  _routeToRegExp : function(route) {
    route = route.replace(escapeRegExp, "\\$&");
    var match = route.match(namedParam, "([^\/?]*)");
    var paramCount = 0;
    var isWildCard = false;

    if (match) {
      route = route.replace(namedParam, "([^\/?]*)");
      paramCount = match.length;
    }
    match = route.match(splatParam);
    if (match) {
      route = route.replace(splatParam, "([^\?]*)");
      isWildcard = true;
    } else {
      // query parameters should not be used with wildcard
      route += '([\?]{1}.*)?';
    }

    var rtn = new RegExp('^' + route + '$');
    rtn.paramCount = paramCount;
    rtn.isWildCard = isWildCard;
    return rtn;
  },

  /**
   * Given a route, and a URL fragment that it matches, return the array of
   * extracted parameters.
   */
  _extractParameters : function(route, fragment) {
    var params = route.exec(fragment).slice(1);

    // do we have an additional query string?
    var match = params.length && params[params.length-1] && params[params.length-1].match(queryStringParam);
    if (match) {
      var queryString = match[1];
      var data = {};
      if (queryString) {
        var self = this;
        iterateQueryString(queryString, function(name, value) {
          self._setParamValue(name, value, data);
        });
      }
      params[params.length-1] = data;
    }

    // decode params
    for (var i=0; i<route.paramCount; i++) {
      if (_.isString(params[i])) {
        params[i] = decodeURIComponent(params[i]);
      }
    }

    return params;
  },

  /**
   * Set the parameter value on the data hash
   */
  _setParamValue : function(key, value, data) {
    // use '.' to define hash separators
    var parts = key.split('.');
    var _data = data;
    for (var i=0; i<parts.length; i++) {
      var part = parts[i];
      if (i === parts.length-1) {
        // set the value
        _data[part] = this._decodeParamValue(value, _data[part]);
      } else {
        _data = _data[part] = _data[part] || {};
      }
    }
  },

  /**
   * Decode an individual parameter value (or list of values)
   * @param value the complete value
   * @param currentValue the currently known value (or list of values)
   */
  _decodeParamValue : function(value, currentValue) {
    // '|' will indicate an array.  Array with 1 value is a=|b - multiple values can be a=b|c
    var splitChar = Backbone.Router.arrayValueSplit;
    if (value.indexOf(splitChar) >= 0) {
      var values = value.split(splitChar);
      // clean it up
      for (var i=values.length-1; i>=0; i--) {
        if (!values[i]) {
          values.splice(i, 1);
        } else {
          values[i] = decodeURIComponent(values[i])
        }
      }
      return values;
    }
    if (!currentValue) {
      return decodeURIComponent(value);
    } else if (_.isArray(currentValue)) {
      currentValue.push(value);
      return currentValue;
    } else {
      return [currentValue, value];
    }
  },

  /**
   * Return the route fragment with queryParameters serialized to query parameter string
   */
  toFragment: function(route, queryParameters) {
    if (queryParameters) {
      if (!_.isString(queryParameters)) {
        queryParameters = this._toQueryString(queryParameters);
      }
      route += '?' + queryParameters;
    }
    return route;
  },

  /**
   * Serialize the val hash to query parameters and return it.  Use the namePrefix to prefix all param names (for recursion)
   */
  _toQueryString: function(val, namePrefix) {
    var splitChar = Backbone.Router.arrayValueSplit;
    function encodeSplit(val) { return val.replace(splitChar, encodeURIComponent(splitChar)); }

    if (!val) return '';
    namePrefix = namePrefix || '';
    var rtn = '';
    for (var name in val) {
      var _val = val[name];
      if (_.isString(_val) || _.isNumber(_val) || _.isBoolean(_val) || _.isDate(_val)) {
        // primitave type
        _val = this._toQueryParam(_val);
        if (_.isBoolean(_val) || _val) {
          rtn += (rtn ? '&' : '') + this._toQueryParamName(name, namePrefix) + '=' + encodeSplit(encodeURIComponent(_val));
        }
      } else if (_.isArray(_val)) {
        // arrrays use Backbone.Router.arrayValueSplit separator
        var str = '';
        for (var i in _val) {
          var param = this._toQueryParam(_val[i]);
          if (_.isBoolean(param) || param) {
            str += splitChar + encodeSplit(param);
          }
        }
        if (str) {
          rtn += (rtn ? '&' : '') + this._toQueryParamName(name, namePrefix) + '=' + str;
        }
      } else {
        // dig into hash
        var result = this._toQueryString(_val, this._toQueryParamName(name, namePrefix, true));
        if (result) {
          rtn += (rtn ? '&' : '') + result;
        }
      }
    }
    return rtn;
  },

  /**
   * return the actual parameter name
   * @param name the parameter name
   * @param namePrefix the prefix to the name
   * @param createPrefix true if we're creating a name prefix, false if we're creating the name
   */
  _toQueryParamName: function(name, prefix, isPrefix) {
    return (prefix + name + (isPrefix ? '.' : ''));
  },

  /**
   * Return the string representation of the param used for the query string
   */
  _toQueryParam: function (param) {
    if (_.isNull(param) || _.isUndefined(param)) {
      return null;
    }
    if (_.isDate(param)) {
      return param.getDate().getTime();
    }
    return param;
  }
});

function iterateQueryString(queryString, callback) {
  var keyValues = queryString.split('&');
  _.each(keyValues, function(keyValue) {
    var arr = keyValue.split('=');
    if (arr.length > 1 && arr[1]) {
      callback(arr[0], arr[1]);
    }
  });
}

})();
<|endoftext|>


<commit_before>(function(_, Backbone) {

var queryStringParam = /^\?(.*)/,
    optionalParam = /\((.*?)\)/g,
    namedParam    = /(\(\?)?:\w+/g,
    splatParam    = /\*\w+/g,
    escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g,
    queryStrip = /(\?.*)$/,
    fragmentStrip = /^([^\?]*)/,
    namesPattern = /[\:\*]([^\:\?\/]+)/g;
Backbone.Router.arrayValueSplit = '|';

var _getFragment = Backbone.History.prototype.getFragment;

_.extend(Backbone.History.prototype, {
  getFragment : function(fragment, forcePushState, excludeQueryString) {
    fragment = _getFragment.apply(this, arguments);
    if (excludeQueryString) {
      fragment = fragment.replace(queryStrip, '');
    }
    return fragment;
  },

  // this will not perform custom query param serialization specific to the router
  // but will return a map of key/value pairs (the value is a string or array)
  getQueryParameters : function(fragment, forcePushState) {
    fragment = _getFragment.apply(this, arguments);
    // if no query string exists, this will still be the original fragment
    var queryString = fragment.replace(fragmentStrip, '');
    var match = queryString.match(queryStringParam);
    if (match) {
      queryString = match[1];
      var rtn = {};
      iterateQueryString(queryString, function(name, value) {
        if (!rtn[name]) {
          rtn[name] = value;
        } else if (_.isString(rtn[name])) {
          rtn[name] = [rtn[name], value];
        } else {
          rtn[name].push(value);
        }
      });
      return rtn;
    } else {
      // no values
      return {};
    }
  }
});

_.extend(Backbone.Router.prototype, {
  initialize: function(options) {
    this.encodedSplatParts = options && options.encodedSplatParts;
  },

  getFragment : function(fragment, forcePushState, excludeQueryString) {
    fragment = _getFragment.apply(this, arguments);
    if (excludeQueryString) {
      fragment = fragment.replace(queryStrip, '');
    }
    return fragment;
  },

  _routeToRegExp: function(route) {
    var splatMatch = (splatParam.exec(route) || {index: -1}),
        namedMatch = (namedParam.exec(route) || {index: -1}),
        paramNames = route.match(namesPattern) || [];

    route = route.replace(escapeRegExp, '\\$&')
                 .replace(optionalParam, '(?:$1)?')
                 .replace(namedParam, function(match, optional){
                   return optional ? match : '([^\\/\\?]+)';
                 })
                 .replace(splatParam, '([^\?]*?)');
    route += '([\?]{1}.*)?';
    var rtn = new RegExp('^' + route + '$');

  // use the rtn value to hold some parameter data
  if (splatMatch.index >= 0) {
    // there is a splat
    if (namedMatch >= 0) {
      // negative value will indicate there is a splat match before any named matches
      rtn.splatMatch = splatMatch.index - namedMatch.index;
    } else {
      rtn.splatMatch = -1;
    }
  }
  rtn.paramNames = paramNames.map(function(name) { return name.substring(1); });

  return rtn;
  },

  /**
   * Given a route, and a URL fragment that it matches, return the array of
   * extracted parameters.
   */
  _extractParameters : function(route, fragment) {
    var params = route.exec(fragment).slice(1),
        namedParams = {};

    // do we have an additional query string?
    var match = params.length && params[params.length-1] && params[params.length-1].match(queryStringParam);
    if (match) {
      var queryString = match[1];
      var data = {};
      if (queryString) {
        var self = this;
        iterateQueryString(queryString, function(name, value) {
          self._setParamValue(name, value, data);
        });
      }
      params[params.length-1] = data;
      _.extend(namedParams, data);
    }

    // decode params
    var length = params.length;
    if (route.splatMatch && this.encodedSplatParts) {
      if (route.splatMatch < 0) {
        // splat param is first
        return params;
      } else {
        length = length - 1;
      }
    }

    for (var i=0; i<length; i++) {
      if (_.isString(params[i])) {
        params[i] = Backbone.Router.decodeParams ? decodeURIComponent(params[i]) : params[i];
        if (route.paramNames.length >= i-1) {
          namedParams[route.paramNames[i]] = params[i];
        }
      }
    }

    return Backbone.Router.namedParameters ? [namedParams] : params;
  },

  /**
   * Set the parameter value on the data hash
   */
  _setParamValue : function(key, value, data) {
    // use '.' to define hash separators
    var parts = key.split('.');
    var _data = data;
    for (var i=0; i<parts.length; i++) {
      var part = parts[i];
      if (i === parts.length-1) {
        // set the value
        _data[part] = this._decodeParamValue(value, _data[part]);
      } else {
        _data = _data[part] = _data[part] || {};
      }
    }
  },

  /**
   * Decode an individual parameter value (or list of values)
   * @param value the complete value
   * @param currentValue the currently known value (or list of values)
   */
  _decodeParamValue : function(value, currentValue) {
    // '|' will indicate an array.  Array with 1 value is a=|b - multiple values can be a=b|c
    var splitChar = Backbone.Router.arrayValueSplit;
    if (value.indexOf(splitChar) >= 0) {
      var values = value.split(splitChar);
      // clean it up
      for (var i=values.length-1; i>=0; i--) {
        if (!values[i]) {
          values.splice(i, 1);
        } else {
          values[i] = decodeURIComponent(values[i]);
        }
      }
      return values;
    }
    if (!currentValue) {
      return decodeURIComponent(value);
    } else if (_.isArray(currentValue)) {
      currentValue.push(decodeURIComponent(value));
      return currentValue;
    } else {
      return [currentValue, decodeURIComponent(value)];
    }
  },

  /**
   * Return the route fragment with queryParameters serialized to query parameter string
   */
  toFragment: function(route, queryParameters) {
    if (queryParameters) {
      if (!_.isString(queryParameters)) {
        queryParameters = this._toQueryString(queryParameters);
      }
      if(queryParameters) {
        route += '?' + queryParameters;
      }
    }
    return route;
  },

  /**
   * Serialize the val hash to query parameters and return it.  Use the namePrefix to prefix all param names (for recursion)
   */
  _toQueryString: function(val, namePrefix) {
    var splitChar = Backbone.Router.arrayValueSplit;
    function encodeSplit(val) { return val.replace(splitChar, encodeURIComponent(splitChar)); }

    if (!val) return '';
    namePrefix = namePrefix || '';
    var rtn = '';
    for (var name in val) {
      var _val = val[name];
      if (_.isString(_val) || _.isNumber(_val) || _.isBoolean(_val) || _.isDate(_val)) {
        // primitive type
        _val = this._toQueryParam(_val);
        if (_.isBoolean(_val) || _.isNumber(_val) || _val) {
          rtn += (rtn ? '&' : '') + this._toQueryParamName(name, namePrefix) + '=' + encodeSplit(encodeURIComponent(_val));
        }
      } else if (_.isArray(_val)) {
        // arrays use Backbone.Router.arrayValueSplit separator
        var str = '';
        for (var i in _val) {
          var param = this._toQueryParam(_val[i]);
          if (_.isBoolean(param) || param) {
            str += splitChar + encodeSplit(param);
          }
        }
        if (str) {
          rtn += (rtn ? '&' : '') + this._toQueryParamName(name, namePrefix) + '=' + str;
        }
      } else {
        // dig into hash
        var result = this._toQueryString(_val, this._toQueryParamName(name, namePrefix, true));
        if (result) {
          rtn += (rtn ? '&' : '') + result;
        }
      }
    }
    return rtn;
  },

  /**
   * return the actual parameter name
   * @param name the parameter name
   * @param namePrefix the prefix to the name
   * @param createPrefix true if we're creating a name prefix, false if we're creating the name
   */
  _toQueryParamName: function(name, prefix, isPrefix) {
    return (prefix + name + (isPrefix ? '.' : ''));
  },

  /**
   * Return the string representation of the param used for the query string
   */
  _toQueryParam: function (param) {
    if (_.isNull(param) || _.isUndefined(param)) {
      return null;
    }
    if (_.isDate(param)) {
      return param.getDate().getTime();
    }
    return param;
  }
});

function iterateQueryString(queryString, callback) {
  var keyValues = queryString.split('&');
  _.each(keyValues, function(keyValue) {
    var i = keyValue.indexOf('=');
    var arr = [keyValue.slice(0,i), keyValue.slice(i+1)];
    if (arr.length > 1) {
      callback(arr[0], arr[1]);
    }
  });
}

})(_, Backbone);
<commit_msg>get backbone and underscore with 'require' on the server<commit_after>(function(_, Backbone) {

// Require Underscore and Backbone if there's a `require` function.
// This makes `backbone.queryparam` work on the server or when using
// `browserify`.
if (typeof require !== 'undefined') {
  _ = require('underscore');
  Backbone = require('backbone');
}

var queryStringParam = /^\?(.*)/,
    optionalParam = /\((.*?)\)/g,
    namedParam    = /(\(\?)?:\w+/g,
    splatParam    = /\*\w+/g,
    escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g,
    queryStrip = /(\?.*)$/,
    fragmentStrip = /^([^\?]*)/,
    namesPattern = /[\:\*]([^\:\?\/]+)/g;
Backbone.Router.arrayValueSplit = '|';

var _getFragment = Backbone.History.prototype.getFragment;

_.extend(Backbone.History.prototype, {
  getFragment : function(fragment, forcePushState, excludeQueryString) {
    fragment = _getFragment.apply(this, arguments);
    if (excludeQueryString) {
      fragment = fragment.replace(queryStrip, '');
    }
    return fragment;
  },

  // this will not perform custom query param serialization specific to the router
  // but will return a map of key/value pairs (the value is a string or array)
  getQueryParameters : function(fragment, forcePushState) {
    fragment = _getFragment.apply(this, arguments);
    // if no query string exists, this will still be the original fragment
    var queryString = fragment.replace(fragmentStrip, '');
    var match = queryString.match(queryStringParam);
    if (match) {
      queryString = match[1];
      var rtn = {};
      iterateQueryString(queryString, function(name, value) {
        if (!rtn[name]) {
          rtn[name] = value;
        } else if (_.isString(rtn[name])) {
          rtn[name] = [rtn[name], value];
        } else {
          rtn[name].push(value);
        }
      });
      return rtn;
    } else {
      // no values
      return {};
    }
  }
});

_.extend(Backbone.Router.prototype, {
  initialize: function(options) {
    this.encodedSplatParts = options && options.encodedSplatParts;
  },

  getFragment : function(fragment, forcePushState, excludeQueryString) {
    fragment = _getFragment.apply(this, arguments);
    if (excludeQueryString) {
      fragment = fragment.replace(queryStrip, '');
    }
    return fragment;
  },

  _routeToRegExp: function(route) {
    var splatMatch = (splatParam.exec(route) || {index: -1}),
        namedMatch = (namedParam.exec(route) || {index: -1}),
        paramNames = route.match(namesPattern) || [];

    route = route.replace(escapeRegExp, '\\$&')
                 .replace(optionalParam, '(?:$1)?')
                 .replace(namedParam, function(match, optional){
                   return optional ? match : '([^\\/\\?]+)';
                 })
                 .replace(splatParam, '([^\?]*?)');
    route += '([\?]{1}.*)?';
    var rtn = new RegExp('^' + route + '$');

  // use the rtn value to hold some parameter data
  if (splatMatch.index >= 0) {
    // there is a splat
    if (namedMatch >= 0) {
      // negative value will indicate there is a splat match before any named matches
      rtn.splatMatch = splatMatch.index - namedMatch.index;
    } else {
      rtn.splatMatch = -1;
    }
  }
  rtn.paramNames = paramNames.map(function(name) { return name.substring(1); });

  return rtn;
  },

  /**
   * Given a route, and a URL fragment that it matches, return the array of
   * extracted parameters.
   */
  _extractParameters : function(route, fragment) {
    var params = route.exec(fragment).slice(1),
        namedParams = {};

    // do we have an additional query string?
    var match = params.length && params[params.length-1] && params[params.length-1].match(queryStringParam);
    if (match) {
      var queryString = match[1];
      var data = {};
      if (queryString) {
        var self = this;
        iterateQueryString(queryString, function(name, value) {
          self._setParamValue(name, value, data);
        });
      }
      params[params.length-1] = data;
      _.extend(namedParams, data);
    }

    // decode params
    var length = params.length;
    if (route.splatMatch && this.encodedSplatParts) {
      if (route.splatMatch < 0) {
        // splat param is first
        return params;
      } else {
        length = length - 1;
      }
    }

    for (var i=0; i<length; i++) {
      if (_.isString(params[i])) {
        params[i] = Backbone.Router.decodeParams ? decodeURIComponent(params[i]) : params[i];
        if (route.paramNames.length >= i-1) {
          namedParams[route.paramNames[i]] = params[i];
        }
      }
    }

    return Backbone.Router.namedParameters ? [namedParams] : params;
  },

  /**
   * Set the parameter value on the data hash
   */
  _setParamValue : function(key, value, data) {
    // use '.' to define hash separators
    var parts = key.split('.');
    var _data = data;
    for (var i=0; i<parts.length; i++) {
      var part = parts[i];
      if (i === parts.length-1) {
        // set the value
        _data[part] = this._decodeParamValue(value, _data[part]);
      } else {
        _data = _data[part] = _data[part] || {};
      }
    }
  },

  /**
   * Decode an individual parameter value (or list of values)
   * @param value the complete value
   * @param currentValue the currently known value (or list of values)
   */
  _decodeParamValue : function(value, currentValue) {
    // '|' will indicate an array.  Array with 1 value is a=|b - multiple values can be a=b|c
    var splitChar = Backbone.Router.arrayValueSplit;
    if (value.indexOf(splitChar) >= 0) {
      var values = value.split(splitChar);
      // clean it up
      for (var i=values.length-1; i>=0; i--) {
        if (!values[i]) {
          values.splice(i, 1);
        } else {
          values[i] = decodeURIComponent(values[i]);
        }
      }
      return values;
    }
    if (!currentValue) {
      return decodeURIComponent(value);
    } else if (_.isArray(currentValue)) {
      currentValue.push(decodeURIComponent(value));
      return currentValue;
    } else {
      return [currentValue, decodeURIComponent(value)];
    }
  },

  /**
   * Return the route fragment with queryParameters serialized to query parameter string
   */
  toFragment: function(route, queryParameters) {
    if (queryParameters) {
      if (!_.isString(queryParameters)) {
        queryParameters = this._toQueryString(queryParameters);
      }
      if(queryParameters) {
        route += '?' + queryParameters;
      }
    }
    return route;
  },

  /**
   * Serialize the val hash to query parameters and return it.  Use the namePrefix to prefix all param names (for recursion)
   */
  _toQueryString: function(val, namePrefix) {
    var splitChar = Backbone.Router.arrayValueSplit;
    function encodeSplit(val) { return val.replace(splitChar, encodeURIComponent(splitChar)); }

    if (!val) return '';
    namePrefix = namePrefix || '';
    var rtn = '';
    for (var name in val) {
      var _val = val[name];
      if (_.isString(_val) || _.isNumber(_val) || _.isBoolean(_val) || _.isDate(_val)) {
        // primitive type
        _val = this._toQueryParam(_val);
        if (_.isBoolean(_val) || _.isNumber(_val) || _val) {
          rtn += (rtn ? '&' : '') + this._toQueryParamName(name, namePrefix) + '=' + encodeSplit(encodeURIComponent(_val));
        }
      } else if (_.isArray(_val)) {
        // arrays use Backbone.Router.arrayValueSplit separator
        var str = '';
        for (var i in _val) {
          var param = this._toQueryParam(_val[i]);
          if (_.isBoolean(param) || param) {
            str += splitChar + encodeSplit(param);
          }
        }
        if (str) {
          rtn += (rtn ? '&' : '') + this._toQueryParamName(name, namePrefix) + '=' + str;
        }
      } else {
        // dig into hash
        var result = this._toQueryString(_val, this._toQueryParamName(name, namePrefix, true));
        if (result) {
          rtn += (rtn ? '&' : '') + result;
        }
      }
    }
    return rtn;
  },

  /**
   * return the actual parameter name
   * @param name the parameter name
   * @param namePrefix the prefix to the name
   * @param createPrefix true if we're creating a name prefix, false if we're creating the name
   */
  _toQueryParamName: function(name, prefix, isPrefix) {
    return (prefix + name + (isPrefix ? '.' : ''));
  },

  /**
   * Return the string representation of the param used for the query string
   */
  _toQueryParam: function (param) {
    if (_.isNull(param) || _.isUndefined(param)) {
      return null;
    }
    if (_.isDate(param)) {
      return param.getDate().getTime();
    }
    return param;
  }
});

function iterateQueryString(queryString, callback) {
  var keyValues = queryString.split('&');
  _.each(keyValues, function(keyValue) {
    var i = keyValue.indexOf('=');
    var arr = [keyValue.slice(0,i), keyValue.slice(i+1)];
    if (arr.length > 1) {
      callback(arr[0], arr[1]);
    }
  });
}

})(_, Backbone);
<|endoftext|>


<commit_before>(function (root, factory) {
   if (typeof exports === 'object' && root.require) {
     module.exports = factory(require("underscore"), require("backbone"));
   } else if (typeof define === "function" && define.amd) {
      // AMD. Register as an anonymous module.
      define(["underscore","backbone"], function(_, Backbone) {
        // Use global variables if the locals are undefined.
        return factory(_ || root._, Backbone || root.Backbone);
      });
   } else {
      // RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
      factory(_, Backbone);
   }
}(this, function(_, Backbone) {

var queryStringParam = /^\?(.*)/,
    optionalParam = /\((.*?)\)/g,
    namedParam    = /(\(\?)?:\w+/g,
    splatParam    = /\*\w+/g,
    escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g,
    queryStrip = /(\?.*)$/,
    fragmentStrip = /^([^\?]*)/,
    hasQueryString = /(\?)[\w-]+/i,
    namesPattern = /[\:\*]([^\:\?\/]+)/g,
    routeStripper = /^[#\/]|\s+$/g,
    trailingSlash = /\/$/;
Backbone.Router.arrayValueSplit = '|';

function _getFragment(fragment, forcePushState) {
  /*jshint eqnull:true */
  if (fragment == null) {
    if (this._hasPushState || !this._wantsHashChange || forcePushState) {
      fragment = this.location.pathname;
      var root = this.root.replace(trailingSlash, '');
      var search = this.location.search;
      if (!fragment.indexOf(root)) {
        fragment = fragment.substr(root.length);
      }
      if (search) {
        fragment += search;
      }
    } else {
      fragment = this.getHash();
    }
  }
  return fragment.replace(routeStripper, '');
}

_.extend(Backbone.History.prototype, {
  getFragment: function(fragment, forcePushState) {
    /*jshint eqnull:true */
    var excludeQueryString = (this._wantsHashChange && this._wantsPushState &&
      !this._hasPushState);
    var _fragment = _getFragment.apply(this, arguments);
    if(fragment == null && _fragment == null && !hasQueryString.test(_fragment)) {
      _fragment += this.location.search;
    } else if (excludeQueryString) {
      _fragment = _fragment.replace(queryStrip, '');
    }
    return _fragment;
  },

  // this will not perform custom query param serialization specific to the router
  // but will return a map of key/value pairs (the value is a string or array)
  getQueryParameters: function(fragment, forcePushState) {
    fragment = _getFragment.apply(this, arguments);
    // if no query string exists, this will still be the original fragment
    var queryString = fragment.replace(fragmentStrip, '');
    var match = queryString.match(queryStringParam);
    if (match) {
      queryString = match[1];
      var rtn = {};
      iterateQueryString(queryString, function(name, value) {
        value = parseParams(value);

        if (!rtn[name]) {
          rtn[name] = value;
        } else if (_.isString(rtn[name])) {
          rtn[name] = [rtn[name], value];
        } else {
          rtn[name].push(value);
        }
      });
      return rtn;
    } else {
      // no values
      return {};
    }
  }
});

_.extend(Backbone.Router.prototype, {
  initialize: function(options) {
    this.encodedSplatParts = options && options.encodedSplatParts;
  },

  _routeToRegExp: function(route) {
    var splatMatch = (splatParam.exec(route) || {index: -1}),
        namedMatch = (namedParam.exec(route) || {index: -1}),
        paramNames = route.match(namesPattern) || [];

    route = route.replace(escapeRegExp, '\\$&')
                 .replace(optionalParam, '(?:$1)?')
                 .replace(namedParam, function(match, optional){
                   return optional ? match : '([^\\/\\?]+)';
                 })
                 // `[^??]` is hacking around a regular expression bug under iOS4.
                 // If only `[^?]` is used then paths like signin/photos will fail
                 // while paths with `?` anywhere, like `signin/photos?`, will succeed.
                 .replace(splatParam, '([^??]*?)');
    route += '(\\?.*)?';
    var rtn = new RegExp('^' + route + '$');

    // use the rtn value to hold some parameter data
    if (splatMatch.index >= 0) {
      // there is a splat
      if (namedMatch >= 0) {
        // negative value will indicate there is a splat match before any named matches
        rtn.splatMatch = splatMatch.index - namedMatch.index;
      } else {
        rtn.splatMatch = -1;
      }
    }
    rtn.paramNames = _.map(paramNames, function(name) { return name.substring(1); });
    rtn.namedParameters = this.namedParameters;

    return rtn;
  },

  /**
   * Given a route, and a URL fragment that it matches, return the array of
   * extracted parameters.
   */
  _extractParameters: function(route, fragment) {
    var params = route.exec(fragment).slice(1),
        namedParams = {};
    if (params.length > 0 && _.isUndefined(params[params.length - 1])) {
      // remove potential invalid data from query params match
      params.splice(params.length - 1, 1);
    }

    // do we have an additional query string?
    var match = params.length && params[params.length-1] && params[params.length-1].match(queryStringParam);
    if (match) {
      var queryString = match[1];
      var data = {};
      if (queryString) {
        var self = this;
        iterateQueryString(queryString, function(name, value) {
          self._setParamValue(name, value, data);
        });
      }
      params[params.length-1] = data;
      _.extend(namedParams, data);
    }

    // decode params
    var length = params.length;
    if (route.splatMatch && this.encodedSplatParts) {
      if (route.splatMatch < 0) {
        // splat param is first
        return params;
      } else {
        length = length - 1;
      }
    }

    for (var i=0; i<length; i++) {
      if (_.isString(params[i])) {
        params[i] = parseParams(params[i]);
        if (route.paramNames && route.paramNames.length >= i-1) {
          namedParams[route.paramNames[i]] = params[i];
        }
      }
    }

    return (Backbone.Router.namedParameters || route.namedParameters) ? [namedParams] : params;
  },

  /**
   * Set the parameter value on the data hash
   */
  _setParamValue: function(key, value, data) {
    // use '.' to define hash separators
    key = key.replace('[]', '');
    key = key.replace('%5B%5D', '');
    var parts = key.split('.');
    var _data = data;
    for (var i=0; i<parts.length; i++) {
      var part = parts[i];
      if (i === parts.length-1) {
        // set the value
        _data[part] = this._decodeParamValue(value, _data[part]);
      } else {
        _data = _data[part] = _data[part] || {};
      }
    }
  },

  /**
   * Decode an individual parameter value (or list of values)
   * @param value the complete value
   * @param currentValue the currently known value (or list of values)
   */
  _decodeParamValue: function(value, currentValue) {
    // '|' will indicate an array.  Array with 1 value is a=|b - multiple values can be a=b|c
    var splitChar = Backbone.Router.arrayValueSplit;
    if (splitChar && value.indexOf(splitChar) >= 0) {
      var values = value.split(splitChar);
      // clean it up
      for (var i=values.length-1; i>=0; i--) {
        if (!values[i]) {
          values.splice(i, 1);
        } else {
          values[i] = parseParams(values[i]);
        }
      }
      return values;
    }

    value = parseParams(value);
    if (!currentValue) {
      return value;
    } else if (_.isArray(currentValue)) {
      currentValue.push(value);
      return currentValue;
    } else {
      return [currentValue, value];
    }
  },

  /**
   * Return the route fragment with queryParameters serialized to query parameter string
   */
  toFragment: function(route, queryParameters) {
    if (queryParameters) {
      if (!_.isString(queryParameters)) {
        queryParameters = toQueryString(queryParameters);
      }
      if(queryParameters) {
        route += '?' + queryParameters;
      }
    }
    return route;
  }
});

/**
 * Serialize the val hash to query parameters and return it.  Use the namePrefix to prefix all param names (for recursion)
 */
function toQueryString(val, namePrefix) {
  /*jshint eqnull:true */
  var splitChar = Backbone.Router.arrayValueSplit;
  function encodeSplit(val) { return String(val).replace(splitChar, encodeURIComponent(splitChar)); }

  if (!val) {
    return '';
  }

  namePrefix = namePrefix || '';
  var rtn = [];
  _.each(val, function(_val, name) {
    name = namePrefix + name;

    if (_.isString(_val) || _.isNumber(_val) || _.isBoolean(_val) || _.isDate(_val)) {
      // primitive type
      if (_val != null) {
        rtn.push(name + '=' + encodeSplit(encodeURIComponent(_val)));
      }
    } else if (_.isArray(_val)) {
      // arrays use Backbone.Router.arrayValueSplit separator
      var str = '';
      for (var i = 0; i < _val.length; i++) {
        var param = _val[i];
        if (param != null) {
          str += splitChar + encodeSplit(param);
        }
      }
      if (str) {
        rtn.push(name + '=' + str);
      }
    } else {
      // dig into hash
      var result = toQueryString(_val, name + '.');
      if (result) {
        rtn.push(result);
      }
    }
  });

  return rtn.join('&');
}

function parseParams(value) {
  // decodeURIComponent doesn't touch '+'
  try {
    return decodeURIComponent(value.replace(/\+/g, ' '));
  } catch (err) {
    // Failover to whatever was passed if we get junk data
    return value;
  }
}

function iterateQueryString(queryString, callback) {
  var keyValues = queryString.split('&');
  _.each(keyValues, function(keyValue) {
    var arr = keyValue.split('=');
    callback(arr.shift(), arr.join('='));
  });
}

}));
<commit_msg>Do not strip search component in getFragment<commit_after>(function (root, factory) {
   if (typeof exports === 'object' && root.require) {
     module.exports = factory(require("underscore"), require("backbone"));
   } else if (typeof define === "function" && define.amd) {
      // AMD. Register as an anonymous module.
      define(["underscore","backbone"], function(_, Backbone) {
        // Use global variables if the locals are undefined.
        return factory(_ || root._, Backbone || root.Backbone);
      });
   } else {
      // RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
      factory(_, Backbone);
   }
}(this, function(_, Backbone) {

var queryStringParam = /^\?(.*)/,
    optionalParam = /\((.*?)\)/g,
    namedParam    = /(\(\?)?:\w+/g,
    splatParam    = /\*\w+/g,
    escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g,
    fragmentStrip = /^([^\?]*)/,
    namesPattern = /[\:\*]([^\:\?\/]+)/g,
    routeStripper = /^[#\/]|\s+$/g,
    trailingSlash = /\/$/;
Backbone.Router.arrayValueSplit = '|';

_.extend(Backbone.History.prototype, {
  getFragment: function(fragment, forcePushState) {
    /*jshint eqnull:true */
    if (fragment == null) {
      if (this._hasPushState || !this._wantsHashChange || forcePushState) {
        fragment = this.location.pathname;
        var root = this.root.replace(trailingSlash, '');
        var search = this.location.search;
        if (!fragment.indexOf(root)) {
          fragment = fragment.substr(root.length);
        }
        if (search && this._hasPushState) {
          fragment += search;
        }
      } else {
        fragment = this.getHash();
      }
    }
    return fragment.replace(routeStripper, '');
  },

  // this will not perform custom query param serialization specific to the router
  // but will return a map of key/value pairs (the value is a string or array)
  getQueryParameters: function(fragment, forcePushState) {
    fragment = this.getFragment(fragment, forcePushState);
    // if no query string exists, this will still be the original fragment
    var queryString = fragment.replace(fragmentStrip, '');
    var match = queryString.match(queryStringParam);
    if (match) {
      queryString = match[1];
      var rtn = {};
      iterateQueryString(queryString, function(name, value) {
        value = parseParams(value);

        if (!rtn[name]) {
          rtn[name] = value;
        } else if (_.isString(rtn[name])) {
          rtn[name] = [rtn[name], value];
        } else {
          rtn[name].push(value);
        }
      });
      return rtn;
    } else {
      // no values
      return {};
    }
  }
});

_.extend(Backbone.Router.prototype, {
  initialize: function(options) {
    this.encodedSplatParts = options && options.encodedSplatParts;
  },

  _routeToRegExp: function(route) {
    var splatMatch = (splatParam.exec(route) || {index: -1}),
        namedMatch = (namedParam.exec(route) || {index: -1}),
        paramNames = route.match(namesPattern) || [];

    route = route.replace(escapeRegExp, '\\$&')
                 .replace(optionalParam, '(?:$1)?')
                 .replace(namedParam, function(match, optional){
                   return optional ? match : '([^\\/\\?]+)';
                 })
                 // `[^??]` is hacking around a regular expression bug under iOS4.
                 // If only `[^?]` is used then paths like signin/photos will fail
                 // while paths with `?` anywhere, like `signin/photos?`, will succeed.
                 .replace(splatParam, '([^??]*?)');
    route += '(\\?.*)?';
    var rtn = new RegExp('^' + route + '$');

    // use the rtn value to hold some parameter data
    if (splatMatch.index >= 0) {
      // there is a splat
      if (namedMatch >= 0) {
        // negative value will indicate there is a splat match before any named matches
        rtn.splatMatch = splatMatch.index - namedMatch.index;
      } else {
        rtn.splatMatch = -1;
      }
    }
    rtn.paramNames = _.map(paramNames, function(name) { return name.substring(1); });
    rtn.namedParameters = this.namedParameters;

    return rtn;
  },

  /**
   * Given a route, and a URL fragment that it matches, return the array of
   * extracted parameters.
   */
  _extractParameters: function(route, fragment) {
    var params = route.exec(fragment).slice(1),
        namedParams = {};
    if (params.length > 0 && _.isUndefined(params[params.length - 1])) {
      // remove potential invalid data from query params match
      params.splice(params.length - 1, 1);
    }

    // do we have an additional query string?
    var match = params.length && params[params.length-1] && params[params.length-1].match(queryStringParam);
    if (match) {
      var queryString = match[1];
      var data = {};
      if (queryString) {
        var self = this;
        iterateQueryString(queryString, function(name, value) {
          self._setParamValue(name, value, data);
        });
      }
      params[params.length-1] = data;
      _.extend(namedParams, data);
    }

    // decode params
    var length = params.length;
    if (route.splatMatch && this.encodedSplatParts) {
      if (route.splatMatch < 0) {
        // splat param is first
        return params;
      } else {
        length = length - 1;
      }
    }

    for (var i=0; i<length; i++) {
      if (_.isString(params[i])) {
        params[i] = parseParams(params[i]);
        if (route.paramNames && route.paramNames.length >= i-1) {
          namedParams[route.paramNames[i]] = params[i];
        }
      }
    }

    return (Backbone.Router.namedParameters || route.namedParameters) ? [namedParams] : params;
  },

  /**
   * Set the parameter value on the data hash
   */
  _setParamValue: function(key, value, data) {
    // use '.' to define hash separators
    key = key.replace('[]', '');
    key = key.replace('%5B%5D', '');
    var parts = key.split('.');
    var _data = data;
    for (var i=0; i<parts.length; i++) {
      var part = parts[i];
      if (i === parts.length-1) {
        // set the value
        _data[part] = this._decodeParamValue(value, _data[part]);
      } else {
        _data = _data[part] = _data[part] || {};
      }
    }
  },

  /**
   * Decode an individual parameter value (or list of values)
   * @param value the complete value
   * @param currentValue the currently known value (or list of values)
   */
  _decodeParamValue: function(value, currentValue) {
    // '|' will indicate an array.  Array with 1 value is a=|b - multiple values can be a=b|c
    var splitChar = Backbone.Router.arrayValueSplit;
    if (splitChar && value.indexOf(splitChar) >= 0) {
      var values = value.split(splitChar);
      // clean it up
      for (var i=values.length-1; i>=0; i--) {
        if (!values[i]) {
          values.splice(i, 1);
        } else {
          values[i] = parseParams(values[i]);
        }
      }
      return values;
    }

    value = parseParams(value);
    if (!currentValue) {
      return value;
    } else if (_.isArray(currentValue)) {
      currentValue.push(value);
      return currentValue;
    } else {
      return [currentValue, value];
    }
  },

  /**
   * Return the route fragment with queryParameters serialized to query parameter string
   */
  toFragment: function(route, queryParameters) {
    if (queryParameters) {
      if (!_.isString(queryParameters)) {
        queryParameters = toQueryString(queryParameters);
      }
      if(queryParameters) {
        route += '?' + queryParameters;
      }
    }
    return route;
  }
});

/**
 * Serialize the val hash to query parameters and return it.  Use the namePrefix to prefix all param names (for recursion)
 */
function toQueryString(val, namePrefix) {
  /*jshint eqnull:true */
  var splitChar = Backbone.Router.arrayValueSplit;
  function encodeSplit(val) { return String(val).replace(splitChar, encodeURIComponent(splitChar)); }

  if (!val) {
    return '';
  }

  namePrefix = namePrefix || '';
  var rtn = [];
  _.each(val, function(_val, name) {
    name = namePrefix + name;

    if (_.isString(_val) || _.isNumber(_val) || _.isBoolean(_val) || _.isDate(_val)) {
      // primitive type
      if (_val != null) {
        rtn.push(name + '=' + encodeSplit(encodeURIComponent(_val)));
      }
    } else if (_.isArray(_val)) {
      // arrays use Backbone.Router.arrayValueSplit separator
      var str = '';
      for (var i = 0; i < _val.length; i++) {
        var param = _val[i];
        if (param != null) {
          str += splitChar + encodeSplit(param);
        }
      }
      if (str) {
        rtn.push(name + '=' + str);
      }
    } else {
      // dig into hash
      var result = toQueryString(_val, name + '.');
      if (result) {
        rtn.push(result);
      }
    }
  });

  return rtn.join('&');
}

function parseParams(value) {
  // decodeURIComponent doesn't touch '+'
  try {
    return decodeURIComponent(value.replace(/\+/g, ' '));
  } catch (err) {
    // Failover to whatever was passed if we get junk data
    return value;
  }
}

function iterateQueryString(queryString, callback) {
  var keyValues = queryString.split('&');
  _.each(keyValues, function(keyValue) {
    var arr = keyValue.split('=');
    callback(arr.shift(), arr.join('='));
  });
}

}));
<|endoftext|>