Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(308)

Unified Diff: runtime/bin/vmservice/observatory/deployed/web/packages/shadow_dom/shadow_dom.debug.js

Issue 839543002: Revert "Build Observatory with runtime" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: runtime/bin/vmservice/observatory/deployed/web/packages/shadow_dom/shadow_dom.debug.js
diff --git a/runtime/bin/vmservice/observatory/deployed/web/packages/shadow_dom/shadow_dom.debug.js b/runtime/bin/vmservice/observatory/deployed/web/packages/shadow_dom/shadow_dom.debug.js
new file mode 100644
index 0000000000000000000000000000000000000000..f411852d5eb1aea73001f3be60b3f21a61691b76
--- /dev/null
+++ b/runtime/bin/vmservice/observatory/deployed/web/packages/shadow_dom/shadow_dom.debug.js
@@ -0,0 +1,7551 @@
+if (!HTMLElement.prototype.createShadowRoot
+ || window.__forceShadowDomPolyfill) {
+
+/*
+ * Copyright 2013 The Polymer Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+(function() {
+ // TODO(jmesserly): fix dart:html to use unprefixed name
+ if (Element.prototype.webkitCreateShadowRoot) {
+ Element.prototype.webkitCreateShadowRoot = function() {
+ return window.ShadowDOMPolyfill.wrapIfNeeded(this).createShadowRoot();
+ };
+ }
+})();
+
+// Copyright 2012 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(global) {
+ 'use strict';
+
+ var PROP_ADD_TYPE = 'add';
+ var PROP_UPDATE_TYPE = 'update';
+ var PROP_RECONFIGURE_TYPE = 'reconfigure';
+ var PROP_DELETE_TYPE = 'delete';
+ var ARRAY_SPLICE_TYPE = 'splice';
+
+ // Detect and do basic sanity checking on Object/Array.observe.
+ function detectObjectObserve() {
+ if (typeof Object.observe !== 'function' ||
+ typeof Array.observe !== 'function') {
+ return false;
+ }
+
+ var records = [];
+
+ function callback(recs) {
+ records = recs;
+ }
+
+ var test = {};
+ Object.observe(test, callback);
+ test.id = 1;
+ test.id = 2;
+ delete test.id;
+ Object.deliverChangeRecords(callback);
+ if (records.length !== 3)
+ return false;
+
+ // TODO(rafaelw): Remove this when new change record type names make it to
+ // chrome release.
+ if (records[0].type == 'new' &&
+ records[1].type == 'updated' &&
+ records[2].type == 'deleted') {
+ PROP_ADD_TYPE = 'new';
+ PROP_UPDATE_TYPE = 'updated';
+ PROP_RECONFIGURE_TYPE = 'reconfigured';
+ PROP_DELETE_TYPE = 'deleted';
+ } else if (records[0].type != 'add' ||
+ records[1].type != 'update' ||
+ records[2].type != 'delete') {
+ console.error('Unexpected change record names for Object.observe. ' +
+ 'Using dirty-checking instead');
+ return false;
+ }
+ Object.unobserve(test, callback);
+
+ test = [0];
+ Array.observe(test, callback);
+ test[1] = 1;
+ test.length = 0;
+ Object.deliverChangeRecords(callback);
+ if (records.length != 2)
+ return false;
+ if (records[0].type != ARRAY_SPLICE_TYPE ||
+ records[1].type != ARRAY_SPLICE_TYPE) {
+ return false;
+ }
+ Array.unobserve(test, callback);
+
+ return true;
+ }
+
+ var hasObserve = detectObjectObserve();
+
+ function detectEval() {
+ // don't test for eval if document has CSP securityPolicy object and we can see that
+ // eval is not supported. This avoids an error message in console even when the exception
+ // is caught
+ if (global.document &&
+ 'securityPolicy' in global.document &&
+ !global.document.securityPolicy.allowsEval) {
+ return false;
+ }
+
+ try {
+ var f = new Function('', 'return true;');
+ return f();
+ } catch (ex) {
+ return false;
+ }
+ }
+
+ var hasEval = detectEval();
+
+ function isIndex(s) {
+ return +s === s >>> 0;
+ }
+
+ function toNumber(s) {
+ return +s;
+ }
+
+ function isObject(obj) {
+ return obj === Object(obj);
+ }
+
+ var numberIsNaN = global.Number.isNaN || function isNaN(value) {
+ return typeof value === 'number' && global.isNaN(value);
+ }
+
+ function areSameValue(left, right) {
+ if (left === right)
+ return left !== 0 || 1 / left === 1 / right;
+ if (numberIsNaN(left) && numberIsNaN(right))
+ return true;
+
+ return left !== left && right !== right;
+ }
+
+ var createObject = ('__proto__' in {}) ?
+ function(obj) { return obj; } :
+ function(obj) {
+ var proto = obj.__proto__;
+ if (!proto)
+ return obj;
+ var newObject = Object.create(proto);
+ Object.getOwnPropertyNames(obj).forEach(function(name) {
+ Object.defineProperty(newObject, name,
+ Object.getOwnPropertyDescriptor(obj, name));
+ });
+ return newObject;
+ };
+
+ var identStart = '[\$_a-zA-Z]';
+ var identPart = '[\$_a-zA-Z0-9]';
+ var ident = identStart + '+' + identPart + '*';
+ var elementIndex = '(?:[0-9]|[1-9]+[0-9]+)';
+ var identOrElementIndex = '(?:' + ident + '|' + elementIndex + ')';
+ var path = '(?:' + identOrElementIndex + ')(?:\\s*\\.\\s*' + identOrElementIndex + ')*';
+ var pathRegExp = new RegExp('^' + path + '$');
+
+ function isPathValid(s) {
+ if (typeof s != 'string')
+ return false;
+ s = s.trim();
+
+ if (s == '')
+ return true;
+
+ if (s[0] == '.')
+ return false;
+
+ return pathRegExp.test(s);
+ }
+
+ var constructorIsPrivate = {};
+
+ function Path(s, privateToken) {
+ if (privateToken !== constructorIsPrivate)
+ throw Error('Use Path.get to retrieve path objects');
+
+ if (s.trim() == '')
+ return this;
+
+ if (isIndex(s)) {
+ this.push(s);
+ return this;
+ }
+
+ s.split(/\s*\.\s*/).filter(function(part) {
+ return part;
+ }).forEach(function(part) {
+ this.push(part);
+ }, this);
+
+ if (hasEval && this.length) {
+ this.getValueFrom = this.compiledGetValueFromFn();
+ }
+ }
+
+ // TODO(rafaelw): Make simple LRU cache
+ var pathCache = {};
+
+ function getPath(pathString) {
+ if (pathString instanceof Path)
+ return pathString;
+
+ if (pathString == null)
+ pathString = '';
+
+ if (typeof pathString !== 'string')
+ pathString = String(pathString);
+
+ var path = pathCache[pathString];
+ if (path)
+ return path;
+ if (!isPathValid(pathString))
+ return invalidPath;
+ var path = new Path(pathString, constructorIsPrivate);
+ pathCache[pathString] = path;
+ return path;
+ }
+
+ Path.get = getPath;
+
+ Path.prototype = createObject({
+ __proto__: [],
+ valid: true,
+
+ toString: function() {
+ return this.join('.');
+ },
+
+ getValueFrom: function(obj, directObserver) {
+ for (var i = 0; i < this.length; i++) {
+ if (obj == null)
+ return;
+ obj = obj[this[i]];
+ }
+ return obj;
+ },
+
+ iterateObjects: function(obj, observe) {
+ for (var i = 0; i < this.length; i++) {
+ if (i)
+ obj = obj[this[i - 1]];
+ if (!obj)
+ return;
+ observe(obj);
+ }
+ },
+
+ compiledGetValueFromFn: function() {
+ var accessors = this.map(function(ident) {
+ return isIndex(ident) ? '["' + ident + '"]' : '.' + ident;
+ });
+
+ var str = '';
+ var pathString = 'obj';
+ str += 'if (obj != null';
+ var i = 0;
+ for (; i < (this.length - 1); i++) {
+ var ident = this[i];
+ pathString += accessors[i];
+ str += ' &&\n ' + pathString + ' != null';
+ }
+ str += ')\n';
+
+ pathString += accessors[i];
+
+ str += ' return ' + pathString + ';\nelse\n return undefined;';
+ return new Function('obj', str);
+ },
+
+ setValueFrom: function(obj, value) {
+ if (!this.length)
+ return false;
+
+ for (var i = 0; i < this.length - 1; i++) {
+ if (!isObject(obj))
+ return false;
+ obj = obj[this[i]];
+ }
+
+ if (!isObject(obj))
+ return false;
+
+ obj[this[i]] = value;
+ return true;
+ }
+ });
+
+ var invalidPath = new Path('', constructorIsPrivate);
+ invalidPath.valid = false;
+ invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
+
+ var MAX_DIRTY_CHECK_CYCLES = 1000;
+
+ function dirtyCheck(observer) {
+ var cycles = 0;
+ while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
+ cycles++;
+ }
+ if (global.testingExposeCycleCount)
+ global.dirtyCheckCycleCount = cycles;
+
+ return cycles > 0;
+ }
+
+ function objectIsEmpty(object) {
+ for (var prop in object)
+ return false;
+ return true;
+ }
+
+ function diffIsEmpty(diff) {
+ return objectIsEmpty(diff.added) &&
+ objectIsEmpty(diff.removed) &&
+ objectIsEmpty(diff.changed);
+ }
+
+ function diffObjectFromOldObject(object, oldObject) {
+ var added = {};
+ var removed = {};
+ var changed = {};
+ var oldObjectHas = {};
+
+ for (var prop in oldObject) {
+ var newValue = object[prop];
+
+ if (newValue !== undefined && newValue === oldObject[prop])
+ continue;
+
+ if (!(prop in object)) {
+ removed[prop] = undefined;
+ continue;
+ }
+
+ if (newValue !== oldObject[prop])
+ changed[prop] = newValue;
+ }
+
+ for (var prop in object) {
+ if (prop in oldObject)
+ continue;
+
+ added[prop] = object[prop];
+ }
+
+ if (Array.isArray(object) && object.length !== oldObject.length)
+ changed.length = object.length;
+
+ return {
+ added: added,
+ removed: removed,
+ changed: changed
+ };
+ }
+
+ var eomTasks = [];
+ function runEOMTasks() {
+ if (!eomTasks.length)
+ return false;
+
+ for (var i = 0; i < eomTasks.length; i++) {
+ eomTasks[i]();
+ }
+ eomTasks.length = 0;
+ return true;
+ }
+
+ var runEOM = hasObserve ? (function(){
+ var eomObj = { pingPong: true };
+ var eomRunScheduled = false;
+
+ Object.observe(eomObj, function() {
+ runEOMTasks();
+ eomRunScheduled = false;
+ });
+
+ return function(fn) {
+ eomTasks.push(fn);
+ if (!eomRunScheduled) {
+ eomRunScheduled = true;
+ eomObj.pingPong = !eomObj.pingPong;
+ }
+ };
+ })() :
+ (function() {
+ return function(fn) {
+ eomTasks.push(fn);
+ };
+ })();
+
+ var observedObjectCache = [];
+
+ function newObservedObject() {
+ var observer;
+ var object;
+ var discardRecords = false;
+ var first = true;
+
+ function callback(records) {
+ if (observer && observer.state_ === OPENED && !discardRecords)
+ observer.check_(records);
+ }
+
+ return {
+ open: function(obs) {
+ if (observer)
+ throw Error('ObservedObject in use');
+
+ if (!first)
+ Object.deliverChangeRecords(callback);
+
+ observer = obs;
+ first = false;
+ },
+ observe: function(obj, arrayObserve) {
+ object = obj;
+ if (arrayObserve)
+ Array.observe(object, callback);
+ else
+ Object.observe(object, callback);
+ },
+ deliver: function(discard) {
+ discardRecords = discard;
+ Object.deliverChangeRecords(callback);
+ discardRecords = false;
+ },
+ close: function() {
+ observer = undefined;
+ Object.unobserve(object, callback);
+ observedObjectCache.push(this);
+ }
+ };
+ }
+
+ function getObservedObject(observer, object, arrayObserve) {
+ var dir = observedObjectCache.pop() || newObservedObject();
+ dir.open(observer);
+ dir.observe(object, arrayObserve);
+ return dir;
+ }
+
+ var emptyArray = [];
+ var observedSetCache = [];
+
+ function newObservedSet() {
+ var observers = [];
+ var observerCount = 0;
+ var objects = [];
+ var toRemove = emptyArray;
+ var resetNeeded = false;
+ var resetScheduled = false;
+
+ function observe(obj) {
+ if (!isObject(obj))
+ return;
+
+ var index = toRemove.indexOf(obj);
+ if (index >= 0) {
+ toRemove[index] = undefined;
+ objects.push(obj);
+ } else if (objects.indexOf(obj) < 0) {
+ objects.push(obj);
+ Object.observe(obj, callback);
+ }
+
+ observe(Object.getPrototypeOf(obj));
+ }
+
+ function reset() {
+ resetScheduled = false;
+ if (!resetNeeded)
+ return;
+
+ var objs = toRemove === emptyArray ? [] : toRemove;
+ toRemove = objects;
+ objects = objs;
+
+ var observer;
+ for (var id in observers) {
+ observer = observers[id];
+ if (!observer || observer.state_ != OPENED)
+ continue;
+
+ observer.iterateObjects_(observe);
+ }
+
+ for (var i = 0; i < toRemove.length; i++) {
+ var obj = toRemove[i];
+ if (obj)
+ Object.unobserve(obj, callback);
+ }
+
+ toRemove.length = 0;
+ }
+
+ function scheduleReset() {
+ if (resetScheduled)
+ return;
+
+ resetNeeded = true;
+ resetScheduled = true;
+ runEOM(reset);
+ }
+
+ function callback() {
+ var observer;
+
+ for (var id in observers) {
+ observer = observers[id];
+ if (!observer || observer.state_ != OPENED)
+ continue;
+
+ observer.check_();
+ }
+
+ scheduleReset();
+ }
+
+ var record = {
+ object: undefined,
+ objects: objects,
+ open: function(obs) {
+ observers[obs.id_] = obs;
+ observerCount++;
+ obs.iterateObjects_(observe);
+ },
+ close: function(obs) {
+ var anyLeft = false;
+
+ observers[obs.id_] = undefined;
+ observerCount--;
+
+ if (observerCount) {
+ scheduleReset();
+ return;
+ }
+ resetNeeded = false;
+
+ for (var i = 0; i < objects.length; i++) {
+ Object.unobserve(objects[i], callback);
+ Observer.unobservedCount++;
+ }
+
+ observers.length = 0;
+ objects.length = 0;
+ observedSetCache.push(this);
+ },
+ reset: scheduleReset
+ };
+
+ return record;
+ }
+
+ var lastObservedSet;
+
+ function getObservedSet(observer, obj) {
+ if (!lastObservedSet || lastObservedSet.object !== obj) {
+ lastObservedSet = observedSetCache.pop() || newObservedSet();
+ lastObservedSet.object = obj;
+ }
+ lastObservedSet.open(observer);
+ return lastObservedSet;
+ }
+
+ var UNOPENED = 0;
+ var OPENED = 1;
+ var CLOSED = 2;
+ var RESETTING = 3;
+
+ var nextObserverId = 1;
+
+ function Observer() {
+ this.state_ = UNOPENED;
+ this.callback_ = undefined;
+ this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
+ this.directObserver_ = undefined;
+ this.value_ = undefined;
+ this.id_ = nextObserverId++;
+ }
+
+ Observer.prototype = {
+ open: function(callback, target) {
+ if (this.state_ != UNOPENED)
+ throw Error('Observer has already been opened.');
+
+ addToAll(this);
+ this.callback_ = callback;
+ this.target_ = target;
+ this.state_ = OPENED;
+ this.connect_();
+ return this.value_;
+ },
+
+ close: function() {
+ if (this.state_ != OPENED)
+ return;
+
+ removeFromAll(this);
+ this.state_ = CLOSED;
+ this.disconnect_();
+ this.value_ = undefined;
+ this.callback_ = undefined;
+ this.target_ = undefined;
+ },
+
+ deliver: function() {
+ if (this.state_ != OPENED)
+ return;
+
+ dirtyCheck(this);
+ },
+
+ report_: function(changes) {
+ try {
+ this.callback_.apply(this.target_, changes);
+ } catch (ex) {
+ Observer._errorThrownDuringCallback = true;
+ console.error('Exception caught during observer callback: ' +
+ (ex.stack || ex));
+ }
+ },
+
+ discardChanges: function() {
+ this.check_(undefined, true);
+ return this.value_;
+ }
+ }
+
+ var collectObservers = !hasObserve;
+ var allObservers;
+ Observer._allObserversCount = 0;
+
+ if (collectObservers) {
+ allObservers = [];
+ }
+
+ function addToAll(observer) {
+ Observer._allObserversCount++;
+ if (!collectObservers)
+ return;
+
+ allObservers.push(observer);
+ }
+
+ function removeFromAll(observer) {
+ Observer._allObserversCount--;
+ }
+
+ var runningMicrotaskCheckpoint = false;
+
+ var hasDebugForceFullDelivery = typeof Object.deliverAllChangeRecords == 'function';
+
+ global.Platform = global.Platform || {};
+
+ global.Platform.performMicrotaskCheckpoint = function() {
+ if (runningMicrotaskCheckpoint)
+ return;
+
+ if (hasDebugForceFullDelivery) {
+ Object.deliverAllChangeRecords();
+ return;
+ }
+
+ if (!collectObservers)
+ return;
+
+ runningMicrotaskCheckpoint = true;
+
+ var cycles = 0;
+ var anyChanged, toCheck;
+
+ do {
+ cycles++;
+ toCheck = allObservers;
+ allObservers = [];
+ anyChanged = false;
+
+ for (var i = 0; i < toCheck.length; i++) {
+ var observer = toCheck[i];
+ if (observer.state_ != OPENED)
+ continue;
+
+ if (observer.check_())
+ anyChanged = true;
+
+ allObservers.push(observer);
+ }
+ if (runEOMTasks())
+ anyChanged = true;
+ } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
+
+ if (global.testingExposeCycleCount)
+ global.dirtyCheckCycleCount = cycles;
+
+ runningMicrotaskCheckpoint = false;
+ };
+
+ if (collectObservers) {
+ global.Platform.clearObservers = function() {
+ allObservers = [];
+ };
+ }
+
+ function ObjectObserver(object) {
+ Observer.call(this);
+ this.value_ = object;
+ this.oldObject_ = undefined;
+ }
+
+ ObjectObserver.prototype = createObject({
+ __proto__: Observer.prototype,
+
+ arrayObserve: false,
+
+ connect_: function(callback, target) {
+ if (hasObserve) {
+ this.directObserver_ = getObservedObject(this, this.value_,
+ this.arrayObserve);
+ } else {
+ this.oldObject_ = this.copyObject(this.value_);
+ }
+
+ },
+
+ copyObject: function(object) {
+ var copy = Array.isArray(object) ? [] : {};
+ for (var prop in object) {
+ copy[prop] = object[prop];
+ };
+ if (Array.isArray(object))
+ copy.length = object.length;
+ return copy;
+ },
+
+ check_: function(changeRecords, skipChanges) {
+ var diff;
+ var oldValues;
+ if (hasObserve) {
+ if (!changeRecords)
+ return false;
+
+ oldValues = {};
+ diff = diffObjectFromChangeRecords(this.value_, changeRecords,
+ oldValues);
+ } else {
+ oldValues = this.oldObject_;
+ diff = diffObjectFromOldObject(this.value_, this.oldObject_);
+ }
+
+ if (diffIsEmpty(diff))
+ return false;
+
+ if (!hasObserve)
+ this.oldObject_ = this.copyObject(this.value_);
+
+ this.report_([
+ diff.added || {},
+ diff.removed || {},
+ diff.changed || {},
+ function(property) {
+ return oldValues[property];
+ }
+ ]);
+
+ return true;
+ },
+
+ disconnect_: function() {
+ if (hasObserve) {
+ this.directObserver_.close();
+ this.directObserver_ = undefined;
+ } else {
+ this.oldObject_ = undefined;
+ }
+ },
+
+ deliver: function() {
+ if (this.state_ != OPENED)
+ return;
+
+ if (hasObserve)
+ this.directObserver_.deliver(false);
+ else
+ dirtyCheck(this);
+ },
+
+ discardChanges: function() {
+ if (this.directObserver_)
+ this.directObserver_.deliver(true);
+ else
+ this.oldObject_ = this.copyObject(this.value_);
+
+ return this.value_;
+ }
+ });
+
+ function ArrayObserver(array) {
+ if (!Array.isArray(array))
+ throw Error('Provided object is not an Array');
+ ObjectObserver.call(this, array);
+ }
+
+ ArrayObserver.prototype = createObject({
+
+ __proto__: ObjectObserver.prototype,
+
+ arrayObserve: true,
+
+ copyObject: function(arr) {
+ return arr.slice();
+ },
+
+ check_: function(changeRecords) {
+ var splices;
+ if (hasObserve) {
+ if (!changeRecords)
+ return false;
+ splices = projectArraySplices(this.value_, changeRecords);
+ } else {
+ splices = calcSplices(this.value_, 0, this.value_.length,
+ this.oldObject_, 0, this.oldObject_.length);
+ }
+
+ if (!splices || !splices.length)
+ return false;
+
+ if (!hasObserve)
+ this.oldObject_ = this.copyObject(this.value_);
+
+ this.report_([splices]);
+ return true;
+ }
+ });
+
+ ArrayObserver.applySplices = function(previous, current, splices) {
+ splices.forEach(function(splice) {
+ var spliceArgs = [splice.index, splice.removed.length];
+ var addIndex = splice.index;
+ while (addIndex < splice.index + splice.addedCount) {
+ spliceArgs.push(current[addIndex]);
+ addIndex++;
+ }
+
+ Array.prototype.splice.apply(previous, spliceArgs);
+ });
+ };
+
+ function PathObserver(object, path) {
+ Observer.call(this);
+
+ this.object_ = object;
+ this.path_ = path instanceof Path ? path : getPath(path);
+ this.directObserver_ = undefined;
+ }
+
+ PathObserver.prototype = createObject({
+ __proto__: Observer.prototype,
+
+ connect_: function() {
+ if (hasObserve)
+ this.directObserver_ = getObservedSet(this, this.object_);
+
+ this.check_(undefined, true);
+ },
+
+ disconnect_: function() {
+ this.value_ = undefined;
+
+ if (this.directObserver_) {
+ this.directObserver_.close(this);
+ this.directObserver_ = undefined;
+ }
+ },
+
+ iterateObjects_: function(observe) {
+ this.path_.iterateObjects(this.object_, observe);
+ },
+
+ check_: function(changeRecords, skipChanges) {
+ var oldValue = this.value_;
+ this.value_ = this.path_.getValueFrom(this.object_);
+ if (skipChanges || areSameValue(this.value_, oldValue))
+ return false;
+
+ this.report_([this.value_, oldValue]);
+ return true;
+ },
+
+ setValue: function(newValue) {
+ if (this.path_)
+ this.path_.setValueFrom(this.object_, newValue);
+ }
+ });
+
+ function CompoundObserver() {
+ Observer.call(this);
+
+ this.value_ = [];
+ this.directObserver_ = undefined;
+ this.observed_ = [];
+ }
+
+ var observerSentinel = {};
+
+ CompoundObserver.prototype = createObject({
+ __proto__: Observer.prototype,
+
+ connect_: function() {
+ this.check_(undefined, true);
+
+ if (!hasObserve)
+ return;
+
+ var object;
+ var needsDirectObserver = false;
+ for (var i = 0; i < this.observed_.length; i += 2) {
+ object = this.observed_[i]
+ if (object !== observerSentinel) {
+ needsDirectObserver = true;
+ break;
+ }
+ }
+
+ if (this.directObserver_) {
+ if (needsDirectObserver) {
+ this.directObserver_.reset();
+ return;
+ }
+ this.directObserver_.close();
+ this.directObserver_ = undefined;
+ return;
+ }
+
+ if (needsDirectObserver)
+ this.directObserver_ = getObservedSet(this, object);
+ },
+
+ closeObservers_: function() {
+ for (var i = 0; i < this.observed_.length; i += 2) {
+ if (this.observed_[i] === observerSentinel)
+ this.observed_[i + 1].close();
+ }
+ this.observed_.length = 0;
+ },
+
+ disconnect_: function() {
+ this.value_ = undefined;
+
+ if (this.directObserver_) {
+ this.directObserver_.close(this);
+ this.directObserver_ = undefined;
+ }
+
+ this.closeObservers_();
+ },
+
+ addPath: function(object, path) {
+ if (this.state_ != UNOPENED && this.state_ != RESETTING)
+ throw Error('Cannot add paths once started.');
+
+ this.observed_.push(object, path instanceof Path ? path : getPath(path));
+ },
+
+ addObserver: function(observer) {
+ if (this.state_ != UNOPENED && this.state_ != RESETTING)
+ throw Error('Cannot add observers once started.');
+
+ observer.open(this.deliver, this);
+ this.observed_.push(observerSentinel, observer);
+ },
+
+ startReset: function() {
+ if (this.state_ != OPENED)
+ throw Error('Can only reset while open');
+
+ this.state_ = RESETTING;
+ this.closeObservers_();
+ },
+
+ finishReset: function() {
+ if (this.state_ != RESETTING)
+ throw Error('Can only finishReset after startReset');
+ this.state_ = OPENED;
+ this.connect_();
+
+ return this.value_;
+ },
+
+ iterateObjects_: function(observe) {
+ var object;
+ for (var i = 0; i < this.observed_.length; i += 2) {
+ object = this.observed_[i]
+ if (object !== observerSentinel)
+ this.observed_[i + 1].iterateObjects(object, observe)
+ }
+ },
+
+ check_: function(changeRecords, skipChanges) {
+ var oldValues;
+ for (var i = 0; i < this.observed_.length; i += 2) {
+ var pathOrObserver = this.observed_[i+1];
+ var object = this.observed_[i];
+ var value = object === observerSentinel ?
+ pathOrObserver.discardChanges() :
+ pathOrObserver.getValueFrom(object)
+
+ if (skipChanges) {
+ this.value_[i / 2] = value;
+ continue;
+ }
+
+ if (areSameValue(value, this.value_[i / 2]))
+ continue;
+
+ oldValues = oldValues || [];
+ oldValues[i / 2] = this.value_[i / 2];
+ this.value_[i / 2] = value;
+ }
+
+ if (!oldValues)
+ return false;
+
+ // TODO(rafaelw): Having observed_ as the third callback arg here is
+ // pretty lame API. Fix.
+ this.report_([this.value_, oldValues, this.observed_]);
+ return true;
+ }
+ });
+
+ function identFn(value) { return value; }
+
+ function ObserverTransform(observable, getValueFn, setValueFn,
+ dontPassThroughSet) {
+ this.callback_ = undefined;
+ this.target_ = undefined;
+ this.value_ = undefined;
+ this.observable_ = observable;
+ this.getValueFn_ = getValueFn || identFn;
+ this.setValueFn_ = setValueFn || identFn;
+ // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
+ // at the moment because of a bug in it's dependency tracking.
+ this.dontPassThroughSet_ = dontPassThroughSet;
+ }
+
+ ObserverTransform.prototype = {
+ open: function(callback, target) {
+ this.callback_ = callback;
+ this.target_ = target;
+ this.value_ =
+ this.getValueFn_(this.observable_.open(this.observedCallback_, this));
+ return this.value_;
+ },
+
+ observedCallback_: function(value) {
+ value = this.getValueFn_(value);
+ if (areSameValue(value, this.value_))
+ return;
+ var oldValue = this.value_;
+ this.value_ = value;
+ this.callback_.call(this.target_, this.value_, oldValue);
+ },
+
+ discardChanges: function() {
+ this.value_ = this.getValueFn_(this.observable_.discardChanges());
+ return this.value_;
+ },
+
+ deliver: function() {
+ return this.observable_.deliver();
+ },
+
+ setValue: function(value) {
+ value = this.setValueFn_(value);
+ if (!this.dontPassThroughSet_ && this.observable_.setValue)
+ return this.observable_.setValue(value);
+ },
+
+ close: function() {
+ if (this.observable_)
+ this.observable_.close();
+ this.callback_ = undefined;
+ this.target_ = undefined;
+ this.observable_ = undefined;
+ this.value_ = undefined;
+ this.getValueFn_ = undefined;
+ this.setValueFn_ = undefined;
+ }
+ }
+
+ var expectedRecordTypes = {};
+ expectedRecordTypes[PROP_ADD_TYPE] = true;
+ expectedRecordTypes[PROP_UPDATE_TYPE] = true;
+ expectedRecordTypes[PROP_DELETE_TYPE] = true;
+
+ function notifyFunction(object, name) {
+ if (typeof Object.observe !== 'function')
+ return;
+
+ var notifier = Object.getNotifier(object);
+ return function(type, oldValue) {
+ var changeRecord = {
+ object: object,
+ type: type,
+ name: name
+ };
+ if (arguments.length === 2)
+ changeRecord.oldValue = oldValue;
+ notifier.notify(changeRecord);
+ }
+ }
+
+ Observer.defineComputedProperty = function(target, name, observable) {
+ var notify = notifyFunction(target, name);
+ var value = observable.open(function(newValue, oldValue) {
+ value = newValue;
+ if (notify)
+ notify(PROP_UPDATE_TYPE, oldValue);
+ });
+
+ Object.defineProperty(target, name, {
+ get: function() {
+ observable.deliver();
+ return value;
+ },
+ set: function(newValue) {
+ observable.setValue(newValue);
+ return newValue;
+ },
+ configurable: true
+ });
+
+ return {
+ close: function() {
+ observable.close();
+ Object.defineProperty(target, name, {
+ value: value,
+ writable: true,
+ configurable: true
+ });
+ }
+ };
+ }
+
+ function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
+ var added = {};
+ var removed = {};
+
+ for (var i = 0; i < changeRecords.length; i++) {
+ var record = changeRecords[i];
+ if (!expectedRecordTypes[record.type]) {
+ console.error('Unknown changeRecord type: ' + record.type);
+ console.error(record);
+ continue;
+ }
+
+ if (!(record.name in oldValues))
+ oldValues[record.name] = record.oldValue;
+
+ if (record.type == PROP_UPDATE_TYPE)
+ continue;
+
+ if (record.type == PROP_ADD_TYPE) {
+ if (record.name in removed)
+ delete removed[record.name];
+ else
+ added[record.name] = true;
+
+ continue;
+ }
+
+ // type = 'delete'
+ if (record.name in added) {
+ delete added[record.name];
+ delete oldValues[record.name];
+ } else {
+ removed[record.name] = true;
+ }
+ }
+
+ for (var prop in added)
+ added[prop] = object[prop];
+
+ for (var prop in removed)
+ removed[prop] = undefined;
+
+ var changed = {};
+ for (var prop in oldValues) {
+ if (prop in added || prop in removed)
+ continue;
+
+ var newValue = object[prop];
+ if (oldValues[prop] !== newValue)
+ changed[prop] = newValue;
+ }
+
+ return {
+ added: added,
+ removed: removed,
+ changed: changed
+ };
+ }
+
+ function newSplice(index, removed, addedCount) {
+ return {
+ index: index,
+ removed: removed,
+ addedCount: addedCount
+ };
+ }
+
+ var EDIT_LEAVE = 0;
+ var EDIT_UPDATE = 1;
+ var EDIT_ADD = 2;
+ var EDIT_DELETE = 3;
+
+ function ArraySplice() {}
+
+ ArraySplice.prototype = {
+
+ // Note: This function is *based* on the computation of the Levenshtein
+ // "edit" distance. The one change is that "updates" are treated as two
+ // edits - not one. With Array splices, an update is really a delete
+ // followed by an add. By retaining this, we optimize for "keeping" the
+ // maximum array items in the original array. For example:
+ //
+ // 'xxxx123' -> '123yyyy'
+ //
+ // With 1-edit updates, the shortest path would be just to update all seven
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
+ // leaves the substring '123' intact.
+ calcEditDistances: function(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ // "Deletion" columns
+ var rowCount = oldEnd - oldStart + 1;
+ var columnCount = currentEnd - currentStart + 1;
+ var distances = new Array(rowCount);
+
+ // "Addition" rows. Initialize null column.
+ for (var i = 0; i < rowCount; i++) {
+ distances[i] = new Array(columnCount);
+ distances[i][0] = i;
+ }
+
+ // Initialize null row
+ for (var j = 0; j < columnCount; j++)
+ distances[0][j] = j;
+
+ for (var i = 1; i < rowCount; i++) {
+ for (var j = 1; j < columnCount; j++) {
+ if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
+ distances[i][j] = distances[i - 1][j - 1];
+ else {
+ var north = distances[i - 1][j] + 1;
+ var west = distances[i][j - 1] + 1;
+ distances[i][j] = north < west ? north : west;
+ }
+ }
+ }
+
+ return distances;
+ },
+
+ // This starts at the final weight, and walks "backward" by finding
+ // the minimum previous weight recursively until the origin of the weight
+ // matrix.
+ spliceOperationsFromEditDistances: function(distances) {
+ var i = distances.length - 1;
+ var j = distances[0].length - 1;
+ var current = distances[i][j];
+ var edits = [];
+ while (i > 0 || j > 0) {
+ if (i == 0) {
+ edits.push(EDIT_ADD);
+ j--;
+ continue;
+ }
+ if (j == 0) {
+ edits.push(EDIT_DELETE);
+ i--;
+ continue;
+ }
+ var northWest = distances[i - 1][j - 1];
+ var west = distances[i - 1][j];
+ var north = distances[i][j - 1];
+
+ var min;
+ if (west < north)
+ min = west < northWest ? west : northWest;
+ else
+ min = north < northWest ? north : northWest;
+
+ if (min == northWest) {
+ if (northWest == current) {
+ edits.push(EDIT_LEAVE);
+ } else {
+ edits.push(EDIT_UPDATE);
+ current = northWest;
+ }
+ i--;
+ j--;
+ } else if (min == west) {
+ edits.push(EDIT_DELETE);
+ i--;
+ current = west;
+ } else {
+ edits.push(EDIT_ADD);
+ j--;
+ current = north;
+ }
+ }
+
+ edits.reverse();
+ return edits;
+ },
+
+ /**
+ * Splice Projection functions:
+ *
+ * A splice map is a representation of how a previous array of items
+ * was transformed into a new array of items. Conceptually it is a list of
+ * tuples of
+ *
+ * <index, removed, addedCount>
+ *
+ * which are kept in ascending index order of. The tuple represents that at
+ * the |index|, |removed| sequence of items were removed, and counting forward
+ * from |index|, |addedCount| items were added.
+ */
+
+ /**
+ * Lacking individual splice mutation information, the minimal set of
+ * splices can be synthesized given the previous state and final state of an
+ * array. The basic approach is to calculate the edit distance matrix and
+ * choose the shortest path through it.
+ *
+ * Complexity: O(l * p)
+ * l: The length of the current array
+ * p: The length of the old array
+ */
+ calcSplices: function(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ var prefixCount = 0;
+ var suffixCount = 0;
+
+ var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+ if (currentStart == 0 && oldStart == 0)
+ prefixCount = this.sharedPrefix(current, old, minLength);
+
+ if (currentEnd == current.length && oldEnd == old.length)
+ suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+
+ currentStart += prefixCount;
+ oldStart += prefixCount;
+ currentEnd -= suffixCount;
+ oldEnd -= suffixCount;
+
+ if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
+ return [];
+
+ if (currentStart == currentEnd) {
+ var splice = newSplice(currentStart, [], 0);
+ while (oldStart < oldEnd)
+ splice.removed.push(old[oldStart++]);
+
+ return [ splice ];
+ } else if (oldStart == oldEnd)
+ return [ newSplice(currentStart, [], currentEnd - currentStart) ];
+
+ var ops = this.spliceOperationsFromEditDistances(
+ this.calcEditDistances(current, currentStart, currentEnd,
+ old, oldStart, oldEnd));
+
+ var splice = undefined;
+ var splices = [];
+ var index = currentStart;
+ var oldIndex = oldStart;
+ for (var i = 0; i < ops.length; i++) {
+ switch(ops[i]) {
+ case EDIT_LEAVE:
+ if (splice) {
+ splices.push(splice);
+ splice = undefined;
+ }
+
+ index++;
+ oldIndex++;
+ break;
+ case EDIT_UPDATE:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.addedCount++;
+ index++;
+
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+ case EDIT_ADD:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.addedCount++;
+ index++;
+ break;
+ case EDIT_DELETE:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+ }
+ }
+
+ if (splice) {
+ splices.push(splice);
+ }
+ return splices;
+ },
+
+ sharedPrefix: function(current, old, searchLength) {
+ for (var i = 0; i < searchLength; i++)
+ if (!this.equals(current[i], old[i]))
+ return i;
+ return searchLength;
+ },
+
+ sharedSuffix: function(current, old, searchLength) {
+ var index1 = current.length;
+ var index2 = old.length;
+ var count = 0;
+ while (count < searchLength && this.equals(current[--index1], old[--index2]))
+ count++;
+
+ return count;
+ },
+
+ calculateSplices: function(current, previous) {
+ return this.calcSplices(current, 0, current.length, previous, 0,
+ previous.length);
+ },
+
+ equals: function(currentValue, previousValue) {
+ return currentValue === previousValue;
+ }
+ };
+
+ var arraySplice = new ArraySplice();
+
+ function calcSplices(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ return arraySplice.calcSplices(current, currentStart, currentEnd,
+ old, oldStart, oldEnd);
+ }
+
+ function intersect(start1, end1, start2, end2) {
+ // Disjoint
+ if (end1 < start2 || end2 < start1)
+ return -1;
+
+ // Adjacent
+ if (end1 == start2 || end2 == start1)
+ return 0;
+
+ // Non-zero intersect, span1 first
+ if (start1 < start2) {
+ if (end1 < end2)
+ return end1 - start2; // Overlap
+ else
+ return end2 - start2; // Contained
+ } else {
+ // Non-zero intersect, span2 first
+ if (end2 < end1)
+ return end2 - start1; // Overlap
+ else
+ return end1 - start1; // Contained
+ }
+ }
+
+ function mergeSplice(splices, index, removed, addedCount) {
+
+ var splice = newSplice(index, removed, addedCount);
+
+ var inserted = false;
+ var insertionOffset = 0;
+
+ for (var i = 0; i < splices.length; i++) {
+ var current = splices[i];
+ current.index += insertionOffset;
+
+ if (inserted)
+ continue;
+
+ var intersectCount = intersect(splice.index,
+ splice.index + splice.removed.length,
+ current.index,
+ current.index + current.addedCount);
+
+ if (intersectCount >= 0) {
+ // Merge the two splices
+
+ splices.splice(i, 1);
+ i--;
+
+ insertionOffset -= current.addedCount - current.removed.length;
+
+ splice.addedCount += current.addedCount - intersectCount;
+ var deleteCount = splice.removed.length +
+ current.removed.length - intersectCount;
+
+ if (!splice.addedCount && !deleteCount) {
+ // merged splice is a noop. discard.
+ inserted = true;
+ } else {
+ var removed = current.removed;
+
+ if (splice.index < current.index) {
+ // some prefix of splice.removed is prepended to current.removed.
+ var prepend = splice.removed.slice(0, current.index - splice.index);
+ Array.prototype.push.apply(prepend, removed);
+ removed = prepend;
+ }
+
+ if (splice.index + splice.removed.length > current.index + current.addedCount) {
+ // some suffix of splice.removed is appended to current.removed.
+ var append = splice.removed.slice(current.index + current.addedCount - splice.index);
+ Array.prototype.push.apply(removed, append);
+ }
+
+ splice.removed = removed;
+ if (current.index < splice.index) {
+ splice.index = current.index;
+ }
+ }
+ } else if (splice.index < current.index) {
+ // Insert splice here.
+
+ inserted = true;
+
+ splices.splice(i, 0, splice);
+ i++;
+
+ var offset = splice.addedCount - splice.removed.length
+ current.index += offset;
+ insertionOffset += offset;
+ }
+ }
+
+ if (!inserted)
+ splices.push(splice);
+ }
+
+ function createInitialSplices(array, changeRecords) {
+ var splices = [];
+
+ for (var i = 0; i < changeRecords.length; i++) {
+ var record = changeRecords[i];
+ switch(record.type) {
+ case ARRAY_SPLICE_TYPE:
+ mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
+ break;
+ case PROP_ADD_TYPE:
+ case PROP_UPDATE_TYPE:
+ case PROP_DELETE_TYPE:
+ if (!isIndex(record.name))
+ continue;
+ var index = toNumber(record.name);
+ if (index < 0)
+ continue;
+ mergeSplice(splices, index, [record.oldValue], 1);
+ break;
+ default:
+ console.error('Unexpected record type: ' + JSON.stringify(record));
+ break;
+ }
+ }
+
+ return splices;
+ }
+
+ function projectArraySplices(array, changeRecords) {
+ var splices = [];
+
+ createInitialSplices(array, changeRecords).forEach(function(splice) {
+ if (splice.addedCount == 1 && splice.removed.length == 1) {
+ if (splice.removed[0] !== array[splice.index])
+ splices.push(splice);
+
+ return
+ };
+
+ splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
+ splice.removed, 0, splice.removed.length));
+ });
+
+ return splices;
+ }
+
+ global.Observer = Observer;
+ global.Observer.runEOM_ = runEOM;
+ global.Observer.hasObjectObserve = hasObserve;
+ global.ArrayObserver = ArrayObserver;
+ global.ArrayObserver.calculateSplices = function(current, previous) {
+ return arraySplice.calculateSplices(current, previous);
+ };
+
+ global.ArraySplice = ArraySplice;
+ global.ObjectObserver = ObjectObserver;
+ global.PathObserver = PathObserver;
+ global.CompoundObserver = CompoundObserver;
+ global.Path = Path;
+ global.ObserverTransform = ObserverTransform;
+
+ // TODO(rafaelw): Only needed for testing until new change record names
+ // make it to release.
+ global.Observer.changeRecordTypes = {
+ add: PROP_ADD_TYPE,
+ update: PROP_UPDATE_TYPE,
+ reconfigure: PROP_RECONFIGURE_TYPE,
+ 'delete': PROP_DELETE_TYPE,
+ splice: ARRAY_SPLICE_TYPE
+ };
+})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window);
+
+/*
+ * Copyright 2012 The Polymer Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+if (typeof WeakMap === 'undefined') {
+ (function() {
+ var defineProperty = Object.defineProperty;
+ var counter = Date.now() % 1e9;
+
+ var WeakMap = function() {
+ this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__');
+ };
+
+ WeakMap.prototype = {
+ set: function(key, value) {
+ var entry = key[this.name];
+ if (entry && entry[0] === key)
+ entry[1] = value;
+ else
+ defineProperty(key, this.name, {value: [key, value], writable: true});
+ },
+ get: function(key) {
+ var entry;
+ return (entry = key[this.name]) && entry[0] === key ?
+ entry[1] : undefined;
+ },
+ delete: function(key) {
+ this.set(key, undefined);
+ }
+ };
+
+ window.WeakMap = WeakMap;
+ })();
+}
+
+// Copyright 2012 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+window.ShadowDOMPolyfill = {};
+
+(function(scope) {
+ 'use strict';
+
+ var constructorTable = new WeakMap();
+ var nativePrototypeTable = new WeakMap();
+ var wrappers = Object.create(null);
+
+ // Don't test for eval if document has CSP securityPolicy object and we can
+ // see that eval is not supported. This avoids an error message in console
+ // even when the exception is caught
+ var hasEval = !('securityPolicy' in document) ||
+ document.securityPolicy.allowsEval;
+ if (hasEval) {
+ try {
+ var f = new Function('', 'return true;');
+ hasEval = f();
+ } catch (ex) {
+ hasEval = false;
+ }
+ }
+
+ function assert(b) {
+ if (!b)
+ throw new Error('Assertion failed');
+ };
+
+ var defineProperty = Object.defineProperty;
+ var getOwnPropertyNames = Object.getOwnPropertyNames;
+ var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+
+ function mixin(to, from) {
+ getOwnPropertyNames(from).forEach(function(name) {
+ defineProperty(to, name, getOwnPropertyDescriptor(from, name));
+ });
+ return to;
+ };
+
+ function mixinStatics(to, from) {
+ getOwnPropertyNames(from).forEach(function(name) {
+ switch (name) {
+ case 'arguments':
+ case 'caller':
+ case 'length':
+ case 'name':
+ case 'prototype':
+ case 'toString':
+ return;
+ }
+ defineProperty(to, name, getOwnPropertyDescriptor(from, name));
+ });
+ return to;
+ };
+
+ function oneOf(object, propertyNames) {
+ for (var i = 0; i < propertyNames.length; i++) {
+ if (propertyNames[i] in object)
+ return propertyNames[i];
+ }
+ }
+
+ // Mozilla's old DOM bindings are bretty busted:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=855844
+ // Make sure they are create before we start modifying things.
+ getOwnPropertyNames(window);
+
+ function getWrapperConstructor(node) {
+ var nativePrototype = node.__proto__ || Object.getPrototypeOf(node);
+ var wrapperConstructor = constructorTable.get(nativePrototype);
+ if (wrapperConstructor)
+ return wrapperConstructor;
+
+ var parentWrapperConstructor = getWrapperConstructor(nativePrototype);
+
+ var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor);
+ registerInternal(nativePrototype, GeneratedWrapper, node);
+
+ return GeneratedWrapper;
+ }
+
+ function addForwardingProperties(nativePrototype, wrapperPrototype) {
+ installProperty(nativePrototype, wrapperPrototype, true);
+ }
+
+ function registerInstanceProperties(wrapperPrototype, instanceObject) {
+ installProperty(instanceObject, wrapperPrototype, false);
+ }
+
+ var isFirefox = /Firefox/.test(navigator.userAgent);
+
+ // This is used as a fallback when getting the descriptor fails in
+ // installProperty.
+ var dummyDescriptor = {
+ get: function() {},
+ set: function(v) {},
+ configurable: true,
+ enumerable: true
+ };
+
+ function isEventHandlerName(name) {
+ return /^on[a-z]+$/.test(name);
+ }
+
+ function isIdentifierName(name) {
+ return /^\w[a-zA-Z_0-9]*$/.test(name);
+ }
+
+ function getGetter(name) {
+ return hasEval && isIdentifierName(name) ?
+ new Function('return this.impl.' + name) :
+ function() { return this.impl[name]; };
+ }
+
+ function getSetter(name) {
+ return hasEval && isIdentifierName(name) ?
+ new Function('v', 'this.impl.' + name + ' = v') :
+ function(v) { this.impl[name] = v; };
+ }
+
+ function getMethod(name) {
+ return hasEval && isIdentifierName(name) ?
+ new Function('return this.impl.' + name +
+ '.apply(this.impl, arguments)') :
+ function() { return this.impl[name].apply(this.impl, arguments); };
+ }
+
+ function getDescriptor(source, name) {
+ try {
+ return Object.getOwnPropertyDescriptor(source, name);
+ } catch (ex) {
+ // JSC and V8 both use data properties instead of accessors which can
+ // cause getting the property desciptor to throw an exception.
+ // https://bugs.webkit.org/show_bug.cgi?id=49739
+ return dummyDescriptor;
+ }
+ }
+
+ function installProperty(source, target, allowMethod, opt_blacklist) {
+ var names = getOwnPropertyNames(source);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ if (name === 'polymerBlackList_')
+ continue;
+
+ if (name in target)
+ continue;
+
+ if (source.polymerBlackList_ && source.polymerBlackList_[name])
+ continue;
+
+ if (isFirefox) {
+ // Tickle Firefox's old bindings.
+ source.__lookupGetter__(name);
+ }
+ var descriptor = getDescriptor(source, name);
+ var getter, setter;
+ if (allowMethod && typeof descriptor.value === 'function') {
+ target[name] = getMethod(name);
+ continue;
+ }
+
+ var isEvent = isEventHandlerName(name);
+ if (isEvent)
+ getter = scope.getEventHandlerGetter(name);
+ else
+ getter = getGetter(name);
+
+ if (descriptor.writable || descriptor.set) {
+ if (isEvent)
+ setter = scope.getEventHandlerSetter(name);
+ else
+ setter = getSetter(name);
+ }
+
+ defineProperty(target, name, {
+ get: getter,
+ set: setter,
+ configurable: descriptor.configurable,
+ enumerable: descriptor.enumerable
+ });
+ }
+ }
+
+ /**
+ * @param {Function} nativeConstructor
+ * @param {Function} wrapperConstructor
+ * @param {Object=} opt_instance If present, this is used to extract
+ * properties from an instance object.
+ */
+ function register(nativeConstructor, wrapperConstructor, opt_instance) {
+ var nativePrototype = nativeConstructor.prototype;
+ registerInternal(nativePrototype, wrapperConstructor, opt_instance);
+ mixinStatics(wrapperConstructor, nativeConstructor);
+ }
+
+ function registerInternal(nativePrototype, wrapperConstructor, opt_instance) {
+ var wrapperPrototype = wrapperConstructor.prototype;
+ assert(constructorTable.get(nativePrototype) === undefined);
+
+ constructorTable.set(nativePrototype, wrapperConstructor);
+ nativePrototypeTable.set(wrapperPrototype, nativePrototype);
+
+ addForwardingProperties(nativePrototype, wrapperPrototype);
+ if (opt_instance)
+ registerInstanceProperties(wrapperPrototype, opt_instance);
+ defineProperty(wrapperPrototype, 'constructor', {
+ value: wrapperConstructor,
+ configurable: true,
+ enumerable: false,
+ writable: true
+ });
+ }
+
+ function isWrapperFor(wrapperConstructor, nativeConstructor) {
+ return constructorTable.get(nativeConstructor.prototype) ===
+ wrapperConstructor;
+ }
+
+ /**
+ * Creates a generic wrapper constructor based on |object| and its
+ * constructor.
+ * @param {Node} object
+ * @return {Function} The generated constructor.
+ */
+ function registerObject(object) {
+ var nativePrototype = Object.getPrototypeOf(object);
+
+ var superWrapperConstructor = getWrapperConstructor(nativePrototype);
+ var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor);
+ registerInternal(nativePrototype, GeneratedWrapper, object);
+
+ return GeneratedWrapper;
+ }
+
+ function createWrapperConstructor(superWrapperConstructor) {
+ function GeneratedWrapper(node) {
+ superWrapperConstructor.call(this, node);
+ }
+ GeneratedWrapper.prototype =
+ Object.create(superWrapperConstructor.prototype);
+ GeneratedWrapper.prototype.constructor = GeneratedWrapper;
+
+ return GeneratedWrapper;
+ }
+
+ var OriginalDOMImplementation = window.DOMImplementation;
+ var OriginalEventTarget = window.EventTarget;
+ var OriginalEvent = window.Event;
+ var OriginalNode = window.Node;
+ var OriginalWindow = window.Window;
+ var OriginalRange = window.Range;
+ var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
+ var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
+ var OriginalSVGElementInstance = window.SVGElementInstance;
+
+ function isWrapper(object) {
+ return object instanceof wrappers.EventTarget ||
+ object instanceof wrappers.Event ||
+ object instanceof wrappers.Range ||
+ object instanceof wrappers.DOMImplementation ||
+ object instanceof wrappers.CanvasRenderingContext2D ||
+ wrappers.WebGLRenderingContext &&
+ object instanceof wrappers.WebGLRenderingContext;
+ }
+
+ function isNative(object) {
+ return OriginalEventTarget && object instanceof OriginalEventTarget ||
+ object instanceof OriginalNode ||
+ object instanceof OriginalEvent ||
+ object instanceof OriginalWindow ||
+ object instanceof OriginalRange ||
+ object instanceof OriginalDOMImplementation ||
+ object instanceof OriginalCanvasRenderingContext2D ||
+ OriginalWebGLRenderingContext &&
+ object instanceof OriginalWebGLRenderingContext ||
+ OriginalSVGElementInstance &&
+ object instanceof OriginalSVGElementInstance;
+ }
+
+ /**
+ * Wraps a node in a WrapperNode. If there already exists a wrapper for the
+ * |node| that wrapper is returned instead.
+ * @param {Node} node
+ * @return {WrapperNode}
+ */
+ function wrap(impl) {
+ if (impl === null)
+ return null;
+
+ assert(isNative(impl));
+ return impl.polymerWrapper_ ||
+ (impl.polymerWrapper_ = new (getWrapperConstructor(impl))(impl));
+ }
+
+ /**
+ * Unwraps a wrapper and returns the node it is wrapping.
+ * @param {WrapperNode} wrapper
+ * @return {Node}
+ */
+ function unwrap(wrapper) {
+ if (wrapper === null)
+ return null;
+ assert(isWrapper(wrapper));
+ return wrapper.impl;
+ }
+
+ /**
+ * Unwraps object if it is a wrapper.
+ * @param {Object} object
+ * @return {Object} The native implementation object.
+ */
+ function unwrapIfNeeded(object) {
+ return object && isWrapper(object) ? unwrap(object) : object;
+ }
+
+ /**
+ * Wraps object if it is not a wrapper.
+ * @param {Object} object
+ * @return {Object} The wrapper for object.
+ */
+ function wrapIfNeeded(object) {
+ return object && !isWrapper(object) ? wrap(object) : object;
+ }
+
+ /**
+ * Overrides the current wrapper (if any) for node.
+ * @param {Node} node
+ * @param {WrapperNode=} wrapper If left out the wrapper will be created as
+ * needed next time someone wraps the node.
+ */
+ function rewrap(node, wrapper) {
+ if (wrapper === null)
+ return;
+ assert(isNative(node));
+ assert(wrapper === undefined || isWrapper(wrapper));
+ node.polymerWrapper_ = wrapper;
+ }
+
+ function defineGetter(constructor, name, getter) {
+ defineProperty(constructor.prototype, name, {
+ get: getter,
+ configurable: true,
+ enumerable: true
+ });
+ }
+
+ function defineWrapGetter(constructor, name) {
+ defineGetter(constructor, name, function() {
+ return wrap(this.impl[name]);
+ });
+ }
+
+ /**
+ * Forwards existing methods on the native object to the wrapper methods.
+ * This does not wrap any of the arguments or the return value since the
+ * wrapper implementation already takes care of that.
+ * @param {Array.<Function>} constructors
+ * @parem {Array.<string>} names
+ */
+ function forwardMethodsToWrapper(constructors, names) {
+ constructors.forEach(function(constructor) {
+ names.forEach(function(name) {
+ constructor.prototype[name] = function() {
+ var w = wrapIfNeeded(this);
+ return w[name].apply(w, arguments);
+ };
+ });
+ });
+ }
+
+ scope.assert = assert;
+ scope.constructorTable = constructorTable;
+ scope.defineGetter = defineGetter;
+ scope.defineWrapGetter = defineWrapGetter;
+ scope.forwardMethodsToWrapper = forwardMethodsToWrapper;
+ scope.isWrapper = isWrapper;
+ scope.isWrapperFor = isWrapperFor;
+ scope.mixin = mixin;
+ scope.nativePrototypeTable = nativePrototypeTable;
+ scope.oneOf = oneOf;
+ scope.registerObject = registerObject;
+ scope.registerWrapper = register;
+ scope.rewrap = rewrap;
+ scope.unwrap = unwrap;
+ scope.unwrapIfNeeded = unwrapIfNeeded;
+ scope.wrap = wrap;
+ scope.wrapIfNeeded = wrapIfNeeded;
+ scope.wrappers = wrappers;
+
+})(window.ShadowDOMPolyfill);
+
+/*
+ * Copyright 2013 The Polymer Authors. All rights reserved.
+ * Use of this source code is goverened by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+(function(context) {
+ 'use strict';
+
+ var OriginalMutationObserver = window.MutationObserver;
+ var callbacks = [];
+ var pending = false;
+ var timerFunc;
+
+ function handle() {
+ pending = false;
+ var copies = callbacks.slice(0);
+ callbacks = [];
+ for (var i = 0; i < copies.length; i++) {
+ (0, copies[i])();
+ }
+ }
+
+ if (OriginalMutationObserver) {
+ var counter = 1;
+ var observer = new OriginalMutationObserver(handle);
+ var textNode = document.createTextNode(counter);
+ observer.observe(textNode, {characterData: true});
+
+ timerFunc = function() {
+ counter = (counter + 1) % 2;
+ textNode.data = counter;
+ };
+
+ } else {
+ timerFunc = window.setImmediate || window.setTimeout;
+ }
+
+ function setEndOfMicrotask(func) {
+ callbacks.push(func);
+ if (pending)
+ return;
+ pending = true;
+ timerFunc(handle, 0);
+ }
+
+ context.setEndOfMicrotask = setEndOfMicrotask;
+
+})(window.ShadowDOMPolyfill);
+
+/*
+ * Copyright 2013 The Polymer Authors. All rights reserved.
+ * Use of this source code is goverened by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+(function(scope) {
+ 'use strict';
+
+ var setEndOfMicrotask = scope.setEndOfMicrotask
+ var wrapIfNeeded = scope.wrapIfNeeded
+ var wrappers = scope.wrappers;
+
+ var registrationsTable = new WeakMap();
+ var globalMutationObservers = [];
+ var isScheduled = false;
+
+ function scheduleCallback(observer) {
+ if (isScheduled)
+ return;
+ setEndOfMicrotask(notifyObservers);
+ isScheduled = true;
+ }
+
+ // http://dom.spec.whatwg.org/#mutation-observers
+ function notifyObservers() {
+ isScheduled = false;
+
+ do {
+ var notifyList = globalMutationObservers.slice();
+ var anyNonEmpty = false;
+ for (var i = 0; i < notifyList.length; i++) {
+ var mo = notifyList[i];
+ var queue = mo.takeRecords();
+ removeTransientObserversFor(mo);
+ if (queue.length) {
+ mo.callback_(queue, mo);
+ anyNonEmpty = true;
+ }
+ }
+ } while (anyNonEmpty);
+ }
+
+ /**
+ * @param {string} type
+ * @param {Node} target
+ * @constructor
+ */
+ function MutationRecord(type, target) {
+ this.type = type;
+ this.target = target;
+ this.addedNodes = new wrappers.NodeList();
+ this.removedNodes = new wrappers.NodeList();
+ this.previousSibling = null;
+ this.nextSibling = null;
+ this.attributeName = null;
+ this.attributeNamespace = null;
+ this.oldValue = null;
+ }
+
+ /**
+ * Registers transient observers to ancestor and its ancesors for the node
+ * which was removed.
+ * @param {!Node} ancestor
+ * @param {!Node} node
+ */
+ function registerTransientObservers(ancestor, node) {
+ for (; ancestor; ancestor = ancestor.parentNode) {
+ var registrations = registrationsTable.get(ancestor);
+ if (!registrations)
+ continue;
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.options.subtree)
+ registration.addTransientObserver(node);
+ }
+ }
+ }
+
+ function removeTransientObserversFor(observer) {
+ for (var i = 0; i < observer.nodes_.length; i++) {
+ var node = observer.nodes_[i];
+ var registrations = registrationsTable.get(node);
+ if (!registrations)
+ return;
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ if (registration.observer === observer)
+ registration.removeTransientObservers();
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#queue-a-mutation-record
+ function enqueueMutation(target, type, data) {
+ // 1.
+ var interestedObservers = Object.create(null);
+ var associatedStrings = Object.create(null);
+
+ // 2.
+ for (var node = target; node; node = node.parentNode) {
+ // 3.
+ var registrations = registrationsTable.get(node);
+ if (!registrations)
+ continue;
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ var options = registration.options;
+ // 1.
+ if (node !== target && !options.subtree)
+ continue;
+
+ // 2.
+ if (type === 'attributes' && !options.attributes)
+ continue;
+
+ // 3. If type is "attributes", options's attributeFilter is present, and
+ // either options's attributeFilter does not contain name or namespace
+ // is non-null, continue.
+ if (type === 'attributes' && options.attributeFilter &&
+ (data.namespace !== null ||
+ options.attributeFilter.indexOf(data.name) === -1)) {
+ continue;
+ }
+
+ // 4.
+ if (type === 'characterData' && !options.characterData)
+ continue;
+
+ // 5.
+ if (type === 'childList' && !options.childList)
+ continue;
+
+ // 6.
+ var observer = registration.observer;
+ interestedObservers[observer.uid_] = observer;
+
+ // 7. If either type is "attributes" and options's attributeOldValue is
+ // true, or type is "characterData" and options's characterDataOldValue
+ // is true, set the paired string of registered observer's observer in
+ // interested observers to oldValue.
+ if (type === 'attributes' && options.attributeOldValue ||
+ type === 'characterData' && options.characterDataOldValue) {
+ associatedStrings[observer.uid_] = data.oldValue;
+ }
+ }
+ }
+
+ var anyRecordsEnqueued = false;
+
+ // 4.
+ for (var uid in interestedObservers) {
+ var observer = interestedObservers[uid];
+ var record = new MutationRecord(type, target);
+
+ // 2.
+ if ('name' in data && 'namespace' in data) {
+ record.attributeName = data.name;
+ record.attributeNamespace = data.namespace;
+ }
+
+ // 3.
+ if (data.addedNodes)
+ record.addedNodes = data.addedNodes;
+
+ // 4.
+ if (data.removedNodes)
+ record.removedNodes = data.removedNodes;
+
+ // 5.
+ if (data.previousSibling)
+ record.previousSibling = data.previousSibling;
+
+ // 6.
+ if (data.nextSibling)
+ record.nextSibling = data.nextSibling;
+
+ // 7.
+ if (associatedStrings[uid] !== undefined)
+ record.oldValue = associatedStrings[uid];
+
+ // 8.
+ observer.records_.push(record);
+
+ anyRecordsEnqueued = true;
+ }
+
+ if (anyRecordsEnqueued)
+ scheduleCallback();
+ }
+
+ var slice = Array.prototype.slice;
+
+ /**
+ * @param {!Object} options
+ * @constructor
+ */
+ function MutationObserverOptions(options) {
+ this.childList = !!options.childList;
+ this.subtree = !!options.subtree;
+
+ // 1. If either options' attributeOldValue or attributeFilter is present
+ // and options' attributes is omitted, set options' attributes to true.
+ if (!('attributes' in options) &&
+ ('attributeOldValue' in options || 'attributeFilter' in options)) {
+ this.attributes = true;
+ } else {
+ this.attributes = !!options.attributes;
+ }
+
+ // 2. If options' characterDataOldValue is present and options'
+ // characterData is omitted, set options' characterData to true.
+ if ('characterDataOldValue' in options && !('characterData' in options))
+ this.characterData = true;
+ else
+ this.characterData = !!options.characterData;
+
+ // 3. & 4.
+ if (!this.attributes &&
+ (options.attributeOldValue || 'attributeFilter' in options) ||
+ // 5.
+ !this.characterData && options.characterDataOldValue) {
+ throw new TypeError();
+ }
+
+ this.characterData = !!options.characterData;
+ this.attributeOldValue = !!options.attributeOldValue;
+ this.characterDataOldValue = !!options.characterDataOldValue;
+ if ('attributeFilter' in options) {
+ if (options.attributeFilter == null ||
+ typeof options.attributeFilter !== 'object') {
+ throw new TypeError();
+ }
+ this.attributeFilter = slice.call(options.attributeFilter);
+ } else {
+ this.attributeFilter = null;
+ }
+ }
+
+ var uidCounter = 0;
+
+ /**
+ * The class that maps to the DOM MutationObserver interface.
+ * @param {Function} callback.
+ * @constructor
+ */
+ function MutationObserver(callback) {
+ this.callback_ = callback;
+ this.nodes_ = [];
+ this.records_ = [];
+ this.uid_ = ++uidCounter;
+
+ // This will leak. There is no way to implement this without WeakRefs :'(
+ globalMutationObservers.push(this);
+ }
+
+ MutationObserver.prototype = {
+ // http://dom.spec.whatwg.org/#dom-mutationobserver-observe
+ observe: function(target, options) {
+ target = wrapIfNeeded(target);
+
+ var newOptions = new MutationObserverOptions(options);
+
+ // 6.
+ var registration;
+ var registrations = registrationsTable.get(target);
+ if (!registrations)
+ registrationsTable.set(target, registrations = []);
+
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i].observer === this) {
+ registration = registrations[i];
+ // 6.1.
+ registration.removeTransientObservers();
+ // 6.2.
+ registration.options = newOptions;
+ }
+ }
+
+ // 7.
+ if (!registration) {
+ registration = new Registration(this, target, newOptions);
+ registrations.push(registration);
+ this.nodes_.push(target);
+ }
+ },
+
+ // http://dom.spec.whatwg.org/#dom-mutationobserver-disconnect
+ disconnect: function() {
+ this.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.observer === this) {
+ registrations.splice(i, 1);
+ // Each node can only have one registered observer associated with
+ // this observer.
+ break;
+ }
+ }
+ }, this);
+ this.records_ = [];
+ },
+
+ takeRecords: function() {
+ var copyOfRecords = this.records_;
+ this.records_ = [];
+ return copyOfRecords;
+ }
+ };
+
+ /**
+ * Class used to represent a registered observer.
+ * @param {MutationObserver} observer
+ * @param {Node} target
+ * @param {MutationObserverOptions} options
+ * @constructor
+ */
+ function Registration(observer, target, options) {
+ this.observer = observer;
+ this.target = target;
+ this.options = options;
+ this.transientObservedNodes = [];
+ }
+
+ Registration.prototype = {
+ /**
+ * Adds a transient observer on node. The transient observer gets removed
+ * next time we deliver the change records.
+ * @param {Node} node
+ */
+ addTransientObserver: function(node) {
+ // Don't add transient observers on the target itself. We already have all
+ // the required listeners set up on the target.
+ if (node === this.target)
+ return;
+
+ this.transientObservedNodes.push(node);
+ var registrations = registrationsTable.get(node);
+ if (!registrations)
+ registrationsTable.set(node, registrations = []);
+
+ // We know that registrations does not contain this because we already
+ // checked if node === this.target.
+ registrations.push(this);
+ },
+
+ removeTransientObservers: function() {
+ var transientObservedNodes = this.transientObservedNodes;
+ this.transientObservedNodes = [];
+
+ for (var i = 0; i < transientObservedNodes.length; i++) {
+ var node = transientObservedNodes[i];
+ var registrations = registrationsTable.get(node);
+ for (var j = 0; j < registrations.length; j++) {
+ if (registrations[j] === this) {
+ registrations.splice(j, 1);
+ // Each node can only have one registered observer associated with
+ // this observer.
+ break;
+ }
+ }
+ }
+ }
+ };
+
+ scope.enqueueMutation = enqueueMutation;
+ scope.registerTransientObservers = registerTransientObservers;
+ scope.wrappers.MutationObserver = MutationObserver;
+ scope.wrappers.MutationRecord = MutationRecord;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrappers = scope.wrappers;
+
+ var wrappedFuns = new WeakMap();
+ var listenersTable = new WeakMap();
+ var handledEventsTable = new WeakMap();
+ var currentlyDispatchingEvents = new WeakMap();
+ var targetTable = new WeakMap();
+ var currentTargetTable = new WeakMap();
+ var relatedTargetTable = new WeakMap();
+ var eventPhaseTable = new WeakMap();
+ var stopPropagationTable = new WeakMap();
+ var stopImmediatePropagationTable = new WeakMap();
+ var eventHandlersTable = new WeakMap();
+ var eventPathTable = new WeakMap();
+
+ function isShadowRoot(node) {
+ return node instanceof wrappers.ShadowRoot;
+ }
+
+ function isInsertionPoint(node) {
+ var localName = node.localName;
+ return localName === 'content' || localName === 'shadow';
+ }
+
+ function isShadowHost(node) {
+ return !!node.shadowRoot;
+ }
+
+ function getEventParent(node) {
+ var dv;
+ return node.parentNode || (dv = node.defaultView) && wrap(dv) || null;
+ }
+
+ // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#dfn-adjusted-parent
+ function calculateParents(node, context, ancestors) {
+ if (ancestors.length)
+ return ancestors.shift();
+
+ // 1.
+ if (isShadowRoot(node))
+ return getInsertionParent(node) || node.host;
+
+ // 2.
+ var eventParents = scope.eventParentsTable.get(node);
+ if (eventParents) {
+ // Copy over the remaining event parents for next iteration.
+ for (var i = 1; i < eventParents.length; i++) {
+ ancestors[i - 1] = eventParents[i];
+ }
+ return eventParents[0];
+ }
+
+ // 3.
+ if (context && isInsertionPoint(node)) {
+ var parentNode = node.parentNode;
+ if (parentNode && isShadowHost(parentNode)) {
+ var trees = scope.getShadowTrees(parentNode);
+ var p = getInsertionParent(context);
+ for (var i = 0; i < trees.length; i++) {
+ if (trees[i].contains(p))
+ return p;
+ }
+ }
+ }
+
+ return getEventParent(node);
+ }
+
+ // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#event-retargeting
+ function retarget(node) {
+ var stack = []; // 1.
+ var ancestor = node; // 2.
+ var targets = [];
+ var ancestors = [];
+ while (ancestor) { // 3.
+ var context = null; // 3.2.
+ // TODO(arv): Change order of these. If the stack is empty we always end
+ // up pushing ancestor, no matter what.
+ if (isInsertionPoint(ancestor)) { // 3.1.
+ context = topMostNotInsertionPoint(stack); // 3.1.1.
+ var top = stack[stack.length - 1] || ancestor; // 3.1.2.
+ stack.push(top);
+ } else if (!stack.length) {
+ stack.push(ancestor); // 3.3.
+ }
+ var target = stack[stack.length - 1]; // 3.4.
+ targets.push({target: target, currentTarget: ancestor}); // 3.5.
+ if (isShadowRoot(ancestor)) // 3.6.
+ stack.pop(); // 3.6.1.
+
+ ancestor = calculateParents(ancestor, context, ancestors); // 3.7.
+ }
+ return targets;
+ }
+
+ function topMostNotInsertionPoint(stack) {
+ for (var i = stack.length - 1; i >= 0; i--) {
+ if (!isInsertionPoint(stack[i]))
+ return stack[i];
+ }
+ return null;
+ }
+
+ // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#dfn-adjusted-related-target
+ function adjustRelatedTarget(target, related) {
+ var ancestors = [];
+ while (target) { // 3.
+ var stack = []; // 3.1.
+ var ancestor = related; // 3.2.
+ var last = undefined; // 3.3. Needs to be reset every iteration.
+ while (ancestor) {
+ var context = null;
+ if (!stack.length) {
+ stack.push(ancestor);
+ } else {
+ if (isInsertionPoint(ancestor)) { // 3.4.3.
+ context = topMostNotInsertionPoint(stack);
+ // isDistributed is more general than checking whether last is
+ // assigned into ancestor.
+ if (isDistributed(last)) { // 3.4.3.2.
+ var head = stack[stack.length - 1];
+ stack.push(head);
+ }
+ }
+ }
+
+ if (inSameTree(ancestor, target)) // 3.4.4.
+ return stack[stack.length - 1];
+
+ if (isShadowRoot(ancestor)) // 3.4.5.
+ stack.pop();
+
+ last = ancestor; // 3.4.6.
+ ancestor = calculateParents(ancestor, context, ancestors); // 3.4.7.
+ }
+ if (isShadowRoot(target)) // 3.5.
+ target = target.host;
+ else
+ target = target.parentNode; // 3.6.
+ }
+ }
+
+ function getInsertionParent(node) {
+ return scope.insertionParentTable.get(node);
+ }
+
+ function isDistributed(node) {
+ return getInsertionParent(node);
+ }
+
+ function rootOfNode(node) {
+ var p;
+ while (p = node.parentNode) {
+ node = p;
+ }
+ return node;
+ }
+
+ function inSameTree(a, b) {
+ return rootOfNode(a) === rootOfNode(b);
+ }
+
+ function enclosedBy(a, b) {
+ if (a === b)
+ return true;
+ if (a instanceof wrappers.ShadowRoot)
+ return enclosedBy(rootOfNode(a.host), b);
+ return false;
+ }
+
+
+ function dispatchOriginalEvent(originalEvent) {
+ // Make sure this event is only dispatched once.
+ if (handledEventsTable.get(originalEvent))
+ return;
+ handledEventsTable.set(originalEvent, true);
+
+ return dispatchEvent(wrap(originalEvent), wrap(originalEvent.target));
+ }
+
+ function dispatchEvent(event, originalWrapperTarget) {
+ if (currentlyDispatchingEvents.get(event))
+ throw new Error('InvalidStateError')
+ currentlyDispatchingEvents.set(event, true);
+
+ // Render to ensure that the event path is correct.
+ scope.renderAllPending();
+ var eventPath = retarget(originalWrapperTarget);
+
+ // For window load events the load event is dispatched at the window but
+ // the target is set to the document.
+ //
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#the-end
+ //
+ // TODO(arv): Find a less hacky way to do this.
+ if (event.type === 'load' &&
+ eventPath.length === 2 &&
+ eventPath[0].target instanceof wrappers.Document) {
+ eventPath.shift();
+ }
+
+ eventPathTable.set(event, eventPath);
+
+ if (dispatchCapturing(event, eventPath)) {
+ if (dispatchAtTarget(event, eventPath)) {
+ dispatchBubbling(event, eventPath);
+ }
+ }
+
+ eventPhaseTable.set(event, Event.NONE);
+ currentTargetTable.delete(event, null);
+ currentlyDispatchingEvents.delete(event);
+
+ return event.defaultPrevented;
+ }
+
+ function dispatchCapturing(event, eventPath) {
+ var phase;
+
+ for (var i = eventPath.length - 1; i > 0; i--) {
+ var target = eventPath[i].target;
+ var currentTarget = eventPath[i].currentTarget;
+ if (target === currentTarget)
+ continue;
+
+ phase = Event.CAPTURING_PHASE;
+ if (!invoke(eventPath[i], event, phase))
+ return false;
+ }
+
+ return true;
+ }
+
+ function dispatchAtTarget(event, eventPath) {
+ var phase = Event.AT_TARGET;
+ return invoke(eventPath[0], event, phase);
+ }
+
+ function dispatchBubbling(event, eventPath) {
+ var bubbles = event.bubbles;
+ var phase;
+
+ for (var i = 1; i < eventPath.length; i++) {
+ var target = eventPath[i].target;
+ var currentTarget = eventPath[i].currentTarget;
+ if (target === currentTarget)
+ phase = Event.AT_TARGET;
+ else if (bubbles && !stopImmediatePropagationTable.get(event))
+ phase = Event.BUBBLING_PHASE;
+ else
+ continue;
+
+ if (!invoke(eventPath[i], event, phase))
+ return;
+ }
+ }
+
+ function invoke(tuple, event, phase) {
+ var target = tuple.target;
+ var currentTarget = tuple.currentTarget;
+
+ var listeners = listenersTable.get(currentTarget);
+ if (!listeners)
+ return true;
+
+ if ('relatedTarget' in event) {
+ var originalEvent = unwrap(event);
+ // X-Tag sets relatedTarget on a CustomEvent. If they do that there is no
+ // way to have relatedTarget return the adjusted target but worse is that
+ // the originalEvent might not have a relatedTarget so we hit an assert
+ // when we try to wrap it.
+ if (originalEvent.relatedTarget) {
+ var relatedTarget = wrap(originalEvent.relatedTarget);
+
+ var adjusted = adjustRelatedTarget(currentTarget, relatedTarget);
+ if (adjusted === target)
+ return true;
+
+ relatedTargetTable.set(event, adjusted);
+ }
+ }
+
+ eventPhaseTable.set(event, phase);
+ var type = event.type;
+
+ var anyRemoved = false;
+ targetTable.set(event, target);
+ currentTargetTable.set(event, currentTarget);
+
+ for (var i = 0; i < listeners.length; i++) {
+ var listener = listeners[i];
+ if (listener.removed) {
+ anyRemoved = true;
+ continue;
+ }
+
+ if (listener.type !== type ||
+ !listener.capture && phase === Event.CAPTURING_PHASE ||
+ listener.capture && phase === Event.BUBBLING_PHASE) {
+ continue;
+ }
+
+ try {
+ if (typeof listener.handler === 'function')
+ listener.handler.call(currentTarget, event);
+ else
+ listener.handler.handleEvent(event);
+
+ if (stopImmediatePropagationTable.get(event))
+ return false;
+
+ } catch (ex) {
+ if (window.onerror)
+ window.onerror(ex.message);
+ else
+ console.error(ex, ex.stack);
+ }
+ }
+
+ if (anyRemoved) {
+ var copy = listeners.slice();
+ listeners.length = 0;
+ for (var i = 0; i < copy.length; i++) {
+ if (!copy[i].removed)
+ listeners.push(copy[i]);
+ }
+ }
+
+ return !stopPropagationTable.get(event);
+ }
+
+ function Listener(type, handler, capture) {
+ this.type = type;
+ this.handler = handler;
+ this.capture = Boolean(capture);
+ }
+ Listener.prototype = {
+ equals: function(that) {
+ return this.handler === that.handler && this.type === that.type &&
+ this.capture === that.capture;
+ },
+ get removed() {
+ return this.handler === null;
+ },
+ remove: function() {
+ this.handler = null;
+ }
+ };
+
+ var OriginalEvent = window.Event;
+ OriginalEvent.prototype.polymerBlackList_ = {
+ returnValue: true,
+ // TODO(arv): keyLocation is part of KeyboardEvent but Firefox does not
+ // support constructable KeyboardEvent so we keep it here for now.
+ keyLocation: true
+ };
+
+ /**
+ * Creates a new Event wrapper or wraps an existin native Event object.
+ * @param {string|Event} type
+ * @param {Object=} options
+ * @constructor
+ */
+ function Event(type, options) {
+ if (type instanceof OriginalEvent)
+ this.impl = type;
+ else
+ return wrap(constructEvent(OriginalEvent, 'Event', type, options));
+ }
+ Event.prototype = {
+ get target() {
+ return targetTable.get(this);
+ },
+ get currentTarget() {
+ return currentTargetTable.get(this);
+ },
+ get eventPhase() {
+ return eventPhaseTable.get(this);
+ },
+ get path() {
+ var nodeList = new wrappers.NodeList();
+ var eventPath = eventPathTable.get(this);
+ if (eventPath) {
+ var index = 0;
+ var lastIndex = eventPath.length - 1;
+ var baseRoot = rootOfNode(currentTargetTable.get(this));
+
+ for (var i = 0; i <= lastIndex; i++) {
+ var currentTarget = eventPath[i].currentTarget;
+ var currentRoot = rootOfNode(currentTarget);
+ if (enclosedBy(baseRoot, currentRoot) &&
+ // Make sure we do not add Window to the path.
+ (i !== lastIndex || currentTarget instanceof wrappers.Node)) {
+ nodeList[index++] = currentTarget;
+ }
+ }
+ nodeList.length = index;
+ }
+ return nodeList;
+ },
+ stopPropagation: function() {
+ stopPropagationTable.set(this, true);
+ },
+ stopImmediatePropagation: function() {
+ stopPropagationTable.set(this, true);
+ stopImmediatePropagationTable.set(this, true);
+ }
+ };
+ registerWrapper(OriginalEvent, Event, document.createEvent('Event'));
+
+ function unwrapOptions(options) {
+ if (!options || !options.relatedTarget)
+ return options;
+ return Object.create(options, {
+ relatedTarget: {value: unwrap(options.relatedTarget)}
+ });
+ }
+
+ function registerGenericEvent(name, SuperEvent, prototype) {
+ var OriginalEvent = window[name];
+ var GenericEvent = function(type, options) {
+ if (type instanceof OriginalEvent)
+ this.impl = type;
+ else
+ return wrap(constructEvent(OriginalEvent, name, type, options));
+ };
+ GenericEvent.prototype = Object.create(SuperEvent.prototype);
+ if (prototype)
+ mixin(GenericEvent.prototype, prototype);
+ if (OriginalEvent) {
+ // - Old versions of Safari fails on new FocusEvent (and others?).
+ // - IE does not support event constructors.
+ // - createEvent('FocusEvent') throws in Firefox.
+ // => Try the best practice solution first and fallback to the old way
+ // if needed.
+ try {
+ registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent('temp'));
+ } catch (ex) {
+ registerWrapper(OriginalEvent, GenericEvent,
+ document.createEvent(name));
+ }
+ }
+ return GenericEvent;
+ }
+
+ var UIEvent = registerGenericEvent('UIEvent', Event);
+ var CustomEvent = registerGenericEvent('CustomEvent', Event);
+
+ var relatedTargetProto = {
+ get relatedTarget() {
+ return relatedTargetTable.get(this) || wrap(unwrap(this).relatedTarget);
+ }
+ };
+
+ function getInitFunction(name, relatedTargetIndex) {
+ return function() {
+ arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]);
+ var impl = unwrap(this);
+ impl[name].apply(impl, arguments);
+ };
+ }
+
+ var mouseEventProto = mixin({
+ initMouseEvent: getInitFunction('initMouseEvent', 14)
+ }, relatedTargetProto);
+
+ var focusEventProto = mixin({
+ initFocusEvent: getInitFunction('initFocusEvent', 5)
+ }, relatedTargetProto);
+
+ var MouseEvent = registerGenericEvent('MouseEvent', UIEvent, mouseEventProto);
+ var FocusEvent = registerGenericEvent('FocusEvent', UIEvent, focusEventProto);
+
+ // In case the browser does not support event constructors we polyfill that
+ // by calling `createEvent('Foo')` and `initFooEvent` where the arguments to
+ // `initFooEvent` are derived from the registered default event init dict.
+ var defaultInitDicts = Object.create(null);
+
+ var supportsEventConstructors = (function() {
+ try {
+ new window.FocusEvent('focus');
+ } catch (ex) {
+ return false;
+ }
+ return true;
+ })();
+
+ /**
+ * Constructs a new native event.
+ */
+ function constructEvent(OriginalEvent, name, type, options) {
+ if (supportsEventConstructors)
+ return new OriginalEvent(type, unwrapOptions(options));
+
+ // Create the arguments from the default dictionary.
+ var event = unwrap(document.createEvent(name));
+ var defaultDict = defaultInitDicts[name];
+ var args = [type];
+ Object.keys(defaultDict).forEach(function(key) {
+ var v = options != null && key in options ?
+ options[key] : defaultDict[key];
+ if (key === 'relatedTarget')
+ v = unwrap(v);
+ args.push(v);
+ });
+ event['init' + name].apply(event, args);
+ return event;
+ }
+
+ if (!supportsEventConstructors) {
+ var configureEventConstructor = function(name, initDict, superName) {
+ if (superName) {
+ var superDict = defaultInitDicts[superName];
+ initDict = mixin(mixin({}, superDict), initDict);
+ }
+
+ defaultInitDicts[name] = initDict;
+ };
+
+ // The order of the default event init dictionary keys is important, the
+ // arguments to initFooEvent is derived from that.
+ configureEventConstructor('Event', {bubbles: false, cancelable: false});
+ configureEventConstructor('CustomEvent', {detail: null}, 'Event');
+ configureEventConstructor('UIEvent', {view: null, detail: 0}, 'Event');
+ configureEventConstructor('MouseEvent', {
+ screenX: 0,
+ screenY: 0,
+ clientX: 0,
+ clientY: 0,
+ ctrlKey: false,
+ altKey: false,
+ shiftKey: false,
+ metaKey: false,
+ button: 0,
+ relatedTarget: null
+ }, 'UIEvent');
+ configureEventConstructor('FocusEvent', {relatedTarget: null}, 'UIEvent');
+ }
+
+ function BeforeUnloadEvent(impl) {
+ Event.call(this);
+ }
+ BeforeUnloadEvent.prototype = Object.create(Event.prototype);
+ mixin(BeforeUnloadEvent.prototype, {
+ get returnValue() {
+ return this.impl.returnValue;
+ },
+ set returnValue(v) {
+ this.impl.returnValue = v;
+ }
+ });
+
+ function isValidListener(fun) {
+ if (typeof fun === 'function')
+ return true;
+ return fun && fun.handleEvent;
+ }
+
+ function isMutationEvent(type) {
+ switch (type) {
+ case 'DOMAttrModified':
+ case 'DOMAttributeNameChanged':
+ case 'DOMCharacterDataModified':
+ case 'DOMElementNameChanged':
+ case 'DOMNodeInserted':
+ case 'DOMNodeInsertedIntoDocument':
+ case 'DOMNodeRemoved':
+ case 'DOMNodeRemovedFromDocument':
+ case 'DOMSubtreeModified':
+ return true;
+ }
+ return false;
+ }
+
+ var OriginalEventTarget = window.EventTarget;
+
+ /**
+ * This represents a wrapper for an EventTarget.
+ * @param {!EventTarget} impl The original event target.
+ * @constructor
+ */
+ function EventTarget(impl) {
+ this.impl = impl;
+ }
+
+ // Node and Window have different internal type checks in WebKit so we cannot
+ // use the same method as the original function.
+ var methodNames = [
+ 'addEventListener',
+ 'removeEventListener',
+ 'dispatchEvent'
+ ];
+
+ [Node, Window].forEach(function(constructor) {
+ var p = constructor.prototype;
+ methodNames.forEach(function(name) {
+ Object.defineProperty(p, name + '_', {value: p[name]});
+ });
+ });
+
+ function getTargetToListenAt(wrapper) {
+ if (wrapper instanceof wrappers.ShadowRoot)
+ wrapper = wrapper.host;
+ return unwrap(wrapper);
+ }
+
+ EventTarget.prototype = {
+ addEventListener: function(type, fun, capture) {
+ if (!isValidListener(fun) || isMutationEvent(type))
+ return;
+
+ var listener = new Listener(type, fun, capture);
+ var listeners = listenersTable.get(this);
+ if (!listeners) {
+ listeners = [];
+ listenersTable.set(this, listeners);
+ } else {
+ // Might have a duplicate.
+ for (var i = 0; i < listeners.length; i++) {
+ if (listener.equals(listeners[i]))
+ return;
+ }
+ }
+
+ listeners.push(listener);
+
+ var target = getTargetToListenAt(this);
+ target.addEventListener_(type, dispatchOriginalEvent, true);
+ },
+ removeEventListener: function(type, fun, capture) {
+ capture = Boolean(capture);
+ var listeners = listenersTable.get(this);
+ if (!listeners)
+ return;
+ var count = 0, found = false;
+ for (var i = 0; i < listeners.length; i++) {
+ if (listeners[i].type === type && listeners[i].capture === capture) {
+ count++;
+ if (listeners[i].handler === fun) {
+ found = true;
+ listeners[i].remove();
+ }
+ }
+ }
+
+ if (found && count === 1) {
+ var target = getTargetToListenAt(this);
+ target.removeEventListener_(type, dispatchOriginalEvent, true);
+ }
+ },
+ dispatchEvent: function(event) {
+ // We want to use the native dispatchEvent because it triggers the default
+ // actions (like checking a checkbox). However, if there are no listeners
+ // in the composed tree then there are no events that will trigger and
+ // listeners in the non composed tree that are part of the event path are
+ // not notified.
+ //
+ // If we find out that there are no listeners in the composed tree we add
+ // a temporary listener to the target which makes us get called back even
+ // in that case.
+
+ var nativeEvent = unwrap(event);
+ var eventType = nativeEvent.type;
+
+ // Allow dispatching the same event again. This is safe because if user
+ // code calls this during an existing dispatch of the same event the
+ // native dispatchEvent throws (that is required by the spec).
+ handledEventsTable.set(nativeEvent, false);
+
+ // Force rendering since we prefer native dispatch and that works on the
+ // composed tree.
+ scope.renderAllPending();
+
+ var tempListener;
+ if (!hasListenerInAncestors(this, eventType)) {
+ tempListener = function() {};
+ this.addEventListener(eventType, tempListener, true);
+ }
+
+ try {
+ return unwrap(this).dispatchEvent_(nativeEvent);
+ } finally {
+ if (tempListener)
+ this.removeEventListener(eventType, tempListener, true);
+ }
+ }
+ };
+
+ function hasListener(node, type) {
+ var listeners = listenersTable.get(node);
+ if (listeners) {
+ for (var i = 0; i < listeners.length; i++) {
+ if (!listeners[i].removed && listeners[i].type === type)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function hasListenerInAncestors(target, type) {
+ for (var node = unwrap(target); node; node = node.parentNode) {
+ if (hasListener(wrap(node), type))
+ return true;
+ }
+ return false;
+ }
+
+ if (OriginalEventTarget)
+ registerWrapper(OriginalEventTarget, EventTarget);
+
+ function wrapEventTargetMethods(constructors) {
+ forwardMethodsToWrapper(constructors, methodNames);
+ }
+
+ var originalElementFromPoint = document.elementFromPoint;
+
+ function elementFromPoint(self, document, x, y) {
+ scope.renderAllPending();
+
+ var element = wrap(originalElementFromPoint.call(document.impl, x, y));
+ var targets = retarget(element, this)
+ for (var i = 0; i < targets.length; i++) {
+ var target = targets[i];
+ if (target.currentTarget === self)
+ return target.target;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a function that is to be used as a getter for `onfoo` properties.
+ * @param {string} name
+ * @return {Function}
+ */
+ function getEventHandlerGetter(name) {
+ return function() {
+ var inlineEventHandlers = eventHandlersTable.get(this);
+ return inlineEventHandlers && inlineEventHandlers[name] &&
+ inlineEventHandlers[name].value || null;
+ };
+ }
+
+ /**
+ * Returns a function that is to be used as a setter for `onfoo` properties.
+ * @param {string} name
+ * @return {Function}
+ */
+ function getEventHandlerSetter(name) {
+ var eventType = name.slice(2);
+ return function(value) {
+ var inlineEventHandlers = eventHandlersTable.get(this);
+ if (!inlineEventHandlers) {
+ inlineEventHandlers = Object.create(null);
+ eventHandlersTable.set(this, inlineEventHandlers);
+ }
+
+ var old = inlineEventHandlers[name];
+ if (old)
+ this.removeEventListener(eventType, old.wrapped, false);
+
+ if (typeof value === 'function') {
+ var wrapped = function(e) {
+ var rv = value.call(this, e);
+ if (rv === false)
+ e.preventDefault();
+ else if (name === 'onbeforeunload' && typeof rv === 'string')
+ e.returnValue = rv;
+ // mouseover uses true for preventDefault but preventDefault for
+ // mouseover is ignored by browsers these day.
+ };
+
+ this.addEventListener(eventType, wrapped, false);
+ inlineEventHandlers[name] = {
+ value: value,
+ wrapped: wrapped
+ };
+ }
+ };
+ }
+
+ scope.adjustRelatedTarget = adjustRelatedTarget;
+ scope.elementFromPoint = elementFromPoint;
+ scope.getEventHandlerGetter = getEventHandlerGetter;
+ scope.getEventHandlerSetter = getEventHandlerSetter;
+ scope.wrapEventTargetMethods = wrapEventTargetMethods;
+ scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent;
+ scope.wrappers.CustomEvent = CustomEvent;
+ scope.wrappers.Event = Event;
+ scope.wrappers.EventTarget = EventTarget;
+ scope.wrappers.FocusEvent = FocusEvent;
+ scope.wrappers.MouseEvent = MouseEvent;
+ scope.wrappers.UIEvent = UIEvent;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2012 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var wrap = scope.wrap;
+
+ function nonEnum(obj, prop) {
+ Object.defineProperty(obj, prop, {enumerable: false});
+ }
+
+ function NodeList() {
+ this.length = 0;
+ nonEnum(this, 'length');
+ }
+ NodeList.prototype = {
+ item: function(index) {
+ return this[index];
+ }
+ };
+ nonEnum(NodeList.prototype, 'item');
+
+ function wrapNodeList(list) {
+ if (list == null)
+ return list;
+ var wrapperList = new NodeList();
+ for (var i = 0, length = list.length; i < length; i++) {
+ wrapperList[i] = wrap(list[i]);
+ }
+ wrapperList.length = length;
+ return wrapperList;
+ }
+
+ function addWrapNodeListMethod(wrapperConstructor, name) {
+ wrapperConstructor.prototype[name] = function() {
+ return wrapNodeList(this.impl[name].apply(this.impl, arguments));
+ };
+ }
+
+ scope.wrappers.NodeList = NodeList;
+ scope.addWrapNodeListMethod = addWrapNodeListMethod;
+ scope.wrapNodeList = wrapNodeList;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2012 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var EventTarget = scope.wrappers.EventTarget;
+ var NodeList = scope.wrappers.NodeList;
+ var assert = scope.assert;
+ var defineWrapGetter = scope.defineWrapGetter;
+ var enqueueMutation = scope.enqueueMutation;
+ var isWrapper = scope.isWrapper;
+ var mixin = scope.mixin;
+ var registerTransientObservers = scope.registerTransientObservers;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrapIfNeeded = scope.wrapIfNeeded;
+
+ function assertIsNodeWrapper(node) {
+ assert(node instanceof Node);
+ }
+
+ function createOneElementNodeList(node) {
+ var nodes = new NodeList();
+ nodes[0] = node;
+ nodes.length = 1;
+ return nodes;
+ }
+
+ var surpressMutations = false;
+
+ /**
+ * Called before node is inserted into a node to enqueue its removal from its
+ * old parent.
+ * @param {!Node} node The node that is about to be removed.
+ * @param {!Node} parent The parent node that the node is being removed from.
+ * @param {!NodeList} nodes The collected nodes.
+ */
+ function enqueueRemovalForInsertedNodes(node, parent, nodes) {
+ enqueueMutation(parent, 'childList', {
+ removedNodes: nodes,
+ previousSibling: node.previousSibling,
+ nextSibling: node.nextSibling
+ });
+ }
+
+ function enqueueRemovalForInsertedDocumentFragment(df, nodes) {
+ enqueueMutation(df, 'childList', {
+ removedNodes: nodes
+ });
+ }
+
+ /**
+ * Collects nodes from a DocumentFragment or a Node for removal followed
+ * by an insertion.
+ *
+ * This updates the internal pointers for node, previousNode and nextNode.
+ */
+ function collectNodes(node, parentNode, previousNode, nextNode) {
+ if (node instanceof DocumentFragment) {
+ var nodes = collectNodesForDocumentFragment(node);
+
+ // The extra loop is to work around bugs with DocumentFragments in IE.
+ surpressMutations = true;
+ for (var i = nodes.length - 1; i >= 0; i--) {
+ node.removeChild(nodes[i]);
+ nodes[i].parentNode_ = parentNode;
+ }
+ surpressMutations = false;
+
+ for (var i = 0; i < nodes.length; i++) {
+ nodes[i].previousSibling_ = nodes[i - 1] || previousNode;
+ nodes[i].nextSibling_ = nodes[i + 1] || nextNode;
+ }
+
+ if (previousNode)
+ previousNode.nextSibling_ = nodes[0];
+ if (nextNode)
+ nextNode.previousSibling_ = nodes[nodes.length - 1];
+
+ return nodes;
+ }
+
+ var nodes = createOneElementNodeList(node);
+ var oldParent = node.parentNode;
+ if (oldParent) {
+ // This will enqueue the mutation record for the removal as needed.
+ oldParent.removeChild(node);
+ }
+
+ node.parentNode_ = parentNode;
+ node.previousSibling_ = previousNode;
+ node.nextSibling_ = nextNode;
+ if (previousNode)
+ previousNode.nextSibling_ = node;
+ if (nextNode)
+ nextNode.previousSibling_ = node;
+
+ return nodes;
+ }
+
+ function collectNodesNative(node) {
+ if (node instanceof DocumentFragment)
+ return collectNodesForDocumentFragment(node);
+
+ var nodes = createOneElementNodeList(node);
+ var oldParent = node.parentNode;
+ if (oldParent)
+ enqueueRemovalForInsertedNodes(node, oldParent, nodes);
+ return nodes;
+ }
+
+ function collectNodesForDocumentFragment(node) {
+ var nodes = new NodeList();
+ var i = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ nodes[i++] = child;
+ }
+ nodes.length = i;
+ enqueueRemovalForInsertedDocumentFragment(node, nodes);
+ return nodes;
+ }
+
+ function snapshotNodeList(nodeList) {
+ // NodeLists are not live at the moment so just return the same object.
+ return nodeList;
+ }
+
+ // http://dom.spec.whatwg.org/#node-is-inserted
+ function nodeWasAdded(node) {
+ node.nodeIsInserted_();
+ }
+
+ function nodesWereAdded(nodes) {
+ for (var i = 0; i < nodes.length; i++) {
+ nodeWasAdded(nodes[i]);
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#node-is-removed
+ function nodeWasRemoved(node) {
+ // Nothing at this point in time.
+ }
+
+ function nodesWereRemoved(nodes) {
+ // Nothing at this point in time.
+ }
+
+ function ensureSameOwnerDocument(parent, child) {
+ var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ?
+ parent : parent.ownerDocument;
+ if (ownerDoc !== child.ownerDocument)
+ ownerDoc.adoptNode(child);
+ }
+
+ function adoptNodesIfNeeded(owner, nodes) {
+ if (!nodes.length)
+ return;
+
+ var ownerDoc = owner.ownerDocument;
+
+ // All nodes have the same ownerDocument when we get here.
+ if (ownerDoc === nodes[0].ownerDocument)
+ return;
+
+ for (var i = 0; i < nodes.length; i++) {
+ scope.adoptNodeNoRemove(nodes[i], ownerDoc);
+ }
+ }
+
+ function unwrapNodesForInsertion(owner, nodes) {
+ adoptNodesIfNeeded(owner, nodes);
+ var length = nodes.length;
+
+ if (length === 1)
+ return unwrap(nodes[0]);
+
+ var df = unwrap(owner.ownerDocument.createDocumentFragment());
+ for (var i = 0; i < length; i++) {
+ df.appendChild(unwrap(nodes[i]));
+ }
+ return df;
+ }
+
+ function clearChildNodes(wrapper) {
+ if (wrapper.firstChild_ !== undefined) {
+ var child = wrapper.firstChild_;
+ while (child) {
+ var tmp = child;
+ child = child.nextSibling_;
+ tmp.parentNode_ = tmp.previousSibling_ = tmp.nextSibling_ = undefined;
+ }
+ }
+ wrapper.firstChild_ = wrapper.lastChild_ = undefined;
+ }
+
+ function removeAllChildNodes(wrapper) {
+ if (wrapper.invalidateShadowRenderer()) {
+ var childWrapper = wrapper.firstChild;
+ while (childWrapper) {
+ assert(childWrapper.parentNode === wrapper);
+ var nextSibling = childWrapper.nextSibling;
+ var childNode = unwrap(childWrapper);
+ var parentNode = childNode.parentNode;
+ if (parentNode)
+ originalRemoveChild.call(parentNode, childNode);
+ childWrapper.previousSibling_ = childWrapper.nextSibling_ =
+ childWrapper.parentNode_ = null;
+ childWrapper = nextSibling;
+ }
+ wrapper.firstChild_ = wrapper.lastChild_ = null;
+ } else {
+ var node = unwrap(wrapper);
+ var child = node.firstChild;
+ var nextSibling;
+ while (child) {
+ nextSibling = child.nextSibling;
+ originalRemoveChild.call(node, child);
+ child = nextSibling;
+ }
+ }
+ }
+
+ function invalidateParent(node) {
+ var p = node.parentNode;
+ return p && p.invalidateShadowRenderer();
+ }
+
+ function cleanupNodes(nodes) {
+ for (var i = 0, n; i < nodes.length; i++) {
+ n = nodes[i];
+ n.parentNode.removeChild(n);
+ }
+ }
+
+ var OriginalNode = window.Node;
+
+ /**
+ * This represents a wrapper of a native DOM node.
+ * @param {!Node} original The original DOM node, aka, the visual DOM node.
+ * @constructor
+ * @extends {EventTarget}
+ */
+ function Node(original) {
+ assert(original instanceof OriginalNode);
+
+ EventTarget.call(this, original);
+
+ // These properties are used to override the visual references with the
+ // logical ones. If the value is undefined it means that the logical is the
+ // same as the visual.
+
+ /**
+ * @type {Node|undefined}
+ * @private
+ */
+ this.parentNode_ = undefined;
+
+ /**
+ * @type {Node|undefined}
+ * @private
+ */
+ this.firstChild_ = undefined;
+
+ /**
+ * @type {Node|undefined}
+ * @private
+ */
+ this.lastChild_ = undefined;
+
+ /**
+ * @type {Node|undefined}
+ * @private
+ */
+ this.nextSibling_ = undefined;
+
+ /**
+ * @type {Node|undefined}
+ * @private
+ */
+ this.previousSibling_ = undefined;
+ }
+
+ var OriginalDocumentFragment = window.DocumentFragment;
+ var originalAppendChild = OriginalNode.prototype.appendChild;
+ var originalCompareDocumentPosition =
+ OriginalNode.prototype.compareDocumentPosition;
+ var originalInsertBefore = OriginalNode.prototype.insertBefore;
+ var originalRemoveChild = OriginalNode.prototype.removeChild;
+ var originalReplaceChild = OriginalNode.prototype.replaceChild;
+
+ var isIe = /Trident/.test(navigator.userAgent);
+
+ var removeChildOriginalHelper = isIe ?
+ function(parent, child) {
+ try {
+ originalRemoveChild.call(parent, child);
+ } catch (ex) {
+ if (!(parent instanceof OriginalDocumentFragment))
+ throw ex;
+ }
+ } :
+ function(parent, child) {
+ originalRemoveChild.call(parent, child);
+ };
+
+ Node.prototype = Object.create(EventTarget.prototype);
+ mixin(Node.prototype, {
+ appendChild: function(childWrapper) {
+ return this.insertBefore(childWrapper, null);
+ },
+
+ insertBefore: function(childWrapper, refWrapper) {
+ assertIsNodeWrapper(childWrapper);
+
+ var refNode;
+ if (refWrapper) {
+ if (isWrapper(refWrapper)) {
+ refNode = unwrap(refWrapper);
+ } else {
+ refNode = refWrapper;
+ refWrapper = wrap(refNode);
+ }
+ } else {
+ refWrapper = null;
+ refNode = null;
+ }
+
+ refWrapper && assert(refWrapper.parentNode === this);
+
+ var nodes;
+ var previousNode =
+ refWrapper ? refWrapper.previousSibling : this.lastChild;
+
+ var useNative = !this.invalidateShadowRenderer() &&
+ !invalidateParent(childWrapper);
+
+ if (useNative)
+ nodes = collectNodesNative(childWrapper);
+ else
+ nodes = collectNodes(childWrapper, this, previousNode, refWrapper);
+
+ if (useNative) {
+ ensureSameOwnerDocument(this, childWrapper);
+ clearChildNodes(this);
+ originalInsertBefore.call(this.impl, unwrap(childWrapper), refNode);
+ } else {
+ if (!previousNode)
+ this.firstChild_ = nodes[0];
+ if (!refWrapper)
+ this.lastChild_ = nodes[nodes.length - 1];
+
+ var parentNode = refNode ? refNode.parentNode : this.impl;
+
+ // insertBefore refWrapper no matter what the parent is?
+ if (parentNode) {
+ originalInsertBefore.call(parentNode,
+ unwrapNodesForInsertion(this, nodes), refNode);
+ } else {
+ adoptNodesIfNeeded(this, nodes);
+ }
+ }
+
+ enqueueMutation(this, 'childList', {
+ addedNodes: nodes,
+ nextSibling: refWrapper,
+ previousSibling: previousNode
+ });
+
+ nodesWereAdded(nodes);
+
+ return childWrapper;
+ },
+
+ removeChild: function(childWrapper) {
+ assertIsNodeWrapper(childWrapper);
+ if (childWrapper.parentNode !== this) {
+ // IE has invalid DOM trees at times.
+ var found = false;
+ var childNodes = this.childNodes;
+ for (var ieChild = this.firstChild; ieChild;
+ ieChild = ieChild.nextSibling) {
+ if (ieChild === childWrapper) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // TODO(arv): DOMException
+ throw new Error('NotFoundError');
+ }
+ }
+
+ var childNode = unwrap(childWrapper);
+ var childWrapperNextSibling = childWrapper.nextSibling;
+ var childWrapperPreviousSibling = childWrapper.previousSibling;
+
+ if (this.invalidateShadowRenderer()) {
+ // We need to remove the real node from the DOM before updating the
+ // pointers. This is so that that mutation event is dispatched before
+ // the pointers have changed.
+ var thisFirstChild = this.firstChild;
+ var thisLastChild = this.lastChild;
+
+ var parentNode = childNode.parentNode;
+ if (parentNode)
+ removeChildOriginalHelper(parentNode, childNode);
+
+ if (thisFirstChild === childWrapper)
+ this.firstChild_ = childWrapperNextSibling;
+ if (thisLastChild === childWrapper)
+ this.lastChild_ = childWrapperPreviousSibling;
+ if (childWrapperPreviousSibling)
+ childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling;
+ if (childWrapperNextSibling) {
+ childWrapperNextSibling.previousSibling_ =
+ childWrapperPreviousSibling;
+ }
+
+ childWrapper.previousSibling_ = childWrapper.nextSibling_ =
+ childWrapper.parentNode_ = undefined;
+ } else {
+ clearChildNodes(this);
+ removeChildOriginalHelper(this.impl, childNode);
+ }
+
+ if (!surpressMutations) {
+ enqueueMutation(this, 'childList', {
+ removedNodes: createOneElementNodeList(childWrapper),
+ nextSibling: childWrapperNextSibling,
+ previousSibling: childWrapperPreviousSibling
+ });
+ }
+
+ registerTransientObservers(this, childWrapper);
+
+ return childWrapper;
+ },
+
+ replaceChild: function(newChildWrapper, oldChildWrapper) {
+ assertIsNodeWrapper(newChildWrapper);
+
+ var oldChildNode;
+ if (isWrapper(oldChildWrapper)) {
+ oldChildNode = unwrap(oldChildWrapper);
+ } else {
+ oldChildNode = oldChildWrapper;
+ oldChildWrapper = wrap(oldChildNode);
+ }
+
+ if (oldChildWrapper.parentNode !== this) {
+ // TODO(arv): DOMException
+ throw new Error('NotFoundError');
+ }
+
+ var nextNode = oldChildWrapper.nextSibling;
+ var previousNode = oldChildWrapper.previousSibling;
+ var nodes;
+
+ var useNative = !this.invalidateShadowRenderer() &&
+ !invalidateParent(newChildWrapper);
+
+ if (useNative) {
+ nodes = collectNodesNative(newChildWrapper);
+ } else {
+ if (nextNode === newChildWrapper)
+ nextNode = newChildWrapper.nextSibling;
+ nodes = collectNodes(newChildWrapper, this, previousNode, nextNode);
+ }
+
+ if (!useNative) {
+ if (this.firstChild === oldChildWrapper)
+ this.firstChild_ = nodes[0];
+ if (this.lastChild === oldChildWrapper)
+ this.lastChild_ = nodes[nodes.length - 1];
+
+ oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ =
+ oldChildWrapper.parentNode_ = undefined;
+
+ // replaceChild no matter what the parent is?
+ if (oldChildNode.parentNode) {
+ originalReplaceChild.call(
+ oldChildNode.parentNode,
+ unwrapNodesForInsertion(this, nodes),
+ oldChildNode);
+ }
+ } else {
+ ensureSameOwnerDocument(this, newChildWrapper);
+ clearChildNodes(this);
+ originalReplaceChild.call(this.impl, unwrap(newChildWrapper),
+ oldChildNode);
+ }
+
+ enqueueMutation(this, 'childList', {
+ addedNodes: nodes,
+ removedNodes: createOneElementNodeList(oldChildWrapper),
+ nextSibling: nextNode,
+ previousSibling: previousNode
+ });
+
+ nodeWasRemoved(oldChildWrapper);
+ nodesWereAdded(nodes);
+
+ return oldChildWrapper;
+ },
+
+ /**
+ * Called after a node was inserted. Subclasses override this to invalidate
+ * the renderer as needed.
+ * @private
+ */
+ nodeIsInserted_: function() {
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ child.nodeIsInserted_();
+ }
+ },
+
+ hasChildNodes: function() {
+ return this.firstChild !== null;
+ },
+
+ /** @type {Node} */
+ get parentNode() {
+ // If the parentNode has not been overridden, use the original parentNode.
+ return this.parentNode_ !== undefined ?
+ this.parentNode_ : wrap(this.impl.parentNode);
+ },
+
+ /** @type {Node} */
+ get firstChild() {
+ return this.firstChild_ !== undefined ?
+ this.firstChild_ : wrap(this.impl.firstChild);
+ },
+
+ /** @type {Node} */
+ get lastChild() {
+ return this.lastChild_ !== undefined ?
+ this.lastChild_ : wrap(this.impl.lastChild);
+ },
+
+ /** @type {Node} */
+ get nextSibling() {
+ return this.nextSibling_ !== undefined ?
+ this.nextSibling_ : wrap(this.impl.nextSibling);
+ },
+
+ /** @type {Node} */
+ get previousSibling() {
+ return this.previousSibling_ !== undefined ?
+ this.previousSibling_ : wrap(this.impl.previousSibling);
+ },
+
+ get parentElement() {
+ var p = this.parentNode;
+ while (p && p.nodeType !== Node.ELEMENT_NODE) {
+ p = p.parentNode;
+ }
+ return p;
+ },
+
+ get textContent() {
+ // TODO(arv): This should fallback to this.impl.textContent if there
+ // are no shadow trees below or above the context node.
+ var s = '';
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType != Node.COMMENT_NODE) {
+ s += child.textContent;
+ }
+ }
+ return s;
+ },
+ set textContent(textContent) {
+ var removedNodes = snapshotNodeList(this.childNodes);
+
+ if (this.invalidateShadowRenderer()) {
+ removeAllChildNodes(this);
+ if (textContent !== '') {
+ var textNode = this.impl.ownerDocument.createTextNode(textContent);
+ this.appendChild(textNode);
+ }
+ } else {
+ clearChildNodes(this);
+ this.impl.textContent = textContent;
+ }
+
+ var addedNodes = snapshotNodeList(this.childNodes);
+
+ enqueueMutation(this, 'childList', {
+ addedNodes: addedNodes,
+ removedNodes: removedNodes
+ });
+
+ nodesWereRemoved(removedNodes);
+ nodesWereAdded(addedNodes);
+ },
+
+ get childNodes() {
+ var wrapperList = new NodeList();
+ var i = 0;
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ wrapperList[i++] = child;
+ }
+ wrapperList.length = i;
+ return wrapperList;
+ },
+
+ cloneNode: function(deep) {
+ var clone = wrap(this.impl.cloneNode(false));
+ if (deep) {
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ clone.appendChild(child.cloneNode(true));
+ }
+ }
+ // TODO(arv): Some HTML elements also clone other data like value.
+ return clone;
+ },
+
+ contains: function(child) {
+ if (!child)
+ return false;
+
+ child = wrapIfNeeded(child);
+
+ // TODO(arv): Optimize using ownerDocument etc.
+ if (child === this)
+ return true;
+ var parentNode = child.parentNode;
+ if (!parentNode)
+ return false;
+ return this.contains(parentNode);
+ },
+
+ compareDocumentPosition: function(otherNode) {
+ // This only wraps, it therefore only operates on the composed DOM and not
+ // the logical DOM.
+ return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode));
+ },
+
+ normalize: function() {
+ var nodes = snapshotNodeList(this.childNodes);
+ var remNodes = [];
+ var s = '';
+ var modNode;
+
+ for (var i = 0, n; i < nodes.length; i++) {
+ n = nodes[i];
+ if (n.nodeType === Node.TEXT_NODE) {
+ if (!modNode && !n.data.length)
+ this.removeNode(n);
+ else if (!modNode)
+ modNode = n;
+ else {
+ s += n.data;
+ remNodes.push(n);
+ }
+ } else {
+ if (modNode && remNodes.length) {
+ modNode.data += s;
+ cleanUpNodes(remNodes);
+ }
+ remNodes = [];
+ s = '';
+ modNode = null;
+ if (n.childNodes.length)
+ n.normalize();
+ }
+ }
+
+ // handle case where >1 text nodes are the last children
+ if (modNode && remNodes.length) {
+ modNode.data += s;
+ cleanupNodes(remNodes);
+ }
+ }
+ });
+
+ defineWrapGetter(Node, 'ownerDocument');
+
+ // We use a DocumentFragment as a base and then delete the properties of
+ // DocumentFragment.prototype from the wrapper Node. Since delete makes
+ // objects slow in some JS engines we recreate the prototype object.
+ registerWrapper(OriginalNode, Node, document.createDocumentFragment());
+ delete Node.prototype.querySelector;
+ delete Node.prototype.querySelectorAll;
+ Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype);
+
+ scope.nodeWasAdded = nodeWasAdded;
+ scope.nodeWasRemoved = nodeWasRemoved;
+ scope.nodesWereAdded = nodesWereAdded;
+ scope.nodesWereRemoved = nodesWereRemoved;
+ scope.snapshotNodeList = snapshotNodeList;
+ scope.wrappers.Node = Node;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ function findOne(node, selector) {
+ var m, el = node.firstElementChild;
+ while (el) {
+ if (el.matches(selector))
+ return el;
+ m = findOne(el, selector);
+ if (m)
+ return m;
+ el = el.nextElementSibling;
+ }
+ return null;
+ }
+
+ function findAll(node, selector, results) {
+ var el = node.firstElementChild;
+ while (el) {
+ if (el.matches(selector))
+ results[results.length++] = el;
+ findAll(el, selector, results);
+ el = el.nextElementSibling;
+ }
+ return results;
+ }
+
+ // find and findAll will only match Simple Selectors,
+ // Structural Pseudo Classes are not guarenteed to be correct
+ // http://www.w3.org/TR/css3-selectors/#simple-selectors
+
+ var SelectorsInterface = {
+ querySelector: function(selector) {
+ return findOne(this, selector);
+ },
+ querySelectorAll: function(selector) {
+ return findAll(this, selector, new NodeList())
+ }
+ };
+
+ var GetElementsByInterface = {
+ getElementsByTagName: function(tagName) {
+ // TODO(arv): Check tagName?
+ return this.querySelectorAll(tagName);
+ },
+ getElementsByClassName: function(className) {
+ // TODO(arv): Check className?
+ return this.querySelectorAll('.' + className);
+ },
+ getElementsByTagNameNS: function(ns, tagName) {
+ if (ns === '*')
+ return this.getElementsByTagName(tagName);
+
+ // TODO(arv): Check tagName?
+ var result = new NodeList;
+ var els = this.getElementsByTagName(tagName);
+ for (var i = 0, j = 0; i < els.length; i++) {
+ if (els[i].namespaceURI === ns)
+ result[j++] = els[i];
+ }
+ result.length = j;
+ return result;
+ }
+ };
+
+ scope.GetElementsByInterface = GetElementsByInterface;
+ scope.SelectorsInterface = SelectorsInterface;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var NodeList = scope.wrappers.NodeList;
+
+ function forwardElement(node) {
+ while (node && node.nodeType !== Node.ELEMENT_NODE) {
+ node = node.nextSibling;
+ }
+ return node;
+ }
+
+ function backwardsElement(node) {
+ while (node && node.nodeType !== Node.ELEMENT_NODE) {
+ node = node.previousSibling;
+ }
+ return node;
+ }
+
+ var ParentNodeInterface = {
+ get firstElementChild() {
+ return forwardElement(this.firstChild);
+ },
+
+ get lastElementChild() {
+ return backwardsElement(this.lastChild);
+ },
+
+ get childElementCount() {
+ var count = 0;
+ for (var child = this.firstElementChild;
+ child;
+ child = child.nextElementSibling) {
+ count++;
+ }
+ return count;
+ },
+
+ get children() {
+ var wrapperList = new NodeList();
+ var i = 0;
+ for (var child = this.firstElementChild;
+ child;
+ child = child.nextElementSibling) {
+ wrapperList[i++] = child;
+ }
+ wrapperList.length = i;
+ return wrapperList;
+ }
+ };
+
+ var ChildNodeInterface = {
+ get nextElementSibling() {
+ return forwardElement(this.nextSibling);
+ },
+
+ get previousElementSibling() {
+ return backwardsElement(this.previousSibling);
+ }
+ };
+
+ scope.ChildNodeInterface = ChildNodeInterface;
+ scope.ParentNodeInterface = ParentNodeInterface;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var ChildNodeInterface = scope.ChildNodeInterface;
+ var Node = scope.wrappers.Node;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+
+ var OriginalCharacterData = window.CharacterData;
+
+ function CharacterData(node) {
+ Node.call(this, node);
+ }
+ CharacterData.prototype = Object.create(Node.prototype);
+ mixin(CharacterData.prototype, {
+ get textContent() {
+ return this.data;
+ },
+ set textContent(value) {
+ this.data = value;
+ },
+ get data() {
+ return this.impl.data;
+ },
+ set data(value) {
+ var oldValue = this.impl.data;
+ enqueueMutation(this, 'characterData', {
+ oldValue: oldValue
+ });
+ this.impl.data = value;
+ }
+ });
+
+ mixin(CharacterData.prototype, ChildNodeInterface);
+
+ registerWrapper(OriginalCharacterData, CharacterData,
+ document.createTextNode(''));
+
+ scope.wrappers.CharacterData = CharacterData;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2014 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var CharacterData = scope.wrappers.CharacterData;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+
+ function toUInt32(x) {
+ return x >>> 0;
+ }
+
+ var OriginalText = window.Text;
+
+ function Text(node) {
+ CharacterData.call(this, node);
+ }
+ Text.prototype = Object.create(CharacterData.prototype);
+ mixin(Text.prototype, {
+ splitText: function(offset) {
+ offset = toUInt32(offset);
+ var s = this.data;
+ if (offset > s.length)
+ throw new Error('IndexSizeError');
+ var head = s.slice(0, offset);
+ var tail = s.slice(offset);
+ this.data = head;
+ var newTextNode = this.ownerDocument.createTextNode(tail);
+ if (this.parentNode)
+ this.parentNode.insertBefore(newTextNode, this.nextSibling);
+ return newTextNode;
+ }
+ });
+
+ registerWrapper(OriginalText, Text, document.createTextNode(''));
+
+ scope.wrappers.Text = Text;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var ChildNodeInterface = scope.ChildNodeInterface;
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var Node = scope.wrappers.Node;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var addWrapNodeListMethod = scope.addWrapNodeListMethod;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var oneOf = scope.oneOf;
+ var registerWrapper = scope.registerWrapper;
+ var wrappers = scope.wrappers;
+
+ var OriginalElement = window.Element;
+
+ var matchesNames = [
+ 'matches', // needs to come first.
+ 'mozMatchesSelector',
+ 'msMatchesSelector',
+ 'webkitMatchesSelector',
+ ].filter(function(name) {
+ return OriginalElement.prototype[name];
+ });
+
+ var matchesName = matchesNames[0];
+
+ var originalMatches = OriginalElement.prototype[matchesName];
+
+ function invalidateRendererBasedOnAttribute(element, name) {
+ // Only invalidate if parent node is a shadow host.
+ var p = element.parentNode;
+ if (!p || !p.shadowRoot)
+ return;
+
+ var renderer = scope.getRendererForHost(p);
+ if (renderer.dependsOnAttribute(name))
+ renderer.invalidate();
+ }
+
+ function enqueAttributeChange(element, name, oldValue) {
+ // This is not fully spec compliant. We should use localName (which might
+ // have a different case than name) and the namespace (which requires us
+ // to get the Attr object).
+ enqueueMutation(element, 'attributes', {
+ name: name,
+ namespace: null,
+ oldValue: oldValue
+ });
+ }
+
+ function Element(node) {
+ Node.call(this, node);
+ }
+ Element.prototype = Object.create(Node.prototype);
+ mixin(Element.prototype, {
+ createShadowRoot: function() {
+ var newShadowRoot = new wrappers.ShadowRoot(this);
+ this.impl.polymerShadowRoot_ = newShadowRoot;
+
+ var renderer = scope.getRendererForHost(this);
+ renderer.invalidate();
+
+ return newShadowRoot;
+ },
+
+ get shadowRoot() {
+ return this.impl.polymerShadowRoot_ || null;
+ },
+
+ setAttribute: function(name, value) {
+ var oldValue = this.impl.getAttribute(name);
+ this.impl.setAttribute(name, value);
+ enqueAttributeChange(this, name, oldValue);
+ invalidateRendererBasedOnAttribute(this, name);
+ },
+
+ removeAttribute: function(name) {
+ var oldValue = this.impl.getAttribute(name);
+ this.impl.removeAttribute(name);
+ enqueAttributeChange(this, name, oldValue);
+ invalidateRendererBasedOnAttribute(this, name);
+ },
+
+ matches: function(selector) {
+ return originalMatches.call(this.impl, selector);
+ }
+ });
+
+ matchesNames.forEach(function(name) {
+ if (name !== 'matches') {
+ Element.prototype[name] = function(selector) {
+ return this.matches(selector);
+ };
+ }
+ });
+
+ if (OriginalElement.prototype.webkitCreateShadowRoot) {
+ Element.prototype.webkitCreateShadowRoot =
+ Element.prototype.createShadowRoot;
+ }
+
+ /**
+ * Useful for generating the accessor pair for a property that reflects an
+ * attribute.
+ */
+ function setterDirtiesAttribute(prototype, propertyName, opt_attrName) {
+ var attrName = opt_attrName || propertyName;
+ Object.defineProperty(prototype, propertyName, {
+ get: function() {
+ return this.impl[propertyName];
+ },
+ set: function(v) {
+ this.impl[propertyName] = v;
+ invalidateRendererBasedOnAttribute(this, attrName);
+ },
+ configurable: true,
+ enumerable: true
+ });
+ }
+
+ setterDirtiesAttribute(Element.prototype, 'id');
+ setterDirtiesAttribute(Element.prototype, 'className', 'class');
+
+ mixin(Element.prototype, ChildNodeInterface);
+ mixin(Element.prototype, GetElementsByInterface);
+ mixin(Element.prototype, ParentNodeInterface);
+ mixin(Element.prototype, SelectorsInterface);
+
+ registerWrapper(OriginalElement, Element,
+ document.createElementNS(null, 'x'));
+
+ // TODO(arv): Export setterDirtiesAttribute and apply it to more bindings
+ // that reflect attributes.
+ scope.matchesNames = matchesNames;
+ scope.wrappers.Element = Element;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var Element = scope.wrappers.Element;
+ var defineGetter = scope.defineGetter;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var nodesWereAdded = scope.nodesWereAdded;
+ var nodesWereRemoved = scope.nodesWereRemoved;
+ var registerWrapper = scope.registerWrapper;
+ var snapshotNodeList = scope.snapshotNodeList;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+
+ /////////////////////////////////////////////////////////////////////////////
+ // innerHTML and outerHTML
+
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#escapingString
+ var escapeAttrRegExp = /[&\u00A0"]/g;
+ var escapeDataRegExp = /[&\u00A0<>]/g;
+
+ function escapeReplace(c) {
+ switch (c) {
+ case '&':
+ return '&amp;';
+ case '<':
+ return '&lt;';
+ case '>':
+ return '&gt;';
+ case '"':
+ return '&quot;'
+ case '\u00A0':
+ return '&nbsp;';
+ }
+ }
+
+ function escapeAttr(s) {
+ return s.replace(escapeAttrRegExp, escapeReplace);
+ }
+
+ function escapeData(s) {
+ return s.replace(escapeDataRegExp, escapeReplace);
+ }
+
+ function makeSet(arr) {
+ var set = {};
+ for (var i = 0; i < arr.length; i++) {
+ set[arr[i]] = true;
+ }
+ return set;
+ }
+
+ // http://www.whatwg.org/specs/web-apps/current-work/#void-elements
+ var voidElements = makeSet([
+ 'area',
+ 'base',
+ 'br',
+ 'col',
+ 'command',
+ 'embed',
+ 'hr',
+ 'img',
+ 'input',
+ 'keygen',
+ 'link',
+ 'meta',
+ 'param',
+ 'source',
+ 'track',
+ 'wbr'
+ ]);
+
+ var plaintextParents = makeSet([
+ 'style',
+ 'script',
+ 'xmp',
+ 'iframe',
+ 'noembed',
+ 'noframes',
+ 'plaintext',
+ 'noscript'
+ ]);
+
+ function getOuterHTML(node, parentNode) {
+ switch (node.nodeType) {
+ case Node.ELEMENT_NODE:
+ var tagName = node.tagName.toLowerCase();
+ var s = '<' + tagName;
+ var attrs = node.attributes;
+ for (var i = 0, attr; attr = attrs[i]; i++) {
+ s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"';
+ }
+ s += '>';
+ if (voidElements[tagName])
+ return s;
+
+ return s + getInnerHTML(node) + '</' + tagName + '>';
+
+ case Node.TEXT_NODE:
+ var data = node.data;
+ if (parentNode && plaintextParents[parentNode.localName])
+ return data;
+ return escapeData(data);
+
+ case Node.COMMENT_NODE:
+ return '<!--' + node.data + '-->';
+
+ default:
+ console.error(node);
+ throw new Error('not implemented');
+ }
+ }
+
+ function getInnerHTML(node) {
+ var s = '';
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ s += getOuterHTML(child, node);
+ }
+ return s;
+ }
+
+ function setInnerHTML(node, value, opt_tagName) {
+ var tagName = opt_tagName || 'div';
+ node.textContent = '';
+ var tempElement = unwrap(node.ownerDocument.createElement(tagName));
+ tempElement.innerHTML = value;
+ var firstChild;
+ while (firstChild = tempElement.firstChild) {
+ node.appendChild(wrap(firstChild));
+ }
+ }
+
+ // IE11 does not have MSIE in the user agent string.
+ var oldIe = /MSIE/.test(navigator.userAgent);
+
+ var OriginalHTMLElement = window.HTMLElement;
+
+ function HTMLElement(node) {
+ Element.call(this, node);
+ }
+ HTMLElement.prototype = Object.create(Element.prototype);
+ mixin(HTMLElement.prototype, {
+ get innerHTML() {
+ // TODO(arv): This should fallback to this.impl.innerHTML if there
+ // are no shadow trees below or above the context node.
+ return getInnerHTML(this);
+ },
+ set innerHTML(value) {
+ // IE9 does not handle set innerHTML correctly on plaintextParents. It
+ // creates element children. For example
+ //
+ // scriptElement.innerHTML = '<a>test</a>'
+ //
+ // Creates a single HTMLAnchorElement child.
+ if (oldIe && plaintextParents[this.localName]) {
+ this.textContent = value;
+ return;
+ }
+
+ var removedNodes = snapshotNodeList(this.childNodes);
+
+ if (this.invalidateShadowRenderer())
+ setInnerHTML(this, value, this.tagName);
+ else
+ this.impl.innerHTML = value;
+ var addedNodes = snapshotNodeList(this.childNodes);
+
+ enqueueMutation(this, 'childList', {
+ addedNodes: addedNodes,
+ removedNodes: removedNodes
+ });
+
+ nodesWereRemoved(removedNodes);
+ nodesWereAdded(addedNodes);
+ },
+
+ get outerHTML() {
+ return getOuterHTML(this, this.parentNode);
+ },
+ set outerHTML(value) {
+ var p = this.parentNode;
+ if (p) {
+ p.invalidateShadowRenderer();
+ var df = frag(p, value);
+ p.replaceChild(df, this);
+ }
+ },
+
+ insertAdjacentHTML: function(position, text) {
+ var contextElement, refNode;
+ switch (String(position).toLowerCase()) {
+ case 'beforebegin':
+ contextElement = this.parentNode;
+ refNode = this;
+ break;
+ case 'afterend':
+ contextElement = this.parentNode;
+ refNode = this.nextSibling;
+ break;
+ case 'afterbegin':
+ contextElement = this;
+ refNode = this.firstChild;
+ break;
+ case 'beforeend':
+ contextElement = this;
+ refNode = null;
+ break;
+ default:
+ return;
+ }
+
+ var df = frag(contextElement, text);
+ contextElement.insertBefore(df, refNode);
+ }
+ });
+
+ function frag(contextElement, html) {
+ // TODO(arv): This does not work with SVG and other non HTML elements.
+ var p = unwrap(contextElement.cloneNode(false));
+ p.innerHTML = html;
+ var df = unwrap(document.createDocumentFragment());
+ var c;
+ while (c = p.firstChild) {
+ df.appendChild(c);
+ }
+ return wrap(df);
+ }
+
+ function getter(name) {
+ return function() {
+ scope.renderAllPending();
+ return this.impl[name];
+ };
+ }
+
+ function getterRequiresRendering(name) {
+ defineGetter(HTMLElement, name, getter(name));
+ }
+
+ [
+ 'clientHeight',
+ 'clientLeft',
+ 'clientTop',
+ 'clientWidth',
+ 'offsetHeight',
+ 'offsetLeft',
+ 'offsetTop',
+ 'offsetWidth',
+ 'scrollHeight',
+ 'scrollWidth',
+ ].forEach(getterRequiresRendering);
+
+ function getterAndSetterRequiresRendering(name) {
+ Object.defineProperty(HTMLElement.prototype, name, {
+ get: getter(name),
+ set: function(v) {
+ scope.renderAllPending();
+ this.impl[name] = v;
+ },
+ configurable: true,
+ enumerable: true
+ });
+ }
+
+ [
+ 'scrollLeft',
+ 'scrollTop',
+ ].forEach(getterAndSetterRequiresRendering);
+
+ function methodRequiresRendering(name) {
+ Object.defineProperty(HTMLElement.prototype, name, {
+ value: function() {
+ scope.renderAllPending();
+ return this.impl[name].apply(this.impl, arguments);
+ },
+ configurable: true,
+ enumerable: true
+ });
+ }
+
+ [
+ 'getBoundingClientRect',
+ 'getClientRects',
+ 'scrollIntoView'
+ ].forEach(methodRequiresRendering);
+
+ // HTMLElement is abstract so we use a subclass that has no members.
+ registerWrapper(OriginalHTMLElement, HTMLElement,
+ document.createElement('b'));
+
+ scope.wrappers.HTMLElement = HTMLElement;
+
+ // TODO: Find a better way to share these two with WrapperShadowRoot.
+ scope.getInnerHTML = getInnerHTML;
+ scope.setInnerHTML = setInnerHTML
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var wrap = scope.wrap;
+
+ var OriginalHTMLCanvasElement = window.HTMLCanvasElement;
+
+ function HTMLCanvasElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype);
+
+ mixin(HTMLCanvasElement.prototype, {
+ getContext: function() {
+ var context = this.impl.getContext.apply(this.impl, arguments);
+ return context && wrap(context);
+ }
+ });
+
+ registerWrapper(OriginalHTMLCanvasElement, HTMLCanvasElement,
+ document.createElement('canvas'));
+
+ scope.wrappers.HTMLCanvasElement = HTMLCanvasElement;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+
+ var OriginalHTMLContentElement = window.HTMLContentElement;
+
+ function HTMLContentElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLContentElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLContentElement.prototype, {
+ get select() {
+ return this.getAttribute('select');
+ },
+ set select(value) {
+ this.setAttribute('select', value);
+ },
+
+ setAttribute: function(n, v) {
+ HTMLElement.prototype.setAttribute.call(this, n, v);
+ if (String(n).toLowerCase() === 'select')
+ this.invalidateShadowRenderer(true);
+ }
+
+ // getDistributedNodes is added in ShadowRenderer
+
+ // TODO: attribute boolean resetStyleInheritance;
+ });
+
+ if (OriginalHTMLContentElement)
+ registerWrapper(OriginalHTMLContentElement, HTMLContentElement);
+
+ scope.wrappers.HTMLContentElement = HTMLContentElement;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var rewrap = scope.rewrap;
+
+ var OriginalHTMLImageElement = window.HTMLImageElement;
+
+ function HTMLImageElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLImageElement.prototype = Object.create(HTMLElement.prototype);
+
+ registerWrapper(OriginalHTMLImageElement, HTMLImageElement,
+ document.createElement('img'));
+
+ function Image(width, height) {
+ if (!(this instanceof Image)) {
+ throw new TypeError(
+ 'DOM object constructor cannot be called as a function.');
+ }
+
+ var node = unwrap(document.createElement('img'));
+ HTMLElement.call(this, node);
+ rewrap(node, this);
+
+ if (width !== undefined)
+ node.width = width;
+ if (height !== undefined)
+ node.height = height;
+ }
+
+ Image.prototype = HTMLImageElement.prototype;
+
+ scope.wrappers.HTMLImageElement = HTMLImageElement;
+ scope.wrappers.Image = Image;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+
+ var OriginalHTMLShadowElement = window.HTMLShadowElement;
+
+ function HTMLShadowElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLShadowElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLShadowElement.prototype, {
+ // TODO: attribute boolean resetStyleInheritance;
+ });
+
+ if (OriginalHTMLShadowElement)
+ registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement);
+
+ scope.wrappers.HTMLShadowElement = HTMLShadowElement;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var getInnerHTML = scope.getInnerHTML;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setInnerHTML = scope.setInnerHTML;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+
+ var contentTable = new WeakMap();
+ var templateContentsOwnerTable = new WeakMap();
+
+ // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
+ function getTemplateContentsOwner(doc) {
+ if (!doc.defaultView)
+ return doc;
+ var d = templateContentsOwnerTable.get(doc);
+ if (!d) {
+ // TODO(arv): This should either be a Document or HTMLDocument depending
+ // on doc.
+ d = doc.implementation.createHTMLDocument('');
+ while (d.lastChild) {
+ d.removeChild(d.lastChild);
+ }
+ templateContentsOwnerTable.set(doc, d);
+ }
+ return d;
+ }
+
+ function extractContent(templateElement) {
+ // templateElement is not a wrapper here.
+ var doc = getTemplateContentsOwner(templateElement.ownerDocument);
+ var df = unwrap(doc.createDocumentFragment());
+ var child;
+ while (child = templateElement.firstChild) {
+ df.appendChild(child);
+ }
+ return df;
+ }
+
+ var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
+
+ function HTMLTemplateElement(node) {
+ HTMLElement.call(this, node);
+ if (!OriginalHTMLTemplateElement) {
+ var content = extractContent(node);
+ contentTable.set(this, wrap(content));
+ }
+ }
+ HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
+
+ mixin(HTMLTemplateElement.prototype, {
+ get content() {
+ if (OriginalHTMLTemplateElement)
+ return wrap(this.impl.content);
+ return contentTable.get(this);
+ },
+
+ get innerHTML() {
+ return getInnerHTML(this.content);
+ },
+ set innerHTML(value) {
+ setInnerHTML(this.content, value);
+ }
+
+ // TODO(arv): cloneNode needs to clone content.
+
+ });
+
+ if (OriginalHTMLTemplateElement)
+ registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement);
+
+ scope.wrappers.HTMLTemplateElement = HTMLTemplateElement;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var registerWrapper = scope.registerWrapper;
+
+ var OriginalHTMLMediaElement = window.HTMLMediaElement;
+
+ function HTMLMediaElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLMediaElement.prototype = Object.create(HTMLElement.prototype);
+
+ registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement,
+ document.createElement('audio'));
+
+ scope.wrappers.HTMLMediaElement = HTMLMediaElement;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLMediaElement = scope.wrappers.HTMLMediaElement;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var rewrap = scope.rewrap;
+
+ var OriginalHTMLAudioElement = window.HTMLAudioElement;
+
+ function HTMLAudioElement(node) {
+ HTMLMediaElement.call(this, node);
+ }
+ HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype);
+
+ registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement,
+ document.createElement('audio'));
+
+ function Audio(src) {
+ if (!(this instanceof Audio)) {
+ throw new TypeError(
+ 'DOM object constructor cannot be called as a function.');
+ }
+
+ var node = unwrap(document.createElement('audio'));
+ HTMLMediaElement.call(this, node);
+ rewrap(node, this);
+
+ node.setAttribute('preload', 'auto');
+ if (src !== undefined)
+ node.setAttribute('src', src);
+ }
+
+ Audio.prototype = HTMLAudioElement.prototype;
+
+ scope.wrappers.HTMLAudioElement = HTMLAudioElement;
+ scope.wrappers.Audio = Audio;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var rewrap = scope.rewrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+
+ var OriginalHTMLOptionElement = window.HTMLOptionElement;
+
+ function trimText(s) {
+ return s.replace(/\s+/g, ' ').trim();
+ }
+
+ function HTMLOptionElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLOptionElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLOptionElement.prototype, {
+ get text() {
+ return trimText(this.textContent);
+ },
+ set text(value) {
+ this.textContent = trimText(String(value));
+ },
+ get form() {
+ return wrap(unwrap(this).form);
+ }
+ });
+
+ registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement,
+ document.createElement('option'));
+
+ function Option(text, value, defaultSelected, selected) {
+ if (!(this instanceof Option)) {
+ throw new TypeError(
+ 'DOM object constructor cannot be called as a function.');
+ }
+
+ var node = unwrap(document.createElement('option'));
+ HTMLElement.call(this, node);
+ rewrap(node, this);
+
+ if (text !== undefined)
+ node.text = text;
+ if (value !== undefined)
+ node.setAttribute('value', value);
+ if (defaultSelected === true)
+ node.setAttribute('selected', '');
+ node.selected = selected === true;
+ }
+
+ Option.prototype = HTMLOptionElement.prototype;
+
+ scope.wrappers.HTMLOptionElement = HTMLOptionElement;
+ scope.wrappers.Option = Option;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLContentElement = scope.wrappers.HTMLContentElement;
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
+ var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+
+ var OriginalHTMLUnknownElement = window.HTMLUnknownElement;
+
+ function HTMLUnknownElement(node) {
+ switch (node.localName) {
+ case 'content':
+ return new HTMLContentElement(node);
+ case 'shadow':
+ return new HTMLShadowElement(node);
+ case 'template':
+ return new HTMLTemplateElement(node);
+ }
+ HTMLElement.call(this, node);
+ }
+ HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype);
+ registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement);
+ scope.wrappers.HTMLUnknownElement = HTMLUnknownElement;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2014 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var registerObject = scope.registerObject;
+
+ var SVG_NS = 'http://www.w3.org/2000/svg';
+ var svgTitleElement = document.createElementNS(SVG_NS, 'title');
+ var SVGTitleElement = registerObject(svgTitleElement);
+ var SVGElement = Object.getPrototypeOf(SVGTitleElement.prototype).constructor;
+
+ scope.wrappers.SVGElement = SVGElement;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2014 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+
+ var OriginalSVGUseElement = window.SVGUseElement;
+
+ // IE uses SVGElement as parent interface, SVG2 (Blink & Gecko) uses
+ // SVGGraphicsElement. Use the <g> element to get the right prototype.
+
+ var SVG_NS = 'http://www.w3.org/2000/svg';
+ var gWrapper = wrap(document.createElementNS(SVG_NS, 'g'));
+ var useElement = document.createElementNS(SVG_NS, 'use');
+ var SVGGElement = gWrapper.constructor;
+ var parentInterfacePrototype = Object.getPrototypeOf(SVGGElement.prototype);
+ var parentInterface = parentInterfacePrototype.constructor;
+
+ function SVGUseElement(impl) {
+ parentInterface.call(this, impl);
+ }
+
+ SVGUseElement.prototype = Object.create(parentInterfacePrototype);
+
+ // Firefox does not expose instanceRoot.
+ if ('instanceRoot' in useElement) {
+ mixin(SVGUseElement.prototype, {
+ get instanceRoot() {
+ return wrap(unwrap(this).instanceRoot);
+ },
+ get animatedInstanceRoot() {
+ return wrap(unwrap(this).animatedInstanceRoot);
+ },
+ });
+ }
+
+ registerWrapper(OriginalSVGUseElement, SVGUseElement, useElement);
+
+ scope.wrappers.SVGUseElement = SVGUseElement;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2014 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var EventTarget = scope.wrappers.EventTarget;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var wrap = scope.wrap;
+
+ var OriginalSVGElementInstance = window.SVGElementInstance;
+ if (!OriginalSVGElementInstance)
+ return;
+
+ function SVGElementInstance(impl) {
+ EventTarget.call(this, impl);
+ }
+
+ SVGElementInstance.prototype = Object.create(EventTarget.prototype);
+ mixin(SVGElementInstance.prototype, {
+ /** @type {SVGElement} */
+ get correspondingElement() {
+ return wrap(this.impl.correspondingElement);
+ },
+
+ /** @type {SVGUseElement} */
+ get correspondingUseElement() {
+ return wrap(this.impl.correspondingUseElement);
+ },
+
+ /** @type {SVGElementInstance} */
+ get parentNode() {
+ return wrap(this.impl.parentNode);
+ },
+
+ /** @type {SVGElementInstanceList} */
+ get childNodes() {
+ throw new Error('Not implemented');
+ },
+
+ /** @type {SVGElementInstance} */
+ get firstChild() {
+ return wrap(this.impl.firstChild);
+ },
+
+ /** @type {SVGElementInstance} */
+ get lastChild() {
+ return wrap(this.impl.lastChild);
+ },
+
+ /** @type {SVGElementInstance} */
+ get previousSibling() {
+ return wrap(this.impl.previousSibling);
+ },
+
+ /** @type {SVGElementInstance} */
+ get nextSibling() {
+ return wrap(this.impl.nextSibling);
+ }
+ });
+
+ registerWrapper(OriginalSVGElementInstance, SVGElementInstance);
+
+ scope.wrappers.SVGElementInstance = SVGElementInstance;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+
+ var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
+
+ function CanvasRenderingContext2D(impl) {
+ this.impl = impl;
+ }
+
+ mixin(CanvasRenderingContext2D.prototype, {
+ get canvas() {
+ return wrap(this.impl.canvas);
+ },
+
+ drawImage: function() {
+ arguments[0] = unwrapIfNeeded(arguments[0]);
+ this.impl.drawImage.apply(this.impl, arguments);
+ },
+
+ createPattern: function() {
+ arguments[0] = unwrap(arguments[0]);
+ return this.impl.createPattern.apply(this.impl, arguments);
+ }
+ });
+
+ registerWrapper(OriginalCanvasRenderingContext2D, CanvasRenderingContext2D,
+ document.createElement('canvas').getContext('2d'));
+
+ scope.wrappers.CanvasRenderingContext2D = CanvasRenderingContext2D;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+
+ var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
+
+ // IE10 does not have WebGL.
+ if (!OriginalWebGLRenderingContext)
+ return;
+
+ function WebGLRenderingContext(impl) {
+ this.impl = impl;
+ }
+
+ mixin(WebGLRenderingContext.prototype, {
+ get canvas() {
+ return wrap(this.impl.canvas);
+ },
+
+ texImage2D: function() {
+ arguments[5] = unwrapIfNeeded(arguments[5]);
+ this.impl.texImage2D.apply(this.impl, arguments);
+ },
+
+ texSubImage2D: function() {
+ arguments[6] = unwrapIfNeeded(arguments[6]);
+ this.impl.texSubImage2D.apply(this.impl, arguments);
+ }
+ });
+
+ // Blink/WebKit has broken DOM bindings. Usually we would create an instance
+ // of the object and pass it into registerWrapper as a "blueprint" but
+ // creating WebGL contexts is expensive and might fail so we use a dummy
+ // object with dummy instance properties for these broken browsers.
+ var instanceProperties = /WebKit/.test(navigator.userAgent) ?
+ {drawingBufferHeight: null, drawingBufferWidth: null} : {};
+
+ registerWrapper(OriginalWebGLRenderingContext, WebGLRenderingContext,
+ instanceProperties);
+
+ scope.wrappers.WebGLRenderingContext = WebGLRenderingContext;
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+
+ var OriginalRange = window.Range;
+
+ function Range(impl) {
+ this.impl = impl;
+ }
+ Range.prototype = {
+ get startContainer() {
+ return wrap(this.impl.startContainer);
+ },
+ get endContainer() {
+ return wrap(this.impl.endContainer);
+ },
+ get commonAncestorContainer() {
+ return wrap(this.impl.commonAncestorContainer);
+ },
+ setStart: function(refNode,offset) {
+ this.impl.setStart(unwrapIfNeeded(refNode), offset);
+ },
+ setEnd: function(refNode,offset) {
+ this.impl.setEnd(unwrapIfNeeded(refNode), offset);
+ },
+ setStartBefore: function(refNode) {
+ this.impl.setStartBefore(unwrapIfNeeded(refNode));
+ },
+ setStartAfter: function(refNode) {
+ this.impl.setStartAfter(unwrapIfNeeded(refNode));
+ },
+ setEndBefore: function(refNode) {
+ this.impl.setEndBefore(unwrapIfNeeded(refNode));
+ },
+ setEndAfter: function(refNode) {
+ this.impl.setEndAfter(unwrapIfNeeded(refNode));
+ },
+ selectNode: function(refNode) {
+ this.impl.selectNode(unwrapIfNeeded(refNode));
+ },
+ selectNodeContents: function(refNode) {
+ this.impl.selectNodeContents(unwrapIfNeeded(refNode));
+ },
+ compareBoundaryPoints: function(how, sourceRange) {
+ return this.impl.compareBoundaryPoints(how, unwrap(sourceRange));
+ },
+ extractContents: function() {
+ return wrap(this.impl.extractContents());
+ },
+ cloneContents: function() {
+ return wrap(this.impl.cloneContents());
+ },
+ insertNode: function(node) {
+ this.impl.insertNode(unwrapIfNeeded(node));
+ },
+ surroundContents: function(newParent) {
+ this.impl.surroundContents(unwrapIfNeeded(newParent));
+ },
+ cloneRange: function() {
+ return wrap(this.impl.cloneRange());
+ },
+ isPointInRange: function(node, offset) {
+ return this.impl.isPointInRange(unwrapIfNeeded(node), offset);
+ },
+ comparePoint: function(node, offset) {
+ return this.impl.comparePoint(unwrapIfNeeded(node), offset);
+ },
+ intersectsNode: function(node) {
+ return this.impl.intersectsNode(unwrapIfNeeded(node));
+ },
+ toString: function() {
+ return this.impl.toString();
+ }
+ };
+
+ // IE9 does not have createContextualFragment.
+ if (OriginalRange.prototype.createContextualFragment) {
+ Range.prototype.createContextualFragment = function(html) {
+ return wrap(this.impl.createContextualFragment(html));
+ };
+ }
+
+ registerWrapper(window.Range, Range, document.createRange());
+
+ scope.wrappers.Range = Range;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var mixin = scope.mixin;
+ var registerObject = scope.registerObject;
+
+ var DocumentFragment = registerObject(document.createDocumentFragment());
+ mixin(DocumentFragment.prototype, ParentNodeInterface);
+ mixin(DocumentFragment.prototype, SelectorsInterface);
+ mixin(DocumentFragment.prototype, GetElementsByInterface);
+
+ var Comment = registerObject(document.createComment(''));
+
+ scope.wrappers.Comment = Comment;
+ scope.wrappers.DocumentFragment = DocumentFragment;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var DocumentFragment = scope.wrappers.DocumentFragment;
+ var elementFromPoint = scope.elementFromPoint;
+ var getInnerHTML = scope.getInnerHTML;
+ var mixin = scope.mixin;
+ var rewrap = scope.rewrap;
+ var setInnerHTML = scope.setInnerHTML;
+ var unwrap = scope.unwrap;
+
+ var shadowHostTable = new WeakMap();
+ var nextOlderShadowTreeTable = new WeakMap();
+
+ var spaceCharRe = /[ \t\n\r\f]/;
+
+ function ShadowRoot(hostWrapper) {
+ var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment());
+ DocumentFragment.call(this, node);
+
+ // createDocumentFragment associates the node with a wrapper
+ // DocumentFragment instance. Override that.
+ rewrap(node, this);
+
+ var oldShadowRoot = hostWrapper.shadowRoot;
+ nextOlderShadowTreeTable.set(this, oldShadowRoot);
+
+ shadowHostTable.set(this, hostWrapper);
+ }
+ ShadowRoot.prototype = Object.create(DocumentFragment.prototype);
+ mixin(ShadowRoot.prototype, {
+ get innerHTML() {
+ return getInnerHTML(this);
+ },
+ set innerHTML(value) {
+ setInnerHTML(this, value);
+ this.invalidateShadowRenderer();
+ },
+
+ get olderShadowRoot() {
+ return nextOlderShadowTreeTable.get(this) || null;
+ },
+
+ get host() {
+ return shadowHostTable.get(this) || null;
+ },
+
+ invalidateShadowRenderer: function() {
+ return shadowHostTable.get(this).invalidateShadowRenderer();
+ },
+
+ elementFromPoint: function(x, y) {
+ return elementFromPoint(this, this.ownerDocument, x, y);
+ },
+
+ getElementById: function(id) {
+ if (spaceCharRe.test(id))
+ return null;
+ return this.querySelector('[id="' + id + '"]');
+ }
+ });
+
+ scope.wrappers.ShadowRoot = ShadowRoot;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var Element = scope.wrappers.Element;
+ var HTMLContentElement = scope.wrappers.HTMLContentElement;
+ var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
+ var Node = scope.wrappers.Node;
+ var ShadowRoot = scope.wrappers.ShadowRoot;
+ var assert = scope.assert;
+ var mixin = scope.mixin;
+ var oneOf = scope.oneOf;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+
+ /**
+ * Updates the fields of a wrapper to a snapshot of the logical DOM as needed.
+ * Up means parentNode
+ * Sideways means previous and next sibling.
+ * @param {!Node} wrapper
+ */
+ function updateWrapperUpAndSideways(wrapper) {
+ wrapper.previousSibling_ = wrapper.previousSibling;
+ wrapper.nextSibling_ = wrapper.nextSibling;
+ wrapper.parentNode_ = wrapper.parentNode;
+ }
+
+ /**
+ * Updates the fields of a wrapper to a snapshot of the logical DOM as needed.
+ * Down means first and last child
+ * @param {!Node} wrapper
+ */
+ function updateWrapperDown(wrapper) {
+ wrapper.firstChild_ = wrapper.firstChild;
+ wrapper.lastChild_ = wrapper.lastChild;
+ }
+
+ function updateAllChildNodes(parentNodeWrapper) {
+ assert(parentNodeWrapper instanceof Node);
+ for (var childWrapper = parentNodeWrapper.firstChild;
+ childWrapper;
+ childWrapper = childWrapper.nextSibling) {
+ updateWrapperUpAndSideways(childWrapper);
+ }
+ updateWrapperDown(parentNodeWrapper);
+ }
+
+ function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) {
+ var parentNode = unwrap(parentNodeWrapper);
+ var newChild = unwrap(newChildWrapper);
+ var refChild = refChildWrapper ? unwrap(refChildWrapper) : null;
+
+ remove(newChildWrapper);
+ updateWrapperUpAndSideways(newChildWrapper);
+
+ if (!refChildWrapper) {
+ parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
+ if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild)
+ parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
+
+ var lastChildWrapper = wrap(parentNode.lastChild);
+ if (lastChildWrapper)
+ lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
+ } else {
+ if (parentNodeWrapper.firstChild === refChildWrapper)
+ parentNodeWrapper.firstChild_ = refChildWrapper;
+
+ refChildWrapper.previousSibling_ = refChildWrapper.previousSibling;
+ }
+
+ parentNode.insertBefore(newChild, refChild);
+ }
+
+ function remove(nodeWrapper) {
+ var node = unwrap(nodeWrapper)
+ var parentNode = node.parentNode;
+ if (!parentNode)
+ return;
+
+ var parentNodeWrapper = wrap(parentNode);
+ updateWrapperUpAndSideways(nodeWrapper);
+
+ if (nodeWrapper.previousSibling)
+ nodeWrapper.previousSibling.nextSibling_ = nodeWrapper;
+ if (nodeWrapper.nextSibling)
+ nodeWrapper.nextSibling.previousSibling_ = nodeWrapper;
+
+ if (parentNodeWrapper.lastChild === nodeWrapper)
+ parentNodeWrapper.lastChild_ = nodeWrapper;
+ if (parentNodeWrapper.firstChild === nodeWrapper)
+ parentNodeWrapper.firstChild_ = nodeWrapper;
+
+ parentNode.removeChild(node);
+ }
+
+ var distributedChildNodesTable = new WeakMap();
+ var eventParentsTable = new WeakMap();
+ var insertionParentTable = new WeakMap();
+ var rendererForHostTable = new WeakMap();
+
+ function distributeChildToInsertionPoint(child, insertionPoint) {
+ getDistributedChildNodes(insertionPoint).push(child);
+ assignToInsertionPoint(child, insertionPoint);
+
+ var eventParents = eventParentsTable.get(child);
+ if (!eventParents)
+ eventParentsTable.set(child, eventParents = []);
+ eventParents.push(insertionPoint);
+ }
+
+ function resetDistributedChildNodes(insertionPoint) {
+ distributedChildNodesTable.set(insertionPoint, []);
+ }
+
+ function getDistributedChildNodes(insertionPoint) {
+ return distributedChildNodesTable.get(insertionPoint);
+ }
+
+ function getChildNodesSnapshot(node) {
+ var result = [], i = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ result[i++] = child;
+ }
+ return result;
+ }
+
+ /**
+ * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor|
+ * function returns |false| the traversal is aborted.
+ * @param {!Node} tree
+ * @param {function(!Node) : boolean} predicate
+ * @param {function(!Node) : *} visitor
+ */
+ function visit(tree, predicate, visitor) {
+ // This operates on logical DOM.
+ for (var node = tree.firstChild; node; node = node.nextSibling) {
+ if (predicate(node)) {
+ if (visitor(node) === false)
+ return;
+ } else {
+ visit(node, predicate, visitor);
+ }
+ }
+ }
+
+ // Matching Insertion Points
+ // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#matching-insertion-points
+
+ // TODO(arv): Verify this... I don't remember why I picked this regexp.
+ var selectorMatchRegExp = /^[*.:#[a-zA-Z_|]/;
+
+ var allowedPseudoRegExp = new RegExp('^:(' + [
+ 'link',
+ 'visited',
+ 'target',
+ 'enabled',
+ 'disabled',
+ 'checked',
+ 'indeterminate',
+ 'nth-child',
+ 'nth-last-child',
+ 'nth-of-type',
+ 'nth-last-of-type',
+ 'first-child',
+ 'last-child',
+ 'first-of-type',
+ 'last-of-type',
+ 'only-of-type',
+ ].join('|') + ')');
+
+
+ /**
+ * @param {Element} node
+ * @oaram {Element} point The insertion point element.
+ * @return {boolean} Whether the node matches the insertion point.
+ */
+ function matchesCriteria(node, point) {
+ var select = point.getAttribute('select');
+ if (!select)
+ return true;
+
+ // Here we know the select attribute is a non empty string.
+ select = select.trim();
+ if (!select)
+ return true;
+
+ if (!(node instanceof Element))
+ return false;
+
+ // The native matches function in IE9 does not correctly work with elements
+ // that are not in the document.
+ // TODO(arv): Implement matching in JS.
+ // https://github.com/Polymer/ShadowDOM/issues/361
+ if (select === '*' || select === node.localName)
+ return true;
+
+ // TODO(arv): This does not seem right. Need to check for a simple selector.
+ if (!selectorMatchRegExp.test(select))
+ return false;
+
+ // TODO(arv): This no longer matches the spec.
+ if (select[0] === ':' && !allowedPseudoRegExp.test(select))
+ return false;
+
+ try {
+ return node.matches(select);
+ } catch (ex) {
+ // Invalid selector.
+ return false;
+ }
+ }
+
+ var request = oneOf(window, [
+ 'requestAnimationFrame',
+ 'mozRequestAnimationFrame',
+ 'webkitRequestAnimationFrame',
+ 'setTimeout'
+ ]);
+
+ var pendingDirtyRenderers = [];
+ var renderTimer;
+
+ function renderAllPending() {
+ for (var i = 0; i < pendingDirtyRenderers.length; i++) {
+ pendingDirtyRenderers[i].render();
+ }
+ pendingDirtyRenderers = [];
+ }
+
+ function handleRequestAnimationFrame() {
+ renderTimer = null;
+ renderAllPending();
+ }
+
+ /**
+ * Returns existing shadow renderer for a host or creates it if it is needed.
+ * @params {!Element} host
+ * @return {!ShadowRenderer}
+ */
+ function getRendererForHost(host) {
+ var renderer = rendererForHostTable.get(host);
+ if (!renderer) {
+ renderer = new ShadowRenderer(host);
+ rendererForHostTable.set(host, renderer);
+ }
+ return renderer;
+ }
+
+ function getShadowRootAncestor(node) {
+ for (; node; node = node.parentNode) {
+ if (node instanceof ShadowRoot)
+ return node;
+ }
+ return null;
+ }
+
+ function getRendererForShadowRoot(shadowRoot) {
+ return getRendererForHost(shadowRoot.host);
+ }
+
+ var spliceDiff = new ArraySplice();
+ spliceDiff.equals = function(renderNode, rawNode) {
+ return unwrap(renderNode.node) === rawNode;
+ };
+
+ /**
+ * RenderNode is used as an in memory "render tree". When we render the
+ * composed tree we create a tree of RenderNodes, then we diff this against
+ * the real DOM tree and make minimal changes as needed.
+ */
+ function RenderNode(node) {
+ this.skip = false;
+ this.node = node;
+ this.childNodes = [];
+ }
+
+ RenderNode.prototype = {
+ append: function(node) {
+ var rv = new RenderNode(node);
+ this.childNodes.push(rv);
+ return rv;
+ },
+
+ sync: function(opt_added) {
+ if (this.skip)
+ return;
+
+ var nodeWrapper = this.node;
+ // plain array of RenderNodes
+ var newChildren = this.childNodes;
+ // plain array of real nodes.
+ var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper));
+ var added = opt_added || new WeakMap();
+
+ var splices = spliceDiff.calculateSplices(newChildren, oldChildren);
+
+ var newIndex = 0, oldIndex = 0;
+ var lastIndex = 0;
+ for (var i = 0; i < splices.length; i++) {
+ var splice = splices[i];
+ for (; lastIndex < splice.index; lastIndex++) {
+ oldIndex++;
+ newChildren[newIndex++].sync(added);
+ }
+
+ var removedCount = splice.removed.length;
+ for (var j = 0; j < removedCount; j++) {
+ var wrapper = wrap(oldChildren[oldIndex++]);
+ if (!added.get(wrapper))
+ remove(wrapper);
+ }
+
+ var addedCount = splice.addedCount;
+ var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]);
+ for (var j = 0; j < addedCount; j++) {
+ var newChildRenderNode = newChildren[newIndex++];
+ var newChildWrapper = newChildRenderNode.node;
+ insertBefore(nodeWrapper, newChildWrapper, refNode);
+
+ // Keep track of added so that we do not remove the node after it
+ // has been added.
+ added.set(newChildWrapper, true);
+
+ newChildRenderNode.sync(added);
+ }
+
+ lastIndex += addedCount;
+ }
+
+ for (var i = lastIndex; i < newChildren.length; i++) {
+ newChildren[i].sync(added);
+ }
+ }
+ };
+
+ function ShadowRenderer(host) {
+ this.host = host;
+ this.dirty = false;
+ this.invalidateAttributes();
+ this.associateNode(host);
+ }
+
+ ShadowRenderer.prototype = {
+
+ // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#rendering-shadow-trees
+ render: function(opt_renderNode) {
+ if (!this.dirty)
+ return;
+
+ this.invalidateAttributes();
+ this.treeComposition();
+
+ var host = this.host;
+ var shadowRoot = host.shadowRoot;
+
+ this.associateNode(host);
+ var topMostRenderer = !renderNode;
+ var renderNode = opt_renderNode || new RenderNode(host);
+
+ for (var node = shadowRoot.firstChild; node; node = node.nextSibling) {
+ this.renderNode(shadowRoot, renderNode, node, false);
+ }
+
+ if (topMostRenderer)
+ renderNode.sync();
+
+ this.dirty = false;
+ },
+
+ invalidate: function() {
+ if (!this.dirty) {
+ this.dirty = true;
+ pendingDirtyRenderers.push(this);
+ if (renderTimer)
+ return;
+ renderTimer = window[request](handleRequestAnimationFrame, 0);
+ }
+ },
+
+ renderNode: function(shadowRoot, renderNode, node, isNested) {
+ if (isShadowHost(node)) {
+ renderNode = renderNode.append(node);
+ var renderer = getRendererForHost(node);
+ renderer.dirty = true; // Need to rerender due to reprojection.
+ renderer.render(renderNode);
+ } else if (isInsertionPoint(node)) {
+ this.renderInsertionPoint(shadowRoot, renderNode, node, isNested);
+ } else if (isShadowInsertionPoint(node)) {
+ this.renderShadowInsertionPoint(shadowRoot, renderNode, node);
+ } else {
+ this.renderAsAnyDomTree(shadowRoot, renderNode, node, isNested);
+ }
+ },
+
+ renderAsAnyDomTree: function(shadowRoot, renderNode, node, isNested) {
+ renderNode = renderNode.append(node);
+
+ if (isShadowHost(node)) {
+ var renderer = getRendererForHost(node);
+ renderNode.skip = !renderer.dirty;
+ renderer.render(renderNode);
+ } else {
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ this.renderNode(shadowRoot, renderNode, child, isNested);
+ }
+ }
+ },
+
+ renderInsertionPoint: function(shadowRoot, renderNode, insertionPoint,
+ isNested) {
+ var distributedChildNodes = getDistributedChildNodes(insertionPoint);
+ if (distributedChildNodes.length) {
+ this.associateNode(insertionPoint);
+
+ for (var i = 0; i < distributedChildNodes.length; i++) {
+ var child = distributedChildNodes[i];
+ if (isInsertionPoint(child) && isNested)
+ this.renderInsertionPoint(shadowRoot, renderNode, child, isNested);
+ else
+ this.renderAsAnyDomTree(shadowRoot, renderNode, child, isNested);
+ }
+ } else {
+ this.renderFallbackContent(shadowRoot, renderNode, insertionPoint);
+ }
+ this.associateNode(insertionPoint.parentNode);
+ },
+
+ renderShadowInsertionPoint: function(shadowRoot, renderNode,
+ shadowInsertionPoint) {
+ var nextOlderTree = shadowRoot.olderShadowRoot;
+ if (nextOlderTree) {
+ assignToInsertionPoint(nextOlderTree, shadowInsertionPoint);
+ this.associateNode(shadowInsertionPoint.parentNode);
+ for (var node = nextOlderTree.firstChild;
+ node;
+ node = node.nextSibling) {
+ this.renderNode(nextOlderTree, renderNode, node, true);
+ }
+ } else {
+ this.renderFallbackContent(shadowRoot, renderNode,
+ shadowInsertionPoint);
+ }
+ },
+
+ renderFallbackContent: function(shadowRoot, renderNode, fallbackHost) {
+ this.associateNode(fallbackHost);
+ this.associateNode(fallbackHost.parentNode);
+ for (var node = fallbackHost.firstChild; node; node = node.nextSibling) {
+ this.renderAsAnyDomTree(shadowRoot, renderNode, node, false);
+ }
+ },
+
+ /**
+ * Invalidates the attributes used to keep track of which attributes may
+ * cause the renderer to be invalidated.
+ */
+ invalidateAttributes: function() {
+ this.attributes = Object.create(null);
+ },
+
+ /**
+ * Parses the selector and makes this renderer dependent on the attribute
+ * being used in the selector.
+ * @param {string} selector
+ */
+ updateDependentAttributes: function(selector) {
+ if (!selector)
+ return;
+
+ var attributes = this.attributes;
+
+ // .class
+ if (/\.\w+/.test(selector))
+ attributes['class'] = true;
+
+ // #id
+ if (/#\w+/.test(selector))
+ attributes['id'] = true;
+
+ selector.replace(/\[\s*([^\s=\|~\]]+)/g, function(_, name) {
+ attributes[name] = true;
+ });
+
+ // Pseudo selectors have been removed from the spec.
+ },
+
+ dependsOnAttribute: function(name) {
+ return this.attributes[name];
+ },
+
+ // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#dfn-distribution-algorithm
+ distribute: function(tree, pool) {
+ var self = this;
+
+ visit(tree, isActiveInsertionPoint,
+ function(insertionPoint) {
+ resetDistributedChildNodes(insertionPoint);
+ self.updateDependentAttributes(
+ insertionPoint.getAttribute('select'));
+
+ for (var i = 0; i < pool.length; i++) { // 1.2
+ var node = pool[i]; // 1.2.1
+ if (node === undefined) // removed
+ continue;
+ if (matchesCriteria(node, insertionPoint)) { // 1.2.2
+ distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2.1
+ pool[i] = undefined; // 1.2.2.2
+ }
+ }
+ });
+ },
+
+ // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#dfn-tree-composition
+ treeComposition: function () {
+ var shadowHost = this.host;
+ var tree = shadowHost.shadowRoot; // 1.
+ var pool = []; // 2.
+
+ for (var child = shadowHost.firstChild;
+ child;
+ child = child.nextSibling) { // 3.
+ if (isInsertionPoint(child)) { // 3.2.
+ var reprojected = getDistributedChildNodes(child); // 3.2.1.
+ // if reprojected is undef... reset it?
+ if (!reprojected || !reprojected.length) // 3.2.2.
+ reprojected = getChildNodesSnapshot(child);
+ pool.push.apply(pool, reprojected); // 3.2.3.
+ } else {
+ pool.push(child); // 3.3.
+ }
+ }
+
+ var shadowInsertionPoint, point;
+ while (tree) { // 4.
+ // 4.1.
+ shadowInsertionPoint = undefined; // Reset every iteration.
+ visit(tree, isActiveShadowInsertionPoint, function(point) {
+ shadowInsertionPoint = point;
+ return false;
+ });
+ point = shadowInsertionPoint;
+
+ this.distribute(tree, pool); // 4.2.
+ if (point) { // 4.3.
+ var nextOlderTree = tree.olderShadowRoot; // 4.3.1.
+ if (!nextOlderTree) {
+ break; // 4.3.1.1.
+ } else {
+ tree = nextOlderTree; // 4.3.2.2.
+ assignToInsertionPoint(tree, point); // 4.3.2.2.
+ continue; // 4.3.2.3.
+ }
+ } else {
+ break; // 4.4.
+ }
+ }
+ },
+
+ associateNode: function(node) {
+ node.impl.polymerShadowRenderer_ = this;
+ }
+ };
+
+ function isInsertionPoint(node) {
+ // Should this include <shadow>?
+ return node instanceof HTMLContentElement;
+ }
+
+ function isActiveInsertionPoint(node) {
+ // <content> inside another <content> or <shadow> is considered inactive.
+ return node instanceof HTMLContentElement;
+ }
+
+ function isShadowInsertionPoint(node) {
+ return node instanceof HTMLShadowElement;
+ }
+
+ function isActiveShadowInsertionPoint(node) {
+ // <shadow> inside another <content> or <shadow> is considered inactive.
+ return node instanceof HTMLShadowElement;
+ }
+
+ function isShadowHost(shadowHost) {
+ return shadowHost.shadowRoot;
+ }
+
+ function getShadowTrees(host) {
+ var trees = [];
+
+ for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) {
+ trees.push(tree);
+ }
+ return trees;
+ }
+
+ function assignToInsertionPoint(tree, point) {
+ insertionParentTable.set(tree, point);
+ }
+
+ // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#rendering-shadow-trees
+ function render(host) {
+ new ShadowRenderer(host).render();
+ };
+
+ // Need to rerender shadow host when:
+ //
+ // - a direct child to the ShadowRoot is added or removed
+ // - a direct child to the host is added or removed
+ // - a new shadow root is created
+ // - a direct child to a content/shadow element is added or removed
+ // - a sibling to a content/shadow element is added or removed
+ // - content[select] is changed
+ // - an attribute in a direct child to a host is modified
+
+ /**
+ * This gets called when a node was added or removed to it.
+ */
+ Node.prototype.invalidateShadowRenderer = function(force) {
+ var renderer = this.impl.polymerShadowRenderer_;
+ if (renderer) {
+ renderer.invalidate();
+ return true;
+ }
+
+ return false;
+ };
+
+ HTMLContentElement.prototype.getDistributedNodes = function() {
+ // TODO(arv): We should only rerender the dirty ancestor renderers (from
+ // the root and down).
+ renderAllPending();
+ return getDistributedChildNodes(this);
+ };
+
+ HTMLShadowElement.prototype.nodeIsInserted_ =
+ HTMLContentElement.prototype.nodeIsInserted_ = function() {
+ // Invalidate old renderer if any.
+ this.invalidateShadowRenderer();
+
+ var shadowRoot = getShadowRootAncestor(this);
+ var renderer;
+ if (shadowRoot)
+ renderer = getRendererForShadowRoot(shadowRoot);
+ this.impl.polymerShadowRenderer_ = renderer;
+ if (renderer)
+ renderer.invalidate();
+ };
+
+ scope.eventParentsTable = eventParentsTable;
+ scope.getRendererForHost = getRendererForHost;
+ scope.getShadowTrees = getShadowTrees;
+ scope.insertionParentTable = insertionParentTable;
+ scope.renderAllPending = renderAllPending;
+
+ // Exposed for testing
+ scope.visual = {
+ insertBefore: insertBefore,
+ remove: remove,
+ };
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var assert = scope.assert;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+
+ var elementsWithFormProperty = [
+ 'HTMLButtonElement',
+ 'HTMLFieldSetElement',
+ 'HTMLInputElement',
+ 'HTMLKeygenElement',
+ 'HTMLLabelElement',
+ 'HTMLLegendElement',
+ 'HTMLObjectElement',
+ // HTMLOptionElement is handled in HTMLOptionElement.js
+ 'HTMLOutputElement',
+ 'HTMLSelectElement',
+ 'HTMLTextAreaElement',
+ ];
+
+ function createWrapperConstructor(name) {
+ if (!window[name])
+ return;
+
+ // Ensure we are not overriding an already existing constructor.
+ assert(!scope.wrappers[name]);
+
+ var GeneratedWrapper = function(node) {
+ // At this point all of them extend HTMLElement.
+ HTMLElement.call(this, node);
+ }
+ GeneratedWrapper.prototype = Object.create(HTMLElement.prototype);
+ mixin(GeneratedWrapper.prototype, {
+ get form() {
+ return wrap(unwrap(this).form);
+ },
+ });
+
+ registerWrapper(window[name], GeneratedWrapper,
+ document.createElement(name.slice(4, -7)));
+ scope.wrappers[name] = GeneratedWrapper;
+ }
+
+ elementsWithFormProperty.forEach(createWrapperConstructor);
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2014 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+
+ var OriginalSelection = window.Selection;
+
+ function Selection(impl) {
+ this.impl = impl;
+ }
+ Selection.prototype = {
+ get anchorNode() {
+ return wrap(this.impl.anchorNode);
+ },
+ get focusNode() {
+ return wrap(this.impl.focusNode);
+ },
+ addRange: function(range) {
+ this.impl.addRange(unwrap(range));
+ },
+ collapse: function(node, index) {
+ this.impl.collapse(unwrapIfNeeded(node), index);
+ },
+ containsNode: function(node, allowPartial) {
+ return this.impl.containsNode(unwrapIfNeeded(node), allowPartial);
+ },
+ extend: function(node, offset) {
+ this.impl.extend(unwrapIfNeeded(node), offset);
+ },
+ getRangeAt: function(index) {
+ return wrap(this.impl.getRangeAt(index));
+ },
+ removeRange: function(range) {
+ this.impl.removeRange(unwrap(range));
+ },
+ selectAllChildren: function(node) {
+ this.impl.selectAllChildren(unwrapIfNeeded(node));
+ },
+ toString: function() {
+ return this.impl.toString();
+ }
+ };
+
+ // WebKit extensions. Not implemented.
+ // readonly attribute Node baseNode;
+ // readonly attribute long baseOffset;
+ // readonly attribute Node extentNode;
+ // readonly attribute long extentOffset;
+ // [RaisesException] void setBaseAndExtent([Default=Undefined] optional Node baseNode,
+ // [Default=Undefined] optional long baseOffset,
+ // [Default=Undefined] optional Node extentNode,
+ // [Default=Undefined] optional long extentOffset);
+ // [RaisesException, ImplementedAs=collapse] void setPosition([Default=Undefined] optional Node node,
+ // [Default=Undefined] optional long offset);
+
+ registerWrapper(window.Selection, Selection, window.getSelection());
+
+ scope.wrappers.Selection = Selection;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var Node = scope.wrappers.Node;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var Selection = scope.wrappers.Selection;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var ShadowRoot = scope.wrappers.ShadowRoot;
+ var defineWrapGetter = scope.defineWrapGetter;
+ var elementFromPoint = scope.elementFromPoint;
+ var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
+ var matchesNames = scope.matchesNames;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var renderAllPending = scope.renderAllPending;
+ var rewrap = scope.rewrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrapEventTargetMethods = scope.wrapEventTargetMethods;
+ var wrapNodeList = scope.wrapNodeList;
+
+ var implementationTable = new WeakMap();
+
+ function Document(node) {
+ Node.call(this, node);
+ }
+ Document.prototype = Object.create(Node.prototype);
+
+ defineWrapGetter(Document, 'documentElement');
+
+ // Conceptually both body and head can be in a shadow but suporting that seems
+ // overkill at this point.
+ defineWrapGetter(Document, 'body');
+ defineWrapGetter(Document, 'head');
+
+ // document cannot be overridden so we override a bunch of its methods
+ // directly on the instance.
+
+ function wrapMethod(name) {
+ var original = document[name];
+ Document.prototype[name] = function() {
+ return wrap(original.apply(this.impl, arguments));
+ };
+ }
+
+ [
+ 'createComment',
+ 'createDocumentFragment',
+ 'createElement',
+ 'createElementNS',
+ 'createEvent',
+ 'createEventNS',
+ 'createRange',
+ 'createTextNode',
+ 'getElementById'
+ ].forEach(wrapMethod);
+
+ var originalAdoptNode = document.adoptNode;
+
+ function adoptNodeNoRemove(node, doc) {
+ originalAdoptNode.call(doc.impl, unwrap(node));
+ adoptSubtree(node, doc);
+ }
+
+ function adoptSubtree(node, doc) {
+ if (node.shadowRoot)
+ doc.adoptNode(node.shadowRoot);
+ if (node instanceof ShadowRoot)
+ adoptOlderShadowRoots(node, doc);
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ adoptSubtree(child, doc);
+ }
+ }
+
+ function adoptOlderShadowRoots(shadowRoot, doc) {
+ var oldShadowRoot = shadowRoot.olderShadowRoot;
+ if (oldShadowRoot)
+ doc.adoptNode(oldShadowRoot);
+ }
+
+ var originalImportNode = document.importNode;
+ var originalGetSelection = document.getSelection;
+
+ mixin(Document.prototype, {
+ adoptNode: function(node) {
+ if (node.parentNode)
+ node.parentNode.removeChild(node);
+ adoptNodeNoRemove(node, this);
+ return node;
+ },
+ elementFromPoint: function(x, y) {
+ return elementFromPoint(this, this, x, y);
+ },
+ importNode: function(node, deep) {
+ // We need to manually walk the tree to ensure we do not include rendered
+ // shadow trees.
+ var clone = wrap(originalImportNode.call(this.impl, unwrap(node), false));
+ if (deep) {
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ clone.appendChild(this.importNode(child, true));
+ }
+ }
+ return clone;
+ },
+ getSelection: function() {
+ renderAllPending();
+ return new Selection(originalGetSelection.call(unwrap(this)));
+ }
+ });
+
+ if (document.registerElement) {
+ var originalRegisterElement = document.registerElement;
+ Document.prototype.registerElement = function(tagName, object) {
+ var prototype = object.prototype;
+
+ // If we already used the object as a prototype for another custom
+ // element.
+ if (scope.nativePrototypeTable.get(prototype)) {
+ // TODO(arv): DOMException
+ throw new Error('NotSupportedError');
+ }
+
+ // Find first object on the prototype chain that already have a native
+ // prototype. Keep track of all the objects before that so we can create
+ // a similar structure for the native case.
+ var proto = Object.getPrototypeOf(prototype);
+ var nativePrototype;
+ var prototypes = [];
+ while (proto) {
+ nativePrototype = scope.nativePrototypeTable.get(proto);
+ if (nativePrototype)
+ break;
+ prototypes.push(proto);
+ proto = Object.getPrototypeOf(proto);
+ }
+
+ if (!nativePrototype) {
+ // TODO(arv): DOMException
+ throw new Error('NotSupportedError');
+ }
+
+ // This works by creating a new prototype object that is empty, but has
+ // the native prototype as its proto. The original prototype object
+ // passed into register is used as the wrapper prototype.
+
+ var newPrototype = Object.create(nativePrototype);
+ for (var i = prototypes.length - 1; i >= 0; i--) {
+ newPrototype = Object.create(newPrototype);
+ }
+
+ // Add callbacks if present.
+ // Names are taken from:
+ // https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/bindings/v8/CustomElementConstructorBuilder.cpp&sq=package:chromium&type=cs&l=156
+ // and not from the spec since the spec is out of date.
+ [
+ 'createdCallback',
+ 'attachedCallback',
+ 'detachedCallback',
+ 'attributeChangedCallback',
+ ].forEach(function(name) {
+ var f = prototype[name];
+ if (!f)
+ return;
+ newPrototype[name] = function() {
+ // if this element has been wrapped prior to registration,
+ // the wrapper is stale; in this case rewrap
+ if (!(wrap(this) instanceof CustomElementConstructor)) {
+ rewrap(this);
+ }
+ f.apply(wrap(this), arguments);
+ };
+ });
+
+ var p = {prototype: newPrototype};
+ if (object.extends)
+ p.extends = object.extends;
+
+ function CustomElementConstructor(node) {
+ if (!node) {
+ if (object.extends) {
+ return document.createElement(object.extends, tagName);
+ } else {
+ return document.createElement(tagName);
+ }
+ }
+ this.impl = node;
+ }
+ CustomElementConstructor.prototype = prototype;
+ CustomElementConstructor.prototype.constructor = CustomElementConstructor;
+
+ scope.constructorTable.set(newPrototype, CustomElementConstructor);
+ scope.nativePrototypeTable.set(prototype, newPrototype);
+
+ // registration is synchronous so do it last
+ var nativeConstructor = originalRegisterElement.call(unwrap(this),
+ tagName, p);
+ return CustomElementConstructor;
+ };
+
+ forwardMethodsToWrapper([
+ window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
+ ], [
+ 'registerElement',
+ ]);
+ }
+
+ // We also override some of the methods on document.body and document.head
+ // for convenience.
+ forwardMethodsToWrapper([
+ window.HTMLBodyElement,
+ window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
+ window.HTMLHeadElement,
+ window.HTMLHtmlElement,
+ ], [
+ 'appendChild',
+ 'compareDocumentPosition',
+ 'contains',
+ 'getElementsByClassName',
+ 'getElementsByTagName',
+ 'getElementsByTagNameNS',
+ 'insertBefore',
+ 'querySelector',
+ 'querySelectorAll',
+ 'removeChild',
+ 'replaceChild',
+ ].concat(matchesNames));
+
+ forwardMethodsToWrapper([
+ window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
+ ], [
+ 'adoptNode',
+ 'importNode',
+ 'contains',
+ 'createComment',
+ 'createDocumentFragment',
+ 'createElement',
+ 'createElementNS',
+ 'createEvent',
+ 'createEventNS',
+ 'createRange',
+ 'createTextNode',
+ 'elementFromPoint',
+ 'getElementById',
+ 'getSelection',
+ ]);
+
+ mixin(Document.prototype, GetElementsByInterface);
+ mixin(Document.prototype, ParentNodeInterface);
+ mixin(Document.prototype, SelectorsInterface);
+
+ mixin(Document.prototype, {
+ get implementation() {
+ var implementation = implementationTable.get(this);
+ if (implementation)
+ return implementation;
+ implementation =
+ new DOMImplementation(unwrap(this).implementation);
+ implementationTable.set(this, implementation);
+ return implementation;
+ }
+ });
+
+ registerWrapper(window.Document, Document,
+ document.implementation.createHTMLDocument(''));
+
+ // Both WebKit and Gecko uses HTMLDocument for document. HTML5/DOM only has
+ // one Document interface and IE implements the standard correctly.
+ if (window.HTMLDocument)
+ registerWrapper(window.HTMLDocument, Document);
+
+ wrapEventTargetMethods([
+ window.HTMLBodyElement,
+ window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
+ window.HTMLHeadElement,
+ ]);
+
+ function DOMImplementation(impl) {
+ this.impl = impl;
+ }
+
+ function wrapImplMethod(constructor, name) {
+ var original = document.implementation[name];
+ constructor.prototype[name] = function() {
+ return wrap(original.apply(this.impl, arguments));
+ };
+ }
+
+ function forwardImplMethod(constructor, name) {
+ var original = document.implementation[name];
+ constructor.prototype[name] = function() {
+ return original.apply(this.impl, arguments);
+ };
+ }
+
+ wrapImplMethod(DOMImplementation, 'createDocumentType');
+ wrapImplMethod(DOMImplementation, 'createDocument');
+ wrapImplMethod(DOMImplementation, 'createHTMLDocument');
+ forwardImplMethod(DOMImplementation, 'hasFeature');
+
+ registerWrapper(window.DOMImplementation, DOMImplementation);
+
+ forwardMethodsToWrapper([
+ window.DOMImplementation,
+ ], [
+ 'createDocumentType',
+ 'createDocument',
+ 'createHTMLDocument',
+ 'hasFeature',
+ ]);
+
+ scope.adoptNodeNoRemove = adoptNodeNoRemove;
+ scope.wrappers.DOMImplementation = DOMImplementation;
+ scope.wrappers.Document = Document;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var EventTarget = scope.wrappers.EventTarget;
+ var Selection = scope.wrappers.Selection;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var renderAllPending = scope.renderAllPending;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+
+ var OriginalWindow = window.Window;
+ var originalGetComputedStyle = window.getComputedStyle;
+ var originalGetSelection = window.getSelection;
+
+ function Window(impl) {
+ EventTarget.call(this, impl);
+ }
+ Window.prototype = Object.create(EventTarget.prototype);
+
+ OriginalWindow.prototype.getComputedStyle = function(el, pseudo) {
+ return wrap(this || window).getComputedStyle(unwrapIfNeeded(el), pseudo);
+ };
+
+ OriginalWindow.prototype.getSelection = function() {
+ return wrap(this || window).getSelection();
+ };
+
+ // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065
+ delete window.getComputedStyle;
+ delete window.getSelection;
+
+ ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(
+ function(name) {
+ OriginalWindow.prototype[name] = function() {
+ var w = wrap(this || window);
+ return w[name].apply(w, arguments);
+ };
+
+ // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065
+ delete window[name];
+ });
+
+ mixin(Window.prototype, {
+ getComputedStyle: function(el, pseudo) {
+ renderAllPending();
+ return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el),
+ pseudo);
+ },
+ getSelection: function() {
+ renderAllPending();
+ return new Selection(originalGetSelection.call(unwrap(this)));
+ },
+ });
+
+ registerWrapper(OriginalWindow, Window);
+
+ scope.wrappers.Window = Window;
+
+})(window.ShadowDOMPolyfill);
+
+// Copyright 2013 The Polymer Authors. All rights reserved.
+// Use of this source code is goverened by a BSD-style
+// license that can be found in the LICENSE file.
+
+(function(scope) {
+ 'use strict';
+
+ var isWrapperFor = scope.isWrapperFor;
+
+ // This is a list of the elements we currently override the global constructor
+ // for.
+ var elements = {
+ 'a': 'HTMLAnchorElement',
+
+ // Do not create an applet element by default since it shows a warning in
+ // IE.
+ // https://github.com/Polymer/polymer/issues/217
+ // 'applet': 'HTMLAppletElement',
+
+ 'area': 'HTMLAreaElement',
+ 'br': 'HTMLBRElement',
+ 'base': 'HTMLBaseElement',
+ 'body': 'HTMLBodyElement',
+ 'button': 'HTMLButtonElement',
+ // 'command': 'HTMLCommandElement', // Not fully implemented in Gecko.
+ 'dl': 'HTMLDListElement',
+ 'datalist': 'HTMLDataListElement',
+ 'data': 'HTMLDataElement',
+ 'dir': 'HTMLDirectoryElement',
+ 'div': 'HTMLDivElement',
+ 'embed': 'HTMLEmbedElement',
+ 'fieldset': 'HTMLFieldSetElement',
+ 'font': 'HTMLFontElement',
+ 'form': 'HTMLFormElement',
+ 'frame': 'HTMLFrameElement',
+ 'frameset': 'HTMLFrameSetElement',
+ 'hr': 'HTMLHRElement',
+ 'head': 'HTMLHeadElement',
+ 'h1': 'HTMLHeadingElement',
+ 'html': 'HTMLHtmlElement',
+ 'iframe': 'HTMLIFrameElement',
+ 'input': 'HTMLInputElement',
+ 'li': 'HTMLLIElement',
+ 'label': 'HTMLLabelElement',
+ 'legend': 'HTMLLegendElement',
+ 'link': 'HTMLLinkElement',
+ 'map': 'HTMLMapElement',
+ 'marquee': 'HTMLMarqueeElement',
+ 'menu': 'HTMLMenuElement',
+ 'menuitem': 'HTMLMenuItemElement',
+ 'meta': 'HTMLMetaElement',
+ 'meter': 'HTMLMeterElement',
+ 'del': 'HTMLModElement',
+ 'ol': 'HTMLOListElement',
+ 'object': 'HTMLObjectElement',
+ 'optgroup': 'HTMLOptGroupElement',
+ 'option': 'HTMLOptionElement',
+ 'output': 'HTMLOutputElement',
+ 'p': 'HTMLParagraphElement',
+ 'param': 'HTMLParamElement',
+ 'pre': 'HTMLPreElement',
+ 'progress': 'HTMLProgressElement',
+ 'q': 'HTMLQuoteElement',
+ 'script': 'HTMLScriptElement',
+ 'select': 'HTMLSelectElement',
+ 'source': 'HTMLSourceElement',
+ 'span': 'HTMLSpanElement',
+ 'style': 'HTMLStyleElement',
+ 'time': 'HTMLTimeElement',
+ 'caption': 'HTMLTableCaptionElement',
+ // WebKit and Moz are wrong:
+ // https://bugs.webkit.org/show_bug.cgi?id=111469
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=848096
+ // 'td': 'HTMLTableCellElement',
+ 'col': 'HTMLTableColElement',
+ 'table': 'HTMLTableElement',
+ 'tr': 'HTMLTableRowElement',
+ 'thead': 'HTMLTableSectionElement',
+ 'tbody': 'HTMLTableSectionElement',
+ 'textarea': 'HTMLTextAreaElement',
+ 'track': 'HTMLTrackElement',
+ 'title': 'HTMLTitleElement',
+ 'ul': 'HTMLUListElement',
+ 'video': 'HTMLVideoElement',
+ };
+
+ function overrideConstructor(tagName) {
+ var nativeConstructorName = elements[tagName];
+ var nativeConstructor = window[nativeConstructorName];
+ if (!nativeConstructor)
+ return;
+ var element = document.createElement(tagName);
+ var wrapperConstructor = element.constructor;
+ window[nativeConstructorName] = wrapperConstructor;
+ }
+
+ Object.keys(elements).forEach(overrideConstructor);
+
+ Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) {
+ window[name] = scope.wrappers[name]
+ });
+
+ // Export for testing.
+ scope.knownElements = elements;
+
+})(window.ShadowDOMPolyfill);
+
+/*
+ * Copyright 2013 The Polymer Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+(function() {
+ var ShadowDOMPolyfill = window.ShadowDOMPolyfill;
+ var wrap = ShadowDOMPolyfill.wrap;
+
+ // patch in prefixed name
+ Object.defineProperties(HTMLElement.prototype, {
+ //TODO(sjmiles): review accessor alias with Arv
+ webkitShadowRoot: {
+ get: function() {
+ return this.shadowRoot;
+ }
+ }
+ });
+
+ // ShadowCSS needs this:
+ window.wrap = window.ShadowDOMPolyfill.wrap;
+ window.unwrap = window.ShadowDOMPolyfill.unwrap;
+
+ //TODO(sjmiles): review method alias with Arv
+ HTMLElement.prototype.webkitCreateShadowRoot =
+ HTMLElement.prototype.createShadowRoot;
+
+ // TODO(jmesserly): we need to wrap document somehow (a dart:html hook?)
+ window.dartExperimentalFixupGetTag = function(originalGetTag) {
+ var NodeList = ShadowDOMPolyfill.wrappers.NodeList;
+ var ShadowRoot = ShadowDOMPolyfill.wrappers.ShadowRoot;
+ var unwrapIfNeeded = ShadowDOMPolyfill.unwrapIfNeeded;
+ function getTag(obj) {
+ // TODO(jmesserly): do we still need these?
+ if (obj instanceof NodeList) return 'NodeList';
+ if (obj instanceof ShadowRoot) return 'ShadowRoot';
+ if (window.MutationRecord && (obj instanceof MutationRecord))
+ return 'MutationRecord';
+ if (window.MutationObserver && (obj instanceof MutationObserver))
+ return 'MutationObserver';
+
+ // TODO(jmesserly): this prevents incorrect interaction between ShadowDOM
+ // and dart:html's <template> polyfill. Essentially, ShadowDOM is
+ // polyfilling native template, but our Dart polyfill fails to detect this
+ // because the unwrapped node is an HTMLUnknownElement, leading it to
+ // think the node has no content.
+ if (obj instanceof HTMLTemplateElement) return 'HTMLTemplateElement';
+
+ var unwrapped = unwrapIfNeeded(obj);
+ if (obj !== unwrapped) {
+ // Fix up class names for Firefox.
+ // For some of them (like HTMLFormElement and HTMLInputElement),
+ // the "constructor" property of the unwrapped nodes points at the
+ // same constructor as the wrapper.
+ var ctor = obj.constructor
+ if (ctor === unwrapped.constructor) {
+ var name = ctor._ShadowDOMPolyfill$cacheTag_;
+ if (!name) {
+ name = Object.prototype.toString.call(unwrapped);
+ name = name.substring(8, name.length - 1);
+ ctor._ShadowDOMPolyfill$cacheTag_ = name;
+ }
+ return name;
+ }
+
+ obj = unwrapped;
+ }
+ return originalGetTag(obj);
+ }
+
+ return getTag;
+ };
+})();
+
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+var Platform = {};
+
+/*
+ * Copyright 2012 The Polymer Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+/*
+ This is a limited shim for ShadowDOM css styling.
+ https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles
+
+ The intention here is to support only the styling features which can be
+ relatively simply implemented. The goal is to allow users to avoid the
+ most obvious pitfalls and do so without compromising performance significantly.
+ For ShadowDOM styling that's not covered here, a set of best practices
+ can be provided that should allow users to accomplish more complex styling.
+
+ The following is a list of specific ShadowDOM styling features and a brief
+ discussion of the approach used to shim.
+
+ Shimmed features:
+
+ * @host: ShadowDOM allows styling of the shadowRoot's host element using the
+ @host rule. To shim this feature, the @host styles are reformatted and
+ prefixed with a given scope name and promoted to a document level stylesheet.
+ For example, given a scope name of .foo, a rule like this:
+
+ @host {
+ * {
+ background: red;
+ }
+ }
+
+ becomes:
+
+ .foo {
+ background: red;
+ }
+
+ * encapsultion: Styles defined within ShadowDOM, apply only to
+ dom inside the ShadowDOM. Polymer uses one of two techniques to imlement
+ this feature.
+
+ By default, rules are prefixed with the host element tag name
+ as a descendant selector. This ensures styling does not leak out of the 'top'
+ of the element's ShadowDOM. For example,
+
+ div {
+ font-weight: bold;
+ }
+
+ becomes:
+
+ x-foo div {
+ font-weight: bold;
+ }
+
+ becomes:
+
+
+ Alternatively, if Platform.ShadowCSS.strictStyling is set to true then
+ selectors are scoped by adding an attribute selector suffix to each
+ simple selector that contains the host element tag name. Each element
+ in the element's ShadowDOM template is also given the scope attribute.
+ Thus, these rules match only elements that have the scope attribute.
+ For example, given a scope name of x-foo, a rule like this:
+
+ div {
+ font-weight: bold;
+ }
+
+ becomes:
+
+ div[x-foo] {
+ font-weight: bold;
+ }
+
+ Note that elements that are dynamically added to a scope must have the scope
+ selector added to them manually.
+
+ * ::pseudo: These rules are converted to rules that take advantage of the
+ pseudo attribute. For example, a shadowRoot like this inside an x-foo
+
+ <div pseudo="x-special">Special</div>
+
+ with a rule like this:
+
+ x-foo::x-special { ... }
+
+ becomes:
+
+ x-foo [pseudo=x-special] { ... }
+
+ * ::part(): These rules are converted to rules that take advantage of the
+ part attribute. For example, a shadowRoot like this inside an x-foo
+
+ <div part="special">Special</div>
+
+ with a rule like this:
+
+ x-foo::part(special) { ... }
+
+ becomes:
+
+ x-foo [part=special] { ... }
+
+ Unaddressed ShadowDOM styling features:
+
+ * upper/lower bound encapsulation: Styles which are defined outside a
+ shadowRoot should not cross the ShadowDOM boundary and should not apply
+ inside a shadowRoot.
+
+ This styling behavior is not emulated. Some possible ways to do this that
+ were rejected due to complexity and/or performance concerns include: (1) reset
+ every possible property for every possible selector for a given scope name;
+ (2) re-implement css in javascript.
+
+ As an alternative, users should make sure to use selectors
+ specific to the scope in which they are working.
+
+ * ::distributed: This behavior is not emulated. It's often not necessary
+ to style the contents of a specific insertion point and instead, descendants
+ of the host element can be styled selectively. Users can also create an
+ extra node around an insertion point and style that node's contents
+ via descendent selectors. For example, with a shadowRoot like this:
+
+ <style>
+ content::-webkit-distributed(div) {
+ background: red;
+ }
+ </style>
+ <content></content>
+
+ could become:
+
+ <style>
+ / *@polyfill .content-container div * /
+ content::-webkit-distributed(div) {
+ background: red;
+ }
+ </style>
+ <div class="content-container">
+ <content></content>
+ </div>
+
+ Note the use of @polyfill in the comment above a ShadowDOM specific style
+ declaration. This is a directive to the styling shim to use the selector
+ in comments in lieu of the next selector when running under polyfill.
+*/
+(function(scope) {
+
+var loader = scope.loader;
+
+var ShadowCSS = {
+ strictStyling: false,
+ registry: {},
+ // Shim styles for a given root associated with a name and extendsName
+ // 1. cache root styles by name
+ // 2. optionally tag root nodes with scope name
+ // 3. shim polyfill directives /* @polyfill */ and /* @polyfill-rule */
+ // 4. shim @host and scoping
+ shimStyling: function(root, name, extendsName) {
+ var typeExtension = this.isTypeExtension(extendsName);
+ // use caching to make working with styles nodes easier and to facilitate
+ // lookup of extendee
+ var def = this.registerDefinition(root, name, extendsName);
+ // find styles and apply shimming...
+ if (this.strictStyling) {
+ this.applyScopeToContent(root, name);
+ }
+ var cssText = this.stylesToShimmedCssText(def.rootStyles, def.scopeStyles,
+ name, typeExtension);
+ // provide shimmedStyle for user extensibility
+ def.shimmedStyle = cssTextToStyle(cssText);
+ if (root) {
+ root.shimmedStyle = def.shimmedStyle;
+ }
+ // remove existing style elements
+ for (var i=0, l=def.rootStyles.length, s; (i<l) && (s=def.rootStyles[i]);
+ i++) {
+ s.parentNode.removeChild(s);
+ }
+ // add style to document
+ addCssToDocument(cssText);
+ },
+ // apply @polyfill rules + @host and scope shimming
+ stylesToShimmedCssText: function(rootStyles, scopeStyles, name,
+ typeExtension) {
+ name = name || '';
+ // insert @polyfill and @polyfill-rule rules into style elements
+ // scoping process takes care of shimming these
+ this.insertPolyfillDirectives(rootStyles);
+ this.insertPolyfillRules(rootStyles);
+ var cssText = this.shimAtHost(scopeStyles, name, typeExtension) +
+ this.shimScoping(scopeStyles, name, typeExtension);
+ // note: we only need to do rootStyles since these are unscoped.
+ cssText += this.extractPolyfillUnscopedRules(rootStyles);
+ return cssText;
+ },
+ registerDefinition: function(root, name, extendsName) {
+ var def = this.registry[name] = {
+ root: root,
+ name: name,
+ extendsName: extendsName
+ }
+ var styles = root ? root.querySelectorAll('style') : [];
+ styles = styles ? Array.prototype.slice.call(styles, 0) : [];
+ def.rootStyles = styles;
+ def.scopeStyles = def.rootStyles;
+ var extendee = this.registry[def.extendsName];
+ if (extendee && (!root || root.querySelector('shadow'))) {
+ def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles);
+ }
+ return def;
+ },
+ isTypeExtension: function(extendsName) {
+ return extendsName && extendsName.indexOf('-') < 0;
+ },
+ applyScopeToContent: function(root, name) {
+ if (root) {
+ // add the name attribute to each node in root.
+ Array.prototype.forEach.call(root.querySelectorAll('*'),
+ function(node) {
+ node.setAttribute(name, '');
+ });
+ // and template contents too
+ Array.prototype.forEach.call(root.querySelectorAll('template'),
+ function(template) {
+ this.applyScopeToContent(template.content, name);
+ },
+ this);
+ }
+ },
+ /*
+ * Process styles to convert native ShadowDOM rules that will trip
+ * up the css parser; we rely on decorating the stylesheet with comments.
+ *
+ * For example, we convert this rule:
+ *
+ * (comment start) @polyfill :host menu-item (comment end)
+ * shadow::-webkit-distributed(menu-item) {
+ *
+ * to this:
+ *
+ * scopeName menu-item {
+ *
+ **/
+ insertPolyfillDirectives: function(styles) {
+ if (styles) {
+ Array.prototype.forEach.call(styles, function(s) {
+ s.textContent = this.insertPolyfillDirectivesInCssText(s.textContent);
+ }, this);
+ }
+ },
+ insertPolyfillDirectivesInCssText: function(cssText) {
+ return cssText.replace(cssPolyfillCommentRe, function(match, p1) {
+ // remove end comment delimiter and add block start
+ return p1.slice(0, -2) + '{';
+ });
+ },
+ /*
+ * Process styles to add rules which will only apply under the polyfill
+ *
+ * For example, we convert this rule:
+ *
+ * (comment start) @polyfill-rule :host menu-item {
+ * ... } (comment end)
+ *
+ * to this:
+ *
+ * scopeName menu-item {...}
+ *
+ **/
+ insertPolyfillRules: function(styles) {
+ if (styles) {
+ Array.prototype.forEach.call(styles, function(s) {
+ s.textContent = this.insertPolyfillRulesInCssText(s.textContent);
+ }, this);
+ }
+ },
+ insertPolyfillRulesInCssText: function(cssText) {
+ return cssText.replace(cssPolyfillRuleCommentRe, function(match, p1) {
+ // remove end comment delimiter
+ return p1.slice(0, -1);
+ });
+ },
+ /*
+ * Process styles to add rules which will only apply under the polyfill
+ * and do not process via CSSOM. (CSSOM is destructive to rules on rare
+ * occasions, e.g. -webkit-calc on Safari.)
+ * For example, we convert this rule:
+ *
+ * (comment start) @polyfill-unscoped-rule menu-item {
+ * ... } (comment end)
+ *
+ * to this:
+ *
+ * menu-item {...}
+ *
+ **/
+ extractPolyfillUnscopedRules: function(styles) {
+ var cssText = '';
+ if (styles) {
+ Array.prototype.forEach.call(styles, function(s) {
+ cssText += this.extractPolyfillUnscopedRulesFromCssText(
+ s.textContent) + '\n\n';
+ }, this);
+ }
+ return cssText;
+ },
+ extractPolyfillUnscopedRulesFromCssText: function(cssText) {
+ var r = '', matches;
+ while (matches = cssPolyfillUnscopedRuleCommentRe.exec(cssText)) {
+ r += matches[1].slice(0, -1) + '\n\n';
+ }
+ return r;
+ },
+ // form: @host { .foo { declarations } }
+ // becomes: scopeName.foo { declarations }
+ shimAtHost: function(styles, name, typeExtension) {
+ if (styles) {
+ return this.convertAtHostStyles(styles, name, typeExtension);
+ }
+ },
+ convertAtHostStyles: function(styles, name, typeExtension) {
+ var cssText = stylesToCssText(styles), self = this;
+ cssText = cssText.replace(hostRuleRe, function(m, p1) {
+ return self.scopeHostCss(p1, name, typeExtension);
+ });
+ cssText = rulesToCss(this.findAtHostRules(cssToRules(cssText),
+ this.makeScopeMatcher(name, typeExtension)));
+ return cssText;
+ },
+ scopeHostCss: function(cssText, name, typeExtension) {
+ var self = this;
+ return cssText.replace(selectorRe, function(m, p1, p2) {
+ return self.scopeHostSelector(p1, name, typeExtension) + ' ' + p2 + '\n\t';
+ });
+ },
+ // supports scopig by name and [is=name] syntax
+ scopeHostSelector: function(selector, name, typeExtension) {
+ var r = [], parts = selector.split(','), is = '[is=' + name + ']';
+ parts.forEach(function(p) {
+ p = p.trim();
+ // selector: *|:scope -> name
+ if (p.match(hostElementRe)) {
+ p = p.replace(hostElementRe, typeExtension ? is + '$1$3' :
+ name + '$1$3');
+ // selector: .foo -> name.foo (OR) [bar] -> name[bar]
+ } else if (p.match(hostFixableRe)) {
+ p = typeExtension ? is + p : name + p;
+ }
+ r.push(p);
+ }, this);
+ return r.join(', ');
+ },
+ // consider styles that do not include component name in the selector to be
+ // unscoped and in need of promotion;
+ // for convenience, also consider keyframe rules this way.
+ findAtHostRules: function(cssRules, matcher) {
+ return Array.prototype.filter.call(cssRules,
+ this.isHostRule.bind(this, matcher));
+ },
+ isHostRule: function(matcher, cssRule) {
+ return (cssRule.selectorText && cssRule.selectorText.match(matcher)) ||
+ (cssRule.cssRules && this.findAtHostRules(cssRule.cssRules, matcher).length) ||
+ (cssRule.type == CSSRule.WEBKIT_KEYFRAMES_RULE);
+ },
+ /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
+ *
+ * .foo {... }
+ *
+ * and converts this to
+ *
+ * scopeName .foo { ... }
+ */
+ shimScoping: function(styles, name, typeExtension) {
+ if (styles) {
+ return this.convertScopedStyles(styles, name, typeExtension);
+ }
+ },
+ convertScopedStyles: function(styles, name, typeExtension) {
+ var cssText = stylesToCssText(styles).replace(hostRuleRe, '');
+ cssText = this.insertPolyfillHostInCssText(cssText);
+ cssText = this.convertColonHost(cssText);
+ cssText = this.convertColonAncestor(cssText);
+ // TODO(sorvell): deprecated, remove
+ cssText = this.convertPseudos(cssText);
+ // TODO(sorvell): deprecated, remove
+ cssText = this.convertParts(cssText);
+ cssText = this.convertCombinators(cssText);
+ var rules = cssToRules(cssText);
+ if (name) {
+ cssText = this.scopeRules(rules, name, typeExtension);
+ }
+ return cssText;
+ },
+ convertPseudos: function(cssText) {
+ return cssText.replace(cssPseudoRe, ' [pseudo=$1]');
+ },
+ convertParts: function(cssText) {
+ return cssText.replace(cssPartRe, ' [part=$1]');
+ },
+ /*
+ * convert a rule like :host(.foo) > .bar { }
+ *
+ * to
+ *
+ * scopeName.foo > .bar
+ */
+ convertColonHost: function(cssText) {
+ return this.convertColonRule(cssText, cssColonHostRe,
+ this.colonHostPartReplacer);
+ },
+ /*
+ * convert a rule like :ancestor(.foo) > .bar { }
+ *
+ * to
+ *
+ * scopeName.foo > .bar, .foo scopeName > .bar { }
+ *
+ * and
+ *
+ * :ancestor(.foo:host) .bar { ... }
+ *
+ * to
+ *
+ * scopeName.foo .bar { ... }
+ */
+ convertColonAncestor: function(cssText) {
+ return this.convertColonRule(cssText, cssColonAncestorRe,
+ this.colonAncestorPartReplacer);
+ },
+ convertColonRule: function(cssText, regExp, partReplacer) {
+ // p1 = :host, p2 = contents of (), p3 rest of rule
+ return cssText.replace(regExp, function(m, p1, p2, p3) {
+ p1 = polyfillHostNoCombinator;
+ if (p2) {
+ var parts = p2.split(','), r = [];
+ for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
+ p = p.trim();
+ r.push(partReplacer(p1, p, p3));
+ }
+ return r.join(',');
+ } else {
+ return p1 + p3;
+ }
+ });
+ },
+ colonAncestorPartReplacer: function(host, part, suffix) {
+ if (part.match(polyfillHost)) {
+ return this.colonHostPartReplacer(host, part, suffix);
+ } else {
+ return host + part + suffix + ', ' + part + ' ' + host + suffix;
+ }
+ },
+ colonHostPartReplacer: function(host, part, suffix) {
+ return host + part.replace(polyfillHost, '') + suffix;
+ },
+ /*
+ * Convert ^ and ^^ combinators by replacing with space.
+ */
+ convertCombinators: function(cssText) {
+ return cssText.replace(/\^\^/g, ' ').replace(/\^/g, ' ');
+ },
+ // change a selector like 'div' to 'name div'
+ scopeRules: function(cssRules, name, typeExtension) {
+ var cssText = '';
+ Array.prototype.forEach.call(cssRules, function(rule) {
+ if (rule.selectorText && (rule.style && rule.style.cssText)) {
+ cssText += this.scopeSelector(rule.selectorText, name, typeExtension,
+ this.strictStyling) + ' {\n\t';
+ cssText += this.propertiesFromRule(rule) + '\n}\n\n';
+ } else if (rule.media) {
+ cssText += '@media ' + rule.media.mediaText + ' {\n';
+ cssText += this.scopeRules(rule.cssRules, name, typeExtension);
+ cssText += '\n}\n\n';
+ } else if (rule.cssText) {
+ cssText += rule.cssText + '\n\n';
+ }
+ }, this);
+ return cssText;
+ },
+ scopeSelector: function(selector, name, typeExtension, strict) {
+ var r = [], parts = selector.split(',');
+ parts.forEach(function(p) {
+ p = p.trim();
+ if (this.selectorNeedsScoping(p, name, typeExtension)) {
+ p = (strict && !p.match(polyfillHostNoCombinator)) ?
+ this.applyStrictSelectorScope(p, name) :
+ this.applySimpleSelectorScope(p, name, typeExtension);
+ }
+ r.push(p);
+ }, this);
+ return r.join(', ');
+ },
+ selectorNeedsScoping: function(selector, name, typeExtension) {
+ var re = this.makeScopeMatcher(name, typeExtension);
+ return !selector.match(re);
+ },
+ makeScopeMatcher: function(name, typeExtension) {
+ var matchScope = typeExtension ? '\\[is=[\'"]?' + name + '[\'"]?\\]' : name;
+ return new RegExp('^(' + matchScope + ')' + selectorReSuffix, 'm');
+ },
+ // scope via name and [is=name]
+ applySimpleSelectorScope: function(selector, name, typeExtension) {
+ var scoper = typeExtension ? '[is=' + name + ']' : name;
+ if (selector.match(polyfillHostRe)) {
+ selector = selector.replace(polyfillHostNoCombinator, scoper);
+ return selector.replace(polyfillHostRe, scoper + ' ');
+ } else {
+ return scoper + ' ' + selector;
+ }
+ },
+ // return a selector with [name] suffix on each simple selector
+ // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name]
+ applyStrictSelectorScope: function(selector, name) {
+ var splits = [' ', '>', '+', '~'],
+ scoped = selector,
+ attrName = '[' + name + ']';
+ splits.forEach(function(sep) {
+ var parts = scoped.split(sep);
+ scoped = parts.map(function(p) {
+ // remove :host since it should be unnecessary
+ var t = p.trim().replace(polyfillHostRe, '');
+ if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) {
+ p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3')
+ }
+ return p;
+ }).join(sep);
+ });
+ return scoped;
+ },
+ insertPolyfillHostInCssText: function(selector) {
+ return selector.replace(hostRe, polyfillHost).replace(colonHostRe,
+ polyfillHost).replace(colonAncestorRe, polyfillAncestor);
+ },
+ propertiesFromRule: function(rule) {
+ // TODO(sorvell): Safari cssom incorrectly removes quotes from the content
+ // property. (https://bugs.webkit.org/show_bug.cgi?id=118045)
+ if (rule.style.content && !rule.style.content.match(/['"]+/)) {
+ return rule.style.cssText.replace(/content:[^;]*;/g, 'content: \'' +
+ rule.style.content + '\';');
+ }
+ return rule.style.cssText;
+ }
+};
+
+var hostRuleRe = /@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,
+ selectorRe = /([^{]*)({[\s\S]*?})/gim,
+ hostElementRe = /(.*)((?:\*)|(?:\:scope))(.*)/,
+ hostFixableRe = /^[.\[:]/,
+ cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
+ cssPolyfillCommentRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim,
+ cssPolyfillRuleCommentRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim,
+ cssPolyfillUnscopedRuleCommentRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim,
+ cssPseudoRe = /::(x-[^\s{,(]*)/gim,
+ cssPartRe = /::part\(([^)]*)\)/gim,
+ // note: :host pre-processed to -shadowcsshost.
+ polyfillHost = '-shadowcsshost',
+ // note: :ancestor pre-processed to -shadowcssancestor.
+ polyfillAncestor = '-shadowcssancestor',
+ parenSuffix = ')(?:\\((' +
+ '(?:\\([^)(]*\\)|[^)(]*)+?' +
+ ')\\))?([^,{]*)';
+ cssColonHostRe = new RegExp('(' + polyfillHost + parenSuffix, 'gim'),
+ cssColonAncestorRe = new RegExp('(' + polyfillAncestor + parenSuffix, 'gim'),
+ selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$',
+ hostRe = /@host/gim,
+ colonHostRe = /\:host/gim,
+ colonAncestorRe = /\:ancestor/gim,
+ /* host name without combinator */
+ polyfillHostNoCombinator = polyfillHost + '-no-combinator',
+ polyfillHostRe = new RegExp(polyfillHost, 'gim');
+ polyfillAncestorRe = new RegExp(polyfillAncestor, 'gim');
+
+function stylesToCssText(styles, preserveComments) {
+ var cssText = '';
+ Array.prototype.forEach.call(styles, function(s) {
+ cssText += s.textContent + '\n\n';
+ });
+ // strip comments for easier processing
+ if (!preserveComments) {
+ cssText = cssText.replace(cssCommentRe, '');
+ }
+ return cssText;
+}
+
+function cssTextToStyle(cssText) {
+ var style = document.createElement('style');
+ style.textContent = cssText;
+ return style;
+}
+
+function cssToRules(cssText) {
+ var style = cssTextToStyle(cssText);
+ document.head.appendChild(style);
+ var rules = style.sheet.cssRules;
+ style.parentNode.removeChild(style);
+ return rules;
+}
+
+function rulesToCss(cssRules) {
+ for (var i=0, css=[]; i < cssRules.length; i++) {
+ css.push(cssRules[i].cssText);
+ }
+ return css.join('\n\n');
+}
+
+function addCssToDocument(cssText) {
+ if (cssText) {
+ getSheet().appendChild(document.createTextNode(cssText));
+ }
+}
+
+var sheet;
+function getSheet() {
+ if (!sheet) {
+ sheet = document.createElement("style");
+ sheet.setAttribute('ShadowCSSShim', '');
+ sheet.shadowCssShim = true;
+ }
+ return sheet;
+}
+
+// add polyfill stylesheet to document
+if (window.ShadowDOMPolyfill) {
+ addCssToDocument('style { display: none !important; }\n');
+ var doc = wrap(document);
+ var head = doc.querySelector('head');
+ head.insertBefore(getSheet(), head.childNodes[0]);
+
+ document.addEventListener('DOMContentLoaded', function() {
+ if (window.HTMLImports && !HTMLImports.useNative) {
+ HTMLImports.importer.preloadSelectors +=
+ ', link[rel=stylesheet]:not([nopolyfill])';
+ HTMLImports.parser.parseGeneric = function(elt) {
+ if (elt.shadowCssShim) {
+ return;
+ }
+ var style = elt;
+ if (!elt.hasAttribute('nopolyfill')) {
+ if (elt.__resource) {
+ style = elt.ownerDocument.createElement('style');
+ style.textContent = Platform.loader.resolveUrlsInCssText(
+ elt.__resource, elt.href);
+ // remove links from main document
+ if (elt.ownerDocument === doc) {
+ elt.parentNode.removeChild(elt);
+ }
+ } else {
+ Platform.loader.resolveUrlsInStyle(style);
+ }
+ var styles = [style];
+ style.textContent = ShadowCSS.stylesToShimmedCssText(styles, styles);
+ style.shadowCssShim = true;
+ }
+ // place in document
+ if (style.parentNode !== head) {
+ head.appendChild(style);
+ }
+ }
+ }
+ });
+}
+
+// exports
+scope.ShadowCSS = ShadowCSS;
+
+})(window.Platform);
+}

Powered by Google App Engine
This is Rietveld 408576698