Skip to content

Commit

Permalink
Add nohost.js shims
Browse files Browse the repository at this point in the history
  • Loading branch information
humphd committed Apr 4, 2014
1 parent 5f2d4d9 commit 67a8863
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 8 deletions.
137 changes: 137 additions & 0 deletions shims/nohost.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
var nohost = (function(window) {

// The server's filesystem
var Filer = require('filer');
var Path = Filer.Path;
var fs;

// List of FSWatchers for files in the filesystem
var watchers = {};

// Content helpers from the nohost source
var Content = require('content');

// Path and Dir for current nohost url
var nohostPath = document.location.search.substring(1);
var nohostDir = Path.dirname(nohostPath);

// Change a url to a path in the filesystem to a Blob URL
function rewriteURL(url, encoding, callback) {
if(!url || /\:?\/\//.test(url) || /\s*data\:/.test(url)) {
return callback(null, url);
}

var path = Path.resolve(nohostDir, url);
fs.exists(path, function(found) {
if(!found) {
return callback(null, url);
}

fs.readFile(path, null, function(err, data) {
if(err) {
return callback(null, url);
}
var mime = Content.mimeFromExt(Path.extname(path));
callback(null, Content.toDataURL(data, mime));
});
});
}

// Alter <img> to intercept paths in the filesystem
function rewireImage() {
var $Image = window.Image;

function Image(w, h) {
var img = new $Image(w, h);
Object.defineProperty(img, 'src', {
get: function() {
return img.getAttribute('src');
},
set: function(value) {
rewriteURL(value, null, function(err, url) {
img.setAttribute('src', url);
});
}
});
return img;
}
window.Image = Image;
}

// Alter <img> to intercept paths in the filesystem
function rewireScript() {
var $Image = window.Image;

function Image(w, h) {
var img = new $Image(w, h);
Object.defineProperty(img, 'src', {
get: function() {
return img.getAttribute('src');
},
set: function(value) {
rewriteURL(value, null, function(err, url) {
img.setAttribute('src', url);
});
}
});
return img;
}
window.Image = Image;
}

// Setup auto-reload behaviour for watched paths
function startWatchers() {
function setupWatchers(watchList) {
watchList.forEach(function(path) {
if(watchers[path]) {
return;
}
watchers[path] = fs.watch(path, function(event, filename) {
Object.keys(watchers).forEach(function(path) {
watchers[path].close();
});
document.location.reload();
});
});
}

// Get possible list of paths to watch from `data-nohost-watchlist` attribute
var scripts = document.getElementsByTagName('script');
for(var i = 0, l = scripts.length; i < l; i++){
var watchList = scripts[i].getAttribute('data-nohost-watchlist');
if(watchList && watchList.length) {
watchList = watchList.split(',');
setupWatchers(watchList);
break;
}
}
}

// Remove things we've added to the JS env that may not be needed
function cleanEnv() {
delete window.define;
delete window.require;
}

function init() {
// Our require setup is still alive in the DOM, so we can get at Filer
fs = new Filer.FileSystem({
provider: new Filer.FileSystem.providers.Fallback()
});


rewireScript();
rewireImage();
startWatchers();
cleanEnv();
}

init();

return {
fs: function() {
return fs;
}
};

}(window));
65 changes: 58 additions & 7 deletions src/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ define(['filer', 'async', 'log', 'content'],
function(Filer, Async, Log, Content) {

var Path = Filer.Path;
var watchList = {};

/**
* Open, Write to the document stream, and Close.
Expand All @@ -13,6 +14,20 @@ function(Filer, Async, Log, Content) {
document.close();
}

/**
* Read a file and maybe setup a watch for changes to trigger a reload.
*/
function _readFile(fs, path, encoding, callback) {
fs.readFile(path, encoding, function(err, data) {
if(err) {
return callback(err);
}

watchList[path] = true;
callback(null, data);
});
}

/**
* Given an HTML string, rewrite it with inline resources
*/
Expand All @@ -37,7 +52,7 @@ function(Filer, Async, Log, Content) {
return cb();
}

fs.readFile(path, function(err, data) {
_readFile(fs, path, null, function(err, data) {
if(err) {
return cb(err);
}
Expand Down Expand Up @@ -90,7 +105,7 @@ function(Filer, Async, Log, Content) {
return cb();
}

fs.readFile(path, 'utf8', function(err, data) {
_readFile(fs, path, 'utf8', function(err, data) {
if(err) {
return cb(err);
}
Expand Down Expand Up @@ -124,7 +139,7 @@ function(Filer, Async, Log, Content) {
return cb();
}

fs.readFile(path, 'utf8', function(err, data) {
_readFile(fs, path, 'utf8', function(err, data) {
if(err) {
return cb(err);
}
Expand All @@ -142,7 +157,7 @@ function(Filer, Async, Log, Content) {
callback();
});
},
function styles(callback) {
function inlineStyles(callback) {
var elems = doc.querySelectorAll('style');

Async.eachSeries(elems, function(elem, cb) {
Expand All @@ -166,6 +181,31 @@ function(Filer, Async, Log, Content) {
callback();
});
},
function styleAttributes(callback) {
var elems = doc.querySelectorAll('[style]');

Async.eachSeries(elems, function(elem, cb) {
var content = elem.getAttribute('style');
if(!content) {
cb();
return;
}

_processCSS(content, path, fs, function(err, css) {
if(err) {
Log.error(err);
}
elem.setAttribute('style', css);
cb();
});
}, function(err) {
if(err) {
Log.error(err);
}
callback();
});

},
function imgs(callback) {
rewriteElements('img', 'src', null, callback);
},
Expand All @@ -180,6 +220,17 @@ function(Filer, Async, Log, Content) {
},
function audios(callback) {
rewriteElements('audio', 'src', null, callback);
},
function nohost(callback) {
// Inject nohost scripts and shims to override native behaviour
var head = doc.getElementsByTagName('head')[0];
var nohostScript = doc.createElement('script');
var nohostWatchlist = Object.keys(watchList).toString();

nohostScript.src = 'shims/nohost.js';
nohostScript.setAttribute('data-nohost-watchlist', nohostWatchlist);
head.insertBefore(nohostScript, head.firstChild);
callback();
}
], function(err, result) {
// Return the processed HTML
Expand All @@ -205,7 +256,7 @@ function(Filer, Async, Log, Content) {
}

var filename = input.splice(0,1)[0];
fs.readFile(Path.resolve(dir, filename), function(err, data) {
_readFile(fs, Path.resolve(dir, filename), null, function(err, data) {
if(err) {
return next("failed on " + path, replacements);
}
Expand Down Expand Up @@ -347,7 +398,7 @@ function(Filer, Async, Log, Content) {
* Send the raw file, making it somewhat more readable
*/
handleFile: function(path, fs) {
fs.readFile(path, 'utf8', function(err, data) {
_readFile(fs, path, 'utf8', function(err, data) {
if(err) {
Log.error('unable to read `' + path + '`');
this.handle404(path);
Expand Down Expand Up @@ -461,7 +512,7 @@ function(Filer, Async, Log, Content) {
* HTML files need to have external resources inlined
*/
handleHTML: function(path, fs) {
fs.readFile(path, 'utf8', function(err, html) {
_readFile(fs, path, 'utf8', function(err, html) {
if(err) {
Log.error('unable to read `' + path + '`');
this.handle404(path);
Expand Down
2 changes: 1 addition & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ requirejs(['filer', 'webserver'], function(Filer, WebServer) {
* ?/path/to/file --> serves a path from the web root
*/
function boot() {
var bootOption = location.search.substring(1).split('=');
var bootOption = document.location.search.substring(1).split('=');
var option = bootOption[0];
var value = bootOption[1];

Expand Down

0 comments on commit 67a8863

Please sign in to comment.