diff --git a/Docs/Utilities/Assets.md b/Docs/Utilities/Assets.md index d578e563..dcc5f717 100644 --- a/Docs/Utilities/Assets.md +++ b/Docs/Utilities/Assets.md @@ -52,6 +52,8 @@ Injects a css file in the page. 1. source - (*string*) The path of the CSS file. 2. properties - (*object*) Some additional attributes you might want to add to the link Element; this is the same as the second argument you might pass to including the Element constructor. For instance you might specify a title attribute or perhaps an id. + - onLoad - (*function*) A function that will be invoked when the CSS is loaded. + - timeout - (*number*, defaults to 3000 ms) The maximum amount of milliseconds to wait for onLoad callback to be called. - document - (*object*, defaults to `document`) The document which the link element should be injected in. diff --git a/Gruntfile.js b/Gruntfile.js index ec344e3f..5263b01e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -82,6 +82,7 @@ module.exports = function(grunt) { options: { captureTimeout: 60000 * 2, + browserNoActivityTimeout: 20000, singleRun: true, frameworks: ['jasmine', 'sinon'], files: [ diff --git a/Source/Utilities/Assets.js b/Source/Utilities/Assets.js index ea86e12d..11ab7e04 100644 --- a/Source/Utilities/Assets.js +++ b/Source/Utilities/Assets.js @@ -50,22 +50,40 @@ var Asset = { css: function(source, properties){ if (!properties) properties = {}; + var load = properties.onload || properties.onLoad, + doc = properties.document || document, + timeout = properties.timeout || 3000; + + ['onload', 'onLoad', 'document'].each(function(prop){ + delete properties[prop]; + }); + var link = new Element('link', { + type: 'text/css', rel: 'stylesheet', media: 'screen', - type: 'text/css', href: source - }); - - var load = properties.onload || properties.onLoad, - doc = properties.document || document; - - delete properties.onload; - delete properties.onLoad; - delete properties.document; + }).setProperties(properties).inject(doc.head); - if (load) link.addEvent('load', load); - return link.set(properties).inject(doc.head); + if (load){ + // based on article at http://www.yearofmoo.com/2011/03/cross-browser-stylesheet-preloading.html + var loaded = false, retries = 0; + var check = function(){ + var stylesheets = document.styleSheets; + for (var i = 0; i < stylesheets.length; i++){ + var file = stylesheets[i]; + var owner = file.ownerNode ? file.ownerNode : file.owningElement; + if (owner && owner == link){ + loaded = true; + return load.call(link); + } + } + retries++; + if (!loaded && retries < timeout / 50) return setTimeout(check, 50); + } + setTimeout(check, 0); + } + return link; }, image: function(source, properties){ diff --git a/Specs/Utilities/Assets.js b/Specs/Utilities/Assets.js index ee20d497..93fcbfc4 100644 --- a/Specs/Utilities/Assets.js +++ b/Specs/Utilities/Assets.js @@ -20,7 +20,7 @@ describe('Assets', function(){ } }); - waits(500); + waits(800); runs(function(){ expect(myScript.get('tag')).toEqual('script'); @@ -31,35 +31,59 @@ describe('Assets', function(){ }); }); - }); describe('Assets.css', function(){ - it('should load a css file and fire the load event', function(){ - - var load = jasmine.createSpy('load'); + function addCSS(source, load){ + new Element('div', { + id: 'moologo' + }).inject($(document.body)); - var myCSS = Asset.css('base/Tests/Specs/assets/Assets.css.test.css', { + return myCSS = Asset.css(source, { id: 'myStyle', title: 'myStyle', - onload: function(){ + onLoad: function(){ load(this); } }); + } + + afterEach(function(){ + $('myStyle').destroy(); + $('moologo').destroy(); + }); - waits(500); + it('should load a external css file and run load callback', function(){ + var load = jasmine.createSpy('load'); + var url = 'https://rawgit.com/mootools/mootools-more/master/Tests/Specs/assets/Assets.css.test.css'; + var myCSS = addCSS(url, load); + var myCSS = addCSS(url, load); + waits(3000); runs(function(){ expect(myCSS.get('tag')).toEqual('link'); expect(myCSS.id).toEqual('myStyle'); - // Current implementation of assets uses the load event which only works in IE/Opera - // expect(load).toHaveBeenCalledWith(myCSS); - myCSS.destroy(); + expect(load).toHaveBeenCalledWith(myCSS); + load = myCSS = null; }); - }); + it('should load a local css file and run load callback', function(){ + var load = jasmine.createSpy('load'); + var url = 'base/Tests/Specs/assets/Assets.css.test.css'; + var myCSS = addCSS(url, load); + + waits(2000); + runs(function(){ + var border = $('moologo').getStyle('border'); + expect(load).toHaveBeenCalledWith(myCSS); + expect(myCSS.get('tag')).toEqual('link'); + expect(myCSS.id).toEqual('myStyle'); + expect(border.contains('4px solid')).toBeTruthy(); + load = myCSS = null; + }); + }); }); describe('Assets.image', function(){ @@ -74,7 +98,7 @@ describe('Assets', function(){ onload: load }); - waits(500); + waits(800); runs(function(){ expect(myImage.get('tag')).toEqual('img'); @@ -97,7 +121,7 @@ describe('Assets', function(){ onerror: error }); - waits(500); + waits(800); runs(function(){ expect(load).not.toHaveBeenCalled(); @@ -122,7 +146,7 @@ describe('Assets', function(){ onload: loaded }); - waits(500); + waits(800); runs(function(){ expect(loadedagain).toHaveBeenCalled(); @@ -141,16 +165,14 @@ describe('Assets', function(){ onerror: error }); - waits(500); + waits(800); runs(function(){ expect(load).not.toHaveBeenCalled(); expect(error).toHaveBeenCalledWith(myImage); myImage.destroy(); }); - }); - }); describe('Assets.images', function(){ @@ -170,7 +192,7 @@ describe('Assets', function(){ onError: error }); - waits(500); + waits(800); runs(function(){ expect(complete).toHaveBeenCalled(); @@ -196,16 +218,13 @@ describe('Assets', function(){ onError: error }); - waits(500); + waits(800); runs(function(){ expect(complete).toHaveBeenCalled(); expect(progress.callCount).toEqual(1); expect(error.callCount).toEqual(2); }); - }); - }); - });