1 /*
  2  * Geddy JavaScript Web development framework
  3  * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
  4  *
  5  * Licensed under the Apache License, Version 2.0 (the "License");
  6  * you may not use this file except in compliance with the License.
  7  * You may obtain a copy of the License at
  8  *
  9  *         http://www.apache.org/licenses/LICENSE-2.0
 10  *
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an "AS IS" BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  t
 17 */
 18 var uri = new (function () {
 19   this.getFileExtension = function (path) {
 20     var match;
 21     if (path) {
 22       match = /.+\.(\w{2,4}$)/.exec(path);
 23     }
 24     return (match && match[1]) || '';
 25   };
 26 
 27   /**
 28    * Convert a JS Object to querystring (key=val&key=val).
 29    * Value in arrays will be added as multiple parameters
 30    * @param obj -- an Object containing only scalars and arrays
 31    * @param o -- JS object of options for how to format
 32    * the return string. Supported options:
 33    *   consolidate: (Boolean) take values from elements that
 34    *      can return multiple values (multi-select, checkbox groups)
 35    *      and collapse into a single, comman-delimited value.
 36    *      Defaults to false.
 37    *      (e.g., thisVar=asdf,qwer,zxcv)
 38    *   includeEmpty: (Boolean) include keys in the string for
 39    *      all elements, even if they have no value set (e.g.,
 40    *      even if elemB has no value: elemA=foo&elemB=&elemC=bar).
 41    *      Defaults to false. Note that some false-y values are always
 42    *      valid even without this option, [0, '']. This option extends
 43    *      coverage to [null, undefined, NaN]
 44    *   snakeize: (Boolean) change param names from
 45    *      camelCase to snake_case. Defaults to false.
 46    *   escapeVals: (Boolean) escape the values for XML entities.
 47    *      Defaults to false.
 48    * @returns A querystring containing the values in the
 49    * Object
 50    */
 51   this.paramify = function (obj, o) {
 52     var opts = o || {},
 53         str = '',
 54         key,
 55         val,
 56         isValid,
 57         itemArray,
 58         arr = [],
 59         arrVal;
 60 
 61     for (var p in obj) {
 62       if (Object.prototype.hasOwnProperty.call(obj, p)) {
 63         val = obj[p];
 64 
 65         // This keeps valid falsy values like false and 0
 66         // It's duplicated in the array block below. Could
 67         // put it in a function but don't want the overhead
 68         isValid = !( val === null || val === undefined ||
 69                     (typeof val === 'number' && isNaN(val)) );
 70 
 71         key = opts.snakeize ? yam.snakeize(p) : p;
 72         if (isValid) {
 73           // Multiple vals -- array
 74           if (this.isArray(val) && val.length) {
 75             itemArray = [];
 76             for (var i = 0, ii = val.length; i < ii; i++) {
 77               arrVal = val[i];
 78               // This keeps valid falsy values like false and 0
 79               isValid = !( arrVal === null || arrVal === undefined ||
 80                            (typeof arrVal === 'number' && isNaN(arrVal)) );
 81 
 82               itemArray[i] = isValid ? encodeURIComponent(arrVal) : '';
 83               if (opts.escapeVals) {
 84                 itemArray[i] = yam.escapeXML(itemArray[i]);
 85               }
 86             }
 87             // Consolidation mode -- single value joined on comma
 88             if (opts.consolidate) {
 89               arr.push(key + '=' + itemArray.join(','));
 90             }
 91             // Normal mode -- multiple, same-named params with each val
 92             else {
 93               // {foo: [1, 2, 3]} => 'foo=1&foo=2&foo=3'
 94               // Add into results array, as this just ends up getting
 95               // joined on ampersand at the end anyhow
 96               arr.push(key + '=' + itemArray.join('&' + key + '='));
 97             }
 98           }
 99           // Single val -- string
100           else {
101             if (opts.escapeVals) {
102               val = yam.escapeXML(val);
103             }
104             arr.push(key + '=' + encodeURIComponent(val));
105           }
106           str += '&';
107         }
108         else {
109           if (opts.includeEmpty) { arr.push(key + '='); }
110         }
111       }
112     }
113     return arr.join('&');
114   };
115 
116   /**
117    * Convert the values in a query string (key=val&key=val) to
118    * an Object
119    * @param str -- A querystring
120    * @param o -- JS object of options, currently:
121    *   consolidate: (Boolean) convert mutliple instances of
122    *      the same key into an array of values instead of
123    *      overwriting. Defaults to true.
124    * @returns JavaScript key/val object with the values from
125    * the querystring
126    */
127   this.objectify = function (str, o) {
128     var opts = o || {};
129     var d = {};
130     var consolidate = typeof opts.consolidate == 'undefined' ?
131         true : opts.consolidate;
132     if (str) {
133       var arr = str.split('&');
134       for (var i = 0; i < arr.length; i++) {
135         var pair = arr[i].split('=');
136         var name = pair[0];
137         var val = decodeURIComponent(pair[1] || '');
138         // "We've already got one!" -- arrayize if the flag
139         // is set
140         if (typeof d[name] != 'undefined' && consolidate) {
141           if (typeof d[name] == 'string') {
142             d[name] = [d[name]];
143           }
144           d[name].push(val);
145         }
146         // Otherwise just set the value
147         else {
148           d[name] = val;
149 150         }
151       }
152     }
153     return d;
154   };
155 
156 })();
157 
158 exports.uri = uri;
159 
160