Ruby has a cool operator by the name of splat(*) that lets you unpack an array as arguments on the fly. See this code below for an example:
# Define the item array
item1 = ['Jack', '39', 'Panda']
# Define a regular function that takes three arguments
def hi(name, age, type)
puts 'Hi ' + name + '! You are ' + age + ' and a ' + type + '!'
end
# Call the function using item1 and the splat operator
hi(*item1)
# Outputs: Hi Jack! You are 35 and a Panda!
While JavaScript does not have such a convenient operator, experimenting around, I’ve found a good enough solution:
var item1 = ['Jack', '39', 'Panda'];
function hi(name, age, type){
console.log('Hi ' + name + '! You are ' + age + ' and a ' + type + '!');
}
hi.apply(this, item1);
// Outputs to your Firebug console: Hi Jack! You are 39 and a Panda!
The apply method is built into all functions in JavaScript and allows you to call a method of an object in the context of a different object (Moz dev page here). Think about it as the equivalent of temporarily moving that method off the current object (the window object if it’s a global function) and attaching it to another object before calling it.
The primary reason to use this method is for changing the this keyword reference inside of the called function, but it has the added benefit of optionally requiring an array of arguments that get passed to the called function. We basically keep the this keyword as the window object (could have specified window instead of this) and use it to unpack our array for us.
This solution is also useful in any situation where you have a function that takes an unlimited number of arguments and you want to to eventually send those arguments to another function that also accepts any number of arguments. This situation is exactly what was puzzling me as I was working on creating a “safe” version of Firebug’s console.log that would work normally in Firefox but alert each argument separately in Internet Explorer.
Lastly, this is usefully for succinct array merging. Instead of looping through one array and pushing each item onto another you can do:
bob = [1,2,3,4];
sue = ['a','b'];
bob.push.apply(bob,sue);
bob
// returns [1,2,3,4,a,b]
The problem
I had to find out the language codes of all supported languages in TinyMCE (this page), but unfortunately, they were only listed in the actual download link for each file. A quick sprinkle of jQuery code in my Firebug console would have returned me a list easily but this page didn’t have it – which lead me to consider making a bookmarklet to add jQuery on any page.
The solution:
My wonderful coworker Bryan told me immediately about a simple bookmarklet that does the above called jQuerify. Looking through it, I saw a few potential issues with this script and also decided I wanted something a little more personal, so I came up with my own version:
$jQuerify
Notable differences:
- Moved the jQuery shortcut
$ to $j. I always do this on any site I work on because I find $ is used by so many other libraries and scripts that I want to minimize conflicts. Using $j also for sure tells me I’m using jQuery.
- If
$ isn’t being used it becomes a shortcut to document.getElementById. I think it’s what people naturally expect $ to do, and in my daily coding I like using $('id'); better than $j('#id')[0]; when all I want to do is return an element.
- Added an initial check for jQuery to prevent running the script unnecessary.
The full source looks like this:
(function(){
if(!window.jQuery){
var s=document.createElement('script');
s.src='http://jquery.com/src/jquery-latest.js';
document.getElementsByTagName('head')[0].appendChild(s);
var z=setInterval(function(){
if(window.jQuery){
$j = jQuery.noConflict();
alert('jQuery loaded!');
window.clearInterval(z);
}
},100);
}
if(!window.$){
window.$=function(x){return document.getElementById(x);};
}
})();
$jQuerify <-- Try me out, drag me to your tool bar.
Happy endings
Now I can just hop back over to the TinyMCE Language Pack Download page, run my bookmarklet, type the following in Firebug:
$j('#examplecontent form:first table tbody tr').each(function(c,i){
var country = $j(this).find('td:eq(2) label').html();
var code= $j(this).find('td:eq(1) a').attr('href').match(/(?:code=)(..)/)[1];
console.log(c,country,'=',code);
});
And the list is returned:
0 Arabic = ar
1 Bokmål, Norwegian; Norwegian Bokmål = nb
2 Bosnian = bs
3 Bulgarian = bg
4 Catalan; Valencian = ca
5 Chamorro = ch
6 Chinese = zh
7 Croatian = hr
8 Czech = cs
9 Danish = da
10 Dutch; Flemish = nl
11 English = en
12 Estonian = et
13 Finnish = fi
14 French = fr
15 German = de
16 Greek, Modern (1453-) = el
17 Hebrew = he
18 Hungarian = hu
19 Interlingua (International Auxiliary Language Association) = ia
20 Italian = it
21 Japanese = ja
22 Korean = ko
23 Latvian = lv
24 Lithuanian = lt
25 Macedonian = mk
26 Malay = ms
27 Northern Sami = se
28 Norwegian Nynorsk; Nynorsk, Norwegian = nn
29 Persian = fa
30 Polish = pl
31 Portuguese = pt
32 Romanian = ro
33 Russian = ru
34 Sardinian = sc
35 Serbian = sr
36 Sichuan Yi = ii
37 Sinhala; Sinhalese = si
38 Slovak = sk
39 Slovenian = sl
40 Spanish; Castilian = es
41 Swedish = sv
42 Tatar = tt
43 Turkish = tr
44 Twi = tw
45 Ukrainian = uk
46 Vietnamese = vi
If you haven’t gathered so far, I think the jQuery JavaScript library is the dog’s bollocks. Its small, efficient, and has improved my life as a developer almost as much as Firebug. jQuery gets flack occasionally for containing complex or cryptic code full of ternary operators and multiple variable declarations in a single statement (eg. a=b=1), which I mostly see as concise and efficient coding however, there’s one thing about the jQuery library that I can’t stand. It’s something every developer should do and if you’re not doing it then you’re a jerk and that, ladies and gentlemen, is omitting your curly brackets.
Look at this code snippet from the jQuery library to see what I mean:
for ( ; i < length; i++ )
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null )
// Extend the base object
for ( var name in options ) {
var src = target[ name ], copy = options[ name ];
// Prevent never-ending loop
if ( target === copy )
continue;
// Recurse if we're merging object values
if ( deep && copy && typeof copy == "object" && !copy.nodeType )
target[ name ] = jQuery.extend( deep,
// Never move original objects, clone them
src || ( copy.length != null ? [ ] : { } )
, copy );
// Don't bring in undefined values
else if ( copy !== undefined )
target[ name ] = copy;
}
// Return the modified object
return target;
Coding in this way leads is extremely error-prone especially for open-souce projects where many others are modifying your code. Please stop it.
Love,
A dedicated user
I’m working on small JavaScript library for cross-(sub)domain communication through iframes for users of our platform and my target audience will have little to no JavaScript experience. While I’m going to have extensive documentation, my goal really is to make this library like HTML – easy to understand and pickup, without necessary having to consult a reference. Generally, I want one site creator (we call them affiliates) to look at another person’s site, say “How did he do that?”, view the source code, copy it, and implement it on her own site by altering the code.
My initial setup code (to configure/initialize) the library looked like this:
document.domain='mydomain.com';
Library.init('sub.domain.com',5166);
After looking back at it I thought about converting it to this:
document.domain='mydomain.com';
Library.setCommunity('sub.domain.com');
Library.setAffiliateSiteId(5166);
Library.init();
This is really clean and easy to understand but a little much if I need to add a ton more settings in the future.
My latest iteration looks like this:
document.domain='mydomain.com';
Library.configure({community:'sub.domain.com', affiliateSiteId: 5166, showErrors: true});
Although passing (JSON) objects around isn’t the most easy to understand for people with little or no JavaScript knowledge, it isn’t that unreadable and I really feel the pros outweighed the cons. In some of my methods there will be so many arguments that passing an object would be necessary and the user implementing the library is going to have to learn the syntax regardless. Plus passing data in a regular argument (x,y,z) format isn’t really good for optional parameters of which they’ll be a ton of in this library.
I’ve also decided to remove the initialize method and just auto-initialize once you’ve configured your settings. The more I can take away from the process the better.
Lastly, I added an optional error mode that will trigger an alert if you’re doing something wrong. The idea would be to disable it (remove or set it to false) when you’re ready for production. This may add substantial bloat to the codebase so it may not stay in – or I could keep this feature in the dev version of the code and recommend affiliates use the production version after the initial creation process.
Note: The library isn’t actually called “Library”. I’m hiding it’s name until it’s ready. ;)
I came across an interesting bug in someone’s code today. They had an iframe that was dynamically created and it’s url was different depending on whether the variable home was true or not. They had declared home a few lines earlier but the condition was always returning true no matter what. Turns out, there is already a variable of the same name in Mozilla-based browsers. It’s a function that when called, takes you to your home page set in your browser preferences.
I don’t have time to hack around with it at the moment, but I’d imagine that this is a slight security risk. Similar to Jeremiah Grossman’s CSS History Hack that can potentially tell all the sites you’ve visited recently, this one would tell what user’s home pages were set to. Initial thoughts are that this would be hard to do since the only way to call this function without leaving the page is to call it in an iframe (tried it and it works) but iframe sandboxing restrictions prevent code from the parent frame from seeing or accessing the contents of this iframe (or it’s window.location object) since it’s from a different domain. A quick check shows that this function doesn’t exist in Safari or Internet Explorer.