Use querystring, stop manually building urls
authorTom MacWright <tom@macwright.org>
Mon, 10 Jun 2013 22:04:07 +0000 (15:04 -0700)
committerJohn Firebaugh <john.firebaugh@gmail.com>
Thu, 18 Jul 2013 17:37:45 +0000 (10:37 -0700)
app/assets/javascripts/application.js
app/assets/javascripts/browse.js
app/assets/javascripts/index.js
app/assets/javascripts/querystring.js [new file with mode: 0644]
app/views/site/_search.html.erb

index ee9b16ff1d5391f23cca4062a446d2b52bfa53f7..eb338aa93539e45b81dfedd4948f4d8f4df86812 100644 (file)
@@ -16,6 +16,9 @@
 //= require richtext
 //= require resize
 //= require geocoder
+//= require querystring
+
+var querystring = require('querystring');
 
 function zoomPrecision(zoom) {
     var decimals = Math.pow(10, Math.floor(zoom/3));
@@ -54,13 +57,15 @@ function updatelinks(loc, zoom, layers, bounds, object) {
   $("#shortlinkanchor").each(setShortlink);
 
   function setGeolink(index, link) {
-    var args = getArgs(link.href);
+    var base = link.href.split('?')[0];
+    var qs = link.href.split('?')[1];
+    var args = querystring.parse(qs);
 
     if ($(link).hasClass("llz")) {
       $.extend(args, {
-          lat: lat,
-          lon: lon,
-          zoom: zoom
+          lat: '' + lat,
+          lon: '' + lon,
+          zoom: '' + zoom
       });
     } else if (minlon && $(link).hasClass("bbox")) {
       $.extend(args, {
@@ -68,13 +73,8 @@ function updatelinks(loc, zoom, layers, bounds, object) {
       });
     }
 
-    if (layers && $(link).hasClass("layers")) {
-      args.layers = layers;
-    }
-
-    if (object && $(link).hasClass("object")) {
-      args[object.type] = object.id;
-    }
+    if (layers && $(link).hasClass("layers")) args.layers = layers;
+    if (object && $(link).hasClass("object")) args[object.type] = object.id;
 
     var minzoom = $(link).data("minzoom");
     if (minzoom) {
@@ -92,14 +92,16 @@ function updatelinks(loc, zoom, layers, bounds, object) {
         }
     }
 
-    link.href = setArgs(link.href, args);
+    link.href = base + '?' + querystring.stringify(args);
   }
 
 
   function setShortlink() {
-    var args = getArgs(this.href);
-    var code = makeShortCode(lat, lon, zoom);
-    var prefix = shortlinkPrefix();
+    var base = link.href.split('?')[0],
+        qs = link.href.split('?')[1],
+        args = querystring.parse(qs),
+        code = makeShortCode(lat, lon, zoom),
+        prefix = shortlinkPrefix();
 
     // Add ?{node,way,relation}=id to the arguments
     if (object) {
@@ -118,7 +120,7 @@ function updatelinks(loc, zoom, layers, bounds, object) {
     // which encodes lat/lon/zoom. If new URL parameters are added to
     // the main slippy map this needs to be changed.
     if (args.layers || object) {
-      this.href = setArgs(prefix + "/go/" + code, args);
+      this.href = prefix + "/go/" + code + '?' + querystring.stringify(args);
     } else {
       this.href = prefix + "/go/" + code;
     }
@@ -140,46 +142,6 @@ function shortlinkPrefix() {
   }
 }
 
-/*
- * Called to get the arguments from a URL as a hash.
- */
-function getArgs(url) {
-  var args = {};
-  var querystart = url.indexOf("?");
-
-  if (querystart >= 0) {
-     var querystring = url.substring(querystart + 1);
-     var queryitems = querystring.split("&");
-
-     for (var i = 0; i < queryitems.length; i++) {
-        if (match = queryitems[i].match(/^(.*)=(.*)$/)) {
-           args[unescape(match[1])] = unescape(match[2]);
-        } else {
-           args[unescape(queryitems[i])] = null;
-        }
-     }
-  }
-
-  return args;
-}
-
-/*
- * Called to set the arguments on a URL from the given hash.
- */
-function setArgs(url, args) {
-   var queryitems = [];
-
-   for (arg in args) {
-      if (args[arg] == null) {
-         queryitems.push(escape(arg));
-      } else {
-         queryitems.push(escape(arg) + "=" + escape(args[arg]));
-      }
-   }
-
-   return url.replace(/\?.*$/, "") + "?" + queryitems.join("&");
-}
-
 /*
  * Called to interlace the bits in x and y, making a Morton code.
  */
index e21cbd67206a068ce803a0c7bfe1923293b7ad9d..4edee5952664d3fd1ab6adef034275e2c568a5c2 100644 (file)
@@ -9,9 +9,21 @@ $(document).ready(function () {
     $("#linkloader").load(function () { loaded = true; });
 
     if (select) {
-      $("#linkloader").attr("src", "http://127.0.0.1:8111/load_and_zoom?left=" + left + "&top=" + top + "&right=" + right + "&bottom=" + bottom + "&select=" + select);
+      $("#linkloader").attr("src", "http://127.0.0.1:8111/load_and_zoom?" +
+        querystring.stringify({
+            left: left,
+            top: top,
+            right: right,
+            bottom: bottom,
+            select: select
+        }));
     } else {
-      $("#linkloader").attr("src", "http://127.0.0.1:8111/load_and_zoom?left=" + left + "&top=" + top + "&right=" + right + "&bottom=" + bottom);
+      $("#linkloader").attr("src", "http://127.0.0.1:8111/load_and_zoom?" + querystring.stringify({
+            left: left,
+            top: top,
+            right: right,
+            bottom: bottom
+        }));
     }
 
     setTimeout(function () {
index f95510f6ce9b96d251cc24090add22e5cfe23438..5d6947d356ad31c825ba231a9c55631a7b03d187 100644 (file)
@@ -65,7 +65,7 @@ $(document).ready(function () {
 
   L.control.share({
       getUrl: function(map) {
-          return setArgs('http://osm.org/', {
+          return 'http://osm.org/' + querystring.stringify({
               lon: map.getCenter().lng,
               lat: map.getCenter().lat
           });
@@ -159,10 +159,13 @@ $(document).ready(function () {
     var loaded = false;
 
     $("#linkloader").load(function () { loaded = true; });
-    $("#linkloader").attr("src", "http://127.0.0.1:8111/load_and_zoom?left=" + extent.getWest()
-                                                                   + "&bottom=" + extent.getSouth()
-                                                                   + "&right=" + extent.getEast()
-                                                                   + "&top=" + extent.getNorth());
+    $("#linkloader").attr("src", "http://127.0.0.1:8111/load_and_zoom?" +
+         querystring.stringify({
+            left: extent.getWest(),
+            bottom: extent.getSouth(),
+            right: extent.getEast(),
+            top: extent.getNorth()
+         }));
 
     setTimeout(function () {
       if (!loaded) alert(I18n.t('site.index.remote_failed'));
diff --git a/app/assets/javascripts/querystring.js b/app/assets/javascripts/querystring.js
new file mode 100644 (file)
index 0000000..16aa63c
--- /dev/null
@@ -0,0 +1,256 @@
+require=(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({"querystring":[function(require,module,exports){
+module.exports=require('SIBfUI');
+},{}],"SIBfUI":[function(require,module,exports){
+var isArray = typeof Array.isArray === 'function'
+    ? Array.isArray
+    : function (xs) {
+        return Object.prototype.toString.call(xs) === '[object Array]'
+    };
+
+var objectKeys = Object.keys || function objectKeys(object) {
+    if (object !== Object(object)) throw new TypeError('Invalid object');
+    var keys = [];
+    for (var key in object) if (object.hasOwnProperty(key)) keys[keys.length] = key;
+    return keys;
+}
+
+
+/*!
+ * querystring
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+/**
+ * Library version.
+ */
+
+exports.version = '0.3.1';
+
+/**
+ * Object#toString() ref for stringify().
+ */
+
+var toString = Object.prototype.toString;
+
+/**
+ * Cache non-integer test regexp.
+ */
+
+var notint = /[^0-9]/;
+
+/**
+ * Parse the given query `str`, returning an object.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api public
+ */
+
+exports.parse = function(str){
+  if (null == str || '' == str) return {};
+
+  function promote(parent, key) {
+    if (parent[key].length == 0) return parent[key] = {};
+    var t = {};
+    for (var i in parent[key]) t[i] = parent[key][i];
+    parent[key] = t;
+    return t;
+  }
+
+  return String(str)
+    .split('&')
+    .reduce(function(ret, pair){
+      try{ 
+        pair = decodeURIComponent(pair.replace(/\+/g, ' '));
+      } catch(e) {
+        // ignore
+      }
+
+      var eql = pair.indexOf('=')
+        , brace = lastBraceInKey(pair)
+        , key = pair.substr(0, brace || eql)
+        , val = pair.substr(brace || eql, pair.length)
+        , val = val.substr(val.indexOf('=') + 1, val.length)
+        , parent = ret;
+
+      // ?foo
+      if ('' == key) key = pair, val = '';
+
+      // nested
+      if (~key.indexOf(']')) {
+        var parts = key.split('[')
+          , len = parts.length
+          , last = len - 1;
+
+        function parse(parts, parent, key) {
+          var part = parts.shift();
+
+          // end
+          if (!part) {
+            if (isArray(parent[key])) {
+              parent[key].push(val);
+            } else if ('object' == typeof parent[key]) {
+              parent[key] = val;
+            } else if ('undefined' == typeof parent[key]) {
+              parent[key] = val;
+            } else {
+              parent[key] = [parent[key], val];
+            }
+          // array
+          } else {
+            obj = parent[key] = parent[key] || [];
+            if (']' == part) {
+              if (isArray(obj)) {
+                if ('' != val) obj.push(val);
+              } else if ('object' == typeof obj) {
+                obj[objectKeys(obj).length] = val;
+              } else {
+                obj = parent[key] = [parent[key], val];
+              }
+            // prop
+            } else if (~part.indexOf(']')) {
+              part = part.substr(0, part.length - 1);
+              if(notint.test(part) && isArray(obj)) obj = promote(parent, key);
+              parse(parts, obj, part);
+            // key
+            } else {
+              if(notint.test(part) && isArray(obj)) obj = promote(parent, key);
+              parse(parts, obj, part);
+            }
+          }
+        }
+
+        parse(parts, parent, 'base');
+      // optimize
+      } else {
+        if (notint.test(key) && isArray(parent.base)) {
+          var t = {};
+          for(var k in parent.base) t[k] = parent.base[k];
+          parent.base = t;
+        }
+        set(parent.base, key, val);
+      }
+
+      return ret;
+    }, {base: {}}).base;
+};
+
+/**
+ * Turn the given `obj` into a query string
+ *
+ * @param {Object} obj
+ * @return {String}
+ * @api public
+ */
+
+var stringify = exports.stringify = function(obj, prefix) {
+  if (isArray(obj)) {
+    return stringifyArray(obj, prefix);
+  } else if ('[object Object]' == toString.call(obj)) {
+    return stringifyObject(obj, prefix);
+  } else if ('string' == typeof obj) {
+    return stringifyString(obj, prefix);
+  } else {
+    return prefix;
+  }
+};
+
+/**
+ * Stringify the given `str`.
+ *
+ * @param {String} str
+ * @param {String} prefix
+ * @return {String}
+ * @api private
+ */
+
+function stringifyString(str, prefix) {
+  if (!prefix) throw new TypeError('stringify expects an object');
+  return prefix + '=' + encodeURIComponent(str);
+}
+
+/**
+ * Stringify the given `arr`.
+ *
+ * @param {Array} arr
+ * @param {String} prefix
+ * @return {String}
+ * @api private
+ */
+
+function stringifyArray(arr, prefix) {
+  var ret = [];
+  if (!prefix) throw new TypeError('stringify expects an object');
+  for (var i = 0; i < arr.length; i++) {
+    ret.push(stringify(arr[i], prefix + '[]'));
+  }
+  return ret.join('&');
+}
+
+/**
+ * Stringify the given `obj`.
+ *
+ * @param {Object} obj
+ * @param {String} prefix
+ * @return {String}
+ * @api private
+ */
+
+function stringifyObject(obj, prefix) {
+  var ret = []
+    , keys = objectKeys(obj)
+    , key;
+  for (var i = 0, len = keys.length; i < len; ++i) {
+    key = keys[i];
+    ret.push(stringify(obj[key], prefix
+      ? prefix + '[' + encodeURIComponent(key) + ']'
+      : encodeURIComponent(key)));
+  }
+  return ret.join('&');
+}
+
+/**
+ * Set `obj`'s `key` to `val` respecting
+ * the weird and wonderful syntax of a qs,
+ * where "foo=bar&foo=baz" becomes an array.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {String} val
+ * @api private
+ */
+
+function set(obj, key, val) {
+  var v = obj[key];
+  if (undefined === v) {
+    obj[key] = val;
+  } else if (isArray(v)) {
+    v.push(val);
+  } else {
+    obj[key] = [v, val];
+  }
+}
+
+/**
+ * Locate last brace in `str` within the key.
+ *
+ * @param {String} str
+ * @return {Number}
+ * @api private
+ */
+
+function lastBraceInKey(str) {
+  var len = str.length
+    , brace
+    , c;
+  for (var i = 0; i < len; ++i) {
+    c = str[i];
+    if (']' == c) brace = false;
+    if ('[' == c) brace = true;
+    if ('=' == c && !brace) return i;
+  }
+}
+
+},{}]},{},[])
+;
\ No newline at end of file
index c074a49a30804082ff6c1d5fbe8e07abf3eb423d..52adf66e1817e31df290976d10bb5f4883da5d21 100644 (file)
@@ -1,7 +1,7 @@
 <script type="text/javascript">
 <!--
   function describeLocation() {
-    var args = getArgs($("#viewanchor").attr("href"));
+    var args = querystring.parse($("#viewanchor").attr("href").split('?')[1]);
 
     $("#sidebar_title").html("<%= t 'site.sidebar.search_results' %>");
     $("#sidebar_content").load("<%= url_for :controller => :geocoder, :action => :description %>", {