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

Commit

Permalink
Merge pull request #41 from Polymer/master
Browse files Browse the repository at this point in the history
6/17 master -> stable
  • Loading branch information
dfreedm committed Jun 17, 2013
2 parents bb7369f + d6187fa commit 849adf7
Show file tree
Hide file tree
Showing 15 changed files with 177 additions and 177 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "buildbot"]
path = buildbot
url = https://github.com/Polymer/buildbot.git
[submodule "MutationObservers"]
path = MutationObservers
url = https://github.com/Polymer/MutationObservers.git
Expand Down
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Custom elements are still elements. We can create, use, manipulate, and compose
### Basic usage

As with any element, custom elements can be created in JavaScript or declared.
**Their name must always contain a dash (-).**
**Custom element names must always contain a dash (-).**

#### Element registration

Expand Down Expand Up @@ -89,13 +89,13 @@ declare the type using the `extends` option when calling `document.register()`:

Example extending `button`:

var XFooPrototype = Object.create(HTMLButtonElement.prototype);
XFooPrototype.readyCallback = function() {
this.textContent = "I'm an x-foo!";
var XFooButtonPrototype = Object.create(HTMLButtonElement.prototype);
XFooButtonPrototype.readyCallback = function() {
this.textContent = "I'm an x-foo button!";
};

var XFoo = document.register('x-foo', {
prototype: XFooPrototype,
var XFooButton = document.register('x-foo-button', {
prototype: XFooButtonPrototype,
extends: 'button'
});

Expand All @@ -106,13 +106,22 @@ standard DOM elements:

<x-foo></x-foo>

In the declarative and `document.register()` examples above, `XFoo` was defined as the new element's constructor. Browser limitations require that we supply the constructor while you supply the prototype. Use the `readyCallback` to do initialization work that might otherwise be in the constructor.
If you've used `extends` to create a custom element that derives from an existing DOM element
(e.g. something other than `HTMLElement`), use the `is` syntax:

<button is="x-foo-button"></button>

In the declarative and `document.register()` example above, `XFoo` was defined as the new element's constructor.
This can also be used to create an instance:

var xFoo = new XFoo();
document.body.appendChild(xFoo);

var xFoo2 = document.createElement('x-foo');
xFoo2.foo(); // "foo() called"
var xFooButton = document.createElement('button', 'x-foo-button');
xFooButton.foo(); // "foo() called"

Browser limitations require that we supply the constructor while you supply the `prototype`.
Use the `readyCallback` to do initialization work that might otherwise be in a constructor.

## Polyfill details

Expand Down
1 change: 0 additions & 1 deletion buildbot
Submodule buildbot deleted from 3a4761
38 changes: 20 additions & 18 deletions src/CustomElements.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,16 @@ function register(inName, inOptions) {
// some platforms require modifications to the user-supplied prototype
// chain
resolvePrototypeChain(definition);
// overrides to implement callbacks
// TODO(sjmiles): should support access via .attributes NamedNodeMap
definition.prototype.setAttribute = setAttribute;
definition.prototype.removeAttribute = removeAttribute;
// overrides to implement attributeChanged callback
overrideAttributeApi(definition.prototype);
// 7.1.5: Register the DEFINITION with DOCUMENT
registerDefinition(inName, 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);
definition.ctor.prototype = definition.prototype;
// force our .constructor to be our actual constructor
definition.prototype.constructor = definition.ctor;
// if initial parsing is complete
if (scope.ready) {
// upgrade any pre-existing nodes of this type
Expand Down Expand Up @@ -152,13 +152,12 @@ function resolvePrototypeChain(inDefinition) {
// 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) {
// for non-trivial extensions, work out both prototypes
var inst = document.createElement(inDefinition.tag);
var native = Object.getPrototypeOf(inst);
} else {
// otherwise, use the default
native = HTMLElement.prototype;
native = Object.getPrototypeOf(inst);
}
}
// cache this in case of mixin
Expand Down Expand Up @@ -241,15 +240,18 @@ function ready(inElement) {

// attribute watching

var originalSetAttribute = HTMLElement.prototype.setAttribute;
var originalRemoveAttribute = HTMLElement.prototype.removeAttribute;

function setAttribute(name, value) {
changeAttribute.call(this, name, value, originalSetAttribute);
}

function removeAttribute(name, value) {
changeAttribute.call(this, name, value, originalRemoveAttribute);
function overrideAttributeApi(prototype) {
// overrides to implement callbacks
// TODO(sjmiles): should support access via .attributes NamedNodeMap
// TODO(sjmiles): preserves user defined overrides, if any
var setAttribute = prototype.setAttribute;
prototype.setAttribute = function(name, value) {
changeAttribute.call(this, name, value, setAttribute);
}
var removeAttribute = prototype.removeAttribute;
prototype.removeAttribute = function(name, value) {
changeAttribute.call(this, name, value, removeAttribute);
}
}

function changeAttribute(name, value, operation) {
Expand Down
39 changes: 11 additions & 28 deletions src/HTMLElementElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ function executeComponentScript(inScript, inContext, inName) {
+ "', function(){"
+ inScript
+ "});"
+ "\n//@ sourceURL=" + url + "\n"
+ "\n//# sourceURL=" + url + "\n"
;
// inject script
eval(code);
Expand All @@ -112,40 +112,23 @@ window.__componentScript = function(inName, inFunc) {

// utility

// copy all properties from inProps (et al) to inObj
function mixin(inObj/*, inProps, inMoreProps, ...*/) {
var obj = inObj || {};
for (var i = 1; i < arguments.length; i++) {
var p = arguments[i];
try {
for (var n in p) {
copyProperty(n, p, obj);
// copy top level properties from props to obj
function mixin(obj, props) {
obj = obj || {};
try {
Object.getOwnPropertyNames(props).forEach(function(n) {
var pd = Object.getOwnPropertyDescriptor(props, n);
if (pd) {
Object.defineProperty(obj, n, pd);
}
} catch(x) {
}
});
} catch(x) {
}
return obj;
}

// copy property inName from inSource object to inTarget object
function copyProperty(inName, inSource, inTarget) {
var pd = getPropertyDescriptor(inSource, inName);
Object.defineProperty(inTarget, inName, pd);
}

// get property descriptor for inName on inObject, even if
// inName exists on some link in inObject's prototype chain
function getPropertyDescriptor(inObject, inName) {
if (inObject) {
var pd = Object.getOwnPropertyDescriptor(inObject, inName);
return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
}
}

// exports

window.HTMLElementElement = HTMLElementElement;
// TODO(sjmiles): completely ad-hoc, used by Polymer.register
window.mixin = mixin;

})();
70 changes: 14 additions & 56 deletions src/Parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,30 @@

(function() {

var IMPORT_LINK_TYPE = 'import';
// import

var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none';

// highlander object for parsing a document tree

var componentParser = {
var parser = {
selectors: [
'link[rel=' + IMPORT_LINK_TYPE + ']',
'link[rel=stylesheet]',
'script[src]',
'script',
'style',
'element'
],
map: {
link: 'parseLink',
script: 'parseScript',
element: 'parseElement',
style: 'parseStyle'
element: 'parseElement'
},
parse: function(inDocument) {
if (!inDocument.__parsed) {
// only parse once
inDocument.__parsed = true;
// all parsable elements in inDocument (depth-first pre-order traversal)
var elts = inDocument.querySelectorAll(cp.selectors);
var elts = inDocument.querySelectorAll(parser.selectors);
// for each parsable node type, call the mapped parsing method
forEach(elts, function(e) {
//console.log(map[e.localName] + ":", path.nodeUrl(e));
cp[cp.map[e.localName]](e);
parser[parser.map[e.localName]](e);
});
// upgrade all upgradeable static elements, anything dynamically
// created should be caught by observer
Expand All @@ -43,68 +38,31 @@ var componentParser = {
CustomElements.observeDocument(inDocument);
}
},
parseLink: function(inLinkElt) {
parseLink: function(linkElt) {
// imports
if (isDocumentLink(inLinkElt)) {
if (inLinkElt.content) {
cp.parse(inLinkElt.content);
}
} else if (canAddToMainDoument(inLinkElt)) {
document.head.appendChild(inLinkElt);
}
},
parseScript: function(inScriptElt) {
if (canAddToMainDoument(inScriptElt)) {
// otherwise, evaluate now
var code = inScriptElt.__resource || inScriptElt.textContent;
if (code) {
code += "\n//@ sourceURL=" + inScriptElt.__nodeUrl + "\n";
eval.call(window, code);
}
if (isDocumentLink(linkElt)) {
this.parseImport(linkElt);
}
},
parseStyle: function(inStyleElt) {
if (canAddToMainDoument(inStyleElt)) {
document.head.appendChild(inStyleElt);
parseImport: function(linkElt) {
if (linkElt.content) {
parser.parse(linkElt.content);
}
},
parseElement: function(inElementElt) {
new HTMLElementElement(inElementElt);
}
};

var cp = componentParser;

// nodes can be moved to the main document
// if they are not in the main document, are not children of <element>
// and are in a tree at parse time.
function canAddToMainDoument(node) {
return !inMainDocument(node)
&& node.parentNode
&& !isElementElementChild(node);
}

function inMainDocument(inElt) {
return inElt.ownerDocument === document ||
// TODO(sjmiles): ShadowDOMPolyfill intrusion
inElt.ownerDocument.impl === document;
}

function isDocumentLink(inElt) {
return (inElt.localName === 'link'
&& inElt.getAttribute('rel') === IMPORT_LINK_TYPE);
}

function isElementElementChild(inElt) {
if (inElt.parentNode && inElt.parentNode.localName === 'element') {
return true;
}
}

var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);

// exports

CustomElements.parser = componentParser;
CustomElements.parser = parser;

})();
34 changes: 18 additions & 16 deletions src/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,42 @@

// bootstrap parsing

// IE shim for CustomEvent
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function(inType) {
var e = document.createEvent('HTMLEvents');
e.initEvent(inType, true, true);
return e;
};
}

function bootstrap() {
// go async so call stack can unwind
setTimeout(function() {
// parse document
CustomElements.parser.parse(document);
// set internal flag
// one more pass before register is 'live'
CustomElements.upgradeDocument(document);
// set internal 'ready' flag, now document.register will trigger
// synchronous upgrades
CustomElements.ready = true;
// capture blunt profiling data
CustomElements.readyTime = new Date().getTime();
if (window.HTMLImports) {
CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime;
}
// notify system
// notify the system that we are bootstrapped
document.body.dispatchEvent(
new CustomEvent('WebComponentsReady', {bubbles: true})
);
}, 0);
}

// TODO(sjmiles): 'window' has no wrappability under ShadowDOM polyfill, so
// we are forced to split into two versions
// CustomEvent shim for IE
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function(inType) {
var e = document.createEvent('HTMLEvents');
e.initEvent(inType, true, true);
return e;
};
}

if (window.HTMLImports) {
document.addEventListener('HTMLImportsLoaded', bootstrap);
if (document.readyState === 'complete') {
bootstrap();
} else {
window.addEventListener('load', bootstrap);
var loadEvent = window.HTMLImports ? 'HTMLImportsLoaded' : 'DOMContentLoaded';
window.addEventListener(loadEvent, bootstrap);
}

})();
Loading

0 comments on commit 849adf7

Please sign in to comment.