diff --git a/bower.json b/bower.json index 73183f2..19515a9 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "aurelia-path", - "version": "1.0.0-beta.1.1.0", + "version": "1.0.0-beta.1.1.1", "description": "Utilities for path manipulation.", "keywords": [ "aurelia", diff --git a/dist/amd/aurelia-path.d.ts b/dist/amd/aurelia-path.d.ts index 2a0dce7..f1eb9a8 100644 --- a/dist/amd/aurelia-path.d.ts +++ b/dist/amd/aurelia-path.d.ts @@ -29,7 +29,7 @@ declare module 'aurelia-path' { /** * Parse a query string. * - * @param The query string to parse. + * @param queryString The query string to parse. * @returns Object with keys and values mapped from the query string. */ export function parseQueryString(queryString: string): Object; diff --git a/dist/amd/aurelia-path.js b/dist/amd/aurelia-path.js index 3588af6..390e951 100644 --- a/dist/amd/aurelia-path.js +++ b/dist/amd/aurelia-path.js @@ -90,29 +90,37 @@ define(['exports'], function (exports) { return scheme + urlPrefix + url3.join('/') + trailingSlash; } + var encode = encodeURIComponent; + var encodeKey = function encodeKey(k) { + return encode(k).replace('%24', '$'); + }; + + function buildParam(key, value) { + var result = []; + if (value === null || value === undefined) { + return result; + } + if (Array.isArray(value)) { + for (var i = 0, l = value.length; i < l; i++) { + var arrayKey = key + '[' + (typeof value[i] === 'object' && value[i] !== null ? i : '') + ']'; + result = result.concat(buildParam(arrayKey, value[i])); + } + } else if (typeof value === 'object') { + for (var propertyName in value) { + result = result.concat(buildParam(key + '[' + propertyName + ']', value[propertyName])); + } + } else { + result.push(encodeKey(key) + '=' + encode(value)); + } + return result; + } + function buildQueryString(params) { var pairs = []; var keys = Object.keys(params || {}).sort(); - var encode = encodeURIComponent; - var encodeKey = function encodeKey(k) { - return encode(k).replace('%24', '$'); - }; - for (var i = 0, len = keys.length; i < len; i++) { var key = keys[i]; - var value = params[key]; - if (value === null || value === undefined) { - continue; - } - - if (Array.isArray(value)) { - var arrayKey = encodeKey(key) + '[]'; - for (var j = 0, l = value.length; j < l; j++) { - pairs.push(arrayKey + '=' + encode(value[j])); - } - } else { - pairs.push(encodeKey(key) + '=' + encode(value)); - } + pairs = pairs.concat(buildParam(key, params[key])); } if (pairs.length === 0) { @@ -122,6 +130,31 @@ define(['exports'], function (exports) { return pairs.join('&'); } + function processScalarParam(existedParam, value, isPrimitive) { + if (Array.isArray(existedParam)) { + existedParam.push(value); + return existedParam; + } + if (existedParam !== undefined) { + return isPrimitive ? value : [existedParam, value]; + } + + return value; + } + + function parseComplexParam(queryParams, keys, value) { + var currentParams = queryParams; + var keysLastIndex = keys.length - 1; + for (var j = 0; j <= keysLastIndex; j++) { + var key = keys[j] === '' ? currentParams.length : keys[j]; + if (j < keysLastIndex) { + currentParams = currentParams[key] = currentParams[key] || (keys[j + 1] ? {} : []); + } else { + currentParams = currentParams[key] = value; + } + } + } + function parseQueryString(queryString) { var queryParams = {}; if (!queryString || typeof queryString !== 'string') { @@ -133,37 +166,38 @@ define(['exports'], function (exports) { query = query.substr(1); } - var pairs = query.split('&'); + var pairs = query.replace(/\+/g, ' ').split('&'); for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split('='); var key = decodeURIComponent(pair[0]); - var keyLength = key.length; - var isArray = false; - var value = undefined; - + var isPrimitive = false; if (!key) { continue; - } else if (pair.length === 1) { - value = true; - } else { - if (keyLength > 2 && key.slice(keyLength - 2) === '[]') { - isArray = true; - key = key.slice(0, keyLength - 2); - if (!queryParams[key]) { - queryParams[key] = []; - } - } + } - value = pair[1] ? decodeURIComponent(pair[1]) : ''; + var keys = key.split(']['); + var keysLastIndex = keys.length - 1; + + if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLastIndex])) { + keys[keysLastIndex] = keys[keysLastIndex].replace(/\]$/, ''); + keys = keys.shift().split('[').concat(keys); + keysLastIndex = keys.length - 1; + } else { + isPrimitive = true; + keysLastIndex = 0; } - if (isArray) { - queryParams[key].push(value); + if (pair.length === 2) { + var value = pair[1] ? decodeURIComponent(pair[1]) : ''; + if (keysLastIndex) { + parseComplexParam(queryParams, keys, value); + } else { + queryParams[key] = processScalarParam(queryParams[key], value, isPrimitive); + } } else { - queryParams[key] = value; + queryParams[key] = true; } } - return queryParams; } }); \ No newline at end of file diff --git a/dist/aurelia-path.d.ts b/dist/aurelia-path.d.ts index 2a0dce7..f1eb9a8 100644 --- a/dist/aurelia-path.d.ts +++ b/dist/aurelia-path.d.ts @@ -29,7 +29,7 @@ declare module 'aurelia-path' { /** * Parse a query string. * - * @param The query string to parse. + * @param queryString The query string to parse. * @returns Object with keys and values mapped from the query string. */ export function parseQueryString(queryString: string): Object; diff --git a/dist/aurelia-path.js b/dist/aurelia-path.js index d354da9..936e505 100644 --- a/dist/aurelia-path.js +++ b/dist/aurelia-path.js @@ -17,8 +17,7 @@ function trimDots(ary: string[]): void { i -= 2; } } - } -} + }} /** * Calcualtes a path relative to a file. @@ -106,6 +105,34 @@ export function join(path1: string, path2: string): string { return scheme + urlPrefix + url3.join('/') + trailingSlash; } +let encode = encodeURIComponent; +let encodeKey = k => encode(k).replace('%24', '$'); +/** +* Recursively builds part of query string for parameter. +* +* @param key Parameter name for query string. +* @param value Parameter value to deserialize. +* @return Array with serialized parameter(s) +*/ +function buildParam(key: string, value: any): Array { + let result = []; + if (value === null || value === undefined) { + return result; + } + if (Array.isArray(value)) { + for (let i = 0, l = value.length; i < l; i++) { + let arrayKey = key + '[' + (typeof value[i] === 'object' && value[i] !== null ? i : '') + ']'; + result = result.concat(buildParam(arrayKey, value[i])); + } + } else if (typeof (value) === 'object') { + for (let propertyName in value) { + result = result.concat(buildParam(key + '[' + propertyName + ']', value[propertyName])); + } + } else { + result.push(`${encodeKey(key) }=${encode(value) }`); + } + return result; +} /** * Generate a query string from an object. @@ -116,24 +143,9 @@ export function join(path1: string, path2: string): string { export function buildQueryString(params: Object): string { let pairs = []; let keys = Object.keys(params || {}).sort(); - let encode = encodeURIComponent; - let encodeKey = k => encode(k).replace('%24', '$'); - for (let i = 0, len = keys.length; i < len; i++) { let key = keys[i]; - let value = params[key]; - if (value === null || value === undefined) { - continue; - } - - if (Array.isArray(value)) { - let arrayKey = `${encodeKey(key)}[]`; - for (let j = 0, l = value.length; j < l; j++) { - pairs.push(`${arrayKey}=${encode(value[j])}`); - } - } else { - pairs.push(`${encodeKey(key)}=${encode(value)}`); - } + pairs = pairs.concat(buildParam(key, params[key])); } if (pairs.length === 0) { @@ -143,10 +155,55 @@ export function buildQueryString(params: Object): string { return pairs.join('&'); } +/** +* Process parameter that was recognized as scalar param (primitive value or shallow array). +* +* @param existedParam Object with previously parsed values for specified key. +* @param value Parameter value to append. +* @param isPrimitive If true and parameter value already specified - method overwrites it. If false - transofrm parameter to array and append new value to it. +* @returns Initial primitive value or transformed existedParam if parameter was recognized as an array. +*/ +function processScalarParam(existedParam: Object, value: Object, isPrimitive: boolean): Object { + if (Array.isArray(existedParam)) { + // value is already an array, so push on the next value. + existedParam.push(value); + return existedParam; + } + if (existedParam !== undefined) { + // value isn't an array, but since a second value has been specified, + // convert value into an array. + return isPrimitive ? value : [existedParam, value]; + } + // value is a scalar. + return value; +} +/** +* Sequentially process parameter that was recognized as complex value (object or array). +* For each keys part, if the current level is undefined create an +* object or array based on the type of the next keys part. +* +* @param queryParams root-level result object. +* @param keys Collection of keys related to this parameter. +* @param value Parameter value to append. +*/ +function parseComplexParam(queryParams: Object, keys: Object, value: any): void { + let currentParams = queryParams; + let keysLastIndex = keys.length - 1; + for (let j = 0; j <= keysLastIndex; j++) { + let key = keys[j] === '' ? currentParams.length : keys[j]; + if (j < keysLastIndex) { + currentParams = currentParams[key] = currentParams[key] || (keys[j + 1] ? {} : []); + } else { + currentParams = currentParams[key] = value; + } + } +} + + /** * Parse a query string. * -* @param The query string to parse. +* @param queryString The query string to parse. * @returns Object with keys and values mapped from the query string. */ export function parseQueryString(queryString: string): Object { @@ -160,37 +217,40 @@ export function parseQueryString(queryString: string): Object { query = query.substr(1); } - let pairs = query.split('&'); + let pairs = query.replace(/\+/g, ' ').split('&'); for (let i = 0; i < pairs.length; i++) { let pair = pairs[i].split('='); let key = decodeURIComponent(pair[0]); - let keyLength = key.length; - let isArray = false; - let value; - + let isPrimitive = false; if (!key) { continue; - } else if (pair.length === 1) { - value = true; - } else { - //Handle arrays - if (keyLength > 2 && key.slice(keyLength - 2) === '[]') { - isArray = true; - key = key.slice(0, keyLength - 2); - if (!queryParams[key]) { - queryParams[key] = []; - } - } + } + //split object key into its parts + let keys = key.split(']['); + let keysLastIndex = keys.length - 1; - value = pair[1] ? decodeURIComponent(pair[1]) : ''; + // If the first keys part contains [ and the last ends with ], then [] + // are correctly balanced, split key to parts + //Else it's basic key + if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLastIndex])) { + keys[keysLastIndex] = keys[keysLastIndex].replace(/\]$/, ''); + keys = keys.shift().split('[').concat(keys); + keysLastIndex = keys.length - 1; + } else { + isPrimitive = true; + keysLastIndex = 0; } - if (isArray) { - queryParams[key].push(value); + if (pair.length === 2) { + let value = pair[1] ? decodeURIComponent(pair[1]) : ''; + if (keysLastIndex) { + parseComplexParam(queryParams, keys, value); + } else { + queryParams[key] = processScalarParam(queryParams[key], value, isPrimitive); + } } else { - queryParams[key] = value; + queryParams[key] = true; } } - return queryParams; } diff --git a/dist/commonjs/aurelia-path.d.ts b/dist/commonjs/aurelia-path.d.ts index 2a0dce7..f1eb9a8 100644 --- a/dist/commonjs/aurelia-path.d.ts +++ b/dist/commonjs/aurelia-path.d.ts @@ -29,7 +29,7 @@ declare module 'aurelia-path' { /** * Parse a query string. * - * @param The query string to parse. + * @param queryString The query string to parse. * @returns Object with keys and values mapped from the query string. */ export function parseQueryString(queryString: string): Object; diff --git a/dist/commonjs/aurelia-path.js b/dist/commonjs/aurelia-path.js index ba05850..15fd06f 100644 --- a/dist/commonjs/aurelia-path.js +++ b/dist/commonjs/aurelia-path.js @@ -89,29 +89,37 @@ function join(path1, path2) { return scheme + urlPrefix + url3.join('/') + trailingSlash; } +var encode = encodeURIComponent; +var encodeKey = function encodeKey(k) { + return encode(k).replace('%24', '$'); +}; + +function buildParam(key, value) { + var result = []; + if (value === null || value === undefined) { + return result; + } + if (Array.isArray(value)) { + for (var i = 0, l = value.length; i < l; i++) { + var arrayKey = key + '[' + (typeof value[i] === 'object' && value[i] !== null ? i : '') + ']'; + result = result.concat(buildParam(arrayKey, value[i])); + } + } else if (typeof value === 'object') { + for (var propertyName in value) { + result = result.concat(buildParam(key + '[' + propertyName + ']', value[propertyName])); + } + } else { + result.push(encodeKey(key) + '=' + encode(value)); + } + return result; +} + function buildQueryString(params) { var pairs = []; var keys = Object.keys(params || {}).sort(); - var encode = encodeURIComponent; - var encodeKey = function encodeKey(k) { - return encode(k).replace('%24', '$'); - }; - for (var i = 0, len = keys.length; i < len; i++) { var key = keys[i]; - var value = params[key]; - if (value === null || value === undefined) { - continue; - } - - if (Array.isArray(value)) { - var arrayKey = encodeKey(key) + '[]'; - for (var j = 0, l = value.length; j < l; j++) { - pairs.push(arrayKey + '=' + encode(value[j])); - } - } else { - pairs.push(encodeKey(key) + '=' + encode(value)); - } + pairs = pairs.concat(buildParam(key, params[key])); } if (pairs.length === 0) { @@ -121,6 +129,31 @@ function buildQueryString(params) { return pairs.join('&'); } +function processScalarParam(existedParam, value, isPrimitive) { + if (Array.isArray(existedParam)) { + existedParam.push(value); + return existedParam; + } + if (existedParam !== undefined) { + return isPrimitive ? value : [existedParam, value]; + } + + return value; +} + +function parseComplexParam(queryParams, keys, value) { + var currentParams = queryParams; + var keysLastIndex = keys.length - 1; + for (var j = 0; j <= keysLastIndex; j++) { + var key = keys[j] === '' ? currentParams.length : keys[j]; + if (j < keysLastIndex) { + currentParams = currentParams[key] = currentParams[key] || (keys[j + 1] ? {} : []); + } else { + currentParams = currentParams[key] = value; + } + } +} + function parseQueryString(queryString) { var queryParams = {}; if (!queryString || typeof queryString !== 'string') { @@ -132,36 +165,37 @@ function parseQueryString(queryString) { query = query.substr(1); } - var pairs = query.split('&'); + var pairs = query.replace(/\+/g, ' ').split('&'); for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split('='); var key = decodeURIComponent(pair[0]); - var keyLength = key.length; - var isArray = false; - var value = undefined; - + var isPrimitive = false; if (!key) { continue; - } else if (pair.length === 1) { - value = true; - } else { - if (keyLength > 2 && key.slice(keyLength - 2) === '[]') { - isArray = true; - key = key.slice(0, keyLength - 2); - if (!queryParams[key]) { - queryParams[key] = []; - } - } + } - value = pair[1] ? decodeURIComponent(pair[1]) : ''; + var keys = key.split(']['); + var keysLastIndex = keys.length - 1; + + if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLastIndex])) { + keys[keysLastIndex] = keys[keysLastIndex].replace(/\]$/, ''); + keys = keys.shift().split('[').concat(keys); + keysLastIndex = keys.length - 1; + } else { + isPrimitive = true; + keysLastIndex = 0; } - if (isArray) { - queryParams[key].push(value); + if (pair.length === 2) { + var value = pair[1] ? decodeURIComponent(pair[1]) : ''; + if (keysLastIndex) { + parseComplexParam(queryParams, keys, value); + } else { + queryParams[key] = processScalarParam(queryParams[key], value, isPrimitive); + } } else { - queryParams[key] = value; + queryParams[key] = true; } } - return queryParams; } \ No newline at end of file diff --git a/dist/es6/aurelia-path.d.ts b/dist/es6/aurelia-path.d.ts index 2a0dce7..f1eb9a8 100644 --- a/dist/es6/aurelia-path.d.ts +++ b/dist/es6/aurelia-path.d.ts @@ -29,7 +29,7 @@ declare module 'aurelia-path' { /** * Parse a query string. * - * @param The query string to parse. + * @param queryString The query string to parse. * @returns Object with keys and values mapped from the query string. */ export function parseQueryString(queryString: string): Object; diff --git a/dist/es6/aurelia-path.js b/dist/es6/aurelia-path.js index d354da9..936e505 100644 --- a/dist/es6/aurelia-path.js +++ b/dist/es6/aurelia-path.js @@ -17,8 +17,7 @@ function trimDots(ary: string[]): void { i -= 2; } } - } -} + }} /** * Calcualtes a path relative to a file. @@ -106,6 +105,34 @@ export function join(path1: string, path2: string): string { return scheme + urlPrefix + url3.join('/') + trailingSlash; } +let encode = encodeURIComponent; +let encodeKey = k => encode(k).replace('%24', '$'); +/** +* Recursively builds part of query string for parameter. +* +* @param key Parameter name for query string. +* @param value Parameter value to deserialize. +* @return Array with serialized parameter(s) +*/ +function buildParam(key: string, value: any): Array { + let result = []; + if (value === null || value === undefined) { + return result; + } + if (Array.isArray(value)) { + for (let i = 0, l = value.length; i < l; i++) { + let arrayKey = key + '[' + (typeof value[i] === 'object' && value[i] !== null ? i : '') + ']'; + result = result.concat(buildParam(arrayKey, value[i])); + } + } else if (typeof (value) === 'object') { + for (let propertyName in value) { + result = result.concat(buildParam(key + '[' + propertyName + ']', value[propertyName])); + } + } else { + result.push(`${encodeKey(key) }=${encode(value) }`); + } + return result; +} /** * Generate a query string from an object. @@ -116,24 +143,9 @@ export function join(path1: string, path2: string): string { export function buildQueryString(params: Object): string { let pairs = []; let keys = Object.keys(params || {}).sort(); - let encode = encodeURIComponent; - let encodeKey = k => encode(k).replace('%24', '$'); - for (let i = 0, len = keys.length; i < len; i++) { let key = keys[i]; - let value = params[key]; - if (value === null || value === undefined) { - continue; - } - - if (Array.isArray(value)) { - let arrayKey = `${encodeKey(key)}[]`; - for (let j = 0, l = value.length; j < l; j++) { - pairs.push(`${arrayKey}=${encode(value[j])}`); - } - } else { - pairs.push(`${encodeKey(key)}=${encode(value)}`); - } + pairs = pairs.concat(buildParam(key, params[key])); } if (pairs.length === 0) { @@ -143,10 +155,55 @@ export function buildQueryString(params: Object): string { return pairs.join('&'); } +/** +* Process parameter that was recognized as scalar param (primitive value or shallow array). +* +* @param existedParam Object with previously parsed values for specified key. +* @param value Parameter value to append. +* @param isPrimitive If true and parameter value already specified - method overwrites it. If false - transofrm parameter to array and append new value to it. +* @returns Initial primitive value or transformed existedParam if parameter was recognized as an array. +*/ +function processScalarParam(existedParam: Object, value: Object, isPrimitive: boolean): Object { + if (Array.isArray(existedParam)) { + // value is already an array, so push on the next value. + existedParam.push(value); + return existedParam; + } + if (existedParam !== undefined) { + // value isn't an array, but since a second value has been specified, + // convert value into an array. + return isPrimitive ? value : [existedParam, value]; + } + // value is a scalar. + return value; +} +/** +* Sequentially process parameter that was recognized as complex value (object or array). +* For each keys part, if the current level is undefined create an +* object or array based on the type of the next keys part. +* +* @param queryParams root-level result object. +* @param keys Collection of keys related to this parameter. +* @param value Parameter value to append. +*/ +function parseComplexParam(queryParams: Object, keys: Object, value: any): void { + let currentParams = queryParams; + let keysLastIndex = keys.length - 1; + for (let j = 0; j <= keysLastIndex; j++) { + let key = keys[j] === '' ? currentParams.length : keys[j]; + if (j < keysLastIndex) { + currentParams = currentParams[key] = currentParams[key] || (keys[j + 1] ? {} : []); + } else { + currentParams = currentParams[key] = value; + } + } +} + + /** * Parse a query string. * -* @param The query string to parse. +* @param queryString The query string to parse. * @returns Object with keys and values mapped from the query string. */ export function parseQueryString(queryString: string): Object { @@ -160,37 +217,40 @@ export function parseQueryString(queryString: string): Object { query = query.substr(1); } - let pairs = query.split('&'); + let pairs = query.replace(/\+/g, ' ').split('&'); for (let i = 0; i < pairs.length; i++) { let pair = pairs[i].split('='); let key = decodeURIComponent(pair[0]); - let keyLength = key.length; - let isArray = false; - let value; - + let isPrimitive = false; if (!key) { continue; - } else if (pair.length === 1) { - value = true; - } else { - //Handle arrays - if (keyLength > 2 && key.slice(keyLength - 2) === '[]') { - isArray = true; - key = key.slice(0, keyLength - 2); - if (!queryParams[key]) { - queryParams[key] = []; - } - } + } + //split object key into its parts + let keys = key.split(']['); + let keysLastIndex = keys.length - 1; - value = pair[1] ? decodeURIComponent(pair[1]) : ''; + // If the first keys part contains [ and the last ends with ], then [] + // are correctly balanced, split key to parts + //Else it's basic key + if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLastIndex])) { + keys[keysLastIndex] = keys[keysLastIndex].replace(/\]$/, ''); + keys = keys.shift().split('[').concat(keys); + keysLastIndex = keys.length - 1; + } else { + isPrimitive = true; + keysLastIndex = 0; } - if (isArray) { - queryParams[key].push(value); + if (pair.length === 2) { + let value = pair[1] ? decodeURIComponent(pair[1]) : ''; + if (keysLastIndex) { + parseComplexParam(queryParams, keys, value); + } else { + queryParams[key] = processScalarParam(queryParams[key], value, isPrimitive); + } } else { - queryParams[key] = value; + queryParams[key] = true; } } - return queryParams; } diff --git a/dist/system/aurelia-path.d.ts b/dist/system/aurelia-path.d.ts index 2a0dce7..f1eb9a8 100644 --- a/dist/system/aurelia-path.d.ts +++ b/dist/system/aurelia-path.d.ts @@ -29,7 +29,7 @@ declare module 'aurelia-path' { /** * Parse a query string. * - * @param The query string to parse. + * @param queryString The query string to parse. * @returns Object with keys and values mapped from the query string. */ export function parseQueryString(queryString: string): Object; diff --git a/dist/system/aurelia-path.js b/dist/system/aurelia-path.js index 42ae643..21e1279 100644 --- a/dist/system/aurelia-path.js +++ b/dist/system/aurelia-path.js @@ -1,6 +1,8 @@ System.register([], function (_export) { 'use strict'; + var encode, encodeKey; + _export('relativeToFile', relativeToFile); _export('join', join); @@ -93,29 +95,32 @@ System.register([], function (_export) { return scheme + urlPrefix + url3.join('/') + trailingSlash; } + function buildParam(key, value) { + var result = []; + if (value === null || value === undefined) { + return result; + } + if (Array.isArray(value)) { + for (var i = 0, l = value.length; i < l; i++) { + var arrayKey = key + '[' + (typeof value[i] === 'object' && value[i] !== null ? i : '') + ']'; + result = result.concat(buildParam(arrayKey, value[i])); + } + } else if (typeof value === 'object') { + for (var propertyName in value) { + result = result.concat(buildParam(key + '[' + propertyName + ']', value[propertyName])); + } + } else { + result.push(encodeKey(key) + '=' + encode(value)); + } + return result; + } + function buildQueryString(params) { var pairs = []; var keys = Object.keys(params || {}).sort(); - var encode = encodeURIComponent; - var encodeKey = function encodeKey(k) { - return encode(k).replace('%24', '$'); - }; - for (var i = 0, len = keys.length; i < len; i++) { var key = keys[i]; - var value = params[key]; - if (value === null || value === undefined) { - continue; - } - - if (Array.isArray(value)) { - var arrayKey = encodeKey(key) + '[]'; - for (var j = 0, l = value.length; j < l; j++) { - pairs.push(arrayKey + '=' + encode(value[j])); - } - } else { - pairs.push(encodeKey(key) + '=' + encode(value)); - } + pairs = pairs.concat(buildParam(key, params[key])); } if (pairs.length === 0) { @@ -125,6 +130,31 @@ System.register([], function (_export) { return pairs.join('&'); } + function processScalarParam(existedParam, value, isPrimitive) { + if (Array.isArray(existedParam)) { + existedParam.push(value); + return existedParam; + } + if (existedParam !== undefined) { + return isPrimitive ? value : [existedParam, value]; + } + + return value; + } + + function parseComplexParam(queryParams, keys, value) { + var currentParams = queryParams; + var keysLastIndex = keys.length - 1; + for (var j = 0; j <= keysLastIndex; j++) { + var key = keys[j] === '' ? currentParams.length : keys[j]; + if (j < keysLastIndex) { + currentParams = currentParams[key] = currentParams[key] || (keys[j + 1] ? {} : []); + } else { + currentParams = currentParams[key] = value; + } + } + } + function parseQueryString(queryString) { var queryParams = {}; if (!queryString || typeof queryString !== 'string') { @@ -136,42 +166,49 @@ System.register([], function (_export) { query = query.substr(1); } - var pairs = query.split('&'); + var pairs = query.replace(/\+/g, ' ').split('&'); for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split('='); var key = decodeURIComponent(pair[0]); - var keyLength = key.length; - var isArray = false; - var value = undefined; - + var isPrimitive = false; if (!key) { continue; - } else if (pair.length === 1) { - value = true; - } else { - if (keyLength > 2 && key.slice(keyLength - 2) === '[]') { - isArray = true; - key = key.slice(0, keyLength - 2); - if (!queryParams[key]) { - queryParams[key] = []; - } - } + } - value = pair[1] ? decodeURIComponent(pair[1]) : ''; + var keys = key.split(']['); + var keysLastIndex = keys.length - 1; + + if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLastIndex])) { + keys[keysLastIndex] = keys[keysLastIndex].replace(/\]$/, ''); + keys = keys.shift().split('[').concat(keys); + keysLastIndex = keys.length - 1; + } else { + isPrimitive = true; + keysLastIndex = 0; } - if (isArray) { - queryParams[key].push(value); + if (pair.length === 2) { + var value = pair[1] ? decodeURIComponent(pair[1]) : ''; + if (keysLastIndex) { + parseComplexParam(queryParams, keys, value); + } else { + queryParams[key] = processScalarParam(queryParams[key], value, isPrimitive); + } } else { - queryParams[key] = value; + queryParams[key] = true; } } - return queryParams; } return { setters: [], - execute: function () {} + execute: function () { + encode = encodeURIComponent; + + encodeKey = function encodeKey(k) { + return encode(k).replace('%24', '$'); + }; + } }; }); \ No newline at end of file diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index abb9141..2f0e9c6 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,3 +1,6 @@ +### 1.0.0-beta.1.1.1 (2016-03-01) + + ### 1.0.0-beta.1.1.0 (2016-01-28) diff --git a/doc/api.json b/doc/api.json index 39b3d56..206535a 100644 --- a/doc/api.json +++ b/doc/api.json @@ -129,6 +129,9 @@ "kind": 32768, "kindString": "Parameter", "flags": {}, + "comment": { + "text": "The query string to parse." + }, "type": { "type": "instrinct", "name": "string" diff --git a/package.json b/package.json index 8cea20b..53d41bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aurelia-path", - "version": "1.0.0-beta.1.1.0", + "version": "1.0.0-beta.1.1.1", "description": "Utilities for path manipulation.", "keywords": [ "aurelia",