Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
throw exception if name argument to document.register contains no d…
Browse files Browse the repository at this point in the history
…ash (+test)
  • Loading branch information
Scott J. Miles committed Aug 19, 2013
1 parent f485280 commit 315f9a4
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 54 deletions.
113 changes: 59 additions & 54 deletions src/CustomElements.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,23 @@ if (useNative) {
*
* When a registered element is created, a `readyCallback` method is called
* in the scope of the element. The `readyCallback` method can be specified on
* either `inOptions.prototype` or `inOptions.lifecycle` with the latter taking
* either `options.prototype` or `options.lifecycle` with the latter taking
* precedence.
*
* @method register
* @param {String} inName The tag name to register. Must include a dash ('-'),
* @param {String} name The tag name to register. Must include a dash ('-'),
* for example 'x-component'.
* @param {Object} inOptions
* @param {String} [inOptions.extends]
* @param {Object} options
* @param {String} [options.extends]
* (_off spec_) Tag name of an element to extend (or blank for a new
* element). This parameter is not part of the specification, but instead
* is a hint for the polyfill because the extendee is difficult to infer.
* Remember that the input prototype must chain to the extended element's
* prototype (or HTMLElement.prototype) regardless of the value of
* `extends`.
* @param {Object} inOptions.prototype The prototype to use for the new
* @param {Object} options.prototype The prototype to use for the new
* element. The prototype must inherit from HTMLElement.
* @param {Object} [inOptions.lifecycle]
* @param {Object} [options.lifecycle]
* Callbacks that fire at important phases in the life of the custom
* element.
*
Expand All @@ -89,18 +89,23 @@ if (useNative) {
* });
* @return {Function} Constructor for the newly registered type.
*/
function register(inName, inOptions) {
//console.warn('document.register("' + inName + '", ', inOptions, ')');
function register(name, options) {
//console.warn('document.register("' + name + '", ', options, ')');
// construct a defintion out of options
// TODO(sjmiles): probably should clone inOptions instead of mutating it
var definition = inOptions || {};
if (!inName) {
// TODO(sjmiles): probably should clone options instead of mutating it
var definition = options || {};
if (!name) {
// TODO(sjmiles): replace with more appropriate error (EricB can probably
// offer guidance)
throw new Error('Name argument must not be empty');
throw new Error('document.register: first argument `name` must not be empty');
}
if (name.indexOf('-') < 0) {
// TODO(sjmiles): replace with more appropriate error (EricB can probably
// offer guidance)
throw new Error('document.register: first argument `name` must contain a dash (\'-\'). Argument was \'' + String(name) + '\'.');
}
// record name
definition.name = inName;
definition.name = name;
// must have a prototype, default to an extension of HTMLElement
// TODO(sjmiles): probably should throw if no prototype, check spec
if (!definition.prototype) {
Expand All @@ -123,7 +128,7 @@ if (useNative) {
// overrides to implement attributeChanged callback
overrideAttributeApi(definition.prototype);
// 7.1.5: Register the DEFINITION with DOCUMENT
registerDefinition(inName, definition);
registerDefinition(name, definition);
// 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
// 7.1.8. Return the output of the previous step.
definition.ctor = generateConstructor(definition);
Expand All @@ -138,98 +143,98 @@ if (useNative) {
return definition.ctor;
}

function ancestry(inExtends) {
var extendee = registry[inExtends];
function ancestry(extnds) {
var extendee = registry[extnds];
if (extendee) {
return ancestry(extendee.extends).concat([extendee]);
}
return [];
}

function resolveTagName(inDefinition) {
function resolveTagName(definition) {
// if we are explicitly extending something, that thing is our
// baseTag, unless it represents a custom component
var baseTag = inDefinition.extends;
var baseTag = definition.extends;
// if our ancestry includes custom components, we only have a
// baseTag if one of them does
for (var i=0, a; (a=inDefinition.ancestry[i]); i++) {
for (var i=0, a; (a=definition.ancestry[i]); i++) {
baseTag = a.is && a.tag;
}
// our tag is our baseTag, if it exists, and otherwise just our name
inDefinition.tag = baseTag || inDefinition.name;
definition.tag = baseTag || definition.name;
if (baseTag) {
// if there is a base tag, use secondary 'is' specifier
inDefinition.is = inDefinition.name;
definition.is = definition.name;
}
}

function resolvePrototypeChain(inDefinition) {
function resolvePrototypeChain(definition) {
// if we don't support __proto__ we need to locate the native level
// prototype for precise mixing in
if (!Object.__proto__) {
// default prototype
var native = HTMLElement.prototype;
// work out prototype when using type-extension
if (inDefinition.is) {
var inst = document.createElement(inDefinition.tag);
if (definition.is) {
var inst = document.createElement(definition.tag);
native = Object.getPrototypeOf(inst);
}
// ensure __proto__ reference is installed at each point on the prototype
// chain.
// NOTE: On platforms without __proto__, a mixin strategy is used instead
// of prototype swizzling. In this case, this generated __proto__ provides
// limited support for prototype traversal.
var proto = inDefinition.prototype, ancestor;
var proto = definition.prototype, ancestor;
while (proto && (proto !== native)) {
var ancestor = Object.getPrototypeOf(proto);
proto.__proto__ = ancestor;
proto = ancestor;
}
}
// cache this in case of mixin
inDefinition.native = native;
definition.native = native;
}

// SECTION 4

function instantiate(inDefinition) {
function instantiate(definition) {
// 4.a.1. Create a new object that implements PROTOTYPE
// 4.a.2. Let ELEMENT by this new object
//
// the custom element instantiation algorithm must also ensure that the
// output is a valid DOM element with the proper wrapper in place.
//
return upgrade(domCreateElement(inDefinition.tag), inDefinition);
return upgrade(domCreateElement(definition.tag), definition);
}

function upgrade(inElement, inDefinition) {
function upgrade(element, definition) {
// some definitions specify an 'is' attribute
if (inDefinition.is) {
inElement.setAttribute('is', inDefinition.is);
if (definition.is) {
element.setAttribute('is', definition.is);
}
// make 'element' implement inDefinition.prototype
implement(inElement, inDefinition);
// make 'element' implement definition.prototype
implement(element, definition);
// flag as upgraded
inElement.__upgraded__ = true;
// there should never be a shadow root on inElement at this point
element.__upgraded__ = true;
// there should never be a shadow root on element at this point
// we require child nodes be upgraded before `created`
scope.upgradeSubtree(inElement);
scope.upgradeSubtree(element);
// lifecycle management
created(inElement);
created(element);
// OUTPUT
return inElement;
return element;
}

function implement(inElement, inDefinition) {
function implement(element, definition) {
// prototype swizzling is best
if (Object.__proto__) {
inElement.__proto__ = inDefinition.prototype;
element.__proto__ = definition.prototype;
} else {
// where above we can re-acquire inPrototype via
// getPrototypeOf(Element), we cannot do so when
// we use mixin, so we install a magic reference
customMixin(inElement, inDefinition.prototype, inDefinition.native);
inElement.__proto__ = inDefinition.prototype;
customMixin(element, definition.prototype, definition.native);
element.__proto__ = definition.prototype;
}
}

Expand Down Expand Up @@ -257,10 +262,10 @@ if (useNative) {
}
}

function created(inElement) {
function created(element) {
// invoke createdCallback
if (inElement.createdCallback) {
inElement.createdCallback();
if (element.createdCallback) {
element.createdCallback();
}
}

Expand Down Expand Up @@ -293,13 +298,13 @@ if (useNative) {

var registry = {};

function registerDefinition(inName, inDefinition) {
registry[inName] = inDefinition;
function registerDefinition(name, definition) {
registry[name] = definition;
}

function generateConstructor(inDefinition) {
function generateConstructor(definition) {
return function() {
return instantiate(inDefinition);
return instantiate(definition);
};
}

Expand All @@ -313,11 +318,11 @@ if (useNative) {
return domCreateElement(tag);
}

function upgradeElement(inElement) {
if (!inElement.__upgraded__ && (inElement.nodeType === Node.ELEMENT_NODE)) {
var type = inElement.getAttribute('is') || inElement.localName;
function upgradeElement(element) {
if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) {
var type = element.getAttribute('is') || element.localName;
var definition = registry[type];
return definition && upgrade(inElement, definition);
return definition && upgrade(element, definition);
}
}

Expand Down Expand Up @@ -353,7 +358,7 @@ if (useNative) {
* if it matches no registered custom tag name.
*
* @method ugprade
* @param {Element} inElement The element to upgrade.
* @param {Element} element The element to upgrade.
* @return {Element} The upgraded element.
*/
scope.upgrade = upgradeElement;
Expand Down
18 changes: 18 additions & 0 deletions test/js/customElements.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@
document.body.removeChild(work);
});

test('document.register requires name argument', function() {
try {
document.register();
} catch(x) {
return;
}
assert.ok(false, 'document.register failed to throw when given no arguments');
});

test('document.register requires name argument to contain a dash', function() {
try {
document.register('xfoo', {prototype: Object.create(HTMLElement.prototype)});
} catch(x) {
return;
}
assert.ok(false, 'document.register failed to throw when given no arguments');
});

test('document.register create via new', function() {
// register x-foo
var XFoo = document.register('x-foo', {prototype: Object.create(HTMLElement.prototype)});
Expand Down

0 comments on commit 315f9a4

Please sign in to comment.