Antonio-Maranhao closed pull request #1031: Remove usage of JQuery param
URL: https://github.com/apache/couchdb-fauxton/pull/1031
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/app/addons/auth/api.js b/app/addons/auth/api.js
index c509eeb20..d70682092 100644
--- a/app/addons/auth/api.js
+++ b/app/addons/auth/api.js
@@ -69,7 +69,7 @@ export function getSession() {
export function login(body) {
return formEncoded(app.host + "/_session", {
method: "POST",
- body: $.param(body)
+ body: app.utils.queryParams(body)
});
}
@@ -77,7 +77,7 @@ export function logout() {
loggedInSessionPromise = null;
return formEncoded(app.host + "/_session", {
method: "DELETE",
- body: $.param({ username: "_", password: "_" })
+ body: app.utils.queryParams({ username: "_", password: "_" })
});
}
diff --git a/app/addons/databases/actions.js b/app/addons/databases/actions.js
index 51341e43e..51827e60c 100644
--- a/app/addons/databases/actions.js
+++ b/app/addons/databases/actions.js
@@ -182,7 +182,7 @@ export default {
},
fetchAllDbsWithKey: (id, callback) => {
- const query = '?' + $.param({
+ const query = '?' + app.utils.queryParams({
startkey: JSON.stringify(id),
endkey: JSON.stringify(id + "\u9999"),
limit: 30
diff --git a/app/addons/databases/resources.js
b/app/addons/databases/resources.js
index 6e0720b8a..639bc0dd0 100644
--- a/app/addons/databases/resources.js
+++ b/app/addons/databases/resources.js
@@ -88,7 +88,7 @@ Databases.Changes = FauxtonAPI.Collection.extend({
url: function (context) {
var query = "";
if (this.params) {
- query = "?" + $.param(this.params);
+ query = "?" + app.utils.queryParams(this.params);
}
if (!context) { context = 'server';}
diff --git a/app/addons/documents/changes/actions.js
b/app/addons/documents/changes/actions.js
index 4d2635f57..a414c470e 100644
--- a/app/addons/documents/changes/actions.js
+++ b/app/addons/documents/changes/actions.js
@@ -58,7 +58,7 @@ export default {
params.feed = 'longpoll';
}
- const query = $.param(params);
+ const query = app.utils.queryParams(params);
const db = app.utils.safeURLName(changesStore.getDatabaseName());
const endpoint = FauxtonAPI.urls('changes', 'server', db, '?' + query);
currentRequest = $.getJSON(endpoint);
diff --git a/app/addons/documents/components/actions.js
b/app/addons/documents/components/actions.js
index fe79504b1..b0f9e5f1a 100644
--- a/app/addons/documents/components/actions.js
+++ b/app/addons/documents/components/actions.js
@@ -10,12 +10,13 @@
// License for the specific language governing permissions and limitations
under
// the License.
import $ from 'jquery';
+import app from "../../../app";
import FauxtonAPI from "../../../core/api";
export default {
fetchAllDocsWithKey: (database) => {
return (id, callback) => {
- const query = '?' + $.param({
+ const query = '?' + app.utils.queryParams({
startkey: JSON.stringify(id),
endkey: JSON.stringify(id + "\u9999"),
limit: 30
diff --git a/app/addons/documents/shared-resources.js
b/app/addons/documents/shared-resources.js
index 440911547..807dea880 100644
--- a/app/addons/documents/shared-resources.js
+++ b/app/addons/documents/shared-resources.js
@@ -224,12 +224,12 @@ Documents.AllDocs = PagingCollection.extend({
if (params) {
if (!_.isEmpty(params)) {
- query = "?" + $.param(params);
+ query = "?" + app.utils.queryParams(params);
} else {
query = '';
}
} else if (this.params) {
- query = "?" + $.param(this.params);
+ query = "?" + app.utils.queryParams(this.params);
}
if (_.isUndefined(context)) {
context = 'server';
diff --git a/app/core/__tests__/utils.test.js b/app/core/__tests__/utils.test.js
index 19b5035a4..5ec547e06 100644
--- a/app/core/__tests__/utils.test.js
+++ b/app/core/__tests__/utils.test.js
@@ -74,13 +74,13 @@ describe('Utils', () => {
assert.isUndefined(utils.localStorageGet('qwerty'));
});
- it ('Should get value after setting it', () => {
+ it('Should get value after setting it', () => {
const key = 'key1';
utils.localStorageSet(key, 1);
assert.equal(utils.localStorageGet(key), 1);
});
- it ('Set and retrieve complex object', () => {
+ it('Set and retrieve complex object', () => {
const key = 'key2',
obj = {
one: 1,
@@ -90,7 +90,7 @@ describe('Utils', () => {
assert.deepEqual(utils.localStorageGet(key), obj);
});
- it ('stripHTML removes HTML', () => {
+ it('stripHTML removes HTML', () => {
[
{ html: '<span>okay</span>', text: 'okay' },
{ html: 'test <span>before</span> and after', text: 'test before and
after' },
@@ -102,4 +102,123 @@ describe('Utils', () => {
});
});
+
+ // Expected values were computed using JQuery (v3.2) $.param().
+ describe('queryParams', () => {
+ it('works with empty values', () => {
+ assert.equal(utils.queryParams(''), '');
+ assert.equal(utils.queryParams({}), '');
+ assert.equal(utils.queryParams([]), '');
+ });
+
+ it('works with basic types', () => {
+ assert.equal(utils.queryParams('abc'), '0=a&1=b&2=c');
+ assert.equal(utils.queryParams(123), '');
+ assert.equal(utils.queryParams(123.1), '');
+ assert.equal(utils.queryParams(true), '');
+ assert.equal(utils.queryParams(false), '');
+ });
+
+ it('works with special characters', () => {
+ assert.equal(
+ utils.queryParams({a: 'abc def!#$%?&*+-=', b: 'XYZ'}),
+ 'a=abc%20def!%23%24%25%CB%86%26*%2B-%3D&b=XYZ'
+ );
+ });
+
+ it('works with simple arrays', () => {
+ assert.equal(
+ utils.queryParams([
+ {name: 'prop1', value: 'value1'},
+ {name: 'prop2', value: 'value!@#$%?&*()-+_="\''},
+ {name: 'prop3', value: 123},
+ {name: 'prop4', value: true},
+ {name: 'prop5', value: false},
+ ]),
+
'prop1=value1&prop2=value!%40%23%24%25%CB%86%26*()-%2B_%3D%22\'&prop3=123&prop4=true&prop5=false'
+ );
+ });
+
+ it('works with arrays with objects', () => {
+ assert.equal(
+ utils.queryParams([
+ {name: 'prop1', value: {a: 123, b: 'abc'}},
+ {name: 'prop2', value: {a2: { x: 456, y: 'def' }, b2: 'abc'}}
+ ]),
+ 'prop1=%5Bobject%20Object%5D&prop2=%5Bobject%20Object%5D'
+ );
+ assert.equal(
+ utils.queryParams([
+ {name: 'prop1', value: {}},
+ {name: 'prop2', value: {a2: null}},
+ {name: 'prop3', value: {a2: undefined}}
+ ]),
+
'prop1=%5Bobject%20Object%5D&prop2=%5Bobject%20Object%5D&prop3=%5Bobject%20Object%5D'
+ );
+ });
+
+ it('works with simple objects', () => {
+ assert.equal(
+ utils.queryParams({a: 'abc', b: 'def'}),
+ 'a=abc&b=def'
+ );
+ assert.equal(
+ utils.queryParams({a: 123, b: 'abc', c: true, d: false}),
+ 'a=123&b=abc&c=true&d=false'
+ );
+ assert.equal(
+ utils.queryParams({a: null, b: undefined}),
+ 'a=&b='
+ );
+ assert.equal(
+ utils.queryParams({
+ startkey: JSON.stringify('_design/app'),
+ endkey: JSON.stringify('_design/app\u9999'),
+ limit:30
+ }),
+
'startkey=%22_design%2Fapp%22&endkey=%22_design%2Fapp%E9%A6%99%22&limit=30'
+ );
+ });
+
+ it('works with objects with arrays', () => {
+ assert.equal(
+ utils.queryParams({
+ arr1: [1, 2, 3],
+ arr2: ['a', 'b', 'c'],
+ arr3: [true, false]
+ }),
+
'arr1%5B%5D=1&arr1%5B%5D=2&arr1%5B%5D=3&arr2%5B%5D=a&arr2%5B%5D=b&arr2%5B%5D=c&arr3%5B%5D=true&arr3%5B%5D=false'
+ );
+ assert.equal(
+ utils.queryParams({
+ arr1: [1, undefined, 'b']
+ }),
+ 'arr1%5B%5D=1&arr1%5B%5D=&arr1%5B%5D=b'
+ );
+ });
+
+ it('works with nested objects', () => {
+ assert.equal(
+ utils.queryParams({
+ obj1: {x: 'abc', y: 123, z: true, w: false},
+ obj2: {
+ sub_obj1: { X: 'def', Y: 456, Z: true, W: false},
+ sub_obj2: { X2: 'def', Y2: 456, Z2: false, W2: true},
+ }
+ }),
+
'obj1%5Bx%5D=abc&obj1%5By%5D=123&obj1%5Bz%5D=true&obj1%5Bw%5D=false&obj2%5Bsub_obj1%5D%5BX%5D=def&obj2%5Bsub_obj1%5D%5BY%5D=456&obj2%5Bsub_obj1%5D%5BZ%5D=true&obj2%5Bsub_obj1%5D%5BW%5D=false&obj2%5Bsub_obj2%5D%5BX2%5D=def&obj2%5Bsub_obj2%5D%5BY2%5D=456&obj2%5Bsub_obj2%5D%5BZ2%5D=false&obj2%5Bsub_obj2%5D%5BW2%5D=true'
+ );
+
+ assert.equal(
+ utils.queryParams({
+ obj1: {x: [1, 2, 4], y: {a: 'abc', b: 123, c: true}},
+ arr1: [
+ { X: 'def', Y: 456, Z: true, W: false},
+ { X2: 'def', Y2: 456, Z2: false, W2: true},
+ ]
+ }),
+
'obj1%5Bx%5D%5B%5D=1&obj1%5Bx%5D%5B%5D=2&obj1%5Bx%5D%5B%5D=4&obj1%5By%5D%5Ba%5D=abc&obj1%5By%5D%5Bb%5D=123&obj1%5By%5D%5Bc%5D=true&arr1%5B0%5D%5BX%5D=def&arr1%5B0%5D%5BY%5D=456&arr1%5B0%5D%5BZ%5D=true&arr1%5B0%5D%5BW%5D=false&arr1%5B1%5D%5BX2%5D=def&arr1%5B1%5D%5BY2%5D=456&arr1%5B1%5D%5BZ2%5D=false&arr1%5B1%5D%5BW2%5D=true'
+ );
+ });
+ });
});
diff --git a/app/core/utils.js b/app/core/utils.js
index b5b736ce4..d185e8906 100644
--- a/app/core/utils.js
+++ b/app/core/utils.js
@@ -18,7 +18,6 @@
// "purely functional" helper system.
-import $ from "jquery";
import _ from "lodash";
const utils = {
@@ -36,11 +35,11 @@ const utils = {
var hash = window.location.hash.split('?')[1];
queryString = queryString || hash || window.location.search.substring(1);
var match,
- urlParams = {},
- pl = /\+/g, // Regex for replacing addition symbol with a space
- search = /([^&=]+)=?([^&]*)/g,
- decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
- query = queryString;
+ urlParams = {},
+ pl = /\+/g, // Regex for replacing addition symbol with a space
+ search = /([^&=]+)=?([^&]*)/g,
+ decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
+ query = queryString;
if (queryString) {
while ((match = search.exec(query))) {
@@ -51,15 +50,6 @@ const utils = {
return urlParams;
},
- // this takes the current URL and replaces all ?x=x with whatever new params
are passed
- replaceQueryParams: function (params) {
- var fragment = window.location.hash.replace(/\?.*$/, "");
- if (!_.isEmpty(params)) {
- fragment = fragment + "?" + $.param(params);
- }
- return fragment;
- },
-
removeSpecialCharacters: function (name) {
return name.replace(/[^\w\s]/gi, "");
},
@@ -71,6 +61,73 @@ const utils = {
return encodeURIComponent(name);
},
+ queryParams: function (obj) {
+ if (Array.isArray(obj)) {
+ //JQuery expects array elements to be {name: <name>, value: <value>}
+ //so we convert the array into an object so the rest of the logic
+ //remains the same
+ obj = obj.reduce((acc, elem) => {
+ if (elem && elem.name) {
+ if (typeof elem.value === 'object') {
+ //JQuery uses the result of toString() (e.g. '[object Object]')
when value is an object
+ acc[elem.name] = elem.value.toString();
+ } else {
+ acc[elem.name] = elem.value;
+ }
+ }
+ return acc;
+ }, {});
+ } else if (typeof obj === 'string') {
+ //JQuery treats a string as an array of chars, so
+ //we map it to an object where fields are the array
+ //indexes
+ obj = obj.split('').reduce((acc, el, idx) => {
+ acc[idx] = el; return acc;
+ }, {});
+ } else if (typeof obj === 'number' || typeof obj === 'boolean') {
+ return '';
+ }
+
+ const objectToListOfParams = (obj, prefix) => {
+ if (typeof obj !== 'object') {
+ return [prefix + '=' + encodeURIComponent(obj)];
+ }
+ return Object.keys(obj).map((key) => {
+ let value = obj[key];
+ if (prefix) {
+ //Properties of inner objects are surrounded with []
+ key = encodeURIComponent('[' + key + ']');
+ }
+ if (value === undefined || value === null) {
+ return prefix + key + '=';
+ } else if (Array.isArray(value)) {
+ const newPrefix = prefix + key;
+ const innerParams = value.map((innerElem, idx) => {
+ const asObj = {};
+ if (typeof innerElem === 'object' || Array.isArray(innerElem)) {
+ asObj[idx] = innerElem;
+ } else {
+ // If it's a basic value (string, number, etc) use empty key
+ asObj[''] = innerElem;
+ }
+ const elemParams = objectToListOfParams(asObj, newPrefix);
+
+ return elemParams.join('&');
+ });
+ return innerParams.join('&');
+ } else if (typeof value === 'object') {
+ const newPrefix = prefix + key;
+ const innerObjParams = objectToListOfParams(value, newPrefix);
+ return innerObjParams.join('&');
+ } else {
+ value = encodeURIComponent(value);
+ return prefix + key + '=' + value;
+ }
+ });
+ };
+ return objectToListOfParams(obj, '').join('&');
+ },
+
getDocTypeFromId: function (id) {
if (id && /^_design\//.test(id)) {
return 'design doc';
diff --git a/assets/js/plugins/cloudant.pagingcollection.js
b/assets/js/plugins/cloudant.pagingcollection.js
index 6d6680501..48c2265fc 100644
--- a/assets/js/plugins/cloudant.pagingcollection.js
+++ b/assets/js/plugins/cloudant.pagingcollection.js
@@ -13,8 +13,7 @@
import _ from 'lodash';
import $ from 'jquery';
import Backbone from "backbone";
-
-const $param = $.param;
+import app from '../../../app/app';
//PagingCollection
//----------------
@@ -134,7 +133,7 @@ export const PagingCollection = Backbone.Collection.extend({
}
}, this);
- this.url = url + '?' + $param(params);
+ this.url = url + '?' + app.utils.queryParams(params);
},
fetch: function () {
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services