Another Example of my SyncArray

November 2, 2011

I refer you to my original post with my SyncArray code

function getSubdirs = function(path, callback){ fs.readdir(path, function(err, files){ var sync = new SyncArray(files); var subdirs = []; sync.forEach(function(file, index, array, finishedOne){ fs.stat(file, function(err, stats){ if (stats.isDirectory()){ subdirs.push(file); } finishedOne(); }); }, function(){ callback(subdirs); }); }); }
Comments

Messing Around in JS Object - Prevent Extensions

October 25, 2011

Lately I've been messing around with Javascript Object in Javascript 1.8.5 or greater. It's interesting but I've come across some peculiarities.

Object.preventExtensions tests

function d(){ this._test = 5; } //Object.preventExtensions(d.prototype); if (Object.isExtensible(d.prototype)){ Object.defineProperty(d.prototype, "test", { get: function(){ console.log("get called"); return this._test; }, enumerable: false, configurable: true } ); } var s = new d(); Object.preventExtensions(s); s.test = 6; console.log(s.test); s.jason = "jason"; console.log(s.jason);

Calling preventExtensions on d.prototype, the "test" property is never defined with the getter, so console.log(s.test) won't also log "get called". Object.preventExtensions(d.prototype) does not prevent extensions on instances of d. This one was weird to me, but they are two separate objects, so creating the property "jason" on "s" works unless I prevent extensions on "s". When preventExtensions is called on "s", console.log(s.test) prints 5 and console.log(s.jason) does not error out although it raises an error if I try to extend the prototype when it is not extensible.

Just some observations... This is the first in a series of posts on Object and its new methods in 1.8.5, I feel I need to know everything about it

Comments

Updated JS Combiner with JSMin!!

October 20, 2011

I updated my JS combiner functionality in the web server with JSMin! I found a Node.js JSMin implementation, added the line of code that minifies my combined file, and writes that out as the file to download. The one instance combined three big jQuery libraries that are used on almost every page, and it went from 37KB to 26KB. Before it was comparable in size to 37KB (probably identical) but it was three calls to the web server. This should limit the calls a ton.

Comments

Synchronized Array Access in Node.JS

October 19, 2011

I couldn't think of a good title for this one, but here's what I needed to accomplish: To loop over an array, but not moving onto the next element until finished with the current one. This is simple in synchronous, blocking code, but once you get to the world of Node.js and non-blocking calls going on everywhere, it becomes wildly more difficult. One could add the word "Semaphore" to this post and it wouldn't be too off the wall.

Take the following code for example:

var array = ["www.google.com", "www.yahoo.com", "www.microsoft.com", "www.jasontconnell.com", "givit.me"]; array.forEach(function(d, i){ http.get({ host: d, port: 80, path: "/" }, function(res){ console.log("index " + i + " got the response for " + d); }); });

Add in the appropriate "require" statements, run the code, and this is what I got the first time I ran it:

index = 0 - got response for www.google.com index = 3 - got response for givit.me index = 2 - got response for www.microsoft.com index = 4 - got response for www.jasontconnell.com index = 1 - got response for www.yahoo.com

That is not a predictable order! So how do we fix this? EventEmitter!! This took me a surprisingly small amount of time to figure out. Here's the code:

var http = require("http"); var EventEmitter = require("events").EventEmitter; var sys = require("sys"); function SyncArray(array){ this.array = array }; require("util").inherits(SyncArray, EventEmitter) SyncArray.prototype.forEach = function(callback, finishedCallback){ var self = this; this.on("nextElement", function(index, callback, finishedCallback){ self.next(++index, callback, finishedCallback); }); this.on("finished", function(){ }); self.next(0, callback, finishedCallback); } SyncArray.prototype.next = function(index, callback, finishedCallback){ var self = this; var obj = index < self.array.length ? self.array[index] : null; if (obj){ callback(obj, index, self.array, function(){ self.emit("nextElement", index, callback, finishedCallback); }); } else { finishedCallback(); self.emit("finished"); } } var array = ["www.google.com","www.yahoo.com", "www.microsoft.com", "givit.me", "www.jasontconnell.com"]; var sync = new SyncArray(array); sync.forEach(function(d, i, array, finishedOne){ http.get({ host: d, port: 80, path: "/" }, function(res){ console.log("index = " + i + " - got response from " + d ); finishedOne(); }); }, function(){ console.log("finished the sync array foreach loop"); });

And the output as we expected:

index = 0 - got response from www.google.com index = 1 - got response from www.yahoo.com index = 2 - got response from www.microsoft.com index = 3 - got response from givit.me index = 4 - got response from www.jasontconnell.com finished the sync array foreach loop

Feel free to update this with best practices, better code, better way to do it in Node.js (perhaps built in?), etc, in the comments. I'm really excited about this since holy F#@$ that's been frustrating!

Comments

Node.js Javascript Combining

October 12, 2011

The practice of combining and minifying JS files is one that should be done at all times. There's two ways to do this, one involves creating the combined file manually. The other involves fun and less future work.

I write my JS into many different files, breaking down functionality, but the client doesn't need to see that.

On my web server, I have a server side tag library that allows me to do stuff like include other files (so I can break up development into components), run "foreach" statements over arrays, conditional processing, and other stuff. I've added to this arsenal with a "combine" tag.

It's used like this:

<jsn:combine> /js/file1.js /js/file2.js /js/file3.js </jsn:combine>

These files get combined and added to the /js/combine/ folder, and the script tag gets written out in the response. This checks each time if one of the js files has been updated since the combined javascript file has been written.

There would be way too much code to spit out to show how all of the tags and the server works, so I'll just show the code that does the combine

var combineRegex = /(.*?\.js)\n/g; var rootdir = site.path + site.contentRoot; var combineFolder = "/js/combine/"; var file = combineFolder + jsnContext.localScope + ".js"; var writeTag = function(jsnContext, file){ var scriptTag = "<script type=\"text/javascript\" src=\"" + file + "\"</script>"; jsnContext.write(scriptTag); callback(jsnContext); } var combineStats = filesystem.fileStats(rootdir + file); var writeFile = combineStats == null; var altroot = null; if (site.config.isMobile && site.main != null){ altroot = site.path + site.main.contentRoot; } var paths = []; while ((match = combineRegex.exec(files)) != null){ var localPath = match[1].trim(); var fullPath = rootdir + localPath; var origStats = filesystem.fileStats(fullPath); var altStats = filesystem.fileStats(altroot+localPath); // add paths and determine if we should overwrite / create the combined file if (origStats != null){ paths.push(fullPath); writeFile = writeFile || combineStats.mtime < origStats.mtime; } else if (altroot != null && altStats != null){ paths.push(altroot+localPath); writeFile = writeFile || combineStats.mtime < altStats.mtime; } } writeTag(jsnContext, file); if (writeFile){ filesystem.ensureFolder(rootdir + combineFolder, function(success){ if (success){ filesystem.readFiles(paths, function(content){ filesystem.writeFile(rootdir+file, content, function(success){ }); }); } }); }

Some other stuff going on... my server has built in mobile site support. You can specify "this is the mobile site for this full site" and "for this mobile site, this is the main site", both running independent but they know of each other. I can have the file /js/mobile-only.js combined on the mobile site, where that file exists in the mobile site's content root, and I can have the file /js/main-site-only.js combined on mobile, where that file only exists on the main site.

Some nice to haves would be to also minify the files on the fly.

Comments

Javascript parseInt gotcha...

August 4, 2011

I was parsing an international date format, like this: 2011-08-04T11:23:21.345Z. After getting back December as the month for August, I was mildly perplexed. Here was the issue

From the Mozilla docs for parseInt: If the input string begins with "0", radix is eight (octal). This feature is non-standard, and some implementations deliberately do not support it (instead using the radix 10). For this reason always specify a radix when using parseInt.

Always specify a radix!!

Comments

Can you break it?

June 23, 2011

<script type="text/javascript">
function cleanseField(str){
str = str.replace(/<(/?)(b|i|u|em)>/ig,"_$1$2_").replace(/<.*?>/g,"").replace(/_(/?)(b|i|u|em)_/ig,"<$1$2>");
return str;
} function doit(){
var str = "<h3>Header</h3>Hello, my name is <eM>jason connell</Em>. <b>You</B> are awesome, like <i>italics</i><<scr"+"ipt>script type='text/javascript'>alert('sup');</sc"+"ript </scr"+"ipt>><u>underline</u>";

var spn = document.getElementById("test");
spn.innerHTML += "<h1>Original</h1>" + str;
spn.innerHTML += "<h1>Cleansed</h1>" + cleanseField(str);
}
</script>

<a href="javascript:void(0);" onclick="doit();">Doit</a>
<span id="test"></span>


Trying to cleanse html but keep certain elements for now. Tested in Chrome and IE 8, but is there a string that will still put out a valid script tag? It's very important.

If only there was a way in HTML that you can specify, "scripts are only valid inside this segment of the page". It's a shame that you could execute a script anywhere.

Comments