Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feat($location): parse query parameters delimited by ; or & #6341

Closed
wants to merge 124 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
7e0288c
feat($location): parse query parameters delimited by ; or &
caitp Feb 7, 2014
b6fe8c7
chore(testing): de-flake a ngHref test for navigating away from the A…
juliemr Feb 6, 2014
2467833
chore(build): Update closure i18n integration
tbosch Feb 7, 2014
8c41c4e
docs(contributing): add code of conduct
btford Feb 7, 2014
85b5a75
revert: refactor(mocks): simplify the `inject` implementation
IgorMinar Feb 7, 2014
3e3d17e
docs(changelog): release notes for 1.2.12
matsko Feb 7, 2014
5479787
chore(release): update cdn version
NgDashboard Feb 7, 2014
018223d
chore(testing): switch Jenkins to test e2e only on chrome
juliemr Feb 8, 2014
0147b09
refactor(doc): separate end to end tests into jquery and jqlite files
juliemr Feb 6, 2014
6232b28
refactor(testing): split travis end to end tests into separate jobs f…
juliemr Feb 6, 2014
51ed285
docs(guide/scope): fix a typo
vicb Feb 10, 2014
adff083
docs(concepts): Remove pointless `* 1`s
Sequoia Feb 10, 2014
ea11fb2
fix($compile): ensure element transclusion directives are linked with…
caitp Jan 28, 2014
b61412f
docs(faq): add link to MIT license
negativetwelve Feb 10, 2014
0c9130b
docs(guide/concepts): removing confusing use of hoisting
Sequoia Feb 10, 2014
9d9ff8f
style(guide/concepts): remove ws
IgorMinar Feb 11, 2014
d63fb17
docs(ngSubmit): ngSubmit also works with the `data-action`/`x-action`…
ZeeStorm Feb 10, 2014
333b57c
docs(guide): add new resource links
JeremyLikness Feb 9, 2014
1f30d01
style(guide): remove ws
IgorMinar Feb 11, 2014
f1ec06e
docs(guide/index): replace "shold" to "should"
Feb 11, 2014
b58a439
refactor(ngTransclude): use transclusion function passed in to link
Dec 13, 2013
8962504
feat(filterFilter): support deeply nested predicate objects
caitp Feb 11, 2014
44b62ff
docs(jqLite): link to jQuery.fn.bind/unbind docs rather than jQuery.f…
githubjeka Feb 7, 2014
d5264f5
docs(core): add closing tag to ngApp directive example
jesselpalmer Jan 31, 2014
1f4224f
chore(dependencies): upgrade kriskowal/q to version ~1.0.0
caitp Feb 6, 2014
d678a1c
docs(tutorial): inject phonecapApp module into unit test
yono38 Feb 11, 2014
78c21af
docs(input): document NgModelController.$isEmpty parameters / return …
MathieuTricoire Feb 12, 2014
b126978
docs(guide/$location): correct link to HTML5 draft section 5.5 (histo…
Feb 12, 2014
08cf30c
docs(currencyFilter): added missing line break in currency doc ptor test
jamiebuilds Feb 12, 2014
d435a51
docs($location): fix link to $rootScope.Scope.$on
caitp Jan 14, 2014
4f09a51
docs($interpolate): fix link to $interpolateProvider#endSymbol
caitp Jan 14, 2014
be20e58
fix(input): setViewValue on compositionend
caitp Dec 16, 2013
f1ac7db
docs(logo): change logo to vector format in .eps file
Feb 13, 2014
7b510c3
fix(input): don't apply textInput to <input type="file">
twhitbeck Feb 13, 2014
8100491
fix($compile) support templates with table content root nodes
Dec 2, 2013
b041b2c
chore(jqLite): expose the _data lookup function to angular.element
matsko Feb 14, 2014
a393967
pref($animate): group all asynchronous requests into one shared buffer
matsko Feb 13, 2014
ad8dd16
pref($animate): only trigger DOM callbacks if registered on the eleme…
matsko Feb 14, 2014
b417b09
fix($animate): ensure $animate doesn't break natural CSS transitions
matsko Feb 14, 2014
e0a6e9c
style(animate): remove ws
IgorMinar Feb 14, 2014
211facc
docs(changelog): release notes for 1.2.13
btford Feb 14, 2014
037da43
chore(release): update cdn version
NgDashboard Feb 15, 2014
8fdfb88
fix(numberFilter): convert all non-finite/non-numbers/non-numeric str…
sagens42 Feb 14, 2014
ce39290
docs(bike-shed-migration): Add missing module tag
petebacondarwin Feb 6, 2014
f02e35d
docs(bike-shed-migration): fix up links outside the domain
petebacondarwin Feb 6, 2014
490a9c3
docs(bike-shed-migration): change tutorial doctype and add @step tag
petebacondarwin Feb 6, 2014
6f581d3
docs(bike-shed-migration): move ng module doc into Angular.js
petebacondarwin Feb 6, 2014
0e4a4d5
docs(bike-shed-migration): convert doctype and names
petebacondarwin Feb 6, 2014
3a586e0
docs(all): convert <pre>/</pre> snippets to GFM snippets
caitp Feb 6, 2014
2e306ee
docs(sce): fix reference to $sceDelegateProvider
petebacondarwin Feb 6, 2014
29eade2
docs(bike-shed-migration): convert <doc:...> examples to <example>...
petebacondarwin Feb 7, 2014
85c24b7
docs(bike-shed-migration): let markdown deal with extenal links
petebacondarwin Feb 7, 2014
3c182f6
docs(sce): fix invalid @name tags
petebacondarwin Feb 7, 2014
40abd50
docs(ngResource): fix over-length line
petebacondarwin Feb 7, 2014
4935559
docs(bike-shed-migration): fix invalid </file name=""> HTML in examples
petebacondarwin Feb 12, 2014
15e0239
docs(bike-shed-migration): fix url-based links refs to AUTO module
petebacondarwin Feb 12, 2014
d941620
chore(doc-gen): new docs
petebacondarwin Feb 12, 2014
1f9ed4f
docs(tutorial): give the tutorial landing page a better name
petebacondarwin Feb 12, 2014
5f29b57
docs(misc): add landing page for the miscellaneous area
petebacondarwin Feb 12, 2014
0e739e3
chore(protractor-generator): add dgeni processor for protractor
juliemr Feb 12, 2014
071cb95
chore(protractor tests): fix up e2e tests
juliemr Feb 12, 2014
8af2cbd
chore(docs): remove 'AngularJS ' prefix from version drop-down
petebacondarwin Feb 13, 2014
1d18356
docs(injector): move angular.injector to the ng module
petebacondarwin Feb 13, 2014
ed652cb
chore(indexPage.template): add back Google analytics and offline support
petebacondarwin Feb 13, 2014
e46e67e
chore(docs-indexPage.template): add onload handler for Google Analytics
petebacondarwin Feb 13, 2014
0facc74
chore(doc-gen): move git info into its own processor
petebacondarwin Feb 13, 2014
82255a1
chore(doc-gen): provide example dependencyPath in config
petebacondarwin Feb 13, 2014
4857bbd
docs(ngSanitize): add dependency on angular-sanitize to example
petebacondarwin Feb 13, 2014
1b1d9d5
docs(examples): fix example dependencies
petebacondarwin Feb 13, 2014
c315271
docs(ngShowHide): fix icons in example
petebacondarwin Feb 14, 2014
d56f6d5
docs(api/index): fix ambiguous ngClick link
petebacondarwin Feb 14, 2014
f70b1d2
test(docs-app-e2e): refactor test inline with new docs app
petebacondarwin Feb 14, 2014
784ad1b
chore(package.json): get dgeni from the npm registry
petebacondarwin Feb 14, 2014
cf9f607
docs(ngHref): don't run the breaking protractor test
petebacondarwin Feb 14, 2014
e194a60
docs(ngRoute): ask for examples to have their base[href] fixed
petebacondarwin Feb 14, 2014
2027f50
docs(ngHref): fix example that navigates away from the page
petebacondarwin Feb 14, 2014
0c2b04e
chore(doc-gen): remove console.log statements
petebacondarwin Feb 14, 2014
a496836
docs(content): fix bad links
petebacondarwin Feb 14, 2014
9f176ac
docs(docs.css): add initial type-hint styles
petebacondarwin Feb 14, 2014
aa6bfc3
docs($locationProvider): tag as a provider rather than object
petebacondarwin Feb 14, 2014
91800a0
docs(api): escape params that have <object> in their type
petebacondarwin Feb 14, 2014
4235736
chore(package.json): move to published npm version of dgeni-packages
petebacondarwin Feb 14, 2014
d20806d
chore(travis): remove test:docgen task from build
caitp Feb 14, 2014
7cc911d
chore(grunt): grunt should error when doc generation grunt errors
juliemr Feb 14, 2014
e424ef2
chore(doc-gen): fix navGroup hrefs
petebacondarwin Feb 15, 2014
35f7df7
chore(doc-gen): only output info (and more severe) log messages
petebacondarwin Feb 15, 2014
9795f3e
docs(guide): remove unnecessary "Developer Guide: " from @names
petebacondarwin Feb 15, 2014
9bc3a30
chore(e2etests): fix a browser check which was actually assigning the…
juliemr Feb 15, 2014
60e40b8
chore(doc-gen): add jshint checking to examples and tests
petebacondarwin Feb 15, 2014
6562ca7
docs(*): fix jshint issues in examples
petebacondarwin Feb 15, 2014
64f3fc1
chore(doc-gen): remove trailing whitespace in ptor template
petebacondarwin Feb 15, 2014
8be7b63
chore(doc-gen): improve error page navigation
petebacondarwin Feb 15, 2014
b120479
docs(NgModelController): don't run contenteditable example on FF
petebacondarwin Feb 15, 2014
8595bf9
docs(bike-shed-migration): convert guide <doc:...> examples to <examp…
caitp Feb 16, 2014
e2c47de
docs(indexPage.template): fix top-nav links to work on localhost
petebacondarwin Feb 16, 2014
495586e
chore(travis): disable Safari and JQuery e2e tests on Travis
petebacondarwin Feb 16, 2014
68706aa
chore(errors): rename folders to match namespaces
petebacondarwin Feb 16, 2014
216a8f8
docs(design): ui-fixes for examples, layout and API components
matsko Feb 16, 2014
05b3efb
docs(design): footer and spacing fixes added to the layout
matsko Feb 17, 2014
0f5299a
chore(ngPluralize): remove useless docs heading
matsko Feb 17, 2014
37cdcbf
chore(docs): replace local bootstrap build with one from bower
matsko Feb 17, 2014
71d346d
chore(docs): use fixed versions with all registered bower components
matsko Feb 17, 2014
660121a
chore(doc-gen): add production deployment environment
petebacondarwin Feb 17, 2014
94b1c01
chore(docs-assets): add versions to paths for imported assets
petebacondarwin Feb 17, 2014
9c007f8
docs(tutorial): add missing @step tags
Feb 17, 2014
a334d40
docs(bike-shed-migration): convert doctype and names
Feb 17, 2014
abdf2fb
docs(guide/concepts): clarify wording
plmetz Feb 15, 2014
80db066
chore(docs): fix broken example tabs
matsko Feb 17, 2014
5f7dc55
docs(tutorial/step-11): add controller to correct object
ahliddin Feb 15, 2014
d5205c3
chore(docs): cope with trailing slash and "index" in URLs
petebacondarwin Feb 18, 2014
2f527c0
docs($interpolate): correct small typo
pauldijou Feb 18, 2014
0b139d3
docs(guide/compiler): remove innacurate statement
shairez Feb 17, 2014
6c88453
test(docs): add check for resilience to trailing slashes, etc.
petebacondarwin Feb 18, 2014
8fdcfd0
docs($q): fixup dgeni ngdoc annotations for $q methods
caitp Feb 18, 2014
f7811ef
style: remove ws and enfore no-trailing-ws jscs rule
IgorMinar Feb 18, 2014
713c92c
docs(ngClass): Explain all 3 ways how to use ngClass
bradw2k Feb 18, 2014
6eee0a0
fix(jqLite): make jqLite('<iframe src="someurl">').contents() return …
caitp Feb 18, 2014
0b236ed
style(jqLite): remove trailing whitespace in new test
caitp Feb 18, 2014
bcf04f7
chore(travis): don't run Travis-CI builds for G3 branches
caitp Feb 19, 2014
6fdc9b9
docs(guide/providers): fix example snippet
IgorMinar Feb 19, 2014
8d86d63
docs(triaging): remove reference to the obsolete chrome extension
IgorMinar Feb 19, 2014
1933802
docs(contributing): remove reference to the obsolete chrome extension
IgorMinar Feb 19, 2014
1073967
feat($location): parse query parameters delimited by ; or & #6167
Feb 19, 2014
2c5afdb
feat($location): parse query parameters delimited by ; or &
Feb 19, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -1055,9 +1055,10 @@ function tryDecodeURIComponent(value) {
* Parses an escaped url query string into key-value pairs.
* @returns Object.<(string|boolean)>
*/
function parseKeyValue(/**string*/keyValue) {
function parseKeyValue(/**string*/keyValue, delimiter) {
delimiter = delimiter === ';' ? delimiter : '&';
var obj = {}, key_value, key;
forEach((keyValue || "").split('&'), function(keyValue){
forEach((keyValue || "").split(delimiter), function(keyValue){
if ( keyValue ) {
key_value = keyValue.split('=');
key = tryDecodeURIComponent(key_value[0]);
Expand All @@ -1076,8 +1077,11 @@ function parseKeyValue(/**string*/keyValue) {
return obj;
}

function toKeyValue(obj) {
function toKeyValue(obj, delimiter) {
var parts = [];
if (delimiter !== '&' && delimiter !== ';') {
delimiter = '&';
}
forEach(obj, function(value, key) {
if (isArray(value)) {
forEach(value, function(arrayValue) {
Expand All @@ -1089,7 +1093,7 @@ function toKeyValue(obj) {
(value === true ? '' : '=' + encodeUriQuery(value, true)));
}
});
return parts.length ? parts.join('&') : '';
return parts.length ? parts.join(delimiter) : '';
}


Expand Down
45 changes: 37 additions & 8 deletions src/ng/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function parseAppUrl(relativeUrl, locationObj, appBase) {
var match = urlResolve(relativeUrl, appBase);
locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
match.pathname.substring(1) : match.pathname);
locationObj.$$search = parseKeyValue(match.search);
locationObj.$$search = parseKeyValue(match.search, locationObj.$$queryDelimiter);
locationObj.$$hash = decodeURIComponent(match.hash);

// make sure path starts with '/';
Expand Down Expand Up @@ -87,9 +87,10 @@ function serverBase(url) {
* @param {string} appBase application base URL
* @param {string} basePrefix url path prefix
*/
function LocationHtml5Url(appBase, basePrefix) {
function LocationHtml5Url(appBase, basePrefix, queryDelimiter) {
this.$$html5 = true;
basePrefix = basePrefix || '';
this.$$queryDelimiter = queryDelimiter;
var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this, appBase);

Expand Down Expand Up @@ -120,7 +121,7 @@ function LocationHtml5Url(appBase, basePrefix) {
* @private
*/
this.$$compose = function() {
var search = toKeyValue(this.$$search),
var search = toKeyValue(this.$$search, this.$$queryDelimiter),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';

this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
Expand Down Expand Up @@ -155,8 +156,9 @@ function LocationHtml5Url(appBase, basePrefix) {
* @param {string} appBase application base URL
* @param {string} hashPrefix hashbang prefix
*/
function LocationHashbangUrl(appBase, hashPrefix) {
function LocationHashbangUrl(appBase, hashPrefix, queryDelimiter) {
var appBaseNoFile = stripFile(appBase);
this.$$queryDelimiter = queryDelimiter;

parseAbsoluteUrl(appBase, this, appBase);

Expand Down Expand Up @@ -227,7 +229,7 @@ function LocationHashbangUrl(appBase, hashPrefix) {
* @private
*/
this.$$compose = function() {
var search = toKeyValue(this.$$search),
var search = toKeyValue(this.$$search, this.$$queryDelimiter),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';

this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
Expand Down Expand Up @@ -287,6 +289,12 @@ LocationHashbangInHtml5Url.prototype =
*/
$$replace: false,

/**
* Allows using ";" instead of "&" to separate query string arguments
* @private
*/
$$queryDelimiter: '&',

/**
* @ngdoc method
* @name $location#absUrl
Expand Down Expand Up @@ -415,7 +423,7 @@ LocationHashbangInHtml5Url.prototype =
return this.$$search;
case 1:
if (isString(search)) {
this.$$search = parseKeyValue(search);
this.$$search = parseKeyValue(search, this.$$queryDelimiter);
} else if (isObject(search)) {
this.$$search = search;
} else {
Expand Down Expand Up @@ -520,7 +528,8 @@ function locationGetterSetter(property, preprocess) {
*/
function $LocationProvider(){
var hashPrefix = '',
html5Mode = false;
html5Mode = false,
queryDelimiter = '&';

/**
* @ngdoc property
Expand Down Expand Up @@ -554,6 +563,26 @@ function $LocationProvider(){
}
};

/**
* @ngdoc property
* @name ng.$locationProvider#queryDelimiter
* @methodOf ng.$locationProvider
* @description
* @param {string=} delimiter String to use as a delimiter for query parameters. Must be '&' or
* ';'
* @returns {*} current value if used as getter or itself (chaining) if used as setter
*/
this.queryDelimiter = function(delimiter) {
if (arguments.length > 0) {
if (delimiter !== ';' && delimiter !== '&') {
delimiter = '&';
}
queryDelimiter = delimiter;
return this;
}
return queryDelimiter;
};

/**
* @ngdoc event
* @name $location#$locationChangeStart
Expand Down Expand Up @@ -596,7 +625,7 @@ function $LocationProvider(){
appBase = stripHash(initialUrl);
LocationMode = LocationHashbangUrl;
}
$location = new LocationMode(appBase, '#' + hashPrefix);
$location = new LocationMode(appBase, '#' + hashPrefix, queryDelimiter);
$location.$$parse($location.$$rewrite(initialUrl));

$rootElement.on('click', function(event) {
Expand Down
75 changes: 75 additions & 0 deletions test/ng/locationSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,44 @@ describe('$location', function() {
expect(url.search()).toEqual({'i j': '<>#'});
expect(url.hash()).toBe('x <>#');
});


it('should decode query params delimited interchangeably by & and ;', function() {
var url = new LocationHashbangUrl('http://host.com/', '#');
url.$$parse('http://host.com/#?foo=1&bar=2;baz');
expect(url.search()).toEqual({
'foo': '1',
'bar': '2;baz'
});

url = new LocationHashbangUrl('http://host.com/', '#', ';');
url.$$parse('http://host.com/#?foo=1&bar=2;baz');
expect(url.search()).toEqual({
'foo': '1&bar',
'baz': true
});
});


it('should honor configured query param delimiter if ; --- otherwise use &', function() {
url = new LocationHtml5Url('http://host.com/', '#', ';');
url.$$parse('http://host.com/');
url.search({
"foo": "1",
"bar": "2",
"baz": "3"
});
expect(url.absUrl()).toMatch(/\?foo=1;bar=2;baz=3$/);

url = new LocationHtml5Url('http://host.com/', '#', '*');
url.$$parse('http://host.com/');
url.search({
"foo": "1",
"bar": "2",
"baz": "3"
});
expect(url.absUrl()).toMatch(/\?foo=1&bar=2&baz=3$/);
});
});
});

Expand Down Expand Up @@ -435,6 +473,23 @@ describe('$location', function() {
});


it('should decode query params delimited interchangeably by & and ;', function() {
var url = new LocationHashbangUrl('http://host.com/', '#');
url.$$parse('http://host.com/#?foo=1&bar=2;baz');
expect(url.search()).toEqual({
'foo': '1',
'bar': '2;baz'
});

url = new LocationHashbangUrl('http://host.com/', '#', ';');
url.$$parse('http://host.com/#?foo=1&bar=2;baz');
expect(url.search()).toEqual({
'foo': '1&bar',
'baz': true
});
});


it('should return decoded characters for search specified with setter', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
locationUrl.$$parse('http://host.com/')
Expand Down Expand Up @@ -464,6 +519,26 @@ describe('$location', function() {
locationUrl.search({'q': '4/5 6'});
expect(locationUrl.absUrl()).toEqual('http://host.com?q=4%2F5%206');
});

it('should honor configured query param delimiter if ; --- otherwise use &', function() {
url = new LocationHashbangUrl('http://host.com/', '#', ';');
url.$$parse('http://host.com/');
url.search({
"foo": "1",
"bar": "2",
"baz": "3"
});
expect(url.absUrl()).toMatch(/\?foo=1;bar=2;baz=3$/);

url = new LocationHashbangUrl('http://host.com/', '#', '*');
url.$$parse('http://host.com/');
url.search({
"foo": "1",
"bar": "2",
"baz": "3"
});
expect(url.absUrl()).toMatch(/\?foo=1&bar=2&baz=3$/);
});
});
});

Expand Down