diff --git a/externs/html5.js b/externs/html5.js
new file mode 100644
index 0000000..987e624
--- /dev/null
+++ b/externs/html5.js
@@ -0,0 +1 @@
+MutationObserver.prototype.takeRecords = function() {};
diff --git a/gulpfile.js b/gulpfile.js
index 2de9749..c0f5977 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -12,17 +12,19 @@
'use strict';
-var
- audit = require('gulp-audit'),
- concat = require('gulp-concat'),
- exec = require('child_process').exec,
- fs = require('fs'),
- gulp = require('gulp'),
- header = require('gulp-header'),
- path = require('path'),
- runseq = require('run-sequence'),
- uglify = require('gulp-uglify')
-;
+var audit = require('gulp-audit');
+var compilerPackage = require('google-closure-compiler');
+var concat = require('gulp-concat');
+var exec = require('child_process').exec;
+var fs = require('fs');
+var gulp = require('gulp');
+var header = require('gulp-header');
+var path = require('path');
+var rename = require('gulp-rename');
+var runseq = require('run-sequence');
+var uglify = require('gulp-uglify');
+
+var closureCompiler = compilerPackage.gulp();
// init tests with gulp
require('web-component-tester').gulp.init(gulp);
@@ -129,7 +131,21 @@ defineBuildTask('HTMLImports');
defineBuildTask('ShadowDOM');
defineBuildTask('MutationObserver');
-gulp.task('build', ['webcomponents', 'webcomponents-lite', 'CustomElements',
+gulp.task('CustomElementsV1', function () {
+ return gulp.src('./src/CustomElements/v1/CustomElements.js', {base: './'})
+ .pipe(closureCompiler({
+ compilation_level: 'ADVANCED',
+ warning_level: 'VERBOSE',
+ language_in: 'ECMASCRIPT6_STRICT',
+ language_out: 'ECMASCRIPT5_STRICT',
+ output_wrapper: '(function(){\n%output%\n}).call(this)',
+ externs: 'externs/html5.js',
+ js_output_file: 'CustomElementsV1.min.js'
+ }))
+ .pipe(gulp.dest('./dist'));
+});
+
+gulp.task('build', ['webcomponents', 'webcomponents-lite', 'CustomElements',
'HTMLImports', 'ShadowDOM', 'copy-bower', 'MutationObserver']);
gulp.task('release', function(cb) {
diff --git a/package.json b/package.json
index a670976..3552258 100644
--- a/package.json
+++ b/package.json
@@ -20,10 +20,12 @@
},
"homepage": "http://webcomponents.org",
"devDependencies": {
+ "google-closure-compiler": "^20160208.7.0",
"gulp": "^3.8.8",
"gulp-audit": "^1.0.0",
"gulp-concat": "^2.4.1",
"gulp-header": "^1.1.1",
+ "gulp-rename": "^1.2.2",
"gulp-uglify": "^1.0.1",
"run-sequence": "^1.0.1",
"web-component-tester": "^4.0.1"
diff --git a/src/CustomElements/v1/CustomElements.js b/src/CustomElements/v1/CustomElements.js
index 65c96a4..cecb26a 100644
--- a/src/CustomElements/v1/CustomElements.js
+++ b/src/CustomElements/v1/CustomElements.js
@@ -11,22 +11,25 @@
/**
* 2.3
* http://w3c.github.io/webcomponents/spec/custom/#dfn-element-definition
- *
- * @typedef {Object} Definition
- * @property {Function} name
- * @property {Function} localName
- * @property {Function} constructor
- * @property {Function} connectedCallback
- * @property {Function} disconnectedCallback
- * @property {Function} attributeChangedCallback
- * @property {String[]} observedAttributes
- * http://w3c.github.io/webcomponents/spec/custom/#dfn-element-definition-construction-stack
- * @property {Function[]} constructionStack
+ * @typedef {{
+ * name: string,
+ * localName: string,
+ * constructor: Function,
+ * connectedCallback: Function,
+ * disconnectedCallback: Function,
+ * attributeChangedCallback: Function,
+ * observedAttributes: Array,
+ * }}
*/
+var CustomElementDefinition;
(function() {
'use strict';
+ /**
+ * @const
+ * @type {Array}
+ */
var reservedTagList = [
'annotation-xml',
'color-profile',
@@ -38,11 +41,13 @@
'missing-glyph',
];
+ /**
+ * @const
+ */
var customNameValidation = /^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/;
function checkCallback(callback, elementName, calllbackName) {
if (callback !== undefined && typeof callback !== 'function') {
- console.warn(typeof callback);
throw new Error(`TypeError: ${elementName} '${calllbackName}' is not a Function`);
}
}
@@ -51,17 +56,26 @@
return reservedTagList.indexOf(name) !== -1;
}
+ /**
+ * @constructor
+ * @property {Map} _defintions
+ * @property {MutationObserver} _observer
+ * @property {MutationObserver} _attributeObserver
+ * @property {HTMLElement} _newInstance
+ * @property {string} _newTagName
+ * @property {boolean} polyfilled
+ */
function CustomElementsRegistry() {
- // @type {Map}
this._definitions = new Map();
this._observer = this._observeRoot(document);
this._attributeObserver =
new MutationObserver(this._handleAttributeChange.bind(this));
- this._newInstance;
- this._newTagName;
+ this._newInstance = null;
+ this._newTagName = null;
this.polyfilled = true;
}
+ /** @lends {CustomElementsRegistry.prototype} */
CustomElementsRegistry.prototype = {
define(name, constructor, options) {
// 5.1.1
@@ -102,12 +116,12 @@
var _extends = options && options.extends || '';
// 5.1.9
- if (_extends !== null) {
- // skip for now
- }
+ // skip for now
+ // if (_extends !== null) {
+ // }
// 5.1.10, 5.1.11
- var observedAttributes = constructor.observedAttributes || [];
+ var observedAttributes = constructor['observedAttributes'] || [];
// 5.1.12
var prototype = constructor.prototype;
@@ -115,20 +129,20 @@
// 5.1.13?
// 5.1.14
- var connectedCallback = prototype.connectedCallback;
+ var connectedCallback = prototype['connectedCallback'];
// 5.1.15
checkCallback(connectedCallback, localName, 'connectedCallback');
// 5.1.16
- var disconnectedCallback = prototype.disconnectedCallback;
+ var disconnectedCallback = prototype['disconnectedCallback'];
// 5.1.17
checkCallback(disconnectedCallback, localName, 'disconnectedCallback');
// 5.1.18
- var attributeChangedCallback = prototype.attributeChangedCallback;
+ var attributeChangedCallback = prototype['attributeChangedCallback'];
// 5.1.19
checkCallback(attributeChangedCallback, localName, 'attributeChangedCallback');
// 5.1.20
- // @type {Definition}
+ // @type {CustomElementDefinition}
var definition = {
name: name,
localName: localName,
@@ -156,7 +170,6 @@
},
_setNewInstance(instance) {
- console.assert(this._newInstance == null);
this._newInstance = instance;
},
@@ -179,12 +192,15 @@
}
},
+ /**
+ * @param {NodeList} nodeList
+ */
_addNodes(nodeList) {
for (var i = 0; i < nodeList.length; i++) {
var root = nodeList[i];
var walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
do {
- var node = walker.currentNode;
+ var node = /** @type {HTMLElement} */ (walker.currentNode);
var definition = this._definitions.get(node.localName);
if (!definition) {
continue;
@@ -202,6 +218,9 @@
}
},
+ /**
+ * @param {NodeList} nodeList
+ */
_removeNodes(nodeList) {
for (var i = 0; i < nodeList.length; i++) {
var root = nodeList[i];
@@ -219,9 +238,14 @@
}
},
+ /**
+ * @param {HTMLElement} element
+ * @param {CustomElementDefinition} definition
+ * @param {boolean} callConstructor
+ */
_upgradeElement(element, definition, callConstructor) {
var prototype = definition.constructor.prototype;
- Object.setPrototypeOf(element, prototype);
+ element.__proto__ = prototype;
if (callConstructor) {
this._setNewInstance(element);
element.__upgraded = true;
@@ -237,6 +261,9 @@
}
},
+ /**
+ * @private
+ */
_handleAttributeChange(mutations) {
for (var i = 0; i < mutations.length; i++) {
var mutation = mutations[i];
@@ -246,71 +273,72 @@
var target = mutation.target;
var newValue = target.getAttribute(name);
var namespace = mutation.attributeNamespace;
- target.attributeChangedCallback(name, oldValue, newValue, namespace);
+ target['attributeChangedCallback'](name, oldValue, newValue, namespace);
}
}
},
+ };
+ // Closure Compiler Exports
+ window['CustomElementsRegistry'] = CustomElementsRegistry;
+ CustomElementsRegistry.prototype['define'] = CustomElementsRegistry.prototype.define;
+ CustomElementsRegistry.prototype['flush'] = CustomElementsRegistry.prototype.flush;
+ CustomElementsRegistry.prototype['setCurrentTag'] = CustomElementsRegistry.prototype.setCurrentTag;
+ CustomElementsRegistry.prototype['polyfilled'] = CustomElementsRegistry.prototype.polyfilled;
+ // patch window.HTMLElement
+
+ // TODO: patch up all built-in subclasses of HTMLElement to use the fake
+ // HTMLElement.prototype
+ var origHTMLElement = window.HTMLElement;
+ window.HTMLElement = function() {
+ if (window['customElements']._newInstance) {
+ var i = window['customElements']._newInstance;
+ window['customElements']._newInstance = null;
+ window['customElements']._newTagName = null;
+ return i;
+ }
+ if (window['customElements']._newTagName) {
+ var tagName = window['customElements']._newTagName.toLowerCase();
+ window['customElements']._newTagName = null;
+ return window.document._createElement(tagName, false);
+ }
+ throw new Error('unknown constructor');
+ }
+ HTMLElement.prototype = Object.create(origHTMLElement.prototype);
+ Object.defineProperty(HTMLElement.prototype, 'constructor', {
+ writable: false,
+ configurable: true,
+ enumerable: false,
+ value: HTMLElement,
+ });
+
+ // patch document.createElement
+
+ var rawCreateElement = document.createElement.bind(document);
+ document._createElement = function(tagName, callConstructor) {
+ var element = rawCreateElement.call(document, tagName);
+ var definition = window['customElements']._definitions.get(tagName.toLowerCase());
+ if (definition) {
+ window['customElements']._upgradeElement(element, definition, callConstructor);
+ }
+ return element;
+ };
+ document.createElement = function(tagName) {
+ return document._createElement(tagName, true);
+ }
+
+ // patch document.createElementNS
+
+ var HTMLNS = 'http://www.w3.org/1999/xhtml';
+ var _origCreateElementNS = document.createElementNS;
+ document.createElementNS = function(namespaceURI, qualifiedName) {
+ if (namespaceURI === 'http://www.w3.org/1999/xhtml') {
+ return document.createElement(qualifiedName);
+ } else {
+ return _origCreateElementNS.call(document, namespaceURI, qualifiedName);
+ }
};
- function patchHTMLElement(win) {
- // TODO: patch up all built-in subclasses of HTMLElement to use the fake
- // HTMLElement.prototype
- var origHTMLElement = HTMLElement;
- window.HTMLElement = function() {
- if (win.customElements._newInstance) {
- var i = win.customElements._newInstance;
- win.customElements._newInstance = null;
- win.customElements._newTagName = null;
- return i;
- }
- if (win.customElements._newTagName) {
- var tagName = win.customElements._newTagName.toLowerCase();
- win.customElements._newTagName = null;
- return win.document._createElement(tagName, false);
- }
- throw new Error('unknown constructor');
- }
- HTMLElement.prototype = Object.create(origHTMLElement.prototype);
- Object.defineProperty(HTMLElement.prototype, 'constructor', {
- writable: false,
- configurable: true,
- enumerable: false,
- value: HTMLElement,
- });
- }
-
- function patchCreateElement(win) {
- var doc = win.document;
- var rawCreateElement = doc.createElement.bind(document);
- doc._createElement = function(tagName, callConstructor) {
- var element = rawCreateElement(tagName);
- var definition = win.customElements._definitions.get(tagName.toLowerCase());
- if (definition) {
- win.customElements._upgradeElement(element, definition, callConstructor);
- }
- return element;
- };
- doc.createElement = function(tagName) {
- return doc._createElement(tagName, true);
- }
- }
-
- function patchCreateElementNS(win) {
- var doc = win.document;
- var HTMLNS = 'http://www.w3.org/1999/xhtml';
- var _origCreateElementNS = document.createElementNS;
- doc.createElementNS = function(namespaceURI, qualifiedName) {
- if (namespaceURI === 'http://www.w3.org/1999/xhtml') {
- return doc.createElement(qualifiedName);
- } else {
- return _origCreateElementNS(namespaceURI, qualifiedName);
- }
- };
- }
-
- window.customElements = new CustomElementsRegistry();
- patchHTMLElement(window);
- patchCreateElement(window);
- patchCreateElementNS(window);
+ /** @type {CustomElementsRegistry} */
+ window['customElements'] = new (window['CustomElementsRegistry'])();
})();
diff --git a/tests/CustomElements/v1/runner.html b/tests/CustomElements/v1/runner.html
index 08b4007..4110fb8 100644
--- a/tests/CustomElements/v1/runner.html
+++ b/tests/CustomElements/v1/runner.html
@@ -11,7 +11,7 @@
CustomElements Tests
-
+