From 758976fb0712d40d4d75f500d6136596024ac72d Mon Sep 17 00:00:00 2001 From: guybedford Date: Fri, 29 Mar 2013 13:14:02 -0700 Subject: [PATCH] fixes #56 and #57 --- css-builder.js | 77 +++++++++++++++++------------------------ css.js | 94 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 101 insertions(+), 70 deletions(-) diff --git a/css-builder.js b/css-builder.js index 456d9fb..0fb2d32 100644 --- a/css-builder.js +++ b/css-builder.js @@ -89,45 +89,6 @@ define(['require', './normalize'], function(req, normalize) { .replace(/[\r]/g, "\\r"); } - var baseUrl; - var cssBase; - - var loadCSS = function(cssId, parse) { - if (!baseUrl) { - var baseParts = req.toUrl('base_url').split('/'); - baseParts.pop(); - baseUrl = baseParts.join('/') + '/'; - } - - var fileUrl = cssId; - - if (fileUrl.substr(fileUrl.length - 4, 4) != '.css' && !parse) - fileUrl += '.css'; - - fileUrl = req.toUrl(fileUrl); - - //external URLS don't get added (just like JS requires) - if (fileUrl.substr(0, 7) == 'http://' || fileUrl.substr(0, 8) == 'https://') - return; - - //add to the buffer - var css = loadCSSFile(fileUrl); - - //make file url absolute - //if (fileUrl.substr(0, 1) != '/') - // fileUrl = '/' + fileUrl; - - //normalize all css to the base url - as the common path reference - //for injection we then only need one normalization from the base url - //css = normalize(css, fileUrl, baseUrl); - - // parse if necessary - if (parse) - css = parse(css); - - return css; - } - // NB add @media query support for media imports var importRegEx = /@import\s*(url)?\s*(('([^']*)'|"([^"]*)")|\(('([^']*)'|"([^"]*)"|([^\)]*))\))\s*;?/g; @@ -159,8 +120,6 @@ define(['require', './normalize'], function(req, normalize) { else importUrl = baseUrl + importUrl; - console.log('importing ' + importUrl); - importUrls.push(importUrl); importIndex.push(importRegEx.lastIndex - match[0].length); importLength.push(match[0].length); @@ -179,8 +138,11 @@ define(['require', './normalize'], function(req, normalize) { return css; } + + var baseUrl; + var cssBase; var curModule; - cssAPI.load = function(name, req, load, config) { + cssAPI.load = function(name, req, load, config, parse) { if (!cssBase) { cssBase = config.cssBase || config.appDir || baseUrl; if (cssBase.substr(cssBase.length - 1, 1) != '/') @@ -196,10 +158,31 @@ define(['require', './normalize'], function(req, normalize) { break; } } + + if (!baseUrl) { + var baseParts = req.toUrl('base_url').split('/'); + baseParts.pop(); + baseUrl = baseParts.join('/') + '/'; + } //store config cssAPI.config = cssAPI.config || config; - //just return - 'write' calls are made after exclusions so we run loading there + + name += !parse ? '.css' : '.less'; + + var fileUrl = req.toUrl(name); + + //external URLS don't get added (just like JS requires) + if (fileUrl.substr(0, 7) == 'http://' || fileUrl.substr(0, 8) == 'https://') + return; + + //add to the buffer + _cssBuffer[name] = loadCSSFile(fileUrl); + + // parse if necessary + if (parse) + _cssBuffer[name] = parse(_cssBuffer[name]); + load(); } @@ -211,13 +194,15 @@ define(['require', './normalize'], function(req, normalize) { //list of cssIds included in this layer var _layerBuffer = []; + var _cssBuffer = []; - cssAPI.write = function(pluginName, moduleName, write, extension, parse) { + cssAPI.write = function(pluginName, moduleName, write, parse) { //external URLS don't get added (just like JS requires) if (moduleName.substr(0, 7) == 'http://' || moduleName.substr(0, 8) == 'https://') return; - _layerBuffer.push(loadCSS(moduleName + (extension ? '.' + extension : ''), parse)); + var resourceName = moduleName + (!parse ? '.css' : '.less'); + _layerBuffer.push(_cssBuffer[resourceName]); var separateCSS = false; if (cssAPI.config.separateCSS) @@ -227,7 +212,7 @@ define(['require', './normalize'], function(req, normalize) { if (separateCSS) write.asModule(pluginName + '!' + moduleName, 'define(function(){})'); else - write("requirejs.s.contexts._.nextTick = function(f){f()}; require(['css'], function(css) { css.addBuffer('" + moduleName + (parse ? ".less', true" : ".css'") + "); }); requirejs.s.contexts._.nextTick = requirejs.nextTick;"); + write("requirejs.s.contexts._.nextTick = function(f){f()}; require(['css'], function(css) { css.addBuffer('" + resourceName + "'); }); requirejs.s.contexts._.nextTick = requirejs.nextTick;"); } cssAPI.onLayerEnd = function(write, data, parser) { diff --git a/css.js b/css.js index 668ee17..47c55e5 100644 --- a/css.js +++ b/css.js @@ -70,17 +70,30 @@ define(['./normalize'], function(normalize) { //main api object var cssAPI = {}; - // for builds, store css for injection - cssAPI.bufferLoaded = {}; cssAPI.pluginBuilder = './css-builder'; // used by layer builds to register their css buffers - var buffer = []; - cssAPI.addBuffer = function(cssId) { - if (indexOf(buffer, cssId) == -1) - buffer.push(cssId); + + // the current layer buffer items (from addBuffer) + var curBuffer = []; + + // the callbacks for buffer loads + var onBufferLoad = []; + + // the full list of resources in the buffer + var bufferResources = []; + + cssAPI.addBuffer = function(resourceId) { + // just in case layer scripts are included twice, also check + // against the previous buffers + if (indexOf(curBuffer, resourceId) != -1) + return; + if (indexOf(bufferResources, resourceId) != -1) + return; + curBuffer.push(resourceId); + bufferResources.push(resourceId); } - cssAPI.setBuffer = function(css, parser) { + cssAPI.setBuffer = function(css, isLess) { var pathname = window.location.pathname.split('/'); pathname.pop(); pathname = pathname.join('/') + '/'; @@ -96,15 +109,53 @@ define(['./normalize'], function(normalize) { cssAPI.inject(normalize(css, baseUrl, pathname)); - for (var i = 0; i < buffer.length; i++) - if (cssAPI.bufferLoaded[buffer[i]] !== true && (!parser == (buffer[i].substr(buffer[i].length - 4, 4) == '.css'))) + // set up attach callback if registered + // clear the current buffer for the next layer + // (just the less or css part as we have two buffers in one effectively) + for (var i = 0; i < curBuffer.length; i++) { + // find the resources in the less or css buffer dependening which one this is + if ((isLess && curBuffer[i].substr(curBuffer[i].length - 5, 5) == '.less') || + (!isLess && curBuffer[i].substr(curBuffer[i].length - 4, 4) == '.css')) { + + // mark that the onBufferLoad is about to be called (set to true if not already a callback function) + onBufferLoad[curBuffer[i]] = onBufferLoad[curBuffer[i]] || true; + + // set a short timeout (as injection isn't instant in Chrome), then call the load (function(i) { setTimeout(function() { - if (typeof cssAPI.bufferLoaded[buffer[i]] == 'function') - cssAPI.bufferLoaded[buffer[i]](); - cssAPI.bufferLoaded[buffer[i]] = true; - }) + if (typeof onBufferLoad[curBuffer[i]] == 'function') + onBufferLoad[curBuffer[i]](); + // remove from onBufferLoad to indicate loaded + delete onBufferLoad[curBuffer[i]]; + }, 7); })(i); + } + } + } + cssAPI.attachBuffer = function(resourceId, load) { + // attach can happen during buffer collecting, or between injection and callback + // we assume it is not possible to attach multiple callbacks + // requirejs plugin load function ensures this by queueing duplicate calls + + // check if the resourceId is in the current buffer + for (var i = 0; i < curBuffer.length; i++) + if (curBuffer[i] == resourceId) { + onBufferLoad[resourceId] = load; + return true; + } + + // check if the resourceId is waiting for injection callback + // (onBufferLoad === true is a shortcut indicator for this) + if (onBufferLoad[resourceId] === true) { + onBufferLoad[resourceId] = load; + return true; + } + + // if it's in the full buffer list and not either of the above, its loaded already + if (bufferResources[resourceId]) { + load(); + return true; + } } var webkitLoadCheck = function(link, callback) { @@ -336,19 +387,14 @@ define(['./normalize'], function(normalize) { waitSeconds = waitSeconds || config.waitSeconds || 7; - var fileUrl = cssId + (parse ? '.less' : '.css'); + var resourceId = cssId + (!parse ? '.css' : '.less'); - // if in the built buffer do injection - for (var i = 0; i < buffer.length; i++) - if (buffer[i] == fileUrl) { - if (cssAPI.bufferLoaded[fileUrl] === true) - load(); - else - cssAPI.bufferLoaded[fileUrl] = load; - return; - } + // attach the load function to a buffer if there is one in registration + // if not, we do a full injection load + if (cssAPI.attachBuffer(resourceId, load)) + return; - fileUrl = req.toUrl(fileUrl); + fileUrl = req.toUrl(resourceId); if (!alerted && testing) { alert(hackLinks ? 'hacking links' : 'not hacking');