Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Toggle query string variables

I've been banging my head over this.

Using jquery or javascript, how can I toggle variables & values and then rebuild the query string? For example, my starting URL is:

http://example.com?color=red&size=small,medium,large&shape=round

Then, if the user clicks a button labeled "red", I want to end up with:

http://example.com?size=small,medium,large&shape=round //color is removed

Then, if the user clicks "red" again, I want to end up with:

http://example.com?size=small,medium,large&shape=round&color=red //color is added back

Then, if the user clicks a button labeled "medium", I want to end up with:

http://example.com?size=small,large&shape=round&color=red //medium is removed from list

Then, if the user clicks the labeled "medium" again, I want to end up with:

http://example.com?size=small,large,medium&shape=round&color=red //medium added back

It doesn't really matter what order the variable are in; I've just been tacking them to the end.

like image 318
xd3v Avatar asked Oct 29 '11 06:10

xd3v


2 Answers

function toggle(url, key, val) {
    var out = [],
        upd = '',
        rm = "([&?])" + key + "=([^&]*?,)?" + val + "(,.*?)?(&.*?)?$",
        ad = key + "=",
        rmrplr = function(url, p1, p2, p3, p4) {
            if (p2) {
                if (p3) out.push(p1, key, '=', p2, p3.substr(1));
                else out.push(p1, key, '=', p2.substr(0, p2.length - 1));
            } else {
                if (p3) out.push(p1, key, '=', p3.substr(1));
                else out.push(p1);
            }
            if (p4) out.push(p4);
            return out.join('').replace(/([&?])&/, '$1').replace(/[&?]$/, ''); //<!2
        },
        adrplr = function(s) {
            return s + val + ',';
        };
    if ((upd = url.replace(new RegExp(rm), rmrplr)) != url) return upd;
    if ((upd = url.replace(new RegExp(ad), adrplr)) != url) return upd;
    return url + (/\?.+/.test(url) ? '&' : '?') + key + '=' + val; //<!1
}

params self described enough, hope this help.

!1: changed from ...? '&' : '' to ... ? '&' : '?'

!2: changed from .replace('?&','?')... to .replace(/([&?]&)/,'$1')...

http://jsfiddle.net/ycw7788/Abxj8/

like image 52
user943702 Avatar answered Nov 06 '22 17:11

user943702


I have written a function, which efficiently results in the expected behaviour, without use of any libraries or frameworks. A dynamic demo can be found at this fiddle: http://jsfiddle.net/w8D2G/1/

Documentation

Definitions:
The shown example values will be used at the Usage section, below
  -   Haystack - The string to search in (default = query string. e.g: ?size=small,medium)
  -   Needle - The key to search for. Example: size
  -   Value - The value to replace/add. Example: medium.

Usage (Example: input > output):

  1. qs_replace(needle, value)
    If value exists, remove: ?size=small,medium > ?size=small
    If value not exists, add: ?size=small > size=small,medium
  2. qs_replace(needle, options)     Object options. Recognised options:
    • find
      String. Returns true if the value exists, false otherwise.
    • add, remove or toggle
      String. Add/remove the given value to/from needle. If remove is used, and the value was the only value, needle is also removed. A value won't be added if it already exists.
    • ignorecase
      Ignore case while looking for the search terms (needle, add, remove or find).
    • separator
      Specify a separator to separate values of needle. Default to comma (,).

Note :   A different value for String haystack can also be defined, by adding it as a first argument: qs_replace(haystack, needle, value) or qs_replace(haystack, needle, options)

Code (examples at bottom). Fiddle: http://jsfiddle.net/w8D2G/1/:

function qs_replace(haystack, needle, options) {
    if(!haystack || !needle) return ""; // Without a haystack or needle.. Bye
    else if(typeof needle == "object") {
        options = needle;
        needle = haystack;
        haystack = location.search;
    } else if(typeof options == "undefined") {
        options = needle;
        needle = haystack;
        haystack = location.search;
    }

    if(typeof options == "string" && options != "") {
        options = {remove: options};
        var toggle = true;
    } else if(typeof options != "object" || options === null) {
        return haystack;
    } else {
        var toggle = !!options.toggle;
        if (toggle) {
            options.remove = options.toggle;
            options.toggle = void 0;
        }
    }

    var find = options.find,
        add = options.add,
        remove = options.remove || options.del, //declare remove
        sep = options.sep || options.separator || ",", //Commas, by default

        flags = (options.ignorecase ? "i" :"");

    needle = encodeURIComponent(needle); //URL-encoding
    var pattern = regexp_special_chars(needle);
    pattern = "([?&])(" + pattern + ")(=|&|$)([^&]*)(&|$)";
    pattern = new RegExp(pattern, flags);
    var subquery_match = haystack.match(pattern);

    var before = /\?/.test(haystack) ? "&" : "?"; //Use ? if not existent, otherwise &
    var re_sep = regexp_special_chars(sep);

    if (!add || find) { //add is not defined, or find is used
        var original_remove = remove;
        if (subquery_match) {
            remove = encodeURIComponent(remove);
            remove = regexp_special_chars(remove);
            remove = "(^|" + re_sep + ")(" + remove + ")(" + re_sep + "|$)";
            remove = new RegExp(remove, flags);
            var fail = subquery_match[4].match(remove);
        } else {
            var fail = false;
        }
        if (!add && !fail && toggle) add = original_remove;
    }
    if(find) return !!subquery_match || fail;
    if (add) { //add is a string, defined previously
        add = encodeURIComponent(add);
        if(subquery_match) {
            var re_add = regexp_special_chars(add);
            re_add = "(^|" + re_sep + ")(" + re_add + ")(?=" + re_sep + "|$)";
            re_add = new RegExp(re_add, flags);
            if (subquery_match && re_add.test(subquery_match[4])) {
                return haystack;
            }
            if (subquery_match[3] != "=") {
                subquery_match = "$1$2=" + add + "$4$5";
            } else {
                subquery_match = "$1$2=$4" + sep + add + "$5";
            }
            return haystack.replace(pattern, subquery_match);
        } else {
            return haystack + before + needle + "=" + add;
        }
    } else if(subquery_match){ // Remove part. We can only remove if a needle exist
        if(subquery_match[3] != "="){
            return haystack;
        } else {
            return haystack.replace(pattern, function(match, prefix, key, separator, value, trailing_sep){
                // The whole match, example: &foo=bar,doo
                // will be replaced by the return value of this function
                var newValue = value.replace(remove, function(m, pre, bye, post){
                    return pre == sep && post == sep ? sep : pre == "?" ? "?" : "";
                });
                if(newValue) { //If the value has any content
                    return prefix + key + separator + newValue + trailing_sep;
                } else {
                    return prefix == "?" ? "?" : trailing_sep; //No value, also remove needle
                }
            }); //End of haystack.replace
        } //End of else if
    } else {
        return haystack;
    }

    // Convert string to RegExp-safe string
    function regexp_special_chars(s){
        return s.replace(/([[^$.|?*+(){}\\])/g, '\\$1');
    }
}

Examples (Fiddle: http://jsfiddle.net/w8D2G/1/):

qs_replace('color', 'red'); //Toggle color=red
qs_replace('size', {add: 'medium'}); //Add `medium` if not exist to size
var starting_url = 'http://example.com?color=red&size=small,medium,large&shape=round'
starting_url = qs_replace(starting_url, 'color', 'red'); //Toggle red, thus remove
starting_url = qs_replace(starting_url, 'color', 'red'); //Toggle red, so add it
alert(starting_url);
like image 38
Rob W Avatar answered Nov 06 '22 18:11

Rob W