| 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 '&';
|
| + case '<':
|
| + return '<';
|
| + case '>':
|
| + return '>';
|
| + case '"':
|
| + return '"'
|
| + case '\u00A0':
|
| + return ' ';
|
| + }
|
| + }
|
| +
|
| + 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);
|
| +}
|
|
|