// Underscore-contrib (underscore.util.strings.js 0.3.0) // (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors // Underscore-contrib may be freely distributed under the MIT license. (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `require` it on the server. if (typeof exports === 'object') { _ = module.exports = require('underscore'); } // Helpers // ------- // No reason to create regex more than once var plusRegex = /\+/g; var bracketRegex = /(?:([^\[]+))|(?:\[(.*?)\])/g; var urlDecode = function(s) { return decodeURIComponent(s.replace(plusRegex, '%20')); }; var urlEncode = function(s) { return encodeURIComponent(s); }; var buildParams = function(prefix, val, top) { if (_.isUndefined(top)) top = true; if (_.isArray(val)) { return _.compact(_.map(val, function(value, key) { return buildParams(top ? key : prefix + '[]', value, false); })).join('&'); } else if (_.isObject(val)) { return _.compact(_.map(val, function(value, key) { return buildParams(top ? key : prefix + '[' + key + ']', value, false); })).join('&'); } else { return urlEncode(prefix) + '=' + urlEncode(val); } }; // Mixing in the string utils // ---------------------------- _.mixin({ // Explodes a string into an array of chars explode: function(s) { return s.split(''); }, // Parses a query string into a hash fromQuery: function(str) { var parameters = str.split('&'), obj = {}, parameter, key, match, lastKey, subKey, depth; // Iterate over key/value pairs _.each(parameters, function(parameter) { parameter = parameter.split('='); key = urlDecode(parameter[0]); lastKey = key; depth = obj; // Reset so we don't have issues when matching the same string bracketRegex.lastIndex = 0; // Attempt to extract nested values while ((match = bracketRegex.exec(key)) !== null) { if (!_.isUndefined(match[1])) { // If we're at the top nested level, no new object needed subKey = match[1]; } else { // If we're at a lower nested level, we need to step down, and make // sure that there is an object to place the value into subKey = match[2]; depth[lastKey] = depth[lastKey] || (subKey ? {} : []); depth = depth[lastKey]; } // Save the correct key as a hash or an array lastKey = subKey || _.size(depth); } // Assign value to nested object depth[lastKey] = urlDecode(parameter[1]); }); return obj; }, // Implodes and array of chars into a string implode: function(a) { return a.join(''); }, // Converts a string to camel case camelCase : function( string ){ return string.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); }, // Converts camel case to dashed (opposite of _.camelCase) toDash : function( string ){ string = string.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();}); // remove first dash return ( string.charAt( 0 ) == '-' ) ? string.substr(1) : string; }, // Creates a query string from a hash toQuery: function(obj) { return buildParams('', obj); }, // Reports whether a string contains a search string. strContains: function(str, search) { if (typeof str != 'string') throw new TypeError; return (str.indexOf(search) != -1); } }); }).call(this);