JavaScript – Passing Arguments By Name

One thing that I think would be cool is if JavaScript provided a way to pass arguments to a function by name. About two years ago I wrote the code for a Function.prototype.invoke but never released it as part of jPaq like I had hoped. Recently, I decided to re-use the code I wrote and improve it. The following defines a Function.prototype.index function which enables parameter passing by name:

/**
 * @license Copyright 2013 - Chris West - MIT Licensed
 */
(function(reComments, reParams, reNames) {
  Function.prototype.index = function(arrParamNames) {
    var fnMe = this;
    arrParamNames = arrParamNames
      || (((fnMe + '').replace(reComments, '')
           .match(reParams)[1] || '')
          .match(reNames) || []);
    return function(namedArgs) {
      var args = [], i = arrParamNames.length;
      args[i] = namedArgs;
      while(i--) {
        args[i] = namedArgs[arrParamNames[i]];
      }
      return fnMe.apply(this, args);
    };
  };
})(
  /\/\*[\s\S]*?\*\/|\/\/.*?[\r\n]/g,
  /\(([\s\S]*?)\)/,
  /[$\w]+/g
);

Now let’s look at an example of using this code. Let’s define a function which accepts four parameters and prints the value of each parameter to the console:

var showA2D = (function(a, b, c, d) {
  console.log('a = ', a);
  console.log('b = ', b);
  console.log('c = ', c);
  console.log('d = ', d);
}).index();

Now we have our function and believe it or not but all we had to do was surround the function definition in parentheses and calls its index function to make it accept parameters by name. Now let’s call the function with some arguments:

showA2D({
  a: "first parameter",
  d: "last parameter",
  b: "second parameter"
});

Executing the above will print the following in the console:

a =  first parameter
b =  second parameter
c =  undefined
d =  last parameter

Another interesting thing we could have done when we defined showA2D was actually define the mapping of the parameters. This is especially useful if you plan on minifying the function which will be indexed. The following shows how we can map the arguments:

// Define the function.
var showA2D = (function(a, b, c, d, args) {
  console.log('a = ', a);
  console.log('b = ', b);
  console.log('c = ', c);
  console.log('d = ', d);
  console.log('args = ', args);
}).index(['a', 'b', 'c', 'd']);

// Call the new function.
showA2D({
  a: "first parameter",
  d: "last parameter",
  c: "third parameter"
});

After running the above code something like the following will be shown in the console:

a =  first parameter
b =  undefined
c =  third parameter
d =  last parameter
args =  Object {a: "first parameter", d: "last parameter", c: "third parameter"}

As you may have determined the Function#index function takes an optional Array which should contain the names of the parameters which will be mapped to the function. Another thing that is important to note is that the object that is passed to the indexed function will be passed to the original function as the final parameter.

If I ever get around to publishing the next version of jPaq this will probably make its way into the codebase. Until then, feel free to use this version (just give me some credit ;) ).

JavaScript – Unicode Escape Sequence Function

A few days ago someone asked me how I would go about determining the unicode escape sequence for an arbitrary character. Of course, I went to Google Chrome’s web console and showed them how it can be done by using the charCodeAt, toString, and slice functions. The following is a function which will take in a string and return the equivalent with all characters converted to their unicode escape sequence:

/**
 * Replaces each character in the string with the corresponding
 * unicode escape sequence.
 * @param {string} str  The string of characters to escape.
 * @return {string}  The string with the escape sequences.
 */
function toUnicodeSequence(str) {
  for(var i = str.length; i--;) {
    str = str.slice(0, i) + '\\u'
        + ('000' + str.charCodeAt(i).toString(16)).slice(-4)
        + str.slice(i + 1);
  }
  return str;
}

Let’s review some basic JavaScript functionality:

  • The String.prototype.charCodeAt function basically provides the ability to find the integer equivalent of any one character within a string.
  • The Number.prototype.toString function provides the ability to convert the number into a string which represents that number in a different base. In otherwords, (5).toString(2) will result in 101 because binary (from the 2 passed in) version of 5 is 101.
  • The String.prototype.slice function is similar to String.prototype.substring function but it has one advantage: you can pass in negative parameters as well. If you pass in a negative number, the index will be calculated from the end of the string instead of the beginning. Therefore executing ("JavaScript").slice(-6) results in "Script" because those are the last 6 characters of the string "JavaScript".
  • By using these three functions together, we can create a function such as toUnicodeSequence which will return the escape sequences for all of the characters. I am not sure how often one would need such a function but have fun with it. 8)

JavaScript – Modify URL Parameters

Recently I had to develop a solution which involved changing the HREF attributes of links in the navigation bar of a site. This had to be done on the JavaScript side because at my company we are the 3rd-party JavaScript solution for modifying other companies’ sites. That being said, I came up with one solution because the URL parameter was always the same but I figured I would publicly release the code for doing this in such a way that you can modify the value of any URL parameter:

/**
 * @license Copyright 2013 - Chris West - MIT Licensed
 */
(function(expCharsToEscape, expEscapedSpace, expNoStart, undefined) {
  /**
   * Modifies the given URL, returning it with the given parameter
   * changed to the given value.  The parameter is added if it didn't
   * already exist.  The parameter is removed if null or undefined is
   * specified as the value.
   * @param {string} url  The URL to be modified.
   * @param {string} paramName  The URL parameter whose value will be
   *     modified.
   * @param {string} paramValue  The value to assign.  This will be
   *     escaped using encodeURIComponent.
   * @return {string}  The updated URL.
   */
  modURLParam = function(url, paramName, paramValue) {
    paramValue = paramValue != undefined
      ? encodeURIComponent(paramValue).replace(expEscapedSpace, '+')
      : paramValue;
    var pattern = new RegExp(
      '([?&]'
      + paramName.replace(expCharsToEscape, '\\$1')
      + '=)[^&]*'
    );
    if(pattern.test(url)) {
      return url.replace(
        pattern,
        function($0, $1) {
          return paramValue != undefined ? $1 + paramValue : ''; 
        }
      ).replace(expNoStart, '$1?');
    }
    else if (paramValue != undefined) {
      return url + (url.indexOf('?') + 1 ? '&' : '?')
        + paramName + '=' + paramValue;
    }
    else {
      return url;
    }
  };
})(/([\\\/\[\]{}().*+?|^$])/g, /%20/g, /^([^?]+)&/);

The following are some example calls to this function:

// Initial URL
var url = 'http://example.com/';
alert(url);

// http://example.com/?q=search+term
url = modURLParam(url, 'q', 'search term');
alert(url);

// http://example.com/?q=search+term&name=Guillermo
url = modURLParam(url, 'name', 'Guillermo');
alert(url);

// http://example.com/?q=search+term+2&name=Guillermo
url = modURLParam(url, 'q', 'search term 2');
alert(url);

// http://example.com/?name=Guillermo
url = modURLParam(url, 'q');
alert(url);

// http://example.com/?name=Guillermo&q=termino
url = modURLParam(url, 'q', 'termino');
alert(url);

// http://example.com/?name=Guillermo
url = modURLParam(url, 'q', null);
alert(url);

Feel free to re-use the code! 8)

JavaScript – Substitution Groups & Functions

One of the things that I often end up doing is using JavaScript’s replace function with a regular expression and a callback function. Recently, though, I have been thinking about redeveloping an HTML application (HTA) that I made a long time ago for PCs. This HTA gave me the ability to write regular expressions and replacement strings which could alter the matched groups in ways that normal string substitution doesn’t allow without using callback functions. Let’s take the following file names as examples:

  • 001-the-nephews-come-to-town.mpg
  • 002-scroogey-scroogers.mp4
  • 32-hewi’s-lost-pet.mp4
  • 109-copped-by-the-coppers.mpeg

The file renamer that I use to have would allow these files to be renamed as the following by using /^(\d+)(.+)(?=\.\w+$)/ as the regular expression and "Episode ${1,RLZ} -${2,D2S,PROPER}" as the replacement:

  • Episode 1 – The Nephews Come To Town.mpg
  • Episode 2 – Scroogey Scroogers.mp4
  • Episode 32 – Hewi’s Lost Pet.mp4
  • Episode 109 – Copped By The Coppers.mpeg

As the first step towards achieving my goal, I wrote the following sub function which can accomplish this:

(function() {
  var functions = {};
  var hasOwnProperty = functions.hasOwnProperty;
  this.sub = function(subject, reTarget, strReplacement, objFns) {
    if(!reTarget && !strReplacement && !objFns) {
      for(var key in subject) {
        if(hasOwnProperty.call(subject, key)) {
          functions[key] = subject[key];
        }
      }
    }
    else {
      return subject.replace(reTarget, function(match) {
        var args = arguments;
        var reGroups = [];
        var i = args.length - 2;
        while(--i > 0) {
          reGroups.push(i);
        }
        reGroups = '(' + reGroups.join('|') + ')';
        reGroups = new RegExp('\\$(?:' + reGroups + '|\\{' + reGroups + '((?:,\\w+)*)\\})', 'g');
        return strReplacement.replace(reGroups, function(match, index, index2, fnsToUse) {
          fnsToUse = fnsToUse ? fnsToUse.slice(1).split(',') : [];
          var ret = args[index || index2];
          for(var i = 0, len = fnsToUse.length; i < len; i++) {
            var fnName = fnsToUse[i];
            var fn;
            if((objFns && hasOwnProperty.call(objFns, fnName) && (fn = objFns[fnName]))
                || (hasOwnProperty.call(functions, fnName) && (fn = functions[fnName]))) {
              ret = fn(ret, args);
            }
          }
          return ret;
        });
      });
    }
  };
})();

This function actually acts differently depending on the parameters that are supplied. The first way in which the function can be called is with four parameters:

  1. subject – string:
    The string that is to be modified.
  2. reTarget – RegExp:
    The regular expression used to capture substrings.
  3. strReplacement – string:
    The replacement string which can have the normal captured group expressions (eg. $1, $3, etc.), the new type of captured group expressions (eg. ${1}, ${3,RLZ}, etc.) and/or normal substrings.
  4. objFns – Object:
    Optional object whose keys correspond to functions referenced in the new type of group expressions. Each value should be a function where the first parameter passed to it will be the matched group and the second will be the arguments object which is normally passed to the callback function. The functions should return the string replacement for the captured group expression.
var filename = sub(
  "023-we-are-the-tigers.jpg",
  /^(\d+)(.+)(?=\.\w+$)/,
  "Episode ${1,RLZ} -${2,D2S,PROPER}",
  {
    RLZ: function(match) {
      return match.replace(/^0+(?!$)/, '');
    },
    D2S: function(match) {
      return match.replace(/-/g, ' ');
    },
    PROPER: function(match) {
      return match.toProperCase();
    },
    UPPER: function(match) {
      return match.toUpperCase();
    },
    CHARCODE: function(match) {
      return '<' + match + '=' + match.charCodeAt(0) + '>';
    }
  }
);
alert(filename);  // "Episode 23 - We Are The Tigers.jpg"

var str = sub(
  "abcdefghijklmnopqrstuvwxyz",
  /([aeiou])/g,
  "<${1,UPPER}>",
  {
    UPPER: function(match) {
      return match.toUpperCase();
    }
  }
);
alert(str);  // "<A>bcd<E>fgh<I>jklmn<O>pqrst<U>vwxyz" 

The reason the fourth parameter is optional is because those captured group expression replacement functions could be predefined for all calls to this sub function. If the function is just called with one parameter, that parameter should be the same as the fourth parameter outlined previously. The captured group expression replacement functions will remain persistent for the remainder of the page/program session:

sub({
  RLZ: function(match) {
    return match.replace(/^0+(?!$)/, '');
  },
  D2S: function(match) {
    return match.replace(/-/g, ' ');
  },
  PROPER: function(match) {
    return match.toProperCase();
  }
});
var filenames = [
  "001-the-nephews-come-to-town.mpg",
  "002-scroogey-scroogers.mp4",
  "32-hewi's-lost-pet.mp4",
  "109-copped-by-the-coppers.mpeg"
];
var exp = /^(\d+)(.+)(?=\.\w+$)/;
var replacement = "Episode ${1,RLZ} -${2,D2S,PROPER}";
for(var i = 0; i < filenames.length; i++) {
  filenames[i] = sub(filenames[i], exp, replacement);
}
alert(filenames.join('\n'));

The above code will result in the following filenames:

Episode 1 - The Nephews Come To Town.mpg
Episode 2 - Scroogey Scroogers.mp4
Episode 32 - Hewi's Lost Pet.mp4
Episode 109 - Copped By The Coppers.mpeg

Well, now that I have this code out of the way, hopefully the next step will be to actually make the File Renamer. When I do finish creating it, you can be sure to find it on this blog. 8)

JScript – File & Folder Chomper

I use to always work on Windows and at times it seemed that certain files just didn’t want to go to the recycling bin. For that reason I would always write a JScript which had the following code:

(new ActiveXObject("Scripting.FileSystemObject").DeleteFile(WScript.Arguments.Item(0))

When a file was dragged onto that script, the file would be de-referenced (deleted permanently). A friend recently called me and asked for a similar solution so I decided to post it for everyone:
Download the File & Folder Chomper

The above script works the same way (on Windows only): drag the files and folders onto the script’s icon in order to have all of the permanently deleted. Have fun!!!