This commit is contained in:
Mu-An ✌️ Chiou 2017-12-19 18:20:07 +08:00
Родитель eaf5c5d618
Коммит 52f834e1a7
9 изменённых файлов: 2082 добавлений и 285 удалений

6
.eslintrc.json Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
"extends": [
"plugin:github/es6",
"plugin:github/browser"
]
}

Просмотреть файл

@ -1,25 +0,0 @@
{
"curly": true,
"eqeqeq": true,
"es3": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"boss": true,
"eqnull": true,
"browser": true,
"globals": {
"fetch": false,
"Promise": false,
"Request": false,
"Response": false,
"WeakMap": false
}
}

Просмотреть файл

@ -1,151 +1,160 @@
(function() {
'use strict';
;(function() {
'use strict'
var privateData = new WeakMap();
const privateData = new WeakMap()
function fire(name, target) {
setTimeout(function() {
var event = target.ownerDocument.createEvent('Event');
event.initEvent(name, false, false);
target.dispatchEvent(event);
}, 0);
const event = target.ownerDocument.createEvent('Event')
event.initEvent(name, false, false)
target.dispatchEvent(event)
}, 0)
}
function handleData(el, data) {
return data.then(function(html) {
var parentNode = el.parentNode;
if (parentNode) {
el.insertAdjacentHTML('afterend', html);
parentNode.removeChild(el);
return data.then(
function(html) {
const parentNode = el.parentNode
if (parentNode) {
el.insertAdjacentHTML('afterend', html)
parentNode.removeChild(el)
}
},
function() {
el.classList.add('is-error')
}
}, function() {
el.classList.add('is-error');
});
)
}
var IncludeFragmentPrototype = Object.create(window.HTMLElement.prototype);
const IncludeFragmentPrototype = Object.create(window.HTMLElement.prototype)
Object.defineProperty(IncludeFragmentPrototype, 'src', {
get: function() {
var src = this.getAttribute('src');
get() {
const src = this.getAttribute('src')
if (src) {
var link = this.ownerDocument.createElement('a');
link.href = src;
return link.href;
const link = this.ownerDocument.createElement('a')
link.href = src
return link.href
} else {
return '';
return ''
}
},
set: function(value) {
this.setAttribute('src', value);
set(value) {
this.setAttribute('src', value)
}
});
})
function getData(el) {
var src = el.src;
var data = privateData.get(el);
const src = el.src
let data = privateData.get(el)
if (data && data.src === src) {
return data.data;
return data.data
} else {
if (src) {
data = el.load();
data = el.load()
} else {
data = Promise.reject(new Error('missing src'));
data = Promise.reject(new Error('missing src'))
}
privateData.set(el, {src: src, data: data});
return data;
privateData.set(el, {src, data})
return data
}
}
Object.defineProperty(IncludeFragmentPrototype, 'data', {
get: function() {
return getData(this);
get() {
return getData(this)
}
});
})
IncludeFragmentPrototype.attributeChangedCallback = function(attrName) {
if (attrName === 'src') {
// Reload data load cache.
var data = getData(this);
const data = getData(this)
// Source changed after attached so replace element.
if (this._attached) {
handleData(this, data);
handleData(this, data)
}
}
};
}
IncludeFragmentPrototype.createdCallback = function() {
// Preload data cache
getData(this)['catch'](function() {
// Ignore `src missing` error on pre-load.
});
};
})
}
IncludeFragmentPrototype.attachedCallback = function() {
this._attached = true;
this._attached = true
if (this.src) {
handleData(this, getData(this));
handleData(this, getData(this))
}
};
}
IncludeFragmentPrototype.detachedCallback = function() {
this._attached = false;
};
this._attached = false
}
IncludeFragmentPrototype.request = function() {
var src = this.src;
const src = this.src
if (!src) {
throw new Error('missing src');
throw new Error('missing src')
}
return new Request(src, {
method: 'GET',
credentials: 'same-origin',
headers: {
'Accept': 'text/html'
Accept: 'text/html'
}
});
};
})
}
IncludeFragmentPrototype.load = function() {
var self = this;
const self = this
return Promise.resolve().then(function() {
var request = self.request();
fire('loadstart', self);
return self.fetch(request);
}).then(function(response) {
if (response.status !== 200) {
throw new Error('Failed to load resource: ' +
'the server responded with a status of ' + response.status);
}
return Promise.resolve()
.then(function() {
const request = self.request()
fire('loadstart', self)
return self.fetch(request)
})
.then(function(response) {
if (response.status !== 200) {
throw new Error(`Failed to load resource: the server responded with a status of ${response.status}`)
}
var ct = response.headers.get('Content-Type');
if (!ct || !ct.match(/^text\/html/)) {
throw new Error('Failed to load resource: ' +
'expected text/html but was ' + ct);
}
const ct = response.headers.get('Content-Type')
if (!ct || !ct.match(/^text\/html/)) {
throw new Error(`Failed to load resource: expected text/html but was ${ct}`)
}
return response;
}).then(function(response) {
return response.text();
}).then(function(data) {
fire('load', self);
fire('loadend', self);
return data;
}, function(error) {
fire('error', self);
fire('loadend', self);
throw error;
});
};
return response
})
.then(function(response) {
return response.text()
})
.then(
function(data) {
fire('load', self)
fire('loadend', self)
return data
},
function(error) {
fire('error', self)
fire('loadend', self)
throw error
}
)
}
IncludeFragmentPrototype.fetch = function(request) {
return fetch(request);
};
return fetch(request)
}
window.IncludeFragmentElement = document.registerElement('include-fragment', {
prototype: IncludeFragmentPrototype
});
})();
})
})()

1814
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -9,12 +9,15 @@
},
"scripts": {
"clean": "rm -rf ./node_modules",
"lint": "jshint *.js test/*.js",
"lint": "eslint include-fragment-element.js test/*.js",
"pretest": "npm run lint",
"test": "karma start ./test/karma.config.js"
},
"devDependencies": {
"chai": "^4.1.2",
"eslint": "^4.13.1",
"eslint-plugin-github": "^0.20.1",
"eslint-plugin-mocha": "^4.11.0",
"jshint": "2.5.2",
"karma": "^1.7.1",
"karma-chai": "^0.1.0",

1
prettier.config.js Normal file
Просмотреть файл

@ -0,0 +1 @@
module.exports = require('eslint-plugin-github/prettier.config')

Просмотреть файл

@ -1,26 +0,0 @@
{
"curly": true,
"eqeqeq": true,
"es3": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": true,
"undef": true,
"unused": true,
"trailing": true,
"boss": true,
"eqnull": true,
"browser": true,
"globals": {
"Promise": false,
"Response": false,
"assert": false,
"module": false,
"test": false,
"setup": false,
"suite": false
}
}

Просмотреть файл

@ -1,10 +1,7 @@
module.exports = function(config) {
config.set({
frameworks: ['mocha', 'chai'],
files: [
'../include-fragment-element.js',
'test.js'
],
files: ['../include-fragment-element.js', 'test.js'],
reporters: ['mocha'],
port: 9876,
client: {mocha: {ui: 'tdd'}},
@ -14,5 +11,5 @@ module.exports = function(config) {
autoWatch: false,
singleRun: true,
concurrency: Infinity
});
};
})
}

Просмотреть файл

@ -1,13 +1,15 @@
var count;
/* eslint-env mocha */
/* global assert */
var responses = {
let count
const responses = {
'/hello': function() {
return new Response('<div id="replaced">hello</div>', {
status: 200,
headers: {
'Content-Type': 'text/html; charset=utf-8'
}
});
})
},
'/one-two': function() {
return new Response('<p id="one">one</p><p id="two">two</p>', {
@ -15,7 +17,7 @@ var responses = {
headers: {
'Content-Type': 'text/html'
}
});
})
},
'/blank-type': function() {
return new Response('<div id="replaced">hello</div>', {
@ -23,225 +25,241 @@ var responses = {
headers: {
'Content-Type': null
}
});
})
},
'/boom': function() {
return new Response('boom', {
status: 500
});
})
},
'/count': function() {
count++;
return new Response(''+count, {
count++
return new Response(`${count}`, {
status: 200,
headers: {
'Content-Type': 'text/html'
}
});
})
}
};
}
window.IncludeFragmentElement.prototype.fetch = function(request) {
var pathname = new URL(request.url).pathname;
return Promise.resolve(responses[pathname](request));
};
const pathname = new URL(request.url).pathname
return Promise.resolve(responses[pathname](request))
}
setup(function() {
count = 0;
});
count = 0
})
suite('include-fragment-element', function() {
test('create from document.createElement', function() {
var el = document.createElement('include-fragment');
assert.equal('INCLUDE-FRAGMENT', el.nodeName);
});
const el = document.createElement('include-fragment')
assert.equal('INCLUDE-FRAGMENT', el.nodeName)
})
test('create from constructor', function() {
var el = new window.IncludeFragmentElement();
assert.equal('INCLUDE-FRAGMENT', el.nodeName);
});
const el = new window.IncludeFragmentElement()
assert.equal('INCLUDE-FRAGMENT', el.nodeName)
})
test('src property', function() {
var el = document.createElement('include-fragment');
assert.equal(null, el.getAttribute('src'));
assert.equal('', el.src);
const el = document.createElement('include-fragment')
assert.equal(null, el.getAttribute('src'))
assert.equal('', el.src)
el.src = '/hello';
assert.equal('/hello', el.getAttribute('src'));
var link = document.createElement('a');
link.href = '/hello';
assert.equal(link.href, el.src);
});
el.src = '/hello'
assert.equal('/hello', el.getAttribute('src'))
const link = document.createElement('a')
link.href = '/hello'
assert.equal(link.href, el.src)
})
test('initial data is in error state', function() {
var el = document.createElement('include-fragment');
const el = document.createElement('include-fragment')
el.data['catch'](function(error) {
assert.ok(error);
});
});
assert.ok(error)
})
})
test('data with src property', function() {
var el = document.createElement('include-fragment');
el.src = '/hello';
const el = document.createElement('include-fragment')
el.src = '/hello'
el.data.then(function(html) {
assert.equal('<div id="replaced">hello</div>', html);
}, function() {
assert.ok(false);
});
});
el.data.then(
function(html) {
assert.equal('<div id="replaced">hello</div>', html)
},
function() {
assert.ok(false)
}
)
})
test('data with src attribute', function() {
var el = document.createElement('include-fragment');
el.setAttribute('src', '/hello');
const el = document.createElement('include-fragment')
el.setAttribute('src', '/hello')
el.data.then(function(html) {
assert.equal('<div id="replaced">hello</div>', html);
}, function() {
assert.ok(false);
});
});
el.data.then(
function(html) {
assert.equal('<div id="replaced">hello</div>', html)
},
function() {
assert.ok(false)
}
)
})
test('setting data with src property multiple times', function() {
var el = document.createElement('include-fragment');
el.src = '/count';
const el = document.createElement('include-fragment')
el.src = '/count'
el.data.then(function(text) {
assert.equal('1', text);
el.src = '/count';
}).then(function() {
return el.data;
}).then(function(text) {
assert.equal('1', text);
})['catch'](function() {
assert.ok(false);
});
});
el.data
.then(function(text) {
assert.equal('1', text)
el.src = '/count'
})
.then(function() {
return el.data
})
.then(function(text) {
assert.equal('1', text)
})
['catch'](function() {
assert.ok(false)
})
})
test('setting data with src attribute multiple times', function() {
var el = document.createElement('include-fragment');
el.setAttribute('src', '/count');
const el = document.createElement('include-fragment')
el.setAttribute('src', '/count')
el.data.then(function(text) {
assert.equal('1', text);
el.setAttribute('src', '/count');
}).then(function() {
return el.data;
}).then(function(text) {
assert.equal('1', text);
})['catch'](function() {
assert.ok(false);
});
});
el.data
.then(function(text) {
assert.equal('1', text)
el.setAttribute('src', '/count')
})
.then(function() {
return el.data
})
.then(function(text) {
assert.equal('1', text)
})
['catch'](function() {
assert.ok(false)
})
})
test('data is not writable', function() {
var el = document.createElement('include-fragment');
assert.ok(el.data !== 42);
const el = document.createElement('include-fragment')
assert.ok(el.data !== 42)
try {
el.data = 42;
} catch(e) {}
assert.ok(el.data !== 42);
});
el.data = 42
} finally {
assert.ok(el.data !== 42)
}
})
test('data is not configurable', function() {
var el = document.createElement('include-fragment');
assert.ok(el.data !== undefined);
const el = document.createElement('include-fragment')
assert.ok(el.data !== undefined)
try {
delete el.data;
} catch(e) {}
assert.ok(el.data !== undefined);
});
delete el.data
} finally {
assert.ok(el.data !== undefined)
}
})
test('replaces element on 200 status', function() {
var div = document.createElement('div');
div.innerHTML = '<include-fragment src="/hello">loading</include-fragment>';
document.body.appendChild(div);
const div = document.createElement('div')
div.innerHTML = '<include-fragment src="/hello">loading</include-fragment>'
document.body.appendChild(div)
div.firstChild.addEventListener('load', function() {
assert.equal(document.querySelector('include-fragment'), null);
assert.equal(document.querySelector('#replaced').textContent, 'hello');
});
});
assert.equal(document.querySelector('include-fragment'), null)
assert.equal(document.querySelector('#replaced').textContent, 'hello')
})
})
test('does not replace element if it has no parent', function() {
var div = document.createElement('div');
div.innerHTML = '<include-fragment src="/hello">loading</include-fragment>';
document.body.appendChild(div);
const div = document.createElement('div')
div.innerHTML = '<include-fragment src="/hello">loading</include-fragment>'
document.body.appendChild(div)
var fragment = div.firstChild;
fragment.remove();
const fragment = div.firstChild
fragment.remove()
window.addEventListener('unhandledrejection', function() {
assert.ok(false);
});
assert.ok(false)
})
fragment.addEventListener('load', function() {
assert.equal(document.querySelector('#replaced'), null);
assert.equal(document.querySelector('#replaced'), null)
div.appendChild(fragment);
div.appendChild(fragment)
setTimeout(function() {
assert.equal(document.querySelector('#replaced').textContent, 'hello');
}, 10);
});
});
assert.equal(document.querySelector('#replaced').textContent, 'hello')
}, 10)
})
})
test('replaces with several new elements on 200 status', function() {
var div = document.createElement('div');
div.innerHTML = '<include-fragment src="/one-two">loading</include-fragment>';
document.body.appendChild(div);
const div = document.createElement('div')
div.innerHTML = '<include-fragment src="/one-two">loading</include-fragment>'
document.body.appendChild(div)
div.firstChild.addEventListener('load', function() {
assert.equal(document.querySelector('include-fragment'), null);
assert.equal(document.querySelector('#one').textContent, 'one');
assert.equal(document.querySelector('#two').textContent, 'two');
});
});
assert.equal(document.querySelector('include-fragment'), null)
assert.equal(document.querySelector('#one').textContent, 'one')
assert.equal(document.querySelector('#two').textContent, 'two')
})
})
test('error event is not cancelable or bubbles', function() {
var div = document.createElement('div');
div.innerHTML = '<include-fragment src="/boom">loading</include-fragment>';
document.body.appendChild(div);
const div = document.createElement('div')
div.innerHTML = '<include-fragment src="/boom">loading</include-fragment>'
document.body.appendChild(div)
div.firstChild.addEventListener('error', function(event) {
assert.equal(event.bubbles, false);
assert.equal(event.cancelable, false);
});
});
assert.equal(event.bubbles, false)
assert.equal(event.cancelable, false)
})
})
test('adds is-error class on 500 status', function() {
var div = document.createElement('div');
div.innerHTML = '<include-fragment src="/boom">loading</include-fragment>';
document.body.appendChild(div);
const div = document.createElement('div')
div.innerHTML = '<include-fragment src="/boom">loading</include-fragment>'
document.body.appendChild(div)
div.firstChild.addEventListener('error', function() {
assert.ok(document.querySelector('include-fragment').classList.contains('is-error'));
});
});
assert.ok(document.querySelector('include-fragment').classList.contains('is-error'))
})
})
test('adds is-error class on mising Content-Type', function() {
var div = document.createElement('div');
div.innerHTML = '<include-fragment src="/blank-type">loading</include-fragment>';
document.body.appendChild(div);
const div = document.createElement('div')
div.innerHTML = '<include-fragment src="/blank-type">loading</include-fragment>'
document.body.appendChild(div)
div.firstChild.addEventListener('error', function() {
assert.ok(document.querySelector('include-fragment').classList.contains('is-error'));
});
});
assert.ok(document.querySelector('include-fragment').classList.contains('is-error'))
})
})
test('replaces element when src attribute is changed', function() {
var div = document.createElement('div');
div.innerHTML = '<include-fragment>loading</include-fragment>';
document.body.appendChild(div);
const div = document.createElement('div')
div.innerHTML = '<include-fragment>loading</include-fragment>'
document.body.appendChild(div)
div.firstChild.addEventListener('load', function() {
assert.equal(document.querySelector('include-fragment'), null);
assert.equal(document.querySelector('#replaced').textContent, 'hello');
});
assert.equal(document.querySelector('include-fragment'), null)
assert.equal(document.querySelector('#replaced').textContent, 'hello')
})
setTimeout(function() {
div.firstChild.src = '/hello';
}, 10);
});
});
div.firstChild.src = '/hello'
}, 10)
})
})