diff --git a/packages/flab/LAB.src.js b/packages/flab/LAB.src.js new file mode 100644 index 000000000..e0a0874f1 --- /dev/null +++ b/packages/flab/LAB.src.js @@ -0,0 +1,516 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ + +(function(global){ + var _$LAB = global.$LAB, + + // constants for the valid keys of the options object + _UseLocalXHR = "UseLocalXHR", + _AlwaysPreserveOrder = "AlwaysPreserveOrder", + _AllowDuplicates = "AllowDuplicates", + _CacheBust = "CacheBust", + /*!START_DEBUG*/_Debug = "Debug",/*!END_DEBUG*/ + _BasePath = "BasePath", + + // stateless variables used across all $LAB instances + root_page = /^[^?#]*\//.exec(location.href)[0], + root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0], + append_to = document.head || document.getElementsByTagName("head"), + + // inferences... ick, but still necessary + opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") || ("MozAppearance" in document.documentElement.style), + +/*!START_DEBUG*/ + // console.log() and console.error() wrappers + log_msg = function(){}, + log_error = log_msg, +/*!END_DEBUG*/ + + // feature sniffs (yay!) + test_script_elem = document.createElement("script"), + explicit_preloading = typeof test_script_elem.preload == "boolean", // http://wiki.whatwg.org/wiki/Script_Execution_Control#Proposal_1_.28Nicholas_Zakas.29 + real_preloading = explicit_preloading || (test_script_elem.readyState && test_script_elem.readyState == "uninitialized"), // will a script preload with `src` set before DOM append? + script_ordered_async = !real_preloading && test_script_elem.async === true, // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order + + // XHR preloading (same-domain) and cache-preloading (remote-domain) are the fallbacks (for some browsers) + xhr_or_cache_preloading = !real_preloading && !script_ordered_async && !opera_or_gecko + ; + +/*!START_DEBUG*/ + // define console wrapper functions if applicable + if (global.console && global.console.log) { + if (!global.console.error) global.console.error = global.console.log; + log_msg = function(msg) { global.console.log(msg); }; + log_error = function(msg,err) { global.console.error(msg,err); }; + } +/*!END_DEBUG*/ + + // test for function + function is_func(func) { return Object.prototype.toString.call(func) == "[object Function]"; } + + // test for array + function is_array(arr) { return Object.prototype.toString.call(arr) == "[object Array]"; } + + // make script URL absolute/canonical + function canonical_uri(src,base_path) { + var absolute_regex = /^\w+\:\/\//; + + // is `src` is protocol-relative (begins with // or ///), prepend protocol + if (/^\/\/\/?/.test(src)) { + src = location.protocol + src; + } + // is `src` page-relative? (not an absolute URL, and not a domain-relative path, beginning with /) + else if (!absolute_regex.test(src) && src.charAt(0) != "/") { + // prepend `base_path`, if any + src = (base_path || "") + src; + } + // make sure to return `src` as absolute + return absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src); + } + + // merge `source` into `target` + function merge_objs(source,target) { + for (var k in source) { if (source.hasOwnProperty(k)) { + target[k] = source[k]; // TODO: does this need to be recursive for our purposes? + }} + return target; + } + + // does the chain group have any ready-to-execute scripts? + function check_chain_group_scripts_ready(chain_group) { + var any_scripts_ready = false; + for (var i=0; i 0) { + for (var i=0; i=0;) { + val = queue.shift(); + $L = $L[val.type].apply(null,val.args); + } + return $L; + }, + + // rollback `[global].$LAB` to what it was before this file was loaded, the return this current instance of $LAB + noConflict:function(){ + global.$LAB = _$LAB; + return instanceAPI; + }, + + // create another clean instance of $LAB + sandbox:function(){ + return create_sandbox(); + } + }; + + return instanceAPI; + } + + // create the main instance of $LAB + global.$LAB = create_sandbox(); + + + /* The following "hack" was suggested by Andrea Giammarchi and adapted from: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html + NOTE: this hack only operates in FF and then only in versions where document.readyState is not present (FF < 3.6?). + + The hack essentially "patches" the **page** that LABjs is loaded onto so that it has a proper conforming document.readyState, so that if a script which does + proper and safe dom-ready detection is loaded onto a page, after dom-ready has passed, it will still be able to detect this state, by inspecting the now hacked + document.readyState property. The loaded script in question can then immediately trigger any queued code executions that were waiting for the DOM to be ready. + For instance, jQuery 1.4+ has been patched to take advantage of document.readyState, which is enabled by this hack. But 1.3.2 and before are **not** safe or + fixed by this hack, and should therefore **not** be lazy-loaded by script loader tools such as LABjs. + */ + (function(addEvent,domLoaded,handler){ + if (document.readyState == null && document[addEvent]){ + document.readyState = "loading"; + document[addEvent](domLoaded,handler = function(){ + document.removeEventListener(domLoaded,handler,false); + document.readyState = "complete"; + },false); + } + })("addEventListener","DOMContentLoaded"); + +})(this); diff --git a/packages/flab/README.md b/packages/flab/README.md new file mode 100644 index 000000000..f2c641d3d --- /dev/null +++ b/packages/flab/README.md @@ -0,0 +1,5 @@ +Future Loading and Blocking JS + +This is based on the awesome [LABjs](https://github.com/getify/LABjs) package. + +TODO: Flesh out this README. :grin: diff --git a/packages/flab/index.js b/packages/flab/index.js new file mode 100644 index 000000000..e80f71bb9 --- /dev/null +++ b/packages/flab/index.js @@ -0,0 +1,4 @@ +module.exports = { + src: require("./string/src"), + min: require("./string/min"), +} diff --git a/packages/flab/minify.js b/packages/flab/minify.js new file mode 100644 index 000000000..984bd8dae --- /dev/null +++ b/packages/flab/minify.js @@ -0,0 +1,11 @@ +require("get-stdin")().then(src => console.log( + "\n"+ + "/*! LAB.js (LABjs :: Loading And Blocking JavaScript)\n"+ + " v2.0.3 (c) Kyle Simpson\n"+ + " MIT License\n"+ + "*/\n"+ + require("uglify-js").minify( + src.replace(/\/\*!START_DEBUG(?:.|[\n\r])*?END_DEBUG\*\//g, ""), + {fromString: true} + ).code +)); diff --git a/packages/flab/package.json b/packages/flab/package.json new file mode 100644 index 000000000..36ed8b609 --- /dev/null +++ b/packages/flab/package.json @@ -0,0 +1,39 @@ +{ + "name": "flab", + "version": "0.0.1", + "description": "Future Loading and Blocking JS", + "main": "index.js", + "files": [ + "index.js", + "string" + ], + "scripts": { + "prepublish": "mkdir -p string && npm run build-src && npm run build-min", + "build-src": "node stringify.js < LAB.src.js > string/src.js", + "build-min": "node minify.js < LAB.src.js | node stringify.js > string/min.js", + "clean": "rimraf string", + "test": "echo \"No tests yet...\"" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/redfin/react-server.git" + }, + "keywords": [ + "loading", + "blocking", + "js", + "asynchronous", + "loader" + ], + "author": "Bo Borgerson", + "license": "MIT", + "bugs": { + "url": "https://github.com/redfin/react-server/issues" + }, + "homepage": "https://github.com/redfin/react-server#readme", + "devDependencies": { + "get-stdin": "^5.0.1", + "rimraf": "^2.5.4", + "uglify-js": "^2.7.5" + } +} diff --git a/packages/flab/stringify.js b/packages/flab/stringify.js new file mode 100644 index 000000000..864dc0b26 --- /dev/null +++ b/packages/flab/stringify.js @@ -0,0 +1,8 @@ +require("get-stdin")().then(src => console.log( + 'module.exports = ' + src + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/^/gm, '"') + .replace(/$/gm, '\\n"+')+ + '"";' +)); diff --git a/packages/react-server/core/renderMiddleware.js b/packages/react-server/core/renderMiddleware.js index aa9814f06..921841701 100644 --- a/packages/react-server/core/renderMiddleware.js +++ b/packages/react-server/core/renderMiddleware.js @@ -6,7 +6,7 @@ var logger = require('./logging').getLogger(__LOGGER__), RequestContext = require('./context/RequestContext'), RequestLocalStorage = require('./util/RequestLocalStorage'), RLS = RequestLocalStorage.getNamespace(), - LABString = require('./util/LABString'), + flab = require('flab'), Q = require('q'), config = require('./config'), ExpressServerRequest = require("./ExpressServerRequest"), @@ -484,7 +484,7 @@ function renderScriptsAsync(scripts, res) { if (!RLS().didLoadLAB){ // This is the full implementation of LABjs. - res.write(LABString); + res.write(flab.min); // We always want scripts to be executed in order. res.write("$LAB.setGlobalDefaults({AlwaysPreserveOrder:true});"); diff --git a/packages/react-server/core/util/LABString.js b/packages/react-server/core/util/LABString.js deleted file mode 100644 index c10a68527..000000000 --- a/packages/react-server/core/util/LABString.js +++ /dev/null @@ -1,11 +0,0 @@ -// Careful: We've got a local patch here! -// -// We've added support for a `crossOrigin` parameter. -// -module.exports = ` -/*! LAB.js (LABjs :: Loading And Blocking JavaScript) - v2.0.3 (c) Kyle Simpson - MIT License -*/ -(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\\//.exec(location.href)[0],D=/^\\w+\\:\\/\\/\\/?[^\\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\\w+\\:\\/\\//;if(/^\\/\\/\\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); -`; diff --git a/packages/react-server/package.json b/packages/react-server/package.json index 961ce9abe..374e7dd75 100644 --- a/packages/react-server/package.json +++ b/packages/react-server/package.json @@ -29,6 +29,7 @@ "cookie-parser": "1.4.3", "eventemitter3": "^2.0.2", "express-state": "^1.4.0", + "flab": "^0.0.1", "lodash": "^4.16.4", "mobile-detect": "^1.3.5", "q": "1.4.1",