Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some script tag after-render #102

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 43 additions & 30 deletions addon/loaders/css.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import RSVP from 'rsvp';
import { createLoadElement, nodeLoader } from './utilities';
import { scheduleWork } from './scheduler';

/**
* Default loader function for CSS assets. Loads them by inserting a link tag
Expand All @@ -19,39 +20,51 @@ export default nodeLoader(function css(uri) {
return resolve();
}

// Try using the default onload/onerror handlers...
const link = createLoadElement('link', resolve, function(error) {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
reject(error);
});
scheduleWork(() => {
let link;
try {
// Try using the default onload/onerror handlers...
const link = createLoadElement('link', resolve, function(error) {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
reject(error);
});

link.rel = 'stylesheet';
link.href = uri;

document.head.appendChild(link);

// In case the browser doesn't fire onload/onerror events, we poll the
// the list of stylesheets to see when it loads...
function checkSheetLoad() {
const resolvedHref = link.href;
const stylesheets = document.styleSheets;
let i = stylesheets.length;

while (i--) {
const sheet = stylesheets[i];
if (sheet.href === resolvedHref) {
// Unfortunately we have no way of knowing if the load was
// successful or not, so we always resolve.
setTimeout(resolve);
return;
}
link.rel = 'stylesheet';
link.href = uri;

document.head.appendChild(link);

// In case the browser doesn't fire onload/onerror events, we poll the
// the list of stylesheets to see when it loads...

setTimeout(checkSheetLoad);
} catch(reason) {
reject(reason);
}

setTimeout(checkSheetLoad);
}
function checkSheetLoad() {
try {
const resolvedHref = link.href;
const stylesheets = document.styleSheets;
let i = stylesheets.length;

while (i--) {
const sheet = stylesheets[i];
if (sheet.href === resolvedHref) {
// Unfortunately we have no way of knowing if the load was
// successful or not, so we always resolve.
setTimeout(resolve);
return;
}
}

setTimeout(checkSheetLoad);
setTimeout(checkSheetLoad);
} catch(reason) {
reject(reason);
}
}
});
});
});
37 changes: 28 additions & 9 deletions addon/loaders/js.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import RSVP from 'rsvp';
import { createLoadElement, nodeLoader } from './utilities';
import { scheduleWork } from './scheduler';

/**
* Default loader function for JS assets. Loads them by inserting a script tag
Expand All @@ -15,16 +16,34 @@ export default nodeLoader(function js(uri) {
return resolve();
}

const script = createLoadElement('script', resolve, function(error) {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
reject(error);
});
// DOM mutation should be batched, this indirection enables this batching.
// By default, it will schedule work on the next afterRender queue. But can
// be configured for further control via.
//
// ```js
// import { setScheduler } from 'ember-asset-loader/scheduler';
//
// setScheduler(function(work /* work is a callback */) {
// someScheduler.scheduleWork(work);
// });
// ```
//
scheduleWork(() => {
try {
const script = createLoadElement('script', resolve, function(error) {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
reject(error);
});

script.src = uri;
script.async = false;
script.src = uri;
script.async = false;

document.head.appendChild(script);
document.head.appendChild(script);
} catch(e) {
reject(e);
}
});
});
});
12 changes: 12 additions & 0 deletions addon/loaders/scheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Ember from 'ember';
export const defaultScheduler = (work) => Ember.run.schedule('afterRender', work);

let scheduler = defaultScheduler;

export function setScheduler(_scheduler) {
scheduler = _scheduler;
}

export function scheduleWork(work) {
Ember.run.join(scheduler, work);
}
16 changes: 9 additions & 7 deletions tests/unit/services/asset-loader-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,12 @@ module('Unit | Service | asset-loader', function(hooks) {
});

test('loadAsset() - js - handles successful load', function(assert) {
assert.expect(1);
assert.expect(0);

const service = this.owner.lookup('service:asset-loader');
const asset = { type: 'js', uri: '/unit-test.js' };

return service.loadAsset(asset).then(shouldHappen(assert), shouldNotHappen(assert));
return service.loadAsset(asset);
});

test('loadAsset() - js - handles failed load', function(assert) {
Expand All @@ -329,7 +329,7 @@ module('Unit | Service | asset-loader', function(hooks) {
const service = this.owner.lookup('service:asset-loader');
const asset = { type: 'js', uri: '/unit-test.jsss' };

return service.loadAsset(asset).then(shouldNotHappen(assert), shouldHappen(assert));
return service.loadAsset(asset).catch(() => assert.ok(true, 'loadAsset should reject'));
});

test('loadAsset() - js - does not insert additional script tag if asset is in DOM already', function(assert) {
Expand Down Expand Up @@ -364,12 +364,12 @@ module('Unit | Service | asset-loader', function(hooks) {
});

test('loadAsset() - css - handles successful load', function(assert) {
assert.expect(1);
assert.expect(0);

const service = this.owner.lookup('service:asset-loader');
const asset = { type: 'css', uri: '/unit-test.css' };

return service.loadAsset(asset).then(shouldHappen(assert), shouldNotHappen(assert));
return service.loadAsset(asset);
});

test('loadAsset() - css - handles failed load', function(assert) {
Expand All @@ -382,9 +382,11 @@ module('Unit | Service | asset-loader', function(hooks) {
// non-Chrome browsers to either resolve or reject (they should do something).
var isChrome = !!window.chrome && window.navigator.vendor === 'Google Inc.';
if (isChrome) {
return service.loadAsset(asset).then(shouldNotHappen(assert), shouldHappen(assert));
return service.loadAsset(asset).catch(() => assert.ok(true, 'loadAsset should reject'));
} else {
return service.loadAsset(asset).then(shouldHappen(assert), shouldHappen(assert));
return service.loadAsset(asset)
.then(() => assert.ok(true, 'loadAsset may resolve'))
.catch(() => assert.ok(true, 'loadAsset may reject'));
}
});

Expand Down