- welcome
-
Case-Hardened JavaScript:
the Short, Fast Version
(Fifteen Slides in Fifteen Minutes)
(Including This One)
Kent Brewster
Web Guy, API Agitator, and Technology Evangelist, Netflix
Online: http://kentbrewster.com
Twitter: @kentbrew
- what-we-gonna-do-tonight-brain
-
In the Next Fifteen Fourteen Minutes We Will:
- Build a Badge
- One Line of JavaScript to Include
- No
document.write!
- Accept User Parameters
- Create Structure, Presentation, and Behavior
- Query Outside APIs
- Allow for Further Interaction
Our Target for Tonight:
Twitter Search
- ye-demo
-
- housekeeping
-
Getting Started
- Create one anonymous function, with closure
- Inside it, create a randomly-named global variable, to allow for callbacks and make namespace collisions highly unlikely
- Hang another anonymous function off your random global, and create a
$ shortcut, to help keep things simple
To Run:
- Find the
SCRIPT tag on the page
- Insert a
DIV to contain the badge
- Remove the
SCRIPT node
- Quit here -- other copies of the same script will ignore each other
- Wait politely until the page loads before firing off
- houskeeeping-code
-
( function() {
var trueName = '';
for (var i = 0; i < 16; i++) {
trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
window[trueName] = {};
var $ = window[trueName];
$.d = document;
$.f = function() {
return {
init : function(target) {
var k = $.d.getElementsByTagName('SCRIPT');
var n = k.length;
for (var i = 0; i < n; i++) {
if (k[i].src.match(target)) {
$.s = $.d.createElement('DIV');
k[i].parentNode.insertBefore($.s, k[i]);
k[i].parentNode.removeChild(k[i]);
break;
}
}
}
}
}();
var thisScript = /behavior.js$/;
if(typeof window.addEventListener !== 'undefined') {
window.addEventListener('load', function() {
$.f.init(thisScript);
}, false);
} else if(typeof window.attachEvent !== 'undefined') {
window.attachEvent('onload', function() {
$.f.init(thisScript);
});
}
})();
- structure
-
Build the Badge
- Header, containing
INPUT box
- Results area, hidden until something shows up
- Footer, with link back to where interested readers can get the badge. Also hidden until results show up.
- structure-code
-
( function() {
var trueName = '';
for (var i = 0; i < 16; i++) {
trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
window[trueName] = {};
var $ = window[trueName];
$.d = document;
$.f = function() {
return {
init : function(target) {
var k = $.d.getElementsByTagName('SCRIPT');
var n = k.length;
for (var i = 0; i < n; i++) {
if (k[i].src.match(target)) {
$.s = $.d.createElement('DIV');
$.f.buildStructure();
k[i].parentNode.insertBefore($.s, k[i]);
k[i].parentNode.removeChild(k[i]);
break;
}
}
},
buildStructure : function() {
$.s.className = trueName;
$.s.q = $.d.createElement('INPUT');
$.s.appendChild($.s.q);
$.s.r = $.d.createElement('UL');
$.s.r.className = 'hidden';
$.s.appendChild($.s.r);
$.s.f = $.d.createElement('P');
$.s.f.className = 'hidden';
var a = $.d.createElement('A');
a.innerHTML = 'get this';
a.target = '_blank';
a.href = 'http://kentbrewster.com/twitter-search-badge';
$.s.f.appendChild(a);
$.s.appendChild($.s.f);
}
}
}();
var thisScript = /behavior.js$/;
if(typeof window.addEventListener !== 'undefined') {
window.addEventListener('load', function() {
$.f.init(thisScript);
}, false);
} else if(typeof window.attachEvent !== 'undefined') {
window.attachEvent('onload', function() {
$.f.init(thisScript);
});
}
})();
- config
-
Pass Some Variables
- Create a root object to hold all variables,
$.a
- Read the
SCRIPT tag's innerHTML*
- Carefully run this by Hedger Wang's marvelous JSON parser
- If it looks like a good JSON object, use it
- Set default values whether or not they're explicitly declared
* Right, I know: technically there's no such thing. But it works cross-browser.
- config-code
-
( function() {
var trueName = '';
for (var i = 0; i < 16; i++) {
trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
window[trueName] = {};
var $ = window[trueName];
$.d = document;
$.f = function() {
return {
init : function(target) {
var k = $.d.getElementsByTagName('SCRIPT');
var n = k.length;
for (var i = 0; i < n; i++) {
if (k[i].src.match(target)) {
$.s = $.d.createElement('DIV');
$.a = {};
if (k[i].innerHTML) {
$.a = $.f.parseJson(k[i].innerHTML);
}
$.f.houseKeep();
$.f.buildStructure();
k[i].parentNode.insertBefore($.s, k[i]);
k[i].parentNode.removeChild(k[i]);
break;
}
}
},
buildStructure : function() {
$.s.className = trueName;
$.s.q = $.d.createElement('INPUT');
$.s.appendChild($.s.q);
$.s.r = $.d.createElement('UL');
$.s.r.className = 'hidden';
$.s.appendChild($.s.r);
$.s.f = $.d.createElement('P');
$.s.f.className = 'hidden';
var a = $.d.createElement('A');
a.innerHTML = 'get this';
a.target = '_blank';
a.href = 'http://kentbrewster.com/twitter-search-badge';
$.s.f.appendChild(a);
$.s.appendChild($.s.f);
},
parseJson : function(json) {
this.parseJson.data = json;
if ( typeof json !== 'string') {
return {"err":"trying to parse a non-string JSON object"};
}
try {
var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',
'Array,String,Math,RegExp,Image,ActiveXObject;',
'return (' , json.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function') , ');'].join(''));
return f();
} catch (e) {
return {"err":"trouble parsing JSON object; running with defaults"};
}
},
houseKeep : function() {
var defaults = {
"count" : 10,
"height" : 350,
"width" : 300,
"background" : "white",
"border" : "1px solid black",
"headerBackground" : "#ffa",
"headerColor" : "#000",
"evenBackground" : "#fff",
"oddBackground" : "#eee",
"linkColor" : "#00e",
"linkColorVisited" : "#551A8B",
"padding" : 5
};
for (var d in defaults) {
if ($.a[d] === undefined) {
$.a[d] = defaults[d];
}
}
$.p = [];
}
}
}();
var thisScript = /behavior.js$/;
if(typeof window.addEventListener !== 'undefined') {
window.addEventListener('load', function() {
$.f.init(thisScript);
}, false);
} else if(typeof window.attachEvent !== 'undefined') {
window.attachEvent('onload', function() {
$.f.init(thisScript);
});
}
})();
- outside-data
-
Hook Up the Outside API
- Check the
INPUT box every second
- If there's a new query, run it
- If the query has been blanked out, hide everything.
- Create a callback to handle the data when it shows up
- Append a new
SCRIPT tag that calls the outside API and asks for data in the callback you created in the previous step **
- When the return shows up, throw up an alert (which we'll fix later). Then delete the callback and the extra
SCRIPT node, to keep things clean
** Obviously this is an insecure method. Do not do this unless you completely trust your data source not to throw you something sneaky that might hijack your client's browser session!
- outside-data-code
-
( function() {
var trueName = '';
for (var i = 0; i < 16; i++) {
trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
window[trueName] = {};
var $ = window[trueName];
$.d = document;
$.f = function() {
return {
init : function(target) {
var k = $.d.getElementsByTagName('SCRIPT');
var n = k.length;
for (var i = 0; i < n; i++) {
if (k[i].src.match(target)) {
$.s = $.d.createElement('DIV');
$.a = {};
if (k[i].innerHTML) {
$.a = $.f.parseJson(k[i].innerHTML);
}
$.f.houseKeep();
$.f.buildStructure();
$.f.buildBehavior();
k[i].parentNode.insertBefore($.s, k[i]);
k[i].parentNode.removeChild(k[i]);
break;
}
}
},
buildStructure : function() {
$.s.className = trueName;
$.s.q = $.d.createElement('INPUT');
$.s.appendChild($.s.q);
$.s.r = $.d.createElement('UL');
$.s.r.className = 'hidden';
$.s.appendChild($.s.r);
$.s.f = $.d.createElement('P');
$.s.f.className = 'hidden';
var a = $.d.createElement('A');
a.innerHTML = 'get this';
a.target = '_blank';
a.href = 'http://kentbrewster.com/twitter-search-badge';
$.s.f.appendChild(a);
$.s.appendChild($.s.f);
},
parseJson : function(json) {
this.parseJson.data = json;
if ( typeof json !== 'string') {
return {"err":"trying to parse a non-string JSON object"};
}
try {
var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',
'Array,String,Math,RegExp,Image,ActiveXObject;',
'return (' , json.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function') , ');'].join(''));
return f();
} catch (e) {
return {"err":"trouble parsing JSON object; running with defaults"};
}
},
houseKeep : function() {
var defaults = {
"count" : 10,
"height" : 350,
"width" : 300,
"background" : "white",
"border" : "1px solid black",
"headerBackground" : "#ffa",
"headerColor" : "#000",
"evenBackground" : "#fff",
"oddBackground" : "#eee",
"linkColor" : "#00e",
"linkColorVisited" : "#551A8B",
"padding" : 5
};
for (var d in defaults) {
if ($.a[d] === undefined) {
$.a[d] = defaults[d];
}
}
$.p = [];
},
buildBehavior : function() {
if ($.a.err) {
alert($.a.err);
} else {
setInterval($.f.runSearch, 1000);
}
},
runSearch : function() {
if ($.s.q.value) {
if ($.s.q.value !== $.lastQuery) {
$.lastQuery = $.s.q.value;
var n = $.p.length;
var id = trueName + '.p[' + n + ']';
$.p[n] = function(r) {
delete($.p[n]);
$.f.removeScript(id);
$.f.renderSearch(r);
};
var url = 'http://search.twitter.com/search.json?rpp=' + $.a.count + '&lang=en&q=' + encodeURIComponent($.s.q.value) + '&callback=' + id;
$.f.runScript(url, id);
}
} else {
if ($.lastQuery) {
$.lastQuery = '';
$.s.r.innerHTML = '';
$.s.r.className = $.s.f.className = 'hidden';
}
}
},
renderSearch : function(z) {
alert('Back with results!');
},
runScript : function(url, id) {
var s = $.d.createElement('script');
s.id = id;
s.type ='text/javascript';
s.src = url;
$.d.getElementsByTagName('body')[0].appendChild(s);
},
removeScript : function(id) {
if ($.d.getElementById(id)) {
var s = $.d.getElementById(id);
s.parentNode.removeChild(s);
}
}
}
}();
var thisScript = /behavior.js$/;
if(typeof window.addEventListener !== 'undefined') {
window.addEventListener('load', function() {
$.f.init(thisScript);
}, false);
} else if(typeof window.attachEvent !== 'undefined') {
window.attachEvent('onload', function() {
$.f.init(thisScript);
});
}
})();
- rendering
-
Rendering the Returned Data
- Render each tweet as a
LI
- Render the user name as a
CITE, with link to next search.
- Insert links and next-searches into the tweet itself, and render it as a
SPAN
- Link the date to the original tweet.
- Link the image to the user's home page.
- Be sure the containing
UL and the footer is visible afterwards.
- If nothing shows up, say so!
- rendering-code
-
( function() {
var trueName = '';
for (var i = 0; i < 16; i++) {
trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
window[trueName] = {};
var $ = window[trueName];
$.d = document;
$.f = function() {
return {
init : function(target) {
var k = $.d.getElementsByTagName('SCRIPT');
var n = k.length;
for (var i = 0; i < n; i++) {
if (k[i].src.match(target)) {
$.s = $.d.createElement('DIV');
$.a = {};
if (k[i].innerHTML) {
$.a = $.f.parseJson(k[i].innerHTML);
}
$.f.houseKeep();
$.f.buildStructure();
$.f.buildBehavior();
k[i].parentNode.insertBefore($.s, k[i]);
k[i].parentNode.removeChild(k[i]);
break;
}
}
},
buildStructure : function() {
$.s.className = trueName;
$.s.q = $.d.createElement('INPUT');
$.s.appendChild($.s.q);
$.s.r = $.d.createElement('UL');
$.s.r.className = 'hidden';
$.s.appendChild($.s.r);
$.s.f = $.d.createElement('P');
$.s.f.className = 'hidden';
var a = $.d.createElement('A');
a.innerHTML = 'get this';
a.target = '_blank';
a.href = 'http://kentbrewster.com/twitter-search-badge';
$.s.f.appendChild(a);
$.s.appendChild($.s.f);
},
parseJson : function(json) {
this.parseJson.data = json;
if ( typeof json !== 'string') {
return {"err":"trying to parse a non-string JSON object"};
}
try {
var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',
'Array,String,Math,RegExp,Image,ActiveXObject;',
'return (' , json.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function') , ');'].join(''));
return f();
} catch (e) {
return {"err":"trouble parsing JSON object; running with defaults"};
}
},
houseKeep : function() {
var defaults = {
"count" : 10,
"height" : 350,
"width" : 300,
"background" : "white",
"border" : "1px solid black",
"headerBackground" : "#ffa",
"headerColor" : "#000",
"evenBackground" : "#fff",
"oddBackground" : "#eee",
"linkColor" : "#00e",
"linkColorVisited" : "#551A8B",
"padding" : 5
};
for (var d in defaults) {
if ($.a[d] === undefined) {
$.a[d] = defaults[d];
}
}
$.p = [];
},
buildBehavior : function() {
if ($.a.err) {
alert($.a.err);
} else {
setInterval($.f.runSearch, 1000);
}
},
runSearch : function() {
if ($.s.q.value) {
if ($.s.q.value !== $.lastQuery) {
$.lastQuery = $.s.q.value;
var n = $.p.length;
var id = trueName + '.p[' + n + ']';
$.p[n] = function(r) {
delete($.p[n]);
$.f.removeScript(id);
$.f.renderSearch(r);
};
var url = 'http://search.twitter.com/search.json?rpp=' + $.a.count + '&lang=en&q=' + encodeURIComponent($.s.q.value) + '&callback=' + id;
$.f.runScript(url, id);
}
} else {
if ($.lastQuery) {
$.lastQuery = '';
$.s.r.innerHTML = '';
$.s.r.className = $.s.f.className = 'hidden';
}
}
},
stuffQuery : function(v, p) {
if (p) {
$.s.q.value = p + v;
} else {
$.s.q.value = v;
}
},
renderSearch : function(z) {
$.s.r.innerHTML = $.s.r.className = '';
var r = z.results;
if (r.length) {
var n = r.length;
for (var i = 0; i < n; i++) {
var li = $.d.createElement('LI');
var a = $.d.createElement('A');
a.href = 'http://twitter.com/' + r[i].from_user;
a.title = r[i].from_user + ' on Twitter';
a.target = '_twitter';
var img = $.d.createElement('IMG');
img.align = 'left';
img.src = r[i].profile_image_url;
a.appendChild(img);
li.appendChild(a);
var cite = $.d.createElement('CITE');
var a = $.d.createElement('A');
a.innerHTML = r[i].from_user;
a.onclick = function() {
$.s.q.value = 'from:' + this.innerHTML;
}
cite.appendChild(a);
li.appendChild(cite);
li.appendChild($.d.createTextNode(': '));
var span = $.d.createElement('SPAN');
var raw = r[i].text;
var cooked = raw.replace(/\/u([^ ]+)/gi, "$1;");
cooked = cooked.replace(/http:\/\/([^ ]+)/g, "<a href=\"http://$1\" target=\"_blank\">http://$1</a>");
var woo = '@<a onclick="' + trueName + '.f.stuffQuery(this.innerHTML, \'from:\');">$1</a>';
cooked = cooked.replace(/@([\w*]+)/g, woo);
var yay = '#<a onclick="' + trueName + '.f.stuffQuery(this.innerHTML);">$1</a>';
cooked = cooked.replace(/#([\w*]+)/g, yay)
span.innerHTML = cooked;
li.appendChild(span);
li.appendChild($.d.createTextNode(' '));
var date = $.d.createElement('DATE');
var a = $.d.createElement('A');
a.innerHTML = r[i].created_at.split(' +')[0];
a.href = 'http://twitter.com/' + r[i].from_user + '/status/' + r[i].id;
a.target = '_twitter';
date.appendChild(a);
li.appendChild(date);
$.s.r.appendChild(li);
}
} else {
var li = $.d.createElement('LI');
li.innerHTML = 'Got nothing, sorry!';
$.s.r.appendChild(li);
}
$.s.f.className = '';
},
runScript : function(url, id) {
var s = $.d.createElement('script');
s.id = id;
s.type ='text/javascript';
s.src = url;
$.d.getElementsByTagName('body')[0].appendChild(s);
},
removeScript : function(id) {
if ($.d.getElementById(id)) {
var s = $.d.getElementById(id);
s.parentNode.removeChild(s);
}
}
}
}();
var thisScript = /behavior.js$/;
if(typeof window.addEventListener !== 'undefined') {
window.addEventListener('load', function() {
$.f.init(thisScript);
}, false);
} else if(typeof window.attachEvent !== 'undefined') {
window.attachEvent('onload', function() {
$.f.init(thisScript);
});
}
})();
- presentation
-
And Finally: Making it Pretty
- Create a
STYLE node
- Loop through a set of rules
- Caution: many browser-specific gotchas lurk here!
- The empty rule is for the root object, which has
trueName as its class
- Do your best to reset fonts and selectors at the top; this is very hard to get right 100% of the time
- For best results, let as much of this go to default as possible and allow your users to sort it out
- presentation-code
-
( function() {
var trueName = '';
for (var i = 0; i < 16; i++) {
trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
window[trueName] = {};
var $ = window[trueName];
$.d = document;
$.f = function() {
return {
init : function(target) {
var k = $.d.getElementsByTagName('SCRIPT');
var n = k.length;
for (var i = 0; i < n; i++) {
if (k[i].src.match(target)) {
$.s = $.d.createElement('DIV');
$.a = {};
if (k[i].innerHTML) {
$.a = $.f.parseJson(k[i].innerHTML);
}
$.f.houseKeep();
$.f.buildStructure();
$.f.buildPresentation();
$.f.buildBehavior();
k[i].parentNode.insertBefore($.s, k[i]);
k[i].parentNode.removeChild(k[i]);
break;
}
}
},
buildStructure : function() {
$.s.className = trueName;
$.s.q = $.d.createElement('INPUT');
$.s.appendChild($.s.q);
$.s.r = $.d.createElement('UL');
$.s.r.className = 'hidden';
$.s.appendChild($.s.r);
$.s.f = $.d.createElement('P');
$.s.f.className = 'hidden';
var a = $.d.createElement('A');
a.innerHTML = 'get this';
a.target = '_blank';
a.href = 'http://kentbrewster.com/twitter-search-badge';
$.s.f.appendChild(a);
$.s.appendChild($.s.f);
},
parseJson : function(json) {
this.parseJson.data = json;
if ( typeof json !== 'string') {
return {"err":"trying to parse a non-string JSON object"};
}
try {
var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',
'Array,String,Math,RegExp,Image,ActiveXObject;',
'return (' , json.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function') , ');'].join(''));
return f();
} catch (e) {
return {"err":"trouble parsing JSON object; running with defaults"};
}
},
houseKeep : function() {
var defaults = {
"count" : 10,
"height" : 350,
"width" : 300,
"background" : "white",
"border" : "1px solid black",
"headerBackground" : "#ffa",
"headerColor" : "#000",
"evenBackground" : "#fff",
"oddBackground" : "#eee",
"linkColor" : "#00e",
"linkColorVisited" : "#551A8B",
"padding" : 5
};
for (var d in defaults) {
if ($.a[d] === undefined) {
$.a[d] = defaults[d];
}
}
$.p = [];
},
buildBehavior : function() {
if ($.a.err) {
alert($.a.err);
} else {
setInterval($.f.runSearch, 1000);
}
},
buildPresentation : function () {
var ns = $.d.createElement('style');
$.d.getElementsByTagName('head')[0].appendChild(ns);
if (!window.createPopup) {
ns.appendChild($.d.createTextNode(''));
ns.setAttribute("type", "text/css");
}
var s = $.d.styleSheets[$.d.styleSheets.length - 1];
var rules = {
"*" : "{margin:0;padding:0;text-align:left;color:#000;font:13px/1.2em tahoma,verdana,arial,helvetica,clean,sans-serif;}",
"" : "{zoom:1;width:" + ($.a.width) + "px;background:" + $.a.background + ";border:" + $.a.border + ";padding:5px;}",
"a" : "{text-decoration:none; color:" + $.a.linkColor + ";cursor:pointer;}",
"a:visited" : "{color:" + $.a.linkColorVisited + ";}",
"a:hover":"{text-decoration:underline;}",
"cite" : "{font-weight:bold;font-style:normal;}",
"ul" : "{list-style:none; margin-top:5px;height:" + $.a.height + "px;overflow-x:hidden;overflow-y:auto;border:1px solid #ccc;}",
"p" : "{font-size:92%;text-align:right;padding:" + $.a.padding + "px; margin:0;}",
"li" : "{margin:0;border-bottom:1px dashed #ccc;padding:" + $.a.padding + "px;}",
"li:hover" : "{background-color:#F7F7F7}",
"input":"{background: transparent url('http://twitter.com/favicon.ico') 0 50% no-repeat; text-indent:18px; width:100%;border:1px solid #ccc;}",
"date" : "{display:block;margin-top:" + $.a.padding + "px; text-align:right; font-family:georgia; font-style:italic; font-size:87%;}",
"date:after" : "{clear:both; content:\".\"; display:block; height:0; visibility:hidden; }",
"img" : "{margin:" + $.a.padding + "px " + $.a.padding + "px 0 0; height:36px; width:36px;border:1px solid #000;}",
".hidden" : "{display:none;}"
};
var ieRules = "";
for (r in rules) {
var selector = '.' + trueName + ' ' + r;
var t = rules[r].replace(/;/g, '!important;');
if (!window.createPopup) {
var theRule = $.d.createTextNode(selector + t);
ns.appendChild(theRule);
} else {
ieRules += selector + t;
}
}
if (window.createPopup) {
s.cssText = ieRules;
}
},
runSearch : function() {
if ($.s.q.value) {
if ($.s.q.value !== $.lastQuery) {
$.lastQuery = $.s.q.value;
var n = $.p.length;
var id = trueName + '.p[' + n + ']';
$.p[n] = function(r) {
delete($.p[n]);
$.f.removeScript(id);
$.f.renderSearch(r);
};
var url = 'http://search.twitter.com/search.json?rpp=' + $.a.count + '&lang=en&q=' + encodeURIComponent($.s.q.value) + '&callback=' + id;
$.f.runScript(url, id);
}
} else {
if ($.lastQuery) {
$.lastQuery = '';
$.s.r.innerHTML = '';
$.s.r.className = $.s.f.className = 'hidden';
}
}
},
stuffQuery : function(v, p) {
if (p) {
$.s.q.value = p + v;
} else {
$.s.q.value = v;
}
},
renderSearch : function(z) {
$.s.r.innerHTML = $.s.r.className = '';
var r = z.results;
if (r.length) {
var n = r.length;
for (var i = 0; i < n; i++) {
var li = $.d.createElement('LI');
var a = $.d.createElement('A');
a.href = 'http://twitter.com/' + r[i].from_user;
a.title = r[i].from_user + ' on Twitter';
a.target = '_twitter';
var img = $.d.createElement('IMG');
img.align = 'left';
img.src = r[i].profile_image_url;
a.appendChild(img);
li.appendChild(a);
var cite = $.d.createElement('CITE');
var a = $.d.createElement('A');
a.innerHTML = r[i].from_user;
a.onclick = function() {
$.s.q.value = 'from:' + this.innerHTML;
}
cite.appendChild(a);
li.appendChild(cite);
li.appendChild($.d.createTextNode(': '));
var span = $.d.createElement('SPAN');
var raw = r[i].text;
var cooked = raw.replace(/\/u([^ ]+)/gi, "$1;");
cooked = cooked.replace(/http:\/\/([^ ]+)/g, "<a href=\"http://$1\" target=\"_blank\">http://$1</a>");
var woo = '@<a onclick="' + trueName + '.f.stuffQuery(this.innerHTML, \'from:\');">$1</a>';
cooked = cooked.replace(/@([\w*]+)/g, woo);
var yay = '#<a onclick="' + trueName + '.f.stuffQuery(this.innerHTML);">$1</a>';
cooked = cooked.replace(/#([\w*]+)/g, yay)
span.innerHTML = cooked;
li.appendChild(span);
li.appendChild($.d.createTextNode(' '));
var date = $.d.createElement('DATE');
var a = $.d.createElement('A');
a.innerHTML = r[i].created_at.split(' +')[0];
a.href = 'http://twitter.com/' + r[i].from_user + '/status/' + r[i].id;
a.target = '_twitter';
date.appendChild(a);
li.appendChild(date);
$.s.r.appendChild(li);
}
} else {
var li = $.d.createElement('LI');
li.innerHTML = 'Got nothing, sorry!';
$.s.r.appendChild(li);
}
$.s.f.className = '';
},
runScript : function(url, id) {
var s = $.d.createElement('script');
s.id = id;
s.type ='text/javascript';
s.src = url;
$.d.getElementsByTagName('body')[0].appendChild(s);
},
removeScript : function(id) {
if ($.d.getElementById(id)) {
var s = $.d.getElementById(id);
s.parentNode.removeChild(s);
}
}
}
}();
var thisScript = /behavior.js$/;
if(typeof window.addEventListener !== 'undefined') {
window.addEventListener('load', function() {
$.f.init(thisScript);
}, false);
} else if(typeof window.attachEvent !== 'undefined') {
window.attachEvent('onload', function() {
$.f.init(thisScript);
});
}
})();
- th-th-that's-all-folks!
-
Thank You
- The folks at Yahoo and Netflix for bankrolling and encouraging the nuttiness
- Isaac Schleuter, Doug Crockford, Scott Schiller, Hedger Wang, Dave Ballmer, and John Resig, for constant inspiration and much patient advice.
- BayJax organizers, for letting me do it!
How to Reach Me Afterwards:
Twitter:
@kentbrew
Online:
http://kentbrewster.com