Wednesday, February 13th, 2008
Crazy way to change third party scripts
Paul Irish posted a crazy method for tweaking third party scripts. His problem was that he was using MultiMap (online Mapping API) and wanted to internationalize the information, but it was hard coded in the JavaScript.
To get around the problem he does this:
-
-
// WARNING!! This is such a massive hack. Oh-so-hackalicious
-
-
// Problem: Multimap doesnt allow internationalization of its buttons, etc.
-
// Solution: Redefine their JS functions to use variables that are internationalized.
-
// Assumption: That these internal function names stay the same.
-
// Risk: If function names change, this code will (probably) silently fail.
-
-
// The following statements change the right-click context menu items and the map/aerial/hybrid buttons.
-
// Instead of hard-coded strings, it will use a variable which we control.
-
-
// ON TO THE HACKS!!
-
// Hack 1: modify the mmjr() and mmfl() functions with funcName.toString().replace()
-
// Hack 2: use eval() to set the definition
-
// Hack 3: browser sniff because IE and FF handle toString()'d strings differently (single-quote vs double-quote)
-
-
var isIE = $.browser.msie; // jQuery browser sniff.
-
-
eval(
-
"mmki.prototype.mmjr = " +
-
mmki.prototype.mmjr
-
.toString()
-
.replace( isIE ? "'Move map to here'" : '"Move map to here"' , 'i18n.retailLocator.moveMapToHere')
-
.replace( isIE ? "'Zoom in to here'" : '"Zoom in to here"' , 'i18n.retailLocator.zoomInToHere')
-
.replace( isIE ? "'Zoom out from here'" : '"Zoom out from here"', 'i18n.retailLocator.zoomOutFromHere')
-
);
-
-
eval(
-
"MultimapViewer.prototype.mmfl = " +
-
MultimapViewer.prototype.mmfl
-
.toString()
-
.replace( isIE ? "'Map'" : '"Map"', 'i18n.retailLocator.map')
-
.replace( isIE ? "'Hybrid'" : '"Hybrid"', "i18n.retailLocator.hybrid")
-
.replace( isIE ? "'Aerial'" : '"Aerial"', 'i18n.retailLocator.aerial')
-
);
-
Yowser :)












now, this really looks like an ugly and very bad hack - without knowledge of the libs internal: can’t believe, that there’s no other way around …
Now, that’s what I call inheritance! :-) Crazy but cute.
1. This method also breaks if the localization string changes
2. Browser detection isn’t necessary. Use RegEx in replace() method. E.g.:
…replace(/['"]Move map to here['"]/, ‘i18n.retailLocator.moveMapToHere’)
3. Cute hack, but there’s no mention of how this script is actually injected into the page. GreaseMonkey, I presume?
@broofa:
I suspect he’s just loading this *hack* script after the core library. This code is simply taking the original method, converting it to a string, replacing some string tokens and converting it back to a method.
I’ve used a similiar technique before–it’s ugly, but it does work (and has worked as earlier as IE4/NS4.)
@broofa:
1. Yes, it will break in that case too. It has a lot of potential to break. It should probably have some added measures built in so that it wont throw an error to the end-user. How about I add a try/catch hack? (just kidding :)
2. Smart move with the regex. That cleans it up a bit. Thanks.
3. Yup Dan’s right — just running that script sequentially in a separate js after the library is included.
@aurora:
I agree its terrible and ugly, but I couldn’t think of a cleaner way around it. Imagine if the Google Maps API forced you use a single kind of icon, and the image file’s path was hardcoded into a createIcon() function. Do they leave you any other options?