├── .gitignore
├── .zuul.yml
├── Makefile
├── Readme.md
├── component.json
├── index.d.ts
├── index.js
├── package.json
└── test
├── hello.js
├── index.js
└── throw.js
/.gitignore:
--------------------------------------------------------------------------------
1 | components
2 | build
3 | node_modules
4 |
--------------------------------------------------------------------------------
/.zuul.yml:
--------------------------------------------------------------------------------
1 | ui: mocha-qunit
2 | browsers:
3 | - name: chrome
4 | version: [oldest, latest]
5 | - name: firefox
6 | version: [oldest, latest]
7 | - name: opera
8 | version: latest
9 | - name: safari
10 | version: oldest..latest
11 | - name: ie
12 | version: oldest..latest
13 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build: index.js
2 | @component build --dev
3 |
4 | clean:
5 | rm -fr build
6 |
7 | .PHONY: clean
8 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # load-script
2 |
3 | Dynamic script loading.
4 |
5 | ## Installation
6 |
7 | via component
8 |
9 | ```
10 | $ component install eldargab/load-script
11 | ```
12 |
13 | via npm
14 |
15 | ```
16 | $ npm install load-script
17 | ```
18 |
19 | ## API
20 | `load-script` appends a `script` node to the `
` element in the dom.
21 |
22 | `require('load-script')` returns a function of the following interface: `function(url[, opts][, cb]) {}`
23 |
24 | ### url
25 | Any url that you would like to load. May be absolute or relative.
26 |
27 | ### [, opts]
28 | A map of options. Here are the currently supported options:
29 |
30 | * `async` - A boolean value used for `script.async`. By default this is `true`.
31 | * `attrs` - A map of attributes to set on the `script` node before appending it to the DOM. By default this is empty.
32 | * `charset` - A string value used for `script.charset`. By default this is `utf8`.
33 | * `text` - A string of text to append to the `script` node before it is appended to the DOM. By default this is empty.
34 | * `type` - A string used for `script.type`. By default this is `text/javascript`.
35 |
36 | ### [, cb]
37 | A callback function of the following interface: `function(err, script) {}` where `err` is an error if any occurred and `script` is the `script` node that was appended to the DOM.
38 |
39 | ## Example Usage
40 |
41 | ```javascript
42 | var load = require('load-script')
43 |
44 | load('foo.js', function (err, script) {
45 | if (err) {
46 | // print useful message
47 | }
48 | else {
49 | console.log(script.src);// Prints 'foo'.js'
50 | // use script
51 | // note that in IE8 and below loading error wouldn't be reported
52 | }
53 | })
54 | ```
55 |
56 | ## License
57 |
58 | MIT
59 |
--------------------------------------------------------------------------------
/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "load-script",
3 | "repo": "eldargab/load-script",
4 | "description": "Dynamic script loading",
5 | "version": "0.0.5",
6 | "keywords": ["script", "load"],
7 | "license": "MIT",
8 | "scripts": [
9 | "index.js"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | type Callback = (errorOrNull: Error | null, script: HTMLScriptElement) => void;
2 |
3 | type AllowedAttributes = 'type' | 'charset' | 'async' | 'text';
4 |
5 | type Options = Partial> & {
6 | attrs?: Record;
7 | }
8 |
9 | declare function load (src: HTMLScriptElement['src'], opts: Callback): void;
10 | declare function load (src: HTMLScriptElement['src'], opts: Options, cb: Callback): void;
11 |
12 | export default load;
13 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function load (src, opts, cb) {
3 | var head = document.head || document.getElementsByTagName('head')[0]
4 | var script = document.createElement('script')
5 |
6 | if (typeof opts === 'function') {
7 | cb = opts
8 | opts = {}
9 | }
10 |
11 | opts = opts || {}
12 | cb = cb || function() {}
13 |
14 | script.type = opts.type || 'text/javascript'
15 | script.charset = opts.charset || 'utf8';
16 | script.async = 'async' in opts ? !!opts.async : true
17 | script.src = src
18 |
19 | if (opts.attrs) {
20 | setAttributes(script, opts.attrs)
21 | }
22 |
23 | if (opts.text) {
24 | script.text = '' + opts.text
25 | }
26 |
27 | var onend = 'onload' in script ? stdOnEnd : ieOnEnd
28 | onend(script, cb)
29 |
30 | // some good legacy browsers (firefox) fail the 'in' detection above
31 | // so as a fallback we always set onload
32 | // old IE will ignore this and new IE will set onload
33 | if (!script.onload) {
34 | stdOnEnd(script, cb);
35 | }
36 |
37 | head.appendChild(script)
38 | }
39 |
40 | function setAttributes(script, attrs) {
41 | for (var attr in attrs) {
42 | script.setAttribute(attr, attrs[attr]);
43 | }
44 | }
45 |
46 | function stdOnEnd (script, cb) {
47 | script.onload = function () {
48 | this.onerror = this.onload = null
49 | cb(null, script)
50 | }
51 | script.onerror = function () {
52 | // this.onload = null here is necessary
53 | // because even IE9 works not like others
54 | this.onerror = this.onload = null
55 | cb(new Error('Failed to load ' + this.src), script)
56 | }
57 | }
58 |
59 | function ieOnEnd (script, cb) {
60 | script.onreadystatechange = function () {
61 | if (this.readyState != 'complete' && this.readyState != 'loaded') return
62 | this.onreadystatechange = null
63 | cb(null, script) // there is no way to catch loading errors in IE8
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "load-script",
3 | "description": "Dynamic script loading for browser",
4 | "version": "2.0.0",
5 | "types": "./index.d.ts",
6 | "keywords": [
7 | "browser",
8 | "script",
9 | "load"
10 | ],
11 | "repository": {
12 | "type": "git",
13 | "url": "git://github.com/eldargab/load-script"
14 | },
15 | "scripts": {
16 | "test": "zuul -- test/index.js",
17 | "test-local": "zuul --local 9005 -- test/index.js"
18 | },
19 | "devDependencies": {
20 | "zuul": "~2.1.0"
21 | },
22 | "license": "MIT"
23 | }
24 |
--------------------------------------------------------------------------------
/test/hello.js:
--------------------------------------------------------------------------------
1 | log('Hello world')
2 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var load = require('../')
3 |
4 | var last_msg = undefined;
5 | log = function(msg) {
6 | last_msg = msg;
7 | }
8 |
9 | test('success', function(done) {
10 | load('test/hello.js', function (err) {
11 | assert.ifError(err);
12 | assert.equal(last_msg, 'Hello world');
13 | last_msg = undefined;
14 | done();
15 | })
16 | });
17 |
18 | test('opts.async', function(done) {
19 | load('test/hello.js', {async: false}, function(err, script) {
20 | assert.ifError(err);
21 | assert.equal(script.async, false);
22 | done();
23 | })
24 | });
25 |
26 | test('opts.attrs', function(done) {
27 | load('test/hello.js', {attrs: {foo: 'boo'}}, function(err, script) {
28 | assert.ifError(err);
29 | assert.equal(script.getAttribute('foo'), 'boo');
30 | done();
31 | })
32 | });
33 |
34 | test('opts.charset', function(done) {
35 | load('test/hello.js', {charset: 'iso-8859-1'}, function(err, script) {
36 | assert.ifError(err);
37 | assert.equal(script.charset, 'iso-8859-1');
38 | done();
39 | })
40 | });
41 |
42 | test('opts.text', function(done) {
43 | load('test/hello.js', {text: 'foo=5;'}, function(err, script) {
44 | assert.ifError(err);
45 | done();
46 | })
47 | });
48 |
49 | test('opts.type', function(done) {
50 | load('test/hello.js', {type: 'text/ecmascript'}, function(err, script) {
51 | assert.ifError(err);
52 | assert.equal(script.type, 'text/ecmascript');
53 | done();
54 | })
55 | });
56 |
57 | test('no exist', function(done) {
58 | load('unexistent.js', function (err, legacy) {
59 | if (!legacy) {
60 | assert.ok(err);
61 | }
62 |
63 | var tid = setTimeout(function() {
64 | done();
65 | }, 200);
66 |
67 | // some browsers will also throw as well as report erro
68 | var old = window.onerror;
69 | window.onerror = function(msg, file, line) {
70 | if (msg !== 'Error loading script') {
71 | assert(false);
72 | }
73 | window.onerror = old;
74 | clearTimeout(tid);
75 | done();
76 | };
77 | })
78 | });
79 |
80 | test('throw', function(done) {
81 | var old = window.onerror;
82 | // silence the script error
83 | window.onerror = function() {};
84 | load('test/throw.js', function (err) {
85 | assert.ifError(err);
86 | window.onerror = old;
87 | done();
88 | })
89 | });
90 |
91 |
--------------------------------------------------------------------------------
/test/throw.js:
--------------------------------------------------------------------------------
1 | throw new Error('Hello error')
2 |
--------------------------------------------------------------------------------