Skip to content

Commit cc18c99

Browse files
committed
adding hasQuery() for query string value checking - closes #71
1 parent ff4aa7f commit cc18c99

File tree

2 files changed

+190
-20
lines changed

2 files changed

+190
-20
lines changed

src/URI.js

Lines changed: 130 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,12 @@ function escapeRegEx(string) {
5858
return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
5959
}
6060

61+
function getType(value) {
62+
return String(Object.prototype.toString.call(value)).slice(8, -1);
63+
}
64+
6165
function isArray(obj) {
62-
return String(Object.prototype.toString.call(obj)) === "[object Array]";
66+
return getType(obj) === "Array";
6367
}
6468

6569
function filterArrayValues(data, value) {
@@ -85,6 +89,57 @@ function filterArrayValues(data, value) {
8589
return data;
8690
}
8791

92+
function arrayContains(list, value) {
93+
var i, length;
94+
95+
// value may be string, number, array, regexp
96+
if (isArray(value)) {
97+
// Note: this can be optimized to O(n) (instead of current O(m * n))
98+
for (i = 0, length = value.length; i < length; i++) {
99+
if (!arrayContains(list, value[i])) {
100+
return false;
101+
}
102+
}
103+
104+
return true;
105+
}
106+
107+
var _type = getType(value);
108+
for (i = 0, length = list.length; i < length; i++) {
109+
if (_type === 'RegExp') {
110+
if (typeof list[i] === 'string' && list[i].match(value)) {
111+
return true;
112+
}
113+
} else if (list[i] === value) {
114+
return true;
115+
}
116+
}
117+
118+
return false;
119+
}
120+
121+
function arraysEqual(one, two) {
122+
if (!isArray(one) || !isArray(two)) {
123+
return false;
124+
}
125+
126+
// arrays can't be equal if they have different amount of content
127+
if (one.length !== two.length) {
128+
return false;
129+
}
130+
131+
one.sort();
132+
two.sort();
133+
134+
for (var i = 0, l = one.length; i < l; i++) {
135+
if (one[i] !== two[i]) {
136+
return false;
137+
}
138+
}
139+
140+
return true;
141+
}
142+
88143
URI._parts = function() {
89144
return {
90145
protocol: null,
@@ -551,6 +606,73 @@ URI.removeQuery = function(data, name, value) {
551606
throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");
552607
}
553608
};
609+
URI.hasQuery = function(data, name, value, withinArray) {
610+
if (typeof name === "object") {
611+
for (var key in name) {
612+
if (hasOwn.call(name, key)) {
613+
if (!URI.hasQuery(data, key, name[key])) {
614+
return false;
615+
}
616+
}
617+
}
618+
619+
return true;
620+
} else if (typeof name !== "string") {
621+
throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter");
622+
}
623+
624+
switch (getType(value)) {
625+
case 'Undefined':
626+
// true if exists (but may be empty)
627+
return name in data; // data[name] !== undefined;
628+
629+
case 'Boolean':
630+
// true if exists and non-empty
631+
var _booly = Boolean(isArray(data[name]) ? data[name].length : data[name]);
632+
return value === _booly;
633+
634+
case 'Function':
635+
// allow complex comparison
636+
return !!value(data[name], name, data);
637+
638+
case 'Array':
639+
if (!isArray(data[name])) {
640+
return false;
641+
}
642+
643+
var op = withinArray ? arrayContains : arraysEqual;
644+
return op(data[name], value);
645+
646+
case 'RegExp':
647+
if (!isArray(data[name])) {
648+
return Boolean(data[name] && data[name].match(value));
649+
}
650+
651+
if (!withinArray) {
652+
return false;
653+
}
654+
655+
return arrayContains(data[name], value);
656+
657+
case 'Number':
658+
value = String(value);
659+
// omit break;
660+
case 'String':
661+
if (!isArray(data[name])) {
662+
return data[name] === value;
663+
}
664+
665+
if (!withinArray) {
666+
return false;
667+
}
668+
669+
return arrayContains(data[name], value);
670+
671+
default:
672+
throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter");
673+
}
674+
};
675+
554676

555677
URI.commonPath = function(one, two) {
556678
var length = Math.min(one.length, two.length);
@@ -1293,9 +1415,14 @@ p.removeQuery = function(name, value, build) {
12931415
this.build(!build);
12941416
return this;
12951417
};
1418+
p.hasQuery = function(name, value, withinArray) {
1419+
var data = URI.parseQuery(this._parts.query);
1420+
return URI.hasQuery(data, name, value, withinArray);
1421+
};
12961422
p.setSearch = p.setQuery;
12971423
p.addSearch = p.addQuery;
12981424
p.removeSearch = p.removeQuery;
1425+
p.hasSearch = p.hasQuery;
12991426

13001427
// sanitizing URLs
13011428
p.normalize = function() {
@@ -1643,24 +1770,8 @@ p.equals = function(uri) {
16431770
if (one_map[key] !== two_map[key]) {
16441771
return false;
16451772
}
1646-
} else {
1647-
if (!isArray(two_map[key])) {
1648-
return false;
1649-
}
1650-
1651-
// arrays can't be equal if they have different amount of content
1652-
if (one_map[key].length !== two_map[key].length) {
1653-
return false;
1654-
}
1655-
1656-
one_map[key].sort();
1657-
two_map[key].sort();
1658-
1659-
for (var i = 0, l = one_map[key].length; i < l; i++) {
1660-
if (one_map[key][i] !== two_map[key][i]) {
1661-
return false;
1662-
}
1663-
}
1773+
} else if (!arraysEqual(one_map[key], two_map[key])) {
1774+
return false;
16641775
}
16651776

16661777
checked[key] = true;

test/test.js

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ test("query callback", function() {
563563
u.query(function(data) {
564564
return {
565565
bla: 'blubb'
566-
}
566+
};
567567
});
568568
equal(u.query(), 'bla=blubb', "overwrite returned value");
569569
});
@@ -689,6 +689,65 @@ test("duplicateQueryParameters", function() {
689689
u.addQuery('bar', 1);
690690
equal(u.toString(), '?bar=1&bar=1&bar=1&bar=1', "parameters NOT de-duplicated after addQuery()");
691691
});
692+
test("hasQuery", function() {
693+
var u = URI('?string=bar&list=one&list=two&number=123&null&empty=');
694+
695+
// exists
696+
equal(u.hasQuery('string'), true, "simple exists check - passing");
697+
equal(u.hasQuery('nono'), false, "simple exists check - failing");
698+
699+
// truthy value
700+
equal(u.hasQuery('string', true), true, "has truthy value check - passing string");
701+
equal(u.hasQuery('number', true), true, "has truthy value check - passing number");
702+
equal(u.hasQuery('list', true), true, "has truthy value check - passing list");
703+
equal(u.hasQuery('empty', true), false, "has truthy value check - failing empty");
704+
equal(u.hasQuery('null', true), false, "has truthy value check - failing null");
705+
706+
// falsy value
707+
equal(u.hasQuery('string', false), false, "has falsy value check - failing string");
708+
equal(u.hasQuery('number', false), false, "has falsy value check - failing number");
709+
equal(u.hasQuery('list', false), false, "has falsy value check - failing list");
710+
equal(u.hasQuery('empty', false), true, "has falsy value check - passing empty");
711+
equal(u.hasQuery('null', false), true, "has falsy value check - passing null");
712+
713+
// match value
714+
equal(u.hasQuery('string', "bar"), true, "value check - passing string");
715+
equal(u.hasQuery('number', 123), true, "value check - passing number");
716+
equal(u.hasQuery('number', "123"), true, "value check - passing number as string");
717+
equal(u.hasQuery('list', "one"), false, "value check - failing list");
718+
equal(u.hasQuery('empty', ""), true, "value check - passing empty");
719+
equal(u.hasQuery('null', ""), false, "value check - failing null");
720+
721+
// matching RegExp
722+
equal(u.hasQuery('string', /ar$/), true, "RegExp check - passing string");
723+
equal(u.hasQuery('number', /2/), true, "RegExp check - passing number");
724+
equal(u.hasQuery('string', /nono/), false, "RegExp check - failing string");
725+
equal(u.hasQuery('number', /999/), false, "RegExp check - failing number");
726+
727+
// matching array
728+
equal(u.hasQuery('string', ['one']), false, "array check - failing string");
729+
equal(u.hasQuery('list', ['one']), false, "array check - failing incomplete list");
730+
equal(u.hasQuery('list', ['one', 'two']), true, "array check - passing list");
731+
equal(u.hasQuery('list', ['two', 'one']), true, "array check - passing unsorted list");
732+
733+
// matching part of array
734+
equal(u.hasQuery('string', ['one'], true), false, "in array check - failing string");
735+
equal(u.hasQuery('list', ['one'], true), true, "in array check - passing incomplete list");
736+
equal(u.hasQuery('list', ['one', 'two'], true), true, "in array check - passing list");
737+
equal(u.hasQuery('list', ['two', 'one'], true), true, "in array check - passing unsorted list");
738+
equal(u.hasQuery('list', [/ne$/], true), true, "in array check - passing RegExp");
739+
740+
// comparison function
741+
equal(u.hasQuery('string', function(value, name, data) {
742+
equal(value, "bar", "Function check - param value");
743+
equal(name, "string", "Function check - param name");
744+
equal(typeof data, "object", "Function check - param data");
745+
return true;
746+
}), true, "Function check - passing true");
747+
equal(u.hasQuery('string', function(value, name, data) {
748+
return false;
749+
}), false, "Function check - passing false");
750+
});
692751

693752
module("normalizing");
694753
test("normalize", function() {

0 commit comments

Comments
 (0)