UNPKG

@polymer/polymer

Version:

The Polymer library makes it easy to create your own web components. Give your element some markup and properties, and then use it on a site. Polymer provides features like dynamic templates and data binding to reduce the amount of boilerplate you need to

1,633 lines (1,491 loc) 69.5 kB
<!doctype html> <!-- @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> <html> <head> <meta charset="utf-8"> <script> if (window.customElements) { customElements.forcePolyfill = true; } window.ShadyDOM = { force: true }; </script> <script src="../../../webcomponentsjs/webcomponents-lite.js"></script> <script src="../../../web-component-tester/browser.js"></script> <link rel="import" href="../../polymer.html"> </head> <body> <dom-module id="x-project"> <template> x-project: [<slot></slot>] </template> </dom-module> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-project' }); }); </script> <dom-module id="x-reproject"> <template> <x-project>x-reproject: [<slot></slot>]</x-project> </template> </dom-module> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-reproject' }); }); </script> <dom-module id='x-rereproject'> <template> <x-reproject>x-rereproject: [<slot></slot>]</x-reproject> </template> </dom-module> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-rereproject', attachedCount: 0, attached: function() { this.attachedCount++; } }); }); </script> <dom-module id="x-test"> <template> <x-rereproject><span id="projected">projected</span></x-rereproject> </template> </dom-module> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-test' }); }); </script> <dom-module id="x-test-no-distribute"> <template> <span>Local dom without insertion point.</span> </template> </dom-module> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-test-no-distribute' }); }); </script> <dom-module id="x-distribute"> <template> <div> <span>Elements without test attribute</span> <div id="notTestContainer" style="color: white; background-color: green; min-height: 1em;"> <slot id="notTestContent"></slot> </div> <span>Elements with test attribute</span> <div style="color: white; background-color: red; min-height: 1em;"> <div id="testContainer"> <slot id="testContent" name="test"></slot> </div> </div> </div> </template> </dom-module> <script> HTMLImports.whenReady(function() { Polymer({ is: "x-distribute" }); }); </script> <dom-module id="x-compose"> <template><x-project id="project"></x-project></template> </dom-module> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-compose' }); }); </script> <dom-module id="x-select3"> <template><slot id="x-select3-slot" name="s3"></slot></template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-select3'}); }); </script> </dom-module> <dom-module id="x-select2"> <template><x-select3 id="select"><slot id="x-select2-slot" name="s2"></slot></x-select3></template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-select2'}); }); </script> </dom-module> <dom-module id="x-select1"> <template><x-select2 id="select"><slot id="x-select1-slot" name="s1"></slot></x-select2></template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-select1'}); }); </script> </dom-module> <dom-module id="x-echo"> <template><slot></slot></template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-echo'}); }); </script> </dom-module> <dom-module id="x-simple"> <template><div>simple</div></template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-simple'}); }); </script> </dom-module> <dom-module id="x-redistribute-a-b"> <template> <x-echo id="echo1"><slot name="a"></slot></x-echo> <x-echo id="echo2"><slot name="b"></slot></x-echo> </template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-redistribute-a-b'}); }); </script> </dom-module> <dom-module id="x-attr"> <template>Attr1</template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-attr', hostAttributes: { slot: 'bar' } }); });</script> </dom-module> <dom-module id="x-attr2"> <template>Attr2</template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-attr2', // TODO(sorvell): cannot put `slot` into properties because it is patched. ready: function() { this.slot = 'foo'; } }); }); </script> </dom-module> <dom-module id="x-select-attr"> <template> Foo: [<slot name="foo"></slot>] Bar: [<slot name="bar"></slot>] </template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-select-attr'}); }); </script> </dom-module> <dom-module id="x-compose-select-attr"> <template> <x-select-attr id="select"> <x-attr id="attr1"></x-attr> <x-attr2 id="attr2"></x-attr2> </x-select-attr> </template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-compose-select-attr'}); }); </script> </dom-module> <dom-module id="x-clonate"> <template><span>[</span><slot></slot><span>]</span></template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-clonate' }); }); </script> </dom-module> <dom-module id="x-attach3"> <template> <style> :host { display: block; border: 1px dashed orange; padding: 4px; box-sizing: border-box; } :host > ::slotted(.add3) { box-sizing: border-box; height: 20px; background: #333; border: 2px solid yellow; } </style> <slot></slot> <template is="dom-if" if="{{shouldIf(done.count)}}"> <x-attach2></x-attach2> </template> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-attach3', properties: { done: {value: {count: 0}} }, ready: function() { this.done.count++; }, attached: function() { var d = document.createElement('div'); d.className = 'add3'; this.appendChild(d); }, shouldIf: function(x) { return x < 3; } }); }); </script> </dom-module> <dom-module id="x-attach2"> <template> <style> :host { display: block; border: 1px dashed tomato; padding: 4px; } x-attach3 > ::slotted(.add2) { box-sizing: border-box; height: 20px; background: gray; border: 2px solid yellow; } </style> <x-attach3><slot></slot></x-attach3> <template is="dom-if" if="{{shouldIf(done.count)}}"> <x-attach1></x-attach1> </template> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-attach2', properties: { done: {value: {count: 0}} }, ready: function() { this.done.count++; }, attached: function() { var d = document.createElement('div'); d.className = 'add2'; this.appendChild(d); }, shouldIf: function(x) { return x < 3; } }); }); </script> </dom-module> <dom-module id="x-attach1"> <template> <style> :host { display: block; border: 1px dashed seagreen; padding: 4px; } .slotContainer ::slotted(.add1) { box-sizing: border-box; height: 20px; background: lightgray; border: 2px solid yellow; } </style> <x-attach2><div class="slotContainer"><slot></slot><div></x-attach2> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-attach1', attached: function() { var d = document.createElement('div'); d.className = 'add1'; this.appendChild(d); } }); }); </script> </dom-module> <dom-module id="x-commented"> <template><span>[</span><!--comment--><slot></slot></span><span>]</span></slot></template> <script> HTMLImports.whenReady(function() { Polymer({is: 'x-commented'}); }); </script> </dom-module> <dom-module id="polymer-dom-repeat"> <template> <div> <div> <div> <div id="container"> <template id="repeater" is="dom-repeat" items="{{items}}"> <div>stuff</div> </template> </div> </div> </div> </div> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'polymer-dom-repeat', properties: { items: { value: function() { return ['a', 'b', 'c', 'd', 'e']; } } } }); }); </script> </dom-module> <dom-module id="x-deep-contains"> <template> <div id="shadowed"></div> <slot name="light"></slot> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-deep-contains', created: function() { var e = document.createElement('div'); e.setAttribute('slot', 'light'); this.appendChild(e); e = document.createElement('div'); e.setAttribute('slot', 'notdistributed'); this.appendChild(e); } }); }); </script> </dom-module> <dom-module id="x-wrapped"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-wrapped' }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-0-0"> <template> <slot></slot> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-0-0', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-0-0-light-0"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-0-0-light-0', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-0-0-light"> <template> <div> <div> <x-shadow-host-root-0-0-light-0></x-shadow-host-root-0-0-light-0> </div> </div> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-0-0-light', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-0-1"> <template> <slot></slot> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-0-1', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-0-1-light"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-0-1-light', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-0"> <template> <slot></slot> <div> <x-shadow-host-root-0-0> <x-shadow-host-root-0-0-light></x-shadow-host-root-0-0-light> </x-shadow-host-root-0-0> </div> <x-shadow-host-root-0-1> <x-shadow-host-root-0-1-light></x-shadow-host-root-0-1-light> </x-shadow-host-root-0-1> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-0', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-0-light"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-0-light', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-1-0"> <template> <slot></slot> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-1-0', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-1-0-light"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-1-0-light', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-1-1"> <template> <slot></slot> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-1-1', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-1-1-light"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-1-1-light', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-1"> <template> <slot></slot> <div> <x-shadow-host-root-1-0> <x-shadow-host-root-1-0-light></x-shadow-host-root-1-0-light> </x-shadow-host-root-1-0> </div> <div> <div> <div> <x-shadow-host-root-1-1> <x-shadow-host-root-1-1-light></x-shadow-host-root-1-1-light> </x-shadow-host-root-1-1> </div> </div> </div> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-1', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-1-light-0"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-1-light-0', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-1-light"> <template> <x-shadow-host-root-1-light-0></x-shadow-host-root-1-light-0> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-1-light', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root"> <template> <slot></slot> <div> <div> <x-shadow-host-root-0> <x-shadow-host-root-0-light></x-shadow-host-root-0-light> </x-shadow-host-root-0> </div> </div> <div> <x-shadow-host-root-1> <x-shadow-host-root-1-light></x-shadow-host-root-1-light> </x-shadow-host-root-1> </div> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-shadow-host-root-light"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-shadow-host-root-light', hostAttributes: { tabindex: '-1' } }); }); </script> </dom-module> <dom-module id="x-outer"> <template> <x-inner id="inner"> <slot></slot> </x-inner> </template> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-outer' }); }); </script> </dom-module> <dom-module id="x-inner"> <script> HTMLImports.whenReady(function() { Polymer({ is: 'x-inner', attached: function(){ this.listen(this, 'click', '_onClick'); }, _onClick: function(event){ this._eventTarget = event.target; } }); }); </script> </dom-module> <x-test></x-test> <div class="accessors"> <x-test-no-distribute><div class="child"></div></x-test-no-distribute> <x-project><div class="child"></div></x-project> </div> <x-test-no-distribute id="noDistribute"> <div class="bar">Bar</div> <div class="foo">Foo</div> </x-test-no-distribute> <x-select1> <div class="select-child"></div> </x-select1> <x-select-class1> <div></div> </x-select-class1> <x-select-attr> <x-attr></x-attr> </x-select-attr> <x-compose-select-attr></x-compose-select-attr> <x-redistribute-a-b></x-redistribute-a-b> <div id="container"> <x-echo></x-echo> <span>1</span> <span>2</span> </div> <x-deep-contains></x-deep-contains> <x-wrapped></x-wrapped> <x-shadow-host-root> <x-shadow-host-root-light></x-shadow-host-root-light> </x-shadow-host-root> <x-outer> <button>click me</button> </x-outer> <script> if (!window.ShadyDOM) { ShadyDOM = { patch: function() {}, flush: function() {} }; } </script> <script> 'use strict'; function createEnabledElement(tag) { var e = document.createElement(tag); document.body.appendChild(e); document.body.removeChild(e); return e; } function allInsertionPoints(e) { var r = []; while (e) { e = e.assignedSlot; if (e) { r.push(e); } } return r; } var checkUnpatchedDom = false; suite('ShadyDOM', function() { var testElement; suiteSetup(function() { testElement = document.querySelector('x-test'); }); test('querySelector (local)', function() { var projected = testElement.root.querySelector('#projected'); assert.equal(projected.textContent, 'projected'); var p2 = testElement.querySelector('#projected'); assert.isNull(p2); var rere = testElement.root.querySelector('x-rereproject'); assert.equal(rere.is, 'x-rereproject'); var re = rere.root.querySelector('x-reproject'); assert.equal(re.is, 'x-reproject'); var p = re.root.querySelector('x-project'); assert.equal(p.is, 'x-project'); }); test('querySelectorAll (local)', function() { var rere = testElement.root.querySelector('x-rereproject'); var re = rere.root.querySelector('x-reproject'); var p = re.root.querySelector('x-project'); var rereList = rere.root.querySelectorAll('*'); assert.include(rereList, re); assert.equal(rereList.length, 2); var reList = re.root.querySelectorAll('*'); assert.include(reList, p); assert.equal(reList.length, 2); var pList = p.root.querySelectorAll('*'); assert.equal(pList.length, 1); }); test('querySelector (light)', function() { var projected = testElement.root.querySelector('#projected'); var rere = testElement.root.querySelector('x-rereproject'); var re = rere.root.querySelector('x-reproject'); var p = re.root.querySelector('x-project'); assert.equal(rere.querySelector('#projected'), projected); assert(re.querySelector('slot')); assert(p.querySelector('slot')); }); test('querySelectorAll (light)', function() { var projected = testElement.root.querySelector('#projected'); var rere = testElement.root.querySelector('x-rereproject'); var re = rere.root.querySelector('x-reproject'); var p = re.root.querySelector('x-project'); assert.equal(rere.querySelectorAll('#projected')[0], projected); assert(re.querySelectorAll('slot').length, 1); assert(p.querySelectorAll('slot').length, 1); }); test('querySelectorAll with dom-repeat', function() { var el = document.createElement('polymer-dom-repeat'); document.body.appendChild(el); el.$.repeater.render(); Polymer.flush(); assert.equal(el.$.container.querySelectorAll('*').length, 7, 'querySelectorAll finds repeated elements'); document.body.removeChild(el); }); test('querySelector document', function() { assert.ok(document.querySelector('body')); }); test('projection', function() { var projected = testElement.root.querySelector('#projected'); assert.equal(projected.textContent, 'projected'); var rere = testElement.root.querySelector('x-rereproject'); assert.equal(rere.is, 'x-rereproject'); var re = rere.root.querySelector('x-reproject'); assert.equal(re.is, 'x-reproject'); var p = re.root.querySelector('x-project'); assert.equal(p.is, 'x-project'); var c1 = rere.root.querySelector('slot'); assert.include(c1.assignedNodes({flatten: true}), projected); var c2 = re.root.querySelector('slot'); assert.include(c2.assignedNodes({flatten: true}), projected); var c3 = p.root.querySelector('slot'); assert.include(c3.assignedNodes({flatten: true}), projected); var ip$ = [c1, c2, c3]; var as$ = allInsertionPoints(projected); assert.deepEqual(as$, ip$); }); test('shadyRoot (reproject)', function() { var select = document.querySelector('x-select1'); var child = select.firstElementChild; var c1 = select.root.querySelector('slot'); var c2 = select.$.select.root.querySelector('slot'); var c3 = select.$.select.$.select.root.querySelector('slot'); assert.equal(c1.getAttribute('name'), 's1'); assert.equal(c2.getAttribute('name'), 's2'); assert.equal(c3.getAttribute('name'), 's3'); // var ip$ = [c1, c2, c3]; assert.equal(child.className, 'select-child'); assert.equal(allInsertionPoints(child).length, 0); child.slot = 's1'; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), [c1]); child.slot = 's2'; c1.setAttribute('name', 's2'); c1.slot = 's2'; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), [c1, c2]); child.slot = 's3'; c1.setAttribute('name', 's3'); c1.slot = 's3'; c2.setAttribute('name', 's3'); c2.slot = 's3'; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), [c1, c2, c3]); child.slot = ''; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), []); child.slot = 's3'; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), [c1, c2, c3]); child.slot = ''; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), []); child.slot = 's2'; c1.setAttribute('name', 's2'); c1.slot = 's2'; c2.setAttribute('name', 's2'); c2.slot = 's2'; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), [c1, c2]); child.slot = 's1'; c1.setAttribute('name', 's1'); c1.slot = 's1'; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), [c1]); child.slot = ''; Polymer.flush(); assert.deepEqual(allInsertionPoints(child), []); }); test('without a host setting hostAttributes/reflecting properties provokes distribution', function() { var e = document.querySelector('x-select-attr'); var ip$ = e.root.querySelectorAll('slot'); var c = e.firstElementChild; Polymer.flush(); assert.equal(allInsertionPoints(c)[0], ip$[1], 'child not distributed based on host attribute'); c.slot = 'foo'; Polymer.flush(); assert.equal(allInsertionPoints(c)[0], ip$[0], 'child not distributed based on reflecting attribute'); c.slot = ''; Polymer.flush(); assert.equal(allInsertionPoints(c).length, 0, 'child not distributed based on reflecting attribute'); }); test('within a host setting hostAttributes/reflecting properties provokes distribution', function() { var e = document.querySelector('x-compose-select-attr'); var ip$ = e.$.select.root.querySelectorAll('slot'); var c1 = e.$.attr1; Polymer.flush(); assert.equal(allInsertionPoints(c1)[0], ip$[1], 'child not distributed based on host attribute'); c1.slot = 'foo'; Polymer.flush(); assert.equal(allInsertionPoints(c1)[0], ip$[0], 'child not distributed based on reflecting attribute'); c1.slot = 'bar'; Polymer.flush(); assert.equal(allInsertionPoints(c1)[0], ip$[1], 'child not distributed based on reflecting attribute'); var c2 = e.$.attr2; Polymer.flush(); assert.equal(allInsertionPoints(c2)[0], ip$[0], 'child not distributed based on default value'); }); test('appendChild (light)', function() { var rere = testElement.root.querySelector('x-rereproject'); var s = document.createElement('span'); s.id = 'added'; s.textContent = 'Added'; rere.appendChild(s); assert.equal(testElement.root.querySelector('#added'), s); }); test('insertBefore (light)', function() { var rere = testElement.root.querySelector('x-rereproject'); var ref = testElement.root.querySelector('#added'); var s = document.createElement('span'); s.id = 'added2'; s.textContent = 'Added2'; rere.insertBefore(s, ref); assert.equal(testElement.root.querySelector('#added2'), s); }); test('removeChild (light)', function() { var added = testElement.root.querySelector('#added'); var added2 = testElement.root.querySelector('#added2'); var rere = testElement.root.querySelector('x-rereproject'); assert.equal(testElement.root.querySelectorAll('*').length, 4); rere.removeChild(added); rere.removeChild(added2); assert.equal(testElement.root.querySelectorAll('*').length, 2); }); test('appendChild (local)', function() { var rere = testElement.root.querySelector('x-rereproject'); var s = document.createElement('span'); s.id = 'local'; s.textContent = 'Local'; rere.root.appendChild(s); assert.equal(rere.root.querySelector('#local'), s); }); test('insertBefore (local)', function() { var rere = testElement.root.querySelector('x-rereproject'); var ref = testElement.root.querySelector('#local'); var s = document.createElement('span'); s.id = 'local2'; s.textContent = 'Local2'; rere.root.insertBefore(s, ref); assert.equal(rere.root.querySelector('#local2'), s); }); test('removeChild (local)', function() { var rere = testElement.root.querySelector('x-rereproject'); var local = rere.root.querySelector('#local'); var local2 = rere.root.querySelector('#local2'); rere.root.removeChild(local); rere.root.removeChild(local2); assert.equal(rere.root.querySelectorAll('#local').length, 0); }); test('localDom.insertBefore first element results in minimal change', function() { var children = testElement.root.childNodes; var rere = testElement.root.querySelector('x-rereproject'); assert.equal(rere.attachedCount, 1); var s = document.createElement('span'); s.id = 'local-first'; s.textContent = 'Local First'; testElement.root.insertBefore(s, children[0]); assert.equal(testElement.root.querySelector('#local-first'), s); assert.equal(rere.attachedCount, 1); testElement.root.removeChild(s); assert.equal(rere.attachedCount, 1); }); test('appendChild (fragment, local)', function() { var rere = testElement.root.querySelector('x-rereproject'); var fragment = document.createDocumentFragment(); var childCount = 5; for (var i=0; i < childCount; i++) { var s = document.createElement('span'); s.textContent = i; fragment.appendChild(s); } rere.root.appendChild(fragment); var added = rere.root.querySelectorAll('span'); assert.equal(added.length, childCount); for (i=0; i < added.length; i++) { rere.root.removeChild(added[i]); } assert.equal(rere.root.querySelectorAll('span').length, 0); }); test('insertBefore (fragment, local)', function() { var rere = testElement.root.querySelector('x-rereproject'); var fragment = document.createDocumentFragment(); var childCount = 5; for (var i=0; i < childCount; i++) { var s = document.createElement('span'); s.textContent = i; fragment.appendChild(s); } var l = document.createElement('span'); l.textContent = 'last'; rere.root.appendChild(l); rere.root.insertBefore(fragment, l); var added = rere.root.querySelectorAll('span'); assert.equal(added.length, childCount+1); assert.equal(added[added.length-1], l); for (i=0; i < added.length; i++) { rere.root.removeChild(added[i]); } assert.equal(rere.root.querySelectorAll('span').length, 0); }); test('mutations using fragments without logical dom', function() { var d = document.createElement('div'); document.body.appendChild(d); assert.equal(d.childNodes.length, 0); var frag = document.createDocumentFragment(); var c = document.createElement('div'); frag.appendChild(c); d.appendChild(frag); assert.equal(d.childNodes.length, 1); assert.equal(d.firstChild, c); var c1 = document.createElement('div'); frag.appendChild(c1); d.appendChild(frag); assert.equal(d.childNodes.length, 2); assert.equal(d.firstChild, c); assert.equal(d.lastChild, c1); }); test('appendChild interacts with unmanaged parent tree', function() { var container = document.querySelector('#container'); var echo = container.firstElementChild; assert.equal(echo.localName, 'x-echo'); var s1 = echo.nextElementSibling; assert.equal(s1.textContent, '1'); var s2 = s1.nextElementSibling; assert.equal(s2.textContent, '2'); assert.equal(container.children.length, 3); echo.appendChild(s1); Polymer.flush(); assert.equal(container.children.length, 2); assert.equal(echo.nextElementSibling, s2); echo.appendChild(s2); Polymer.flush(); assert.equal(container.children.length, 1); assert.equal(echo.nextElementSibling, null); container.appendChild(s1); Polymer.flush(); assert.equal(container.children.length, 2); assert.equal(echo.nextElementSibling, s1); container.appendChild(s2); Polymer.flush(); assert.equal(container.children.length, 3); assert.equal(echo.nextElementSibling, s1); assert.equal(s1.nextElementSibling, s2); }); test('distribute (forced)', function() { var rere = testElement.root.querySelector('x-rereproject'); var re = rere.root.querySelector('x-reproject'); var p = re.root.querySelector('x-project'); var s = document.createElement('span'); s.id = 'light'; s.textContent = 'Light'; rere.appendChild(s); assert.equal(rere.querySelector('#light'), s); assert.equal(s.parentNode, rere); if (checkUnpatchedDom) { assert.notEqual(Polymer.TreeApi.Composed.getParentNode(s), rere); } if (checkUnpatchedDom) { assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p); } rere.removeChild(s); if (checkUnpatchedDom) { assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p); } if (checkUnpatchedDom) { assert.equal(Polymer.TreeApi.Composed.getParentNode(s), null); } }); test('queryDistributedElements', function() { var rere = testElement.root.querySelector('x-rereproject'); var re = rere.root.querySelector('x-reproject'); var p = re.root.querySelector('x-project'); var projected = testElement.root.querySelector('#projected'); var d$ = p.queryDistributedElements('*'); assert.equal(d$.length, 1); assert.equal(d$[0], projected); }); test('getEffectiveChildNodes', function() { var rere = testElement.root.querySelector('x-rereproject'); var re = rere.root.querySelector('x-reproject'); var projected = testElement.root.querySelector('#projected'); var c$ = re.getEffectiveChildNodes(); assert.equal(c$.length, 3); assert.equal(c$[1], projected); }); test('querySelector', function() { var test = document.querySelector('x-test'); var rere = document.querySelector('x-rereproject'); var projected = document.querySelector('#projected'); assert.ok(test); assert.notOk(rere); assert.notOk(projected); }); test('event', function() { var rere = testElement.root.querySelector('x-rereproject'); var re = rere.root.querySelector('x-reproject'); var p = re.root.querySelector('x-project'); var eventHandled = 0; testElement.addEventListener('test-event', function(e) { eventHandled++; assert.equal(e.composedPath()[0], p); assert.equal(e.target, testElement); var path = e.composedPath(); // path includes window only on more recent Shadow DOM implementations // account for that here. assert.ok(path.length >= 10); assert.equal(path[0], p); assert.equal(path[2], re); assert.equal(path[4], rere); assert.equal(path[6], testElement); // event.path *should* be an array assert.isArray(path); assert.isFunction(path.indexOf); assert(path.indexOf(testElement) > -1); }); rere.addEventListener('test-event', function(e) { eventHandled++; assert.equal(e.target, rere); }); p.fire('test-event'); assert.equal(eventHandled, 2); }); test('event.target', function() { var e = document.querySelector('x-outer'); var b = document.querySelector('x-outer button'); b.dispatchEvent(new Event('click', {bubbles: true})); assert.equal(e.$.inner._eventTarget.localName, 'button', 'event restarted incorrectly'); }); test('parentNode', function() { var rere = testElement.root.querySelector('x-rereproject'); var projected = testElement.root.querySelector('#projected'); assert.equal(testElement.parentNode, document.body); assert.equal(projected.parentNode, rere); }); test('childNodes is an array', function() { var test = document.querySelector('x-test'); assert.isTrue(Array.isArray(test.childNodes)); }); test('cloneNode shallow', function() { var a = document.createElement('div'); a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>'; var b = a.firstElementChild.cloneNode(); document.body.appendChild(b); assert.equal(b.childNodes.length, 0, 'shallow copy has incorrect children'); if (checkUnpatchedDom) { assert.equal(b.children.length, 2, 'shallow copy has incorrect composed children'); } }); test('cloneNode deep', function() { var a = document.createElement('div'); a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>'; var b = a.cloneNode(true); document.body.appendChild(b); assert.equal(b.firstElementChild.childNodes.length, 2, 'deep copy has incorrect children'); if (checkUnpatchedDom) { assert.equal(b.children.length, 4, 'deep copy has incorrect composed children'); } }); test('importNode shallow', function() { var a = document.createElement('div'); a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>'; // NOTE: Safari defaults do `deep` true for importNode so be explicit here. var b = document.importNode(a.firstElementChild, false); document.body.appendChild(b); assert.equal(b.childNodes.length, 0, 'shallow import has incorrect children'); if (checkUnpatchedDom) { assert.equal(b.children.length, 2, 'shallow import has incorrect composed children'); } }); test('importNode deep', function() { var a = document.createElement('div'); a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>'; var b = document.importNode(a, true); document.body.appendChild(b); assert.equal(b.firstElementChild.childNodes.length, 2, 'deep copy has incorrect children'); if (checkUnpatchedDom) { assert.equal(b.children.length, 4, 'deep copy has incorrect composed children'); } }); test('styling: flush causes attached and re-flushes if necessary', function(done) { var a = document.createElement('x-attach1'); document.body.appendChild(a); Polymer.flush(); assert.equal(a.offsetHeight, 540); done(); }); test('flush reentrancy', function() { // Setup callbacks var order = []; var cb1 = sinon.spy(function() { order.push(cb1); }); var cb2 = sinon.spy(function() { order.push(cb2); }); var cb3 = sinon.spy(function() { order.push(cb3); }); var cb4 = sinon.spy(function() { order.push(cb4); }); var cbReentrant = sinon.spy(function() { order.push(cbReentrant); ShadyDOM.enqueue(cb3); Polymer.flush(); ShadyDOM.enqueue(cb4); }); // Enqueue ShadyDOM.enqueue(cb1); ShadyDOM.enqueue(cbReentrant); ShadyDOM.enqueue(cb2); // Flush Polymer.flush(); // Check callbacks called and in correct order assert.isTrue(cb1.calledOnce); assert.isTrue(cb2.calledOnce); assert.isTrue(cb3.calledOnce); assert.isTrue(cb4.calledOnce); assert.isTrue(cbReentrant.calledOnce); assert.sameMembers(order, [cb1, cbReentrant, cb2, cb3, cb4]); }); test('event.composedPath correctly calculated for elements with destination insertion points', function(done) { var re = document.createElement('x-reproject'); document.body.appendChild(re); Polymer.flush(); var p = re.root.querySelector('x-project'); var child = document.createElement('p'); child.innerHTML = "hello"; // child will be inserted into p after distributeContent is performed. re.appendChild(child); Polymer.flush(); child.addEventListener('child-event', function(e){ var path = e.composedPath(); assert.isTrue(path.indexOf(p) !== -1, 'path contains p'); assert.isTrue(path.indexOf(re) !== -1, 'path contains re'); done(); }); var evt = new CustomEvent('child-event'); child.dispatchEvent(evt); document.body.removeChild(re); }); }); suite('Accessors', function() { var noDistribute, distribute; suiteSetup(function() { noDistribute = document.querySelector('.accessors x-test-no-distribute'); distribute = document.querySelector('.accessors x-project'); }); test('node accessors (no distribute)', function() { var child = noDistribute.children[0]; assert.isTrue(child.classList.contains('child'), 'test node could not be found'); var before = document.createElement('div'); var after = document.createElement('div'); noDistribute.insertBefore(before, child); noDistribute.appendChild(after); Polymer.flush(); assert.equal(noDistribute.firstChild, before, 'firstChild incorrect'); assert.equal(noDistribute.lastChild, after, 'lastChild incorrect'); assert.equal(before.nextSibling, child, 'nextSibling incorrect'); assert.equal(child.nextSibling, after, 'nextSibling incorrect'); assert.equal(after.previousSibling, child, 'previousSibling incorrect'); assert.equal(after.nextSibling, null, 'nextSibling incorrect'); assert.equal(child.previousSibling, before, 'previousSibling incorrect'); }); test('node accessors (distribute)', function() { var child = distribute.children[0]; assert.isTrue(child.classList.contains('child'), 'test node could not be found'); var before = document.createElement('div'); var after = document.createElement('div'); distribute.insertBefore(before, child); distribute.appendChild(after); Polymer.flush(); assert.equal(distribute.firstChild, before, 'firstChild incorrect'); assert.equal(distribute.lastChild, after, 'lastChild incorrect'); assert.equal(before.nextSibling, child, 'nextSibling incorrect'); assert.equal(child.nextSibling, after, 'nextSibling incorrect'); assert.equal(after.previousSibling, child, 'previousSibling incorrect'); assert.equal(after.nextSibling, null, 'nextSibling incorrect'); assert.equal(child.previousSibling, before, 'previousSibling incorrect'); }); test('element accessors (no distribute)', function() { var parent = document.createElement('x-test-no-distribute'); var child = document.createElement('div'); parent.appendChild(child); var before = document.createElement('div'); var after = document.createElement('div'); parent.insertBefore(before, child); parent.appendChild(after); assert.equal(parent.firstElementChild, before, 'firstElementChild incorrect'); assert.equal(parent.lastElementChild, after, 'lastElementChild incorrect'); assert.equal(before.nextElementSibling, child, 'nextElementSibling incorrect'); assert.equal(child.nextElementSibling, after, 'nextElementSibling incorrect'); assert.equal(after.previousElementSibling, child, 'previousElementSibling incorrect'); assert.equal(child.previousElementSibling, before, 'previousElementSibling incorrect'); }); test('element accessors (distribute)', function() { var parent = document.createElement('x-project'); var child = document.createElement('div'); parent.appendChild(child); var before = document.createElement('div'); var after = document.createElement('div'); parent.insertBefore(before, child); parent.appendChild(after); assert.equal(parent.firstElementChild, before, 'firstElementChild incorrect'); assert.equal(parent.lastElementChild, after, 'lastElementChild incorrect'); assert.equal(before.nextElementSibling, child, 'nextElementSibling incorrect'); assert.equal(child.nextElementSibling, after, 'nextElementSibling incorrect'); assert.equal(after.previousElementSibling, child, 'previousElementSibling incorrect'); assert.equal(child.previousElementSibling, before, 'previousElementSibling incorrect'); }); test('node accessors (empty logical tree)', function() { var element = document.createElement('x-simple'); assert.equal(element.parentNode, null, 'parentNode incorrect'); assert.equal(element.firstChild, null, 'firstChild incorrect'); assert.equal(element.lastChild, null, 'lastChild incorrect'); assert.equal(element.nextSibling, null, 'nextSibling incorrect'); assert.equal(element.previousSibling, null, 'previousSibling incorrect'); assert.equal(element.firstElementChild, null, 'firstElementChild incorrect'); assert.equal(element.lastElementChild, null, 'lastElementChild incorrect'); assert.equal(element.nextElementSibling, null, 'nextElementSibling incorrect'); assert.equal(element.previousElementSibling, null, 'previousElementSibling incorrect'); }); test('node accessors (unmanaged logical tree)', function() { var element = document.createElement('div'); var child1 = document.createElement('div'); var child2 = document.createElement('div'); element.appendChild(child1); element.appendChild(child2); assert.equal(element.parentNode, null, 'parentNode incorrect'); assert.equal(element.firstChild, child1, 'firstChild incorrect'); assert.equal(element.lastChild, child2, 'lastChild incorrect'); assert.equal(element.nextSibling, null, 'nextSibling incorrect'); assert.equal(element.previousSibling, null, 'previousSibling incorrect'); assert.equal(element.firstElementChild, child1, 'firstElementChild incorrect'); assert.equal(element.lastElementChild, child2, 'lastElementChild incorrect'); assert.equal(element.nextElementSibling, null, 'nextElementSibling incorrect'); assert.equal(element.previousElementSibling, null, 'previousElementSibling incorrect'); }); test('textContent', function() { var testElement = document.createElement('x-project'); testElement.textContent = 'Hello World'; assert.equal(testElement.textContent, 'Hello World', 'textContent getter incorrect'); if (checkUnpatchedDom) { Polymer.flush(); assert.equal(Polymer.TreeApi.Composed.getChildNodes(testElement)[1].textContent, 'Hello World', 'text content setter incorrect'); } testElement = createEnabledElement('x-commented'); assert.equal(testElement.root.textContent, '[]', 'text content getter with comment incorrect'); var textNode = document.createTextNode('foo'); assert.equal(textNode.textContent, 'foo', 'text content getter on textnode incorrect'); textNode.textContent = 'bar'; assert.equal(textNode.textContent, 'bar', 'text content setter on textnode incorrect'); var commentNode = document.createComment('foo'); assert.equal(commentNode.textContent, 'foo', 'text content getter on commentnode incorrect'); commentNode.textContent = 'bar'; assert.equal(commentNode.textContent, 'bar', 'text content setter on commentnode incorrect'); }); test('innerHTML', function() { var testElement = document.createElement('x-project'); testElement.innerHTML = '<div>Hello World</div><div>2</div><div>3</div>'; var added = testElement.firstChild; assert.equal(added.textContent , 'Hello World', 'innerHTML setter incorrect'); assert.equal(testElement.innerHTML , '<div>Hello World</div><div>2</div><div>3</div>', 'innerHTML getter incorrect'); if (checkUnpatchedDom) { Polymer.flush(); var children = Polymer.TreeApi.Composed.getChildNodes(testElement); assert.equal(children[1], added, 'innerHTML setter composed incorrectly'); assert.equal(children[2].textContent, '2', 'innerHTML setter composed incorrectly'); assert.equal(children[3].textContent, '3', 'innerHTML setter composed incorrectly'); } }); test('innerHTML (non-composed)', function() { var testElement = document.createElement('div'); document.body.appendChild(testElement); testElement.innerHTML = '<div>Hello World</div><div>2</div><div>3</div>'; var added = testElement.firstChild; assert.equal(added.textContent , 'Hello World', 'innerHTML setter incorrect'); assert.equal(testElement.innerHTML , '<div>Hello World</div><div>2</div><div>3</div>', 'innerHTML getter incorrect'); assert.equal(testElement.children.length, 3); }); }); suite('activeElement', function() { var r; // light var r_l; // shadow var r_0; // light var r_0_l; // shadow var r_0_0; // light var r_0_0_l; // shadow var r_0_0_l_0; var r_0_1; // light var r_0_1_l; var r_1; // light var r_1_l; // shadow var r_1_l_0; // shadow var r_1_0; // light var r_1_0_l; var r_1_1; // light var r_1_1_l; suiteSetup(function() { r = document.querySelector('x-shadow-host-root'); r_l = r.querySelector('x-shadow-host-root-light'); r_0 = r.root.querySelector('x-shadow-host-root-0'); r_0_l = r_0.querySelector('x-shadow-host-root-0-light'); r_0_0 = r_0.root.querySelector('x-shadow-host-root-0-0'); r_0_0_l = r_0_0.querySelector('x-shadow-host-root-0-0-light'); r_0_0_l_0 = r_0_0_l.root.querySelector('x-shadow-host-root-0-0-light-0'); r_0_1 = r_0.root.querySelector('x-shadow-host-root-0-1'); r_0_1_l = r_0_1.querySelector('x-shadow-host-root-0-1-light'); r_1 = r.root.querySelector('x-shadow-host-root-1'); r_1_l = r_1.querySelector('x-shadow-host-root-1-light'); r_1_l_0 = r_1_l.root.querySelector('x-shadow-host-root-1-light-0'); r_1_0 = r_1.root.querySelector('x-shadow-host-root-1-0'); r_1_0_l = r_1_0.querySelector('x-shadow-host-root-1-0-light'); r_1_1 = r_1.root.querySelector('x-shadow-host-root-1-1'); r_1_1_l = r_1_1.querySelector('x-shadow-host-root-1-1-light'); }); test('r.focus()', function() { r.focus(); assert.equal(document._activeElement, r, 'document.activeElement === r'); assert.equal(r.root.activeElement, null, 'r.root.activeElement === null'); assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null'); assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null'); assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null'); assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null'); assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null'); assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null'); }); test('r_l.focus()', function() { r_l.focus(); assert.equal(document._activeElement, r_l, 'document.activeElement === r_l'); assert.equal(r.root.activeElement, null, 'r.root.activeElement === null'); assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null'); assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null'); assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null'); assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null'); assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null'); assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null'); }); test('r_0.focus()', function() { r_0.focus(); assert.equal(document._activeElement, r, 'document.activeElement === r'); assert.equal(r.root.activeElement, r_0, 'r.root.activeElement === r_0'); assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null'); assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null'); assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null'); assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null'); assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null'); assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null'); }); test('r_0_l.focus()', function() { r_0_l.focus(); assert.equal(document._activeElement, r, 'document.activeElement === r'); assert.equal(r.root.activeElement, r_0_l, 'r.root.activeElement === r_0_l'); assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null'); assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null'); assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null'); assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null'); assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null'); assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null'); }); test('r_0_0.focus()', function() { r_0_0.focus(); assert.equal(document._activeElement, r, 'document.activeElement === r'); assert.equal(r.root.ac