Index: runtime/bin/vmservice/client/deployed/web/packages/web_components/platform.concat.js |
diff --git a/runtime/bin/vmservice/client/deployed/web/packages/web_components/platform.concat.js b/runtime/bin/vmservice/client/deployed/web/packages/web_components/platform.concat.js |
deleted file mode 100644 |
index 9fa19b5c3d0c77a98d5e5a12c59c39c79628b508..0000000000000000000000000000000000000000 |
--- a/runtime/bin/vmservice/client/deployed/web/packages/web_components/platform.concat.js |
+++ /dev/null |
@@ -1,17703 +0,0 @@ |
-/* |
- * 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 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'; |
- |
- // 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 = {}; |
- var arr = []; |
- Object.observe(test, callback); |
- Array.observe(arr, callback); |
- test.id = 1; |
- test.id = 2; |
- delete test.id; |
- arr.push(1, 2); |
- arr.length = 0; |
- |
- Object.deliverChangeRecords(callback); |
- if (records.length !== 5) |
- return false; |
- |
- if (records[0].type != 'add' || |
- records[1].type != 'update' || |
- records[2].type != 'delete' || |
- records[3].type != 'splice' || |
- records[4].type != 'splice') { |
- return false; |
- } |
- |
- Object.unobserve(test, callback); |
- Array.unobserve(arr, 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 (!isObject(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 = {}; |
- |
- 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 (!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() { |
- 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 scheduledReset() { |
- resetScheduled = false; |
- if (!resetNeeded) |
- return; |
- |
- reset(); |
- } |
- |
- function scheduleReset() { |
- if (resetScheduled) |
- return; |
- |
- resetNeeded = true; |
- resetScheduled = true; |
- runEOM(scheduledReset); |
- } |
- |
- function callback() { |
- reset(); |
- |
- var observer; |
- |
- for (var id in observers) { |
- observer = observers[id]; |
- if (!observer || observer.state_ != OPENED) |
- continue; |
- |
- observer.check_(); |
- } |
- } |
- |
- 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 = hasObserve && (function() { |
- try { |
- eval('%RunMicrotasks()'); |
- return true; |
- } catch (ex) { |
- return false; |
- } |
- })(); |
- |
- global.Platform = global.Platform || {}; |
- |
- global.Platform.performMicrotaskCheckpoint = function() { |
- if (runningMicrotaskCheckpoint) |
- return; |
- |
- if (hasDebugForceFullDelivery) { |
- eval('%RunMicrotasks()'); |
- 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 = { |
- add: true, |
- update: true, |
- delete: 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('update', 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 == 'update') |
- continue; |
- |
- if (record.type == 'add') { |
- 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 'splice': |
- mergeSplice(splices, record.index, record.removed.slice(), record.addedCount); |
- break; |
- case 'add': |
- case 'update': |
- case 'delete': |
- 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; |
-})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window); |
- |
-// prepoulate window.Platform.flags for default controls |
-window.Platform = window.Platform || {}; |
-// prepopulate window.logFlags if necessary |
-window.logFlags = window.logFlags || {}; |
-// process flags |
-(function(scope){ |
- // import |
- var flags = scope.flags || {}; |
- // populate flags from location |
- location.search.slice(1).split('&').forEach(function(o) { |
- o = o.split('='); |
- o[0] && (flags[o[0]] = o[1] || true); |
- }); |
- var entryPoint = document.currentScript || |
- document.querySelector('script[src*="platform.js"]'); |
- if (entryPoint) { |
- var a = entryPoint.attributes; |
- for (var i = 0, n; i < a.length; i++) { |
- n = a[i]; |
- if (n.name !== 'src') { |
- flags[n.name] = n.value || true; |
- } |
- } |
- } |
- if (flags.log) { |
- flags.log.split(',').forEach(function(f) { |
- window.logFlags[f] = true; |
- }); |
- } |
- // If any of these flags match 'native', then force native ShadowDOM; any |
- // other truthy value, or failure to detect native |
- // ShadowDOM, results in polyfill |
- flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill; |
- if (flags.shadow === 'native') { |
- flags.shadow = false; |
- } else { |
- flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot; |
- } |
- |
- if (flags.shadow && document.querySelectorAll('script').length > 1) { |
- console.warn('platform.js is not the first script on the page. ' + |
- 'See http://www.polymer-project.org/docs/start/platform.html#setup ' + |
- 'for details.'); |
- } |
- |
- // CustomElements polyfill flag |
- if (flags.register) { |
- window.CustomElements = window.CustomElements || {flags: {}}; |
- window.CustomElements.flags.register = flags.register; |
- } |
- |
- if (flags.imports) { |
- window.HTMLImports = window.HTMLImports || {flags: {}}; |
- window.HTMLImports.flags.imports = flags.imports; |
- } |
- |
- // export |
- scope.flags = flags; |
-})(Platform); |
- |
-// select ShadowDOM impl |
-if (Platform.flags.shadow) { |
- |
-// 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 |
- }); |
- // Set it again. Some VMs optimizes objects that are used as prototypes. |
- wrapperConstructor.prototype = wrapperPrototype; |
- } |
- |
- 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); |
- } |
- var p = Object.create(superWrapperConstructor.prototype); |
- p.constructor = GeneratedWrapper; |
- GeneratedWrapper.prototype = p; |
- |
- 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 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'; |
- |
- /** |
- * A tree scope represents the root of a tree. All nodes in a tree point to |
- * the same TreeScope object. The tree scope of a node get set the first time |
- * it is accessed or when a node is added or remove to a tree. |
- * @constructor |
- */ |
- function TreeScope(root, parent) { |
- this.root = root; |
- this.parent = parent; |
- } |
- |
- TreeScope.prototype = { |
- get renderer() { |
- if (this.root instanceof scope.wrappers.ShadowRoot) { |
- return scope.getRendererForHost(this.root.host); |
- } |
- return null; |
- }, |
- |
- contains: function(treeScope) { |
- for (; treeScope; treeScope = treeScope.parent) { |
- if (treeScope === this) |
- return true; |
- } |
- return false; |
- } |
- }; |
- |
- function setTreeScope(node, treeScope) { |
- if (node.treeScope_ !== treeScope) { |
- node.treeScope_ = treeScope; |
- for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) { |
- sr.treeScope_.parent = treeScope; |
- } |
- for (var child = node.firstChild; child; child = child.nextSibling) { |
- setTreeScope(child, treeScope); |
- } |
- } |
- } |
- |
- function getTreeScope(node) { |
- if (node.treeScope_) |
- return node.treeScope_; |
- var parent = node.parentNode; |
- var treeScope; |
- if (parent) |
- treeScope = getTreeScope(parent); |
- else |
- treeScope = new TreeScope(node, null); |
- return node.treeScope_ = treeScope; |
- } |
- |
- scope.TreeScope = TreeScope; |
- scope.getTreeScope = getTreeScope; |
- scope.setTreeScope = setTreeScope; |
- |
-})(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 getTreeScope = scope.getTreeScope; |
- 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 inSameTree(a, b) { |
- return getTreeScope(a) === getTreeScope(b); |
- } |
- |
- function dispatchOriginalEvent(originalEvent) { |
- // Make sure this event is only dispatched once. |
- if (handledEventsTable.get(originalEvent)) |
- return; |
- handledEventsTable.set(originalEvent, true); |
- dispatchEvent(wrap(originalEvent), wrap(originalEvent.target)); |
- } |
- |
- function isLoadLikeEvent(event) { |
- switch (event.type) { |
- case 'beforeunload': |
- case 'load': |
- case 'unload': |
- return true; |
- } |
- return false; |
- } |
- |
- 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 (eventPath.length === 2 && |
- eventPath[0].target instanceof wrappers.Document && |
- isLoadLikeEvent(event)) { |
- 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); |
- var unwrappedRelatedTarget = originalEvent.relatedTarget; |
- |
- // 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 (unwrappedRelatedTarget) { |
- // In IE we can get objects that are not EventTargets at this point. |
- // Safari does not have an EventTarget interface so revert to checking |
- // for addEventListener as an approximation. |
- if (unwrappedRelatedTarget instanceof Object && |
- unwrappedRelatedTarget.addEventListener) { |
- var relatedTarget = wrap(unwrappedRelatedTarget); |
- |
- var adjusted = adjustRelatedTarget(currentTarget, relatedTarget); |
- if (adjusted === target) |
- return true; |
- } else { |
- adjusted = null; |
- } |
- 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) { |
- var impl = type; |
- if (!OriginalBeforeUnloadEvent && impl.type === 'beforeunload') |
- return new BeforeUnloadEvent(impl); |
- this.impl = impl; |
- } 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 = getTreeScope(currentTargetTable.get(this)); |
- |
- for (var i = 0; i <= lastIndex; i++) { |
- var currentTarget = eventPath[i].currentTarget; |
- var currentRoot = getTreeScope(currentTarget); |
- if (currentRoot.contains(baseRoot) && |
- // 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() { |
- var relatedTarget = relatedTargetTable.get(this); |
- // relatedTarget can be null. |
- if (relatedTarget !== undefined) |
- return relatedTarget; |
- return 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'); |
- } |
- |
- // Safari 7 does not yet have BeforeUnloadEvent. |
- // https://bugs.webkit.org/show_bug.cgi?id=120849 |
- var OriginalBeforeUnloadEvent = window.BeforeUnloadEvent; |
- |
- function BeforeUnloadEvent(impl) { |
- Event.call(this, impl); |
- } |
- BeforeUnloadEvent.prototype = Object.create(Event.prototype); |
- mixin(BeforeUnloadEvent.prototype, { |
- get returnValue() { |
- return this.impl.returnValue; |
- }, |
- set returnValue(v) { |
- this.impl.returnValue = v; |
- } |
- }); |
- |
- if (OriginalBeforeUnloadEvent) |
- registerWrapper(OriginalBeforeUnloadEvent, BeforeUnloadEvent); |
- |
- 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 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'; |
- |
- // TODO(arv): Implement. |
- |
- scope.wrapHTMLCollection = scope.wrapNodeList; |
- scope.wrappers.HTMLCollection = scope.wrappers.NodeList; |
- |
-})(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 TreeScope = scope.TreeScope; |
- var assert = scope.assert; |
- var defineWrapGetter = scope.defineWrapGetter; |
- var enqueueMutation = scope.enqueueMutation; |
- var getTreeScope = scope.getTreeScope; |
- var isWrapper = scope.isWrapper; |
- var mixin = scope.mixin; |
- var registerTransientObservers = scope.registerTransientObservers; |
- var registerWrapper = scope.registerWrapper; |
- var setTreeScope = scope.setTreeScope; |
- var unwrap = scope.unwrap; |
- var unwrapIfNeeded = scope.unwrapIfNeeded; |
- var wrap = scope.wrap; |
- var wrapIfNeeded = scope.wrapIfNeeded; |
- var wrappers = scope.wrappers; |
- |
- 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, treeScope) { |
- setTreeScope(node, treeScope); |
- node.nodeIsInserted_(); |
- } |
- |
- function nodesWereAdded(nodes, parent) { |
- var treeScope = getTreeScope(parent); |
- for (var i = 0; i < nodes.length; i++) { |
- nodeWasAdded(nodes[i], treeScope); |
- } |
- } |
- |
- // http://dom.spec.whatwg.org/#node-is-removed |
- function nodeWasRemoved(node) { |
- setTreeScope(node, new TreeScope(node, null)); |
- } |
- |
- function nodesWereRemoved(nodes) { |
- for (var i = 0; i < nodes.length; i++) { |
- nodeWasRemoved(nodes[i]); |
- } |
- } |
- |
- 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 originalImportNode = document.importNode; |
- var originalCloneNode = window.Node.prototype.cloneNode; |
- |
- function cloneNode(node, deep, opt_doc) { |
- var clone; |
- if (opt_doc) |
- clone = wrap(originalImportNode.call(opt_doc, node.impl, false)); |
- else |
- clone = wrap(originalCloneNode.call(node.impl, false)); |
- |
- if (deep) { |
- for (var child = node.firstChild; child; child = child.nextSibling) { |
- clone.appendChild(cloneNode(child, true, opt_doc)); |
- } |
- |
- if (node instanceof wrappers.HTMLTemplateElement) { |
- var cloneContent = clone.content; |
- for (var child = node.content.firstChild; |
- child; |
- child = child.nextSibling) { |
- cloneContent.appendChild(cloneNode(child, true, opt_doc)); |
- } |
- } |
- } |
- // TODO(arv): Some HTML elements also clone other data like value. |
- return clone; |
- } |
- |
- function contains(self, child) { |
- if (!child || getTreeScope(self) !== getTreeScope(child)) |
- return false; |
- |
- for (var node = child; node; node = node.parentNode) { |
- if (node === self) |
- return true; |
- } |
- return false; |
- } |
- |
- 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; |
- |
- this.treeScope_ = 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, this); |
- |
- 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, this); |
- |
- 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, this); |
- }, |
- |
- 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) { |
- return cloneNode(this, deep); |
- }, |
- |
- contains: function(child) { |
- return contains(this, wrapIfNeeded(child)); |
- }, |
- |
- compareDocumentPosition: function(otherNode) { |
- // This only wraps, it therefore only operates on the composed DOM and not |
- // the logical DOM. |
- return originalCompareDocumentPosition.call(this.impl, |
- unwrapIfNeeded(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.cloneNode = cloneNode; |
- 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; |
- }, |
- |
- remove: function() { |
- var p = this.parentNode; |
- if (p) |
- p.removeChild(this); |
- } |
- }; |
- |
- 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; |
- var wrappers = scope.wrappers; |
- |
- ///////////////////////////////////////////////////////////////////////////// |
- // 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) { |
- if (node instanceof wrappers.HTMLTemplateElement) |
- node = node.content; |
- |
- 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; |
- var OriginalHTMLTemplateElement = window.HTMLTemplateElement; |
- |
- function HTMLElement(node) { |
- Element.call(this, node); |
- } |
- HTMLElement.prototype = Object.create(Element.prototype); |
- mixin(HTMLElement.prototype, { |
- get innerHTML() { |
- 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()) { |
- if (this instanceof wrappers.HTMLTemplateElement) |
- setInnerHTML(this.content, value); |
- else |
- setInnerHTML(this, value, this.tagName); |
- |
- // If we have a non native template element we need to handle this |
- // manually since setting impl.innerHTML would add the html as direct |
- // children and not be moved over to the content fragment. |
- } else if (!OriginalHTMLTemplateElement && |
- this instanceof wrappers.HTMLTemplateElement) { |
- setInnerHTML(this.content, value); |
- } else { |
- this.impl.innerHTML = value; |
- } |
- |
- var addedNodes = snapshotNodeList(this.childNodes); |
- |
- enqueueMutation(this, 'childList', { |
- addedNodes: addedNodes, |
- removedNodes: removedNodes |
- }); |
- |
- nodesWereRemoved(removedNodes); |
- nodesWereAdded(addedNodes, this); |
- }, |
- |
- 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 mixin = scope.mixin; |
- var registerWrapper = scope.registerWrapper; |
- 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); |
- }, |
- |
- // 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 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 HTMLElement = scope.wrappers.HTMLElement; |
- var mixin = scope.mixin; |
- var registerWrapper = scope.registerWrapper; |
- var unwrap = scope.unwrap; |
- var wrap = scope.wrap; |
- |
- var OriginalHTMLSelectElement = window.HTMLSelectElement; |
- |
- function HTMLSelectElement(node) { |
- HTMLElement.call(this, node); |
- } |
- HTMLSelectElement.prototype = Object.create(HTMLElement.prototype); |
- mixin(HTMLSelectElement.prototype, { |
- add: function(element, before) { |
- if (typeof before === 'object') // also includes null |
- before = unwrap(before); |
- unwrap(this).add(unwrap(element), before); |
- }, |
- |
- remove: function(indexOrNode) { |
- // Spec only allows index but implementations allow index or node. |
- // remove() is also allowed which is same as remove(undefined) |
- if (indexOrNode === undefined) { |
- HTMLElement.prototype.remove.call(this); |
- return; |
- } |
- |
- if (typeof indexOrNode === 'object') |
- indexOrNode = unwrap(indexOrNode); |
- |
- unwrap(this).remove(indexOrNode); |
- }, |
- |
- get form() { |
- return wrap(unwrap(this).form); |
- } |
- }); |
- |
- registerWrapper(OriginalHTMLSelectElement, HTMLSelectElement, |
- document.createElement('select')); |
- |
- scope.wrappers.HTMLSelectElement = HTMLSelectElement; |
-})(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 HTMLElement = scope.wrappers.HTMLElement; |
- var mixin = scope.mixin; |
- var registerWrapper = scope.registerWrapper; |
- var unwrap = scope.unwrap; |
- var wrap = scope.wrap; |
- var wrapHTMLCollection = scope.wrapHTMLCollection; |
- |
- var OriginalHTMLTableElement = window.HTMLTableElement; |
- |
- function HTMLTableElement(node) { |
- HTMLElement.call(this, node); |
- } |
- HTMLTableElement.prototype = Object.create(HTMLElement.prototype); |
- mixin(HTMLTableElement.prototype, { |
- get caption() { |
- return wrap(unwrap(this).caption); |
- }, |
- createCaption: function() { |
- return wrap(unwrap(this).createCaption()); |
- }, |
- |
- get tHead() { |
- return wrap(unwrap(this).tHead); |
- }, |
- createTHead: function() { |
- return wrap(unwrap(this).createTHead()); |
- }, |
- |
- createTFoot: function() { |
- return wrap(unwrap(this).createTFoot()); |
- }, |
- get tFoot() { |
- return wrap(unwrap(this).tFoot); |
- }, |
- |
- get tBodies() { |
- return wrapHTMLCollection(unwrap(this).tBodies); |
- }, |
- createTBody: function() { |
- return wrap(unwrap(this).createTBody()); |
- }, |
- |
- get rows() { |
- return wrapHTMLCollection(unwrap(this).rows); |
- }, |
- insertRow: function(index) { |
- return wrap(unwrap(this).insertRow(index)); |
- } |
- }); |
- |
- registerWrapper(OriginalHTMLTableElement, HTMLTableElement, |
- document.createElement('table')); |
- |
- scope.wrappers.HTMLTableElement = HTMLTableElement; |
-})(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 HTMLElement = scope.wrappers.HTMLElement; |
- var mixin = scope.mixin; |
- var registerWrapper = scope.registerWrapper; |
- var wrapHTMLCollection = scope.wrapHTMLCollection; |
- var unwrap = scope.unwrap; |
- var wrap = scope.wrap; |
- |
- var OriginalHTMLTableSectionElement = window.HTMLTableSectionElement; |
- |
- function HTMLTableSectionElement(node) { |
- HTMLElement.call(this, node); |
- } |
- HTMLTableSectionElement.prototype = Object.create(HTMLElement.prototype); |
- mixin(HTMLTableSectionElement.prototype, { |
- get rows() { |
- return wrapHTMLCollection(unwrap(this).rows); |
- }, |
- insertRow: function(index) { |
- return wrap(unwrap(this).insertRow(index)); |
- } |
- }); |
- |
- registerWrapper(OriginalHTMLTableSectionElement, HTMLTableSectionElement, |
- document.createElement('thead')); |
- |
- scope.wrappers.HTMLTableSectionElement = HTMLTableSectionElement; |
-})(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 HTMLElement = scope.wrappers.HTMLElement; |
- var mixin = scope.mixin; |
- var registerWrapper = scope.registerWrapper; |
- var wrapHTMLCollection = scope.wrapHTMLCollection; |
- var unwrap = scope.unwrap; |
- var wrap = scope.wrap; |
- |
- var OriginalHTMLTableRowElement = window.HTMLTableRowElement; |
- |
- function HTMLTableRowElement(node) { |
- HTMLElement.call(this, node); |
- } |
- HTMLTableRowElement.prototype = Object.create(HTMLElement.prototype); |
- mixin(HTMLTableRowElement.prototype, { |
- get cells() { |
- return wrapHTMLCollection(unwrap(this).cells); |
- }, |
- |
- insertCell: function(index) { |
- return wrap(unwrap(this).insertCell(index)); |
- } |
- }); |
- |
- registerWrapper(OriginalHTMLTableRowElement, HTMLTableRowElement, |
- document.createElement('tr')); |
- |
- scope.wrappers.HTMLTableRowElement = HTMLTableRowElement; |
-})(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 TreeScope = scope.TreeScope; |
- var elementFromPoint = scope.elementFromPoint; |
- var getInnerHTML = scope.getInnerHTML; |
- var getTreeScope = scope.getTreeScope; |
- 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); |
- |
- this.treeScope_ = new TreeScope(this, getTreeScope(hostWrapper)); |
- |
- 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 getTreeScope = scope.getTreeScope; |
- 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) { |
- var rv = distributedChildNodesTable.get(insertionPoint); |
- if (!rv) |
- distributedChildNodesTable.set(insertionPoint, rv = []); |
- return rv; |
- } |
- |
- 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() { |
- // TODO(arv): Order these in document order. That way we do not have to |
- // render something twice. |
- for (var i = 0; i < pendingDirtyRenderers.length; i++) { |
- var renderer = pendingDirtyRenderers[i]; |
- var parentRenderer = renderer.parentRenderer; |
- if (parentRenderer && parentRenderer.dirty) |
- continue; |
- renderer.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) { |
- var root = getTreeScope(node).root; |
- if (root instanceof ShadowRoot) |
- return root; |
- 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; |
- }, |
- |
- get parentRenderer() { |
- return getTreeScope(this.host).renderer; |
- }, |
- |
- 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 is handled in HTMLSelectElement.js |
- '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 TreeScope = scope.TreeScope; |
- var cloneNode = scope.cloneNode; |
- 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); |
- this.treeScope_ = new TreeScope(this, null); |
- } |
- 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 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) { |
- return cloneNode(node, deep, this.impl); |
- }, |
- 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, extendsOption; |
- if (object !== undefined) { |
- prototype = object.prototype; |
- extendsOption = object.extends; |
- } |
- |
- if (!prototype) |
- prototype = Object.create(HTMLElement.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 (extendsOption) |
- p.extends = extendsOption; |
- |
- function CustomElementConstructor(node) { |
- if (!node) { |
- if (extendsOption) { |
- return document.createElement(extendsOption, 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 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 unwrap = scope.unwrap; |
- |
- // DataTransfer (Clipboard in old Blink/WebKit) has a single method that |
- // requires wrapping. Since it is only a method we do not need a real wrapper, |
- // we can just override the method. |
- |
- var OriginalDataTransfer = window.DataTransfer || window.Clipboard; |
- var OriginalDataTransferSetDragImage = |
- OriginalDataTransfer.prototype.setDragImage; |
- |
- OriginalDataTransfer.prototype.setDragImage = function(image, x, y) { |
- OriginalDataTransferSetDragImage.call(this, unwrap(image), x, y); |
- }; |
- |
-})(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', |
- 'audio': 'HTMLAudioElement', |
- 'base': 'HTMLBaseElement', |
- 'body': 'HTMLBodyElement', |
- 'br': 'HTMLBRElement', |
- 'button': 'HTMLButtonElement', |
- 'canvas': 'HTMLCanvasElement', |
- 'caption': 'HTMLTableCaptionElement', |
- 'col': 'HTMLTableColElement', |
- // 'command': 'HTMLCommandElement', // Not fully implemented in Gecko. |
- 'content': 'HTMLContentElement', |
- 'data': 'HTMLDataElement', |
- 'datalist': 'HTMLDataListElement', |
- 'del': 'HTMLModElement', |
- 'dir': 'HTMLDirectoryElement', |
- 'div': 'HTMLDivElement', |
- 'dl': 'HTMLDListElement', |
- 'embed': 'HTMLEmbedElement', |
- 'fieldset': 'HTMLFieldSetElement', |
- 'font': 'HTMLFontElement', |
- 'form': 'HTMLFormElement', |
- 'frame': 'HTMLFrameElement', |
- 'frameset': 'HTMLFrameSetElement', |
- 'h1': 'HTMLHeadingElement', |
- 'head': 'HTMLHeadElement', |
- 'hr': 'HTMLHRElement', |
- 'html': 'HTMLHtmlElement', |
- 'iframe': 'HTMLIFrameElement', |
- 'img': 'HTMLImageElement', |
- 'input': 'HTMLInputElement', |
- 'keygen': 'HTMLKeygenElement', |
- 'label': 'HTMLLabelElement', |
- 'legend': 'HTMLLegendElement', |
- 'li': 'HTMLLIElement', |
- 'link': 'HTMLLinkElement', |
- 'map': 'HTMLMapElement', |
- 'marquee': 'HTMLMarqueeElement', |
- 'menu': 'HTMLMenuElement', |
- 'menuitem': 'HTMLMenuItemElement', |
- 'meta': 'HTMLMetaElement', |
- 'meter': 'HTMLMeterElement', |
- 'object': 'HTMLObjectElement', |
- 'ol': 'HTMLOListElement', |
- 'optgroup': 'HTMLOptGroupElement', |
- 'option': 'HTMLOptionElement', |
- 'output': 'HTMLOutputElement', |
- 'p': 'HTMLParagraphElement', |
- 'param': 'HTMLParamElement', |
- 'pre': 'HTMLPreElement', |
- 'progress': 'HTMLProgressElement', |
- 'q': 'HTMLQuoteElement', |
- 'script': 'HTMLScriptElement', |
- 'select': 'HTMLSelectElement', |
- 'shadow': 'HTMLShadowElement', |
- 'source': 'HTMLSourceElement', |
- 'span': 'HTMLSpanElement', |
- 'style': 'HTMLStyleElement', |
- 'table': 'HTMLTableElement', |
- 'tbody': 'HTMLTableSectionElement', |
- // 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', |
- 'template': 'HTMLTemplateElement', |
- 'textarea': 'HTMLTextAreaElement', |
- 'thead': 'HTMLTableSectionElement', |
- 'time': 'HTMLTimeElement', |
- 'title': 'HTMLTitleElement', |
- 'tr': 'HTMLTableRowElement', |
- 'track': 'HTMLTrackElement', |
- '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] |
- }); |
- |
-})(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() { |
- |
- // convenient global |
- window.wrap = ShadowDOMPolyfill.wrapIfNeeded; |
- window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded; |
- |
- // users may want to customize other types |
- // TODO(sjmiles): 'button' is now supported by ShadowDOMPolyfill, but |
- // I've left this code here in case we need to temporarily patch another |
- // type |
- /* |
- (function() { |
- var elts = {HTMLButtonElement: 'button'}; |
- for (var c in elts) { |
- window[c] = function() { throw 'Patched Constructor'; }; |
- window[c].prototype = Object.getPrototypeOf( |
- document.createElement(elts[c])); |
- } |
- })(); |
- */ |
- |
- // patch in prefixed name |
- Object.defineProperty(Element.prototype, 'webkitShadowRoot', |
- Object.getOwnPropertyDescriptor(Element.prototype, 'shadowRoot')); |
- |
- var originalCreateShadowRoot = Element.prototype.createShadowRoot; |
- Element.prototype.createShadowRoot = function() { |
- var root = originalCreateShadowRoot.call(this); |
- CustomElements.watchShadow(this); |
- return root; |
- }; |
- |
- Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot; |
-})(); |
- |
-/* |
- * 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, :host-context: 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. |
- |
- * 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(div) { |
- background: red; |
- } |
- </style> |
- <content></content> |
- |
- could become: |
- |
- <style> |
- / *@polyfill .content-container div * / |
- ::content(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 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 scopeStyles = this.prepareRoot(root, name, extendsName); |
- var typeExtension = this.isTypeExtension(extendsName); |
- var scopeSelector = this.makeScopeSelector(name, typeExtension); |
- // use caching to make working with styles nodes easier and to facilitate |
- // lookup of extendee |
- var cssText = stylesToCssText(scopeStyles, true); |
- cssText = this.scopeCssText(cssText, scopeSelector); |
- // cache shimmed css on root for user extensibility |
- if (root) { |
- root.shimmedStyle = cssText; |
- } |
- // add style to document |
- this.addCssToDocument(cssText, name); |
- }, |
- /* |
- * Shim a style element with the given selector. Returns cssText that can |
- * be included in the document via Platform.ShadowCSS.addCssToDocument(css). |
- */ |
- shimStyle: function(style, selector) { |
- return this.shimCssText(style.textContent, selector); |
- }, |
- /* |
- * Shim some cssText with the given selector. Returns cssText that can |
- * be included in the document via Platform.ShadowCSS.addCssToDocument(css). |
- */ |
- shimCssText: function(cssText, selector) { |
- cssText = this.insertDirectives(cssText); |
- return this.scopeCssText(cssText, selector); |
- }, |
- makeScopeSelector: function(name, typeExtension) { |
- if (name) { |
- return typeExtension ? '[is=' + name + ']' : name; |
- } |
- return ''; |
- }, |
- isTypeExtension: function(extendsName) { |
- return extendsName && extendsName.indexOf('-') < 0; |
- }, |
- prepareRoot: function(root, name, extendsName) { |
- var def = this.registerRoot(root, name, extendsName); |
- this.replaceTextInStyles(def.rootStyles, this.insertDirectives); |
- // remove existing style elements |
- this.removeStyles(root, def.rootStyles); |
- // apply strict attr |
- if (this.strictStyling) { |
- this.applyScopeToContent(root, name); |
- } |
- return def.scopeStyles; |
- }, |
- removeStyles: function(root, styles) { |
- for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) { |
- s.parentNode.removeChild(s); |
- } |
- }, |
- registerRoot: function(root, name, extendsName) { |
- var def = this.registry[name] = { |
- root: root, |
- name: name, |
- extendsName: extendsName |
- } |
- var styles = this.findStyles(root); |
- def.rootStyles = styles; |
- def.scopeStyles = def.rootStyles; |
- var extendee = this.registry[def.extendsName]; |
- if (extendee) { |
- def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles); |
- } |
- return def; |
- }, |
- findStyles: function(root) { |
- if (!root) { |
- return []; |
- } |
- var styles = root.querySelectorAll('style'); |
- return Array.prototype.filter.call(styles, function(s) { |
- return !s.hasAttribute(NO_SHIM_ATTRIBUTE); |
- }); |
- }, |
- 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); |
- } |
- }, |
- insertDirectives: function(cssText) { |
- cssText = this.insertPolyfillDirectivesInCssText(cssText); |
- return this.insertPolyfillRulesInCssText(cssText); |
- }, |
- /* |
- * Process styles to convert native ShadowDOM rules that will trip |
- * up the css parser; we rely on decorating the stylesheet with inert rules. |
- * |
- * For example, we convert this rule: |
- * |
- * polyfill-next-selector { content: ':host menu-item'; } |
- * ::content menu-item { |
- * |
- * to this: |
- * |
- * scopeName menu-item { |
- * |
- **/ |
- insertPolyfillDirectivesInCssText: function(cssText) { |
- // TODO(sorvell): remove either content or comment |
- cssText = cssText.replace(cssCommentNextSelectorRe, function(match, p1) { |
- // remove end comment delimiter and add block start |
- return p1.slice(0, -2) + '{'; |
- }); |
- return cssText.replace(cssContentNextSelectorRe, function(match, p1) { |
- return p1 + ' {'; |
- }); |
- }, |
- /* |
- * Process styles to add rules which will only apply under the polyfill |
- * |
- * For example, we convert this rule: |
- * |
- * polyfill-rule { |
- * content: ':host menu-item'; |
- * ... |
- * } |
- * |
- * to this: |
- * |
- * scopeName menu-item {...} |
- * |
- **/ |
- insertPolyfillRulesInCssText: function(cssText) { |
- // TODO(sorvell): remove either content or comment |
- cssText = cssText.replace(cssCommentRuleRe, function(match, p1) { |
- // remove end comment delimiter |
- return p1.slice(0, -1); |
- }); |
- return cssText.replace(cssContentRuleRe, function(match, p1, p2, p3) { |
- var rule = match.replace(p1, '').replace(p2, ''); |
- return p3 + rule; |
- }); |
- }, |
- /* Ensure styles are scoped. Pseudo-scoping takes a rule like: |
- * |
- * .foo {... } |
- * |
- * and converts this to |
- * |
- * scopeName .foo { ... } |
- */ |
- scopeCssText: function(cssText, scopeSelector) { |
- var unscoped = this.extractUnscopedRulesFromCssText(cssText); |
- cssText = this.insertPolyfillHostInCssText(cssText); |
- cssText = this.convertColonHost(cssText); |
- cssText = this.convertColonHostContext(cssText); |
- cssText = this.convertCombinators(cssText); |
- if (scopeSelector) { |
- var self = this, cssText; |
- withCssRules(cssText, function(rules) { |
- cssText = self.scopeRules(rules, scopeSelector); |
- }); |
- |
- } |
- cssText = cssText + '\n' + unscoped; |
- return cssText.trim(); |
- }, |
- /* |
- * 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 {...} |
- * |
- **/ |
- extractUnscopedRulesFromCssText: function(cssText) { |
- // TODO(sorvell): remove either content or comment |
- var r = '', m; |
- while (m = cssCommentUnscopedRuleRe.exec(cssText)) { |
- r += m[1].slice(0, -1) + '\n\n'; |
- } |
- while (m = cssContentUnscopedRuleRe.exec(cssText)) { |
- r += m[0].replace(m[2], '').replace(m[1], m[3]) + '\n\n'; |
- } |
- return r; |
- }, |
- /* |
- * 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 :host-context(.foo) > .bar { } |
- * |
- * to |
- * |
- * scopeName.foo > .bar, .foo scopeName > .bar { } |
- * |
- * and |
- * |
- * :host-context(.foo:host) .bar { ... } |
- * |
- * to |
- * |
- * scopeName.foo .bar { ... } |
- */ |
- convertColonHostContext: function(cssText) { |
- return this.convertColonRule(cssText, cssColonHostContextRe, |
- this.colonHostContextPartReplacer); |
- }, |
- 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; |
- } |
- }); |
- }, |
- colonHostContextPartReplacer: 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) { |
- for (var i=0; i < combinatorsRe.length; i++) { |
- cssText = cssText.replace(combinatorsRe[i], ' '); |
- } |
- return cssText; |
- }, |
- // change a selector like 'div' to 'name div' |
- scopeRules: function(cssRules, scopeSelector) { |
- var cssText = ''; |
- if (cssRules) { |
- Array.prototype.forEach.call(cssRules, function(rule) { |
- if (rule.selectorText && (rule.style && rule.style.cssText)) { |
- cssText += this.scopeSelector(rule.selectorText, scopeSelector, |
- this.strictStyling) + ' {\n\t'; |
- cssText += this.propertiesFromRule(rule) + '\n}\n\n'; |
- } else if (rule.type === CSSRule.MEDIA_RULE) { |
- cssText += '@media ' + rule.media.mediaText + ' {\n'; |
- cssText += this.scopeRules(rule.cssRules, scopeSelector); |
- cssText += '\n}\n\n'; |
- } else if (rule.cssText) { |
- cssText += rule.cssText + '\n\n'; |
- } |
- }, this); |
- } |
- return cssText; |
- }, |
- scopeSelector: function(selector, scopeSelector, strict) { |
- var r = [], parts = selector.split(','); |
- parts.forEach(function(p) { |
- p = p.trim(); |
- if (this.selectorNeedsScoping(p, scopeSelector)) { |
- p = (strict && !p.match(polyfillHostNoCombinator)) ? |
- this.applyStrictSelectorScope(p, scopeSelector) : |
- this.applySimpleSelectorScope(p, scopeSelector); |
- } |
- r.push(p); |
- }, this); |
- return r.join(', '); |
- }, |
- selectorNeedsScoping: function(selector, scopeSelector) { |
- var re = this.makeScopeMatcher(scopeSelector); |
- return !selector.match(re); |
- }, |
- makeScopeMatcher: function(scopeSelector) { |
- scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\[/g, '\\]'); |
- return new RegExp('^(' + scopeSelector + ')' + selectorReSuffix, 'm'); |
- }, |
- // scope via name and [is=name] |
- applySimpleSelectorScope: function(selector, scopeSelector) { |
- if (selector.match(polyfillHostRe)) { |
- selector = selector.replace(polyfillHostNoCombinator, scopeSelector); |
- return selector.replace(polyfillHostRe, scopeSelector + ' '); |
- } else { |
- return scopeSelector + ' ' + 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, scopeSelector) { |
- scopeSelector = scopeSelector.replace(/\[is=([^\]]*)\]/g, '$1'); |
- var splits = [' ', '>', '+', '~'], |
- scoped = selector, |
- attrName = '[' + scopeSelector + ']'; |
- 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(colonHostContextRe, polyfillHostContext).replace( |
- colonHostRe, polyfillHost); |
- }, |
- propertiesFromRule: function(rule) { |
- var cssText = rule.style.cssText; |
- // TODO(sorvell): Safari cssom incorrectly removes quotes from the content |
- // property. (https://bugs.webkit.org/show_bug.cgi?id=118045) |
- // don't replace attr rules |
- if (rule.style.content && !rule.style.content.match(/['"]+|attr/)) { |
- cssText = cssText.replace(/content:[^;]*;/g, 'content: \'' + |
- rule.style.content + '\';'); |
- } |
- // TODO(sorvell): we can workaround this issue here, but we need a list |
- // of troublesome properties to fix https://github.com/Polymer/platform/issues/53 |
- // |
- // inherit rules can be omitted from cssText |
- // TODO(sorvell): remove when Blink bug is fixed: |
- // https://code.google.com/p/chromium/issues/detail?id=358273 |
- var style = rule.style; |
- for (var i in style) { |
- if (style[i] === 'initial') { |
- cssText += i + ': initial; '; |
- } |
- } |
- return cssText; |
- }, |
- replaceTextInStyles: function(styles, action) { |
- if (styles && action) { |
- if (!(styles instanceof Array)) { |
- styles = [styles]; |
- } |
- Array.prototype.forEach.call(styles, function(s) { |
- s.textContent = action.call(this, s.textContent); |
- }, this); |
- } |
- }, |
- addCssToDocument: function(cssText, name) { |
- if (cssText.match('@import')) { |
- addOwnSheet(cssText, name); |
- } else { |
- addCssToDocument(cssText); |
- } |
- } |
-}; |
- |
-var selectorRe = /([^{]*)({[\s\S]*?})/gim, |
- cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim, |
- // TODO(sorvell): remove either content or comment |
- cssCommentNextSelectorRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim, |
- cssContentNextSelectorRe = /polyfill-next-selector[^}]*content\:[\s]*'([^']*)'[^}]*}([^{]*?){/gim, |
- // TODO(sorvell): remove either content or comment |
- cssCommentRuleRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, |
- cssContentRuleRe = /(polyfill-rule)[^}]*(content\:[\s]*'([^']*)'[^;]*;)[^}]*}/gim, |
- // TODO(sorvell): remove either content or comment |
- cssCommentUnscopedRuleRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, |
- cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content\:[\s]*'([^']*)'[^;]*;)[^}]*}/gim, |
- cssPseudoRe = /::(x-[^\s{,(]*)/gim, |
- cssPartRe = /::part\(([^)]*)\)/gim, |
- // note: :host pre-processed to -shadowcsshost. |
- polyfillHost = '-shadowcsshost', |
- // note: :host-context pre-processed to -shadowcsshostcontext. |
- polyfillHostContext = '-shadowcsscontext', |
- parenSuffix = ')(?:\\((' + |
- '(?:\\([^)(]*\\)|[^)(]*)+?' + |
- ')\\))?([^,{]*)'; |
- cssColonHostRe = new RegExp('(' + polyfillHost + parenSuffix, 'gim'), |
- cssColonHostContextRe = new RegExp('(' + polyfillHostContext + parenSuffix, 'gim'), |
- selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$', |
- colonHostRe = /\:host/gim, |
- colonHostContextRe = /\:host-context/gim, |
- /* host name without combinator */ |
- polyfillHostNoCombinator = polyfillHost + '-no-combinator', |
- polyfillHostRe = new RegExp(polyfillHost, 'gim'), |
- polyfillHostContextRe = new RegExp(polyfillHostContext, 'gim'), |
- combinatorsRe = [ |
- /\^\^/g, |
- /\^/g, |
- /\/shadow\//g, |
- /\/shadow-deep\//g, |
- /::shadow/g, |
- /\/deep\//g |
- ]; |
- |
-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 = []; |
- if (style.sheet) { |
- // TODO(sorvell): Firefox throws when accessing the rules of a stylesheet |
- // with an @import |
- // https://bugzilla.mozilla.org/show_bug.cgi?id=625013 |
- try { |
- rules = style.sheet.cssRules; |
- } catch(e) { |
- // |
- } |
- } else { |
- console.warn('sheet not found', style); |
- } |
- style.parentNode.removeChild(style); |
- return rules; |
-} |
- |
-var frame = document.createElement('iframe'); |
-frame.style.display = 'none'; |
- |
-function initFrame() { |
- frame.initialized = true; |
- document.body.appendChild(frame); |
- var doc = frame.contentDocument; |
- var base = doc.createElement('base'); |
- base.href = document.baseURI; |
- doc.head.appendChild(base); |
-} |
- |
-function inFrame(fn) { |
- if (!frame.initialized) { |
- initFrame(); |
- } |
- document.body.appendChild(frame); |
- fn(frame.contentDocument); |
- document.body.removeChild(frame); |
-} |
- |
-// TODO(sorvell): use an iframe if the cssText contains an @import to workaround |
-// https://code.google.com/p/chromium/issues/detail?id=345114 |
-var isChrome = navigator.userAgent.match('Chrome'); |
-function withCssRules(cssText, callback) { |
- if (!callback) { |
- return; |
- } |
- var rules; |
- if (cssText.match('@import') && isChrome) { |
- var style = cssTextToStyle(cssText); |
- inFrame(function(doc) { |
- doc.head.appendChild(style.impl); |
- rules = style.sheet.cssRules; |
- callback(rules); |
- }); |
- } else { |
- rules = cssToRules(cssText); |
- callback(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)); |
- } |
-} |
- |
-function addOwnSheet(cssText, name) { |
- var style = cssTextToStyle(cssText); |
- style.setAttribute(name, ''); |
- style.setAttribute(SHIMMED_ATTRIBUTE, ''); |
- document.head.appendChild(style); |
-} |
- |
-var SHIM_ATTRIBUTE = 'shim-shadowdom'; |
-var SHIMMED_ATTRIBUTE = 'shim-shadowdom-css'; |
-var NO_SHIM_ATTRIBUTE = 'no-shim'; |
- |
-var sheet; |
-function getSheet() { |
- if (!sheet) { |
- sheet = document.createElement("style"); |
- sheet.setAttribute(SHIMMED_ATTRIBUTE, ''); |
- sheet[SHIMMED_ATTRIBUTE] = 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]); |
- |
- // TODO(sorvell): monkey-patching HTMLImports is abusive; |
- // consider a better solution. |
- document.addEventListener('DOMContentLoaded', function() { |
- var urlResolver = scope.urlResolver; |
- |
- if (window.HTMLImports && !HTMLImports.useNative) { |
- var SHIM_SHEET_SELECTOR = 'link[rel=stylesheet]' + |
- '[' + SHIM_ATTRIBUTE + ']'; |
- var SHIM_STYLE_SELECTOR = 'style[' + SHIM_ATTRIBUTE + ']'; |
- HTMLImports.importer.documentPreloadSelectors += ',' + SHIM_SHEET_SELECTOR; |
- HTMLImports.importer.importsPreloadSelectors += ',' + SHIM_SHEET_SELECTOR; |
- |
- HTMLImports.parser.documentSelectors = [ |
- HTMLImports.parser.documentSelectors, |
- SHIM_SHEET_SELECTOR, |
- SHIM_STYLE_SELECTOR |
- ].join(','); |
- |
- var originalParseGeneric = HTMLImports.parser.parseGeneric; |
- |
- HTMLImports.parser.parseGeneric = function(elt) { |
- if (elt[SHIMMED_ATTRIBUTE]) { |
- return; |
- } |
- var style = elt.__importElement || elt; |
- if (!style.hasAttribute(SHIM_ATTRIBUTE)) { |
- originalParseGeneric.call(this, elt); |
- return; |
- } |
- if (elt.__resource) { |
- style = elt.ownerDocument.createElement('style'); |
- style.textContent = urlResolver.resolveCssText( |
- elt.__resource, elt.href); |
- } else { |
- urlResolver.resolveStyle(style); |
- } |
- style.textContent = ShadowCSS.shimStyle(style); |
- style.removeAttribute(SHIM_ATTRIBUTE, ''); |
- style.setAttribute(SHIMMED_ATTRIBUTE, ''); |
- style[SHIMMED_ATTRIBUTE] = true; |
- // place in document |
- if (style.parentNode !== head) { |
- // replace links in head |
- if (elt.parentNode === head) { |
- head.replaceChild(style, elt); |
- } else { |
- head.appendChild(style); |
- } |
- } |
- style.__importParsed = true; |
- this.markParsingComplete(elt); |
- } |
- |
- var hasResource = HTMLImports.parser.hasResource; |
- HTMLImports.parser.hasResource = function(node) { |
- if (node.localName === 'link' && node.rel === 'stylesheet' && |
- node.hasAttribute(SHIM_ATTRIBUTE)) { |
- return (node.__resource); |
- } else { |
- return hasResource.call(this, node); |
- } |
- } |
- |
- } |
- }); |
-} |
- |
-// exports |
-scope.ShadowCSS = ShadowCSS; |
- |
-})(window.Platform); |
-} else { |
-/* |
- * 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() { |
- |
- // poor man's adapter for template.content on various platform scenarios |
- window.templateContent = window.templateContent || function(inTemplate) { |
- return inTemplate.content; |
- }; |
- |
- // so we can call wrap/unwrap without testing for ShadowDOMPolyfill |
- |
- window.wrap = window.unwrap = function(n){ |
- return n; |
- } |
- |
- addEventListener('DOMContentLoaded', function() { |
- if (CustomElements.useNative === false) { |
- var originalCreateShadowRoot = Element.prototype.createShadowRoot; |
- Element.prototype.createShadowRoot = function() { |
- var root = originalCreateShadowRoot.call(this); |
- CustomElements.watchShadow(this); |
- return root; |
- }; |
- } |
- }); |
- |
- window.templateContent = function(inTemplate) { |
- // if MDV exists, it may need to boostrap this template to reveal content |
- if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) { |
- HTMLTemplateElement.bootstrap(inTemplate); |
- } |
- // fallback when there is no Shadow DOM polyfill, no MDV polyfill, and no |
- // native template support |
- if (!inTemplate.content && !inTemplate._content) { |
- var frag = document.createDocumentFragment(); |
- while (inTemplate.firstChild) { |
- frag.appendChild(inTemplate.firstChild); |
- } |
- inTemplate._content = frag; |
- } |
- return inTemplate.content || inTemplate._content; |
- }; |
- |
-})(); |
-} |
-/* Any copyright is dedicated to the Public Domain. |
- * http://creativecommons.org/publicdomain/zero/1.0/ */ |
- |
-(function(scope) { |
- 'use strict'; |
- |
- // feature detect for URL constructor |
- var hasWorkingUrl = false; |
- if (!scope.forceJURL) { |
- try { |
- var u = new URL('b', 'http://a'); |
- hasWorkingUrl = u.href === 'http://a/b'; |
- } catch(e) {} |
- } |
- |
- if (hasWorkingUrl) |
- return; |
- |
- var relative = Object.create(null); |
- relative['ftp'] = 21; |
- relative['file'] = 0; |
- relative['gopher'] = 70; |
- relative['http'] = 80; |
- relative['https'] = 443; |
- relative['ws'] = 80; |
- relative['wss'] = 443; |
- |
- var relativePathDotMapping = Object.create(null); |
- relativePathDotMapping['%2e'] = '.'; |
- relativePathDotMapping['.%2e'] = '..'; |
- relativePathDotMapping['%2e.'] = '..'; |
- relativePathDotMapping['%2e%2e'] = '..'; |
- |
- function isRelativeScheme(scheme) { |
- return relative[scheme] !== undefined; |
- } |
- |
- function invalid() { |
- clear.call(this); |
- this._isInvalid = true; |
- } |
- |
- function IDNAToASCII(h) { |
- if ('' == h) { |
- invalid.call(this) |
- } |
- // XXX |
- return h.toLowerCase() |
- } |
- |
- function percentEscape(c) { |
- var unicode = c.charCodeAt(0); |
- if (unicode > 0x20 && |
- unicode < 0x7F && |
- // " # < > ? ` |
- [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1 |
- ) { |
- return c; |
- } |
- return encodeURIComponent(c); |
- } |
- |
- function percentEscapeQuery(c) { |
- // XXX This actually needs to encode c using encoding and then |
- // convert the bytes one-by-one. |
- |
- var unicode = c.charCodeAt(0); |
- if (unicode > 0x20 && |
- unicode < 0x7F && |
- // " # < > ` (do not escape '?') |
- [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1 |
- ) { |
- return c; |
- } |
- return encodeURIComponent(c); |
- } |
- |
- var EOF = undefined, |
- ALPHA = /[a-zA-Z]/, |
- ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/; |
- |
- function parse(input, stateOverride, base) { |
- function err(message) { |
- errors.push(message) |
- } |
- |
- var state = stateOverride || 'scheme start', |
- cursor = 0, |
- buffer = '', |
- seenAt = false, |
- seenBracket = false, |
- errors = []; |
- |
- loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) { |
- var c = input[cursor]; |
- switch (state) { |
- case 'scheme start': |
- if (c && ALPHA.test(c)) { |
- buffer += c.toLowerCase(); // ASCII-safe |
- state = 'scheme'; |
- } else if (!stateOverride) { |
- buffer = ''; |
- state = 'no scheme'; |
- continue; |
- } else { |
- err('Invalid scheme.'); |
- break loop; |
- } |
- break; |
- |
- case 'scheme': |
- if (c && ALPHANUMERIC.test(c)) { |
- buffer += c.toLowerCase(); // ASCII-safe |
- } else if (':' == c) { |
- this._scheme = buffer; |
- buffer = ''; |
- if (stateOverride) { |
- break loop; |
- } |
- if (isRelativeScheme(this._scheme)) { |
- this._isRelative = true; |
- } |
- if ('file' == this._scheme) { |
- state = 'relative'; |
- } else if (this._isRelative && base && base._scheme == this._scheme) { |
- state = 'relative or authority'; |
- } else if (this._isRelative) { |
- state = 'authority first slash'; |
- } else { |
- state = 'scheme data'; |
- } |
- } else if (!stateOverride) { |
- buffer = ''; |
- cursor = 0; |
- state = 'no scheme'; |
- continue; |
- } else if (EOF == c) { |
- break loop; |
- } else { |
- err('Code point not allowed in scheme: ' + c) |
- break loop; |
- } |
- break; |
- |
- case 'scheme data': |
- if ('?' == c) { |
- query = '?'; |
- state = 'query'; |
- } else if ('#' == c) { |
- this._fragment = '#'; |
- state = 'fragment'; |
- } else { |
- // XXX error handling |
- if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { |
- this._schemeData += percentEscape(c); |
- } |
- } |
- break; |
- |
- case 'no scheme': |
- if (!base || !(isRelativeScheme(base._scheme))) { |
- err('Missing scheme.'); |
- invalid.call(this); |
- } else { |
- state = 'relative'; |
- continue; |
- } |
- break; |
- |
- case 'relative or authority': |
- if ('/' == c && '/' == input[cursor+1]) { |
- state = 'authority ignore slashes'; |
- } else { |
- err('Expected /, got: ' + c); |
- state = 'relative'; |
- continue |
- } |
- break; |
- |
- case 'relative': |
- this._isRelative = true; |
- if ('file' != this._scheme) |
- this._scheme = base._scheme; |
- if (EOF == c) { |
- this._host = base._host; |
- this._port = base._port; |
- this._path = base._path.slice(); |
- this._query = base._query; |
- break loop; |
- } else if ('/' == c || '\\' == c) { |
- if ('\\' == c) |
- err('\\ is an invalid code point.'); |
- state = 'relative slash'; |
- } else if ('?' == c) { |
- this._host = base._host; |
- this._port = base._port; |
- this._path = base._path.slice(); |
- this._query = '?'; |
- state = 'query'; |
- } else if ('#' == c) { |
- this._host = base._host; |
- this._port = base._port; |
- this._path = base._path.slice(); |
- this._query = base._query; |
- this._fragment = '#'; |
- state = 'fragment'; |
- } else { |
- var nextC = input[cursor+1] |
- var nextNextC = input[cursor+2] |
- if ( |
- 'file' != this._scheme || !ALPHA.test(c) || |
- (nextC != ':' && nextC != '|') || |
- (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) { |
- this._host = base._host; |
- this._port = base._port; |
- this._path = base._path.slice(); |
- this._path.pop(); |
- } |
- state = 'relative path'; |
- continue; |
- } |
- break; |
- |
- case 'relative slash': |
- if ('/' == c || '\\' == c) { |
- if ('\\' == c) { |
- err('\\ is an invalid code point.'); |
- } |
- if ('file' == this._scheme) { |
- state = 'file host'; |
- } else { |
- state = 'authority ignore slashes'; |
- } |
- } else { |
- if ('file' != this._scheme) { |
- this._host = base._host; |
- this._port = base._port; |
- } |
- state = 'relative path'; |
- continue; |
- } |
- break; |
- |
- case 'authority first slash': |
- if ('/' == c) { |
- state = 'authority second slash'; |
- } else { |
- err("Expected '/', got: " + c); |
- state = 'authority ignore slashes'; |
- continue; |
- } |
- break; |
- |
- case 'authority second slash': |
- state = 'authority ignore slashes'; |
- if ('/' != c) { |
- err("Expected '/', got: " + c); |
- continue; |
- } |
- break; |
- |
- case 'authority ignore slashes': |
- if ('/' != c && '\\' != c) { |
- state = 'authority'; |
- continue; |
- } else { |
- err('Expected authority, got: ' + c); |
- } |
- break; |
- |
- case 'authority': |
- if ('@' == c) { |
- if (seenAt) { |
- err('@ already seen.'); |
- buffer += '%40'; |
- } |
- seenAt = true; |
- for (var i = 0; i < buffer.length; i++) { |
- var cp = buffer[i]; |
- if ('\t' == cp || '\n' == cp || '\r' == cp) { |
- err('Invalid whitespace in authority.'); |
- continue; |
- } |
- // XXX check URL code points |
- if (':' == cp && null === this._password) { |
- this._password = ''; |
- continue; |
- } |
- var tempC = percentEscape(cp); |
- (null !== this._password) ? this._password += tempC : this._username += tempC; |
- } |
- buffer = ''; |
- } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { |
- cursor -= buffer.length; |
- buffer = ''; |
- state = 'host'; |
- continue; |
- } else { |
- buffer += c; |
- } |
- break; |
- |
- case 'file host': |
- if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { |
- if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) { |
- state = 'relative path'; |
- } else if (buffer.length == 0) { |
- state = 'relative path start'; |
- } else { |
- this._host = IDNAToASCII.call(this, buffer); |
- buffer = ''; |
- state = 'relative path start'; |
- } |
- continue; |
- } else if ('\t' == c || '\n' == c || '\r' == c) { |
- err('Invalid whitespace in file host.'); |
- } else { |
- buffer += c; |
- } |
- break; |
- |
- case 'host': |
- case 'hostname': |
- if (':' == c && !seenBracket) { |
- // XXX host parsing |
- this._host = IDNAToASCII.call(this, buffer); |
- buffer = ''; |
- state = 'port'; |
- if ('hostname' == stateOverride) { |
- break loop; |
- } |
- } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { |
- this._host = IDNAToASCII.call(this, buffer); |
- buffer = ''; |
- state = 'relative path start'; |
- if (stateOverride) { |
- break loop; |
- } |
- continue; |
- } else if ('\t' != c && '\n' != c && '\r' != c) { |
- if ('[' == c) { |
- seenBracket = true; |
- } else if (']' == c) { |
- seenBracket = false; |
- } |
- buffer += c; |
- } else { |
- err('Invalid code point in host/hostname: ' + c); |
- } |
- break; |
- |
- case 'port': |
- if (/[0-9]/.test(c)) { |
- buffer += c; |
- } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c || stateOverride) { |
- if ('' != buffer) { |
- var temp = parseInt(buffer, 10); |
- if (temp != relative[this._scheme]) { |
- this._port = temp + ''; |
- } |
- buffer = ''; |
- } |
- if (stateOverride) { |
- break loop; |
- } |
- state = 'relative path start'; |
- continue; |
- } else if ('\t' == c || '\n' == c || '\r' == c) { |
- err('Invalid code point in port: ' + c); |
- } else { |
- invalid.call(this); |
- } |
- break; |
- |
- case 'relative path start': |
- if ('\\' == c) |
- err("'\\' not allowed in path."); |
- state = 'relative path'; |
- if ('/' != c && '\\' != c) { |
- continue; |
- } |
- break; |
- |
- case 'relative path': |
- if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) { |
- if ('\\' == c) { |
- err('\\ not allowed in relative path.'); |
- } |
- var tmp; |
- if (tmp = relativePathDotMapping[buffer.toLowerCase()]) { |
- buffer = tmp; |
- } |
- if ('..' == buffer) { |
- this._path.pop(); |
- if ('/' != c && '\\' != c) { |
- this._path.push(''); |
- } |
- } else if ('.' == buffer && '/' != c && '\\' != c) { |
- this._path.push(''); |
- } else if ('.' != buffer) { |
- if ('file' == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') { |
- buffer = buffer[0] + ':'; |
- } |
- this._path.push(buffer); |
- } |
- buffer = ''; |
- if ('?' == c) { |
- this._query = '?'; |
- state = 'query'; |
- } else if ('#' == c) { |
- this._fragment = '#'; |
- state = 'fragment'; |
- } |
- } else if ('\t' != c && '\n' != c && '\r' != c) { |
- buffer += percentEscape(c); |
- } |
- break; |
- |
- case 'query': |
- if (!stateOverride && '#' == c) { |
- this._fragment = '#'; |
- state = 'fragment'; |
- } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { |
- this._query += percentEscapeQuery(c); |
- } |
- break; |
- |
- case 'fragment': |
- if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { |
- this._fragment += c; |
- } |
- break; |
- } |
- |
- cursor++; |
- } |
- } |
- |
- function clear() { |
- this._scheme = ''; |
- this._schemeData = ''; |
- this._username = ''; |
- this._password = null; |
- this._host = ''; |
- this._port = ''; |
- this._path = []; |
- this._query = ''; |
- this._fragment = ''; |
- this._isInvalid = false; |
- this._isRelative = false; |
- } |
- |
- // Does not process domain names or IP addresses. |
- // Does not handle encoding for the query parameter. |
- function jURL(url, base /* , encoding */) { |
- if (base !== undefined && !(base instanceof jURL)) |
- base = new jURL(String(base)); |
- |
- this._url = url; |
- clear.call(this); |
- |
- var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, ''); |
- // encoding = encoding || 'utf-8' |
- |
- parse.call(this, input, null, base); |
- } |
- |
- jURL.prototype = { |
- get href() { |
- if (this._isInvalid) |
- return this._url; |
- |
- var authority = ''; |
- if ('' != this._username || null != this._password) { |
- authority = this._username + |
- (null != this._password ? ':' + this._password : '') + '@'; |
- } |
- |
- return this.protocol + |
- (this._isRelative ? '//' + authority + this.host : '') + |
- this.pathname + this._query + this._fragment; |
- }, |
- set href(href) { |
- clear.call(this); |
- parse.call(this, href); |
- }, |
- |
- get protocol() { |
- return this._scheme + ':'; |
- }, |
- set protocol(protocol) { |
- if (this._isInvalid) |
- return; |
- parse.call(this, protocol + ':', 'scheme start'); |
- }, |
- |
- get host() { |
- return this._isInvalid ? '' : this._port ? |
- this._host + ':' + this._port : this._host; |
- }, |
- set host(host) { |
- if (this._isInvalid || !this._isRelative) |
- return; |
- parse.call(this, host, 'host'); |
- }, |
- |
- get hostname() { |
- return this._host; |
- }, |
- set hostname(hostname) { |
- if (this._isInvalid || !this._isRelative) |
- return; |
- parse.call(this, hostname, 'hostname'); |
- }, |
- |
- get port() { |
- return this._port; |
- }, |
- set port(port) { |
- if (this._isInvalid || !this._isRelative) |
- return; |
- parse.call(this, port, 'port'); |
- }, |
- |
- get pathname() { |
- return this._isInvalid ? '' : this._isRelative ? |
- '/' + this._path.join('/') : this._schemeData; |
- }, |
- set pathname(pathname) { |
- if (this._isInvalid || !this._isRelative) |
- return; |
- this._path = []; |
- parse.call(this, pathname, 'relative path start'); |
- }, |
- |
- get search() { |
- return this._isInvalid || !this._query || '?' == this._query ? |
- '' : this._query; |
- }, |
- set search(search) { |
- if (this._isInvalid || !this._isRelative) |
- return; |
- this._query = '?'; |
- if ('?' == search[0]) |
- search = search.slice(1); |
- parse.call(this, search, 'query'); |
- }, |
- |
- get hash() { |
- return this._isInvalid || !this._fragment || '#' == this._fragment ? |
- '' : this._fragment; |
- }, |
- set hash(hash) { |
- if (this._isInvalid) |
- return; |
- this._fragment = '#'; |
- if ('#' == hash[0]) |
- hash = hash.slice(1); |
- parse.call(this, hash, 'fragment'); |
- } |
- }; |
- |
- scope.URL = jURL; |
- |
-})(window); |
- |
-/* |
- * 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) { |
- |
-// Old versions of iOS do not have bind. |
- |
-if (!Function.prototype.bind) { |
- Function.prototype.bind = function(scope) { |
- var self = this; |
- var args = Array.prototype.slice.call(arguments, 1); |
- return function() { |
- var args2 = args.slice(); |
- args2.push.apply(args2, arguments); |
- return self.apply(scope, args2); |
- }; |
- }; |
-} |
- |
-// mixin |
- |
-// copy all properties from inProps (et al) to inObj |
-function mixin(inObj/*, inProps, inMoreProps, ...*/) { |
- var obj = inObj || {}; |
- for (var i = 1; i < arguments.length; i++) { |
- var p = arguments[i]; |
- try { |
- for (var n in p) { |
- copyProperty(n, p, obj); |
- } |
- } catch(x) { |
- } |
- } |
- return obj; |
-} |
- |
-// copy property inName from inSource object to inTarget object |
-function copyProperty(inName, inSource, inTarget) { |
- var pd = getPropertyDescriptor(inSource, inName); |
- Object.defineProperty(inTarget, inName, pd); |
-} |
- |
-// get property descriptor for inName on inObject, even if |
-// inName exists on some link in inObject's prototype chain |
-function getPropertyDescriptor(inObject, inName) { |
- if (inObject) { |
- var pd = Object.getOwnPropertyDescriptor(inObject, inName); |
- return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName); |
- } |
-} |
- |
-// export |
- |
-scope.mixin = mixin; |
- |
-})(window.Platform); |
-// Copyright 2011 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(scope) { |
- |
- 'use strict'; |
- |
- // polyfill DOMTokenList |
- // * add/remove: allow these methods to take multiple classNames |
- // * toggle: add a 2nd argument which forces the given state rather |
- // than toggling. |
- |
- var add = DOMTokenList.prototype.add; |
- var remove = DOMTokenList.prototype.remove; |
- DOMTokenList.prototype.add = function() { |
- for (var i = 0; i < arguments.length; i++) { |
- add.call(this, arguments[i]); |
- } |
- }; |
- DOMTokenList.prototype.remove = function() { |
- for (var i = 0; i < arguments.length; i++) { |
- remove.call(this, arguments[i]); |
- } |
- }; |
- DOMTokenList.prototype.toggle = function(name, bool) { |
- if (arguments.length == 1) { |
- bool = !this.contains(name); |
- } |
- bool ? this.add(name) : this.remove(name); |
- }; |
- DOMTokenList.prototype.switch = function(oldName, newName) { |
- oldName && this.remove(oldName); |
- newName && this.add(newName); |
- }; |
- |
- // add array() to NodeList, NamedNodeMap, HTMLCollection |
- |
- var ArraySlice = function() { |
- return Array.prototype.slice.call(this); |
- }; |
- |
- var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {}); |
- |
- NodeList.prototype.array = ArraySlice; |
- namedNodeMap.prototype.array = ArraySlice; |
- HTMLCollection.prototype.array = ArraySlice; |
- |
- // polyfill performance.now |
- |
- if (!window.performance) { |
- var start = Date.now(); |
- // only at millisecond precision |
- window.performance = {now: function(){ return Date.now() - start }}; |
- } |
- |
- // polyfill for requestAnimationFrame |
- |
- if (!window.requestAnimationFrame) { |
- window.requestAnimationFrame = (function() { |
- var nativeRaf = window.webkitRequestAnimationFrame || |
- window.mozRequestAnimationFrame; |
- |
- return nativeRaf ? |
- function(callback) { |
- return nativeRaf(function() { |
- callback(performance.now()); |
- }); |
- } : |
- function( callback ){ |
- return window.setTimeout(callback, 1000 / 60); |
- }; |
- })(); |
- } |
- |
- if (!window.cancelAnimationFrame) { |
- window.cancelAnimationFrame = (function() { |
- return window.webkitCancelAnimationFrame || |
- window.mozCancelAnimationFrame || |
- function(id) { |
- clearTimeout(id); |
- }; |
- })(); |
- } |
- |
- // utility |
- |
- function createDOM(inTagOrNode, inHTML, inAttrs) { |
- var dom = typeof inTagOrNode == 'string' ? |
- document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true); |
- dom.innerHTML = inHTML; |
- if (inAttrs) { |
- for (var n in inAttrs) { |
- dom.setAttribute(n, inAttrs[n]); |
- } |
- } |
- return dom; |
- } |
- // Make a stub for Polymer() for polyfill purposes; under the HTMLImports |
- // polyfill, scripts in the main document run before imports. That means |
- // if (1) polymer is imported and (2) Polymer() is called in the main document |
- // in a script after the import, 2 occurs before 1. We correct this here |
- // by specfiically patching Polymer(); this is not necessary under native |
- // HTMLImports. |
- var elementDeclarations = []; |
- |
- var polymerStub = function(name, dictionary) { |
- elementDeclarations.push(arguments); |
- } |
- window.Polymer = polymerStub; |
- |
- // deliver queued delcarations |
- scope.deliverDeclarations = function() { |
- scope.deliverDeclarations = function() { |
- throw 'Possible attempt to load Polymer twice'; |
- }; |
- return elementDeclarations; |
- } |
- |
- // Once DOMContent has loaded, any main document scripts that depend on |
- // Polymer() should have run. Calling Polymer() now is an error until |
- // polymer is imported. |
- window.addEventListener('DOMContentLoaded', function() { |
- if (window.Polymer === polymerStub) { |
- window.Polymer = function() { |
- console.error('You tried to use polymer without loading it first. To ' + |
- 'load polymer, <link rel="import" href="' + |
- 'components/polymer/polymer.html">'); |
- }; |
- } |
- }); |
- |
- // exports |
- scope.createDOM = createDOM; |
- |
-})(window.Platform); |
- |
-/* |
- * 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. |
- */ |
- |
-// poor man's adapter for template.content on various platform scenarios |
-window.templateContent = window.templateContent || function(inTemplate) { |
- return inTemplate.content; |
-}; |
-(function(scope) { |
- |
- scope = scope || (window.Inspector = {}); |
- |
- var inspector; |
- |
- window.sinspect = function(inNode, inProxy) { |
- if (!inspector) { |
- inspector = window.open('', 'ShadowDOM Inspector', null, true); |
- inspector.document.write(inspectorHTML); |
- //inspector.document.close(); |
- inspector.api = { |
- shadowize: shadowize |
- }; |
- } |
- inspect(inNode || wrap(document.body), inProxy); |
- }; |
- |
- var inspectorHTML = [ |
- '<!DOCTYPE html>', |
- '<html>', |
- ' <head>', |
- ' <title>ShadowDOM Inspector</title>', |
- ' <style>', |
- ' body {', |
- ' }', |
- ' pre {', |
- ' font: 9pt "Courier New", monospace;', |
- ' line-height: 1.5em;', |
- ' }', |
- ' tag {', |
- ' color: purple;', |
- ' }', |
- ' ul {', |
- ' margin: 0;', |
- ' padding: 0;', |
- ' list-style: none;', |
- ' }', |
- ' li {', |
- ' display: inline-block;', |
- ' background-color: #f1f1f1;', |
- ' padding: 4px 6px;', |
- ' border-radius: 4px;', |
- ' margin-right: 4px;', |
- ' }', |
- ' </style>', |
- ' </head>', |
- ' <body>', |
- ' <ul id="crumbs">', |
- ' </ul>', |
- ' <div id="tree"></div>', |
- ' </body>', |
- '</html>' |
- ].join('\n'); |
- |
- var crumbs = []; |
- |
- var displayCrumbs = function() { |
- // alias our document |
- var d = inspector.document; |
- // get crumbbar |
- var cb = d.querySelector('#crumbs'); |
- // clear crumbs |
- cb.textContent = ''; |
- // build new crumbs |
- for (var i=0, c; c=crumbs[i]; i++) { |
- var a = d.createElement('a'); |
- a.href = '#'; |
- a.textContent = c.localName; |
- a.idx = i; |
- a.onclick = function(event) { |
- var c; |
- while (crumbs.length > this.idx) { |
- c = crumbs.pop(); |
- } |
- inspect(c.shadow || c, c); |
- event.preventDefault(); |
- }; |
- cb.appendChild(d.createElement('li')).appendChild(a); |
- } |
- }; |
- |
- var inspect = function(inNode, inProxy) { |
- // alias our document |
- var d = inspector.document; |
- // reset list of drillable nodes |
- drillable = []; |
- // memoize our crumb proxy |
- var proxy = inProxy || inNode; |
- crumbs.push(proxy); |
- // update crumbs |
- displayCrumbs(); |
- // reflect local tree |
- d.body.querySelector('#tree').innerHTML = |
- '<pre>' + output(inNode, inNode.childNodes) + '</pre>'; |
- }; |
- |
- var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |
- |
- var blacklisted = {STYLE:1, SCRIPT:1, "#comment": 1, TEMPLATE: 1}; |
- var blacklist = function(inNode) { |
- return blacklisted[inNode.nodeName]; |
- }; |
- |
- var output = function(inNode, inChildNodes, inIndent) { |
- if (blacklist(inNode)) { |
- return ''; |
- } |
- var indent = inIndent || ''; |
- if (inNode.localName || inNode.nodeType == 11) { |
- var name = inNode.localName || 'shadow-root'; |
- //inChildNodes = ShadowDOM.localNodes(inNode); |
- var info = indent + describe(inNode); |
- // if only textNodes |
- // TODO(sjmiles): make correct for ShadowDOM |
- /*if (!inNode.children.length && inNode.localName !== 'content' && inNode.localName !== 'shadow') { |
- info += catTextContent(inChildNodes); |
- } else*/ { |
- // TODO(sjmiles): native <shadow> has no reference to its projection |
- if (name == 'content' /*|| name == 'shadow'*/) { |
- inChildNodes = inNode.getDistributedNodes(); |
- } |
- info += '<br/>'; |
- var ind = indent + ' '; |
- forEach(inChildNodes, function(n) { |
- info += output(n, n.childNodes, ind); |
- }); |
- info += indent; |
- } |
- if (!({br:1}[name])) { |
- info += '<tag></' + name + '></tag>'; |
- info += '<br/>'; |
- } |
- } else { |
- var text = inNode.textContent.trim(); |
- info = text ? indent + '"' + text + '"' + '<br/>' : ''; |
- } |
- return info; |
- }; |
- |
- var catTextContent = function(inChildNodes) { |
- var info = ''; |
- forEach(inChildNodes, function(n) { |
- info += n.textContent.trim(); |
- }); |
- return info; |
- }; |
- |
- var drillable = []; |
- |
- var describe = function(inNode) { |
- var tag = '<tag>' + '<'; |
- var name = inNode.localName || 'shadow-root'; |
- if (inNode.webkitShadowRoot || inNode.shadowRoot) { |
- tag += ' <button idx="' + drillable.length + |
- '" onclick="api.shadowize.call(this)">' + name + '</button>'; |
- drillable.push(inNode); |
- } else { |
- tag += name || 'shadow-root'; |
- } |
- if (inNode.attributes) { |
- forEach(inNode.attributes, function(a) { |
- tag += ' ' + a.name + (a.value ? '="' + a.value + '"' : ''); |
- }); |
- } |
- tag += '>'+ '</tag>'; |
- return tag; |
- }; |
- |
- // remote api |
- |
- shadowize = function() { |
- var idx = Number(this.attributes.idx.value); |
- //alert(idx); |
- var node = drillable[idx]; |
- if (node) { |
- inspect(node.webkitShadowRoot || node.shadowRoot, node) |
- } else { |
- console.log("bad shadowize node"); |
- console.dir(this); |
- } |
- }; |
- |
- // export |
- |
- scope.output = output; |
- |
-})(window.Inspector); |
- |
- |
- |
-/* |
- * 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) { |
- |
- // TODO(sorvell): It's desireable to provide a default stylesheet |
- // that's convenient for styling unresolved elements, but |
- // it's cumbersome to have to include this manually in every page. |
- // It would make sense to put inside some HTMLImport but |
- // the HTMLImports polyfill does not allow loading of stylesheets |
- // that block rendering. Therefore this injection is tolerated here. |
- |
- var style = document.createElement('style'); |
- style.textContent = '' |
- + 'body {' |
- + 'transition: opacity ease-in 0.2s;' |
- + ' } \n' |
- + 'body[unresolved] {' |
- + 'opacity: 0; display: block; overflow: hidden;' |
- + ' } \n' |
- ; |
- var head = document.querySelector('head'); |
- head.insertBefore(style, head.firstChild); |
- |
-})(Platform); |
- |
-(function(scope) { |
- |
- function withDependencies(task, depends) { |
- depends = depends || []; |
- if (!depends.map) { |
- depends = [depends]; |
- } |
- return task.apply(this, depends.map(marshal)); |
- } |
- |
- function module(name, dependsOrFactory, moduleFactory) { |
- var module; |
- switch (arguments.length) { |
- case 0: |
- return; |
- case 1: |
- module = null; |
- break; |
- case 2: |
- module = dependsOrFactory.apply(this); |
- break; |
- default: |
- module = withDependencies(moduleFactory, dependsOrFactory); |
- break; |
- } |
- modules[name] = module; |
- }; |
- |
- function marshal(name) { |
- return modules[name]; |
- } |
- |
- var modules = {}; |
- |
- function using(depends, task) { |
- HTMLImports.whenImportsReady(function() { |
- withDependencies(task, depends); |
- }); |
- }; |
- |
- // exports |
- |
- scope.marshal = marshal; |
- scope.module = module; |
- scope.using = using; |
- |
-})(window); |
-/* |
- * 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) { |
- |
-var iterations = 0; |
-var callbacks = []; |
-var twiddle = document.createTextNode(''); |
- |
-function endOfMicrotask(callback) { |
- twiddle.textContent = iterations++; |
- callbacks.push(callback); |
-} |
- |
-function atEndOfMicrotask() { |
- while (callbacks.length) { |
- callbacks.shift()(); |
- } |
-} |
- |
-new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask) |
- .observe(twiddle, {characterData: true}) |
- ; |
- |
-// exports |
- |
-scope.endOfMicrotask = endOfMicrotask; |
- |
-})(Platform); |
- |
- |
-/* |
- * 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) { |
- |
-var urlResolver = { |
- resolveDom: function(root, url) { |
- url = url || root.ownerDocument.baseURI; |
- this.resolveAttributes(root, url); |
- this.resolveStyles(root, url); |
- // handle template.content |
- var templates = root.querySelectorAll('template'); |
- if (templates) { |
- for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i++) { |
- if (t.content) { |
- this.resolveDom(t.content, url); |
- } |
- } |
- } |
- }, |
- resolveTemplate: function(template) { |
- this.resolveDom(template.content, template.ownerDocument.baseURI); |
- }, |
- resolveStyles: function(root, url) { |
- var styles = root.querySelectorAll('style'); |
- if (styles) { |
- for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) { |
- this.resolveStyle(s, url); |
- } |
- } |
- }, |
- resolveStyle: function(style, url) { |
- url = url || style.ownerDocument.baseURI; |
- style.textContent = this.resolveCssText(style.textContent, url); |
- }, |
- resolveCssText: function(cssText, baseUrl) { |
- cssText = replaceUrlsInCssText(cssText, baseUrl, CSS_URL_REGEXP); |
- return replaceUrlsInCssText(cssText, baseUrl, CSS_IMPORT_REGEXP); |
- }, |
- resolveAttributes: function(root, url) { |
- if (root.hasAttributes && root.hasAttributes()) { |
- this.resolveElementAttributes(root, url); |
- } |
- // search for attributes that host urls |
- var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR); |
- if (nodes) { |
- for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) { |
- this.resolveElementAttributes(n, url); |
- } |
- } |
- }, |
- resolveElementAttributes: function(node, url) { |
- url = url || node.ownerDocument.baseURI; |
- URL_ATTRS.forEach(function(v) { |
- var attr = node.attributes[v]; |
- if (attr && attr.value && |
- (attr.value.search(URL_TEMPLATE_SEARCH) < 0)) { |
- var urlPath = resolveRelativeUrl(url, attr.value); |
- attr.value = urlPath; |
- } |
- }); |
- } |
-}; |
- |
-var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; |
-var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; |
-var URL_ATTRS = ['href', 'src', 'action']; |
-var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']'; |
-var URL_TEMPLATE_SEARCH = '{{.*}}'; |
- |
-function replaceUrlsInCssText(cssText, baseUrl, regexp) { |
- return cssText.replace(regexp, function(m, pre, url, post) { |
- var urlPath = url.replace(/["']/g, ''); |
- urlPath = resolveRelativeUrl(baseUrl, urlPath); |
- return pre + '\'' + urlPath + '\'' + post; |
- }); |
-} |
- |
-function resolveRelativeUrl(baseUrl, url) { |
- var u = new URL(url, baseUrl); |
- return makeDocumentRelPath(u.href); |
-} |
- |
-function makeDocumentRelPath(url) { |
- var root = document.baseURI; |
- var u = new URL(url, root); |
- if (u.host === root.host && u.port === root.port && |
- u.protocol === root.protocol) { |
- return makeRelPath(root.pathname, u.pathname); |
- } else { |
- return url; |
- } |
-} |
- |
-// make a relative path from source to target |
-function makeRelPath(source, target) { |
- var s = source.split('/'); |
- var t = target.split('/'); |
- while (s.length && s[0] === t[0]){ |
- s.shift(); |
- t.shift(); |
- } |
- for (var i = 0, l = s.length - 1; i < l; i++) { |
- t.unshift('..'); |
- } |
- return t.join('/'); |
-} |
- |
-// exports |
-scope.urlResolver = urlResolver; |
- |
-})(Platform); |
- |
-/* |
- * 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(global) { |
- |
- var registrationsTable = new WeakMap(); |
- |
- // We use setImmediate or postMessage for our future callback. |
- var setImmediate = window.msSetImmediate; |
- |
- // Use post message to emulate setImmediate. |
- if (!setImmediate) { |
- var setImmediateQueue = []; |
- var sentinel = String(Math.random()); |
- window.addEventListener('message', function(e) { |
- if (e.data === sentinel) { |
- var queue = setImmediateQueue; |
- setImmediateQueue = []; |
- queue.forEach(function(func) { |
- func(); |
- }); |
- } |
- }); |
- setImmediate = function(func) { |
- setImmediateQueue.push(func); |
- window.postMessage(sentinel, '*'); |
- }; |
- } |
- |
- // This is used to ensure that we never schedule 2 callas to setImmediate |
- var isScheduled = false; |
- |
- // Keep track of observers that needs to be notified next time. |
- var scheduledObservers = []; |
- |
- /** |
- * Schedules |dispatchCallback| to be called in the future. |
- * @param {MutationObserver} observer |
- */ |
- function scheduleCallback(observer) { |
- scheduledObservers.push(observer); |
- if (!isScheduled) { |
- isScheduled = true; |
- setImmediate(dispatchCallbacks); |
- } |
- } |
- |
- function wrapIfNeeded(node) { |
- return window.ShadowDOMPolyfill && |
- window.ShadowDOMPolyfill.wrapIfNeeded(node) || |
- node; |
- } |
- |
- function dispatchCallbacks() { |
- // http://dom.spec.whatwg.org/#mutation-observers |
- |
- isScheduled = false; // Used to allow a new setImmediate call above. |
- |
- var observers = scheduledObservers; |
- scheduledObservers = []; |
- // Sort observers based on their creation UID (incremental). |
- observers.sort(function(o1, o2) { |
- return o1.uid_ - o2.uid_; |
- }); |
- |
- var anyNonEmpty = false; |
- observers.forEach(function(observer) { |
- |
- // 2.1, 2.2 |
- var queue = observer.takeRecords(); |
- // 2.3. Remove all transient registered observers whose observer is mo. |
- removeTransientObserversFor(observer); |
- |
- // 2.4 |
- if (queue.length) { |
- observer.callback_(queue, observer); |
- anyNonEmpty = true; |
- } |
- }); |
- |
- // 3. |
- if (anyNonEmpty) |
- dispatchCallbacks(); |
- } |
- |
- function removeTransientObserversFor(observer) { |
- observer.nodes_.forEach(function(node) { |
- var registrations = registrationsTable.get(node); |
- if (!registrations) |
- return; |
- registrations.forEach(function(registration) { |
- if (registration.observer === observer) |
- registration.removeTransientObservers(); |
- }); |
- }); |
- } |
- |
- /** |
- * This function is used for the "For each registered observer observer (with |
- * observer's options as options) in target's list of registered observers, |
- * run these substeps:" and the "For each ancestor ancestor of target, and for |
- * each registered observer observer (with options options) in ancestor's list |
- * of registered observers, run these substeps:" part of the algorithms. The |
- * |options.subtree| is checked to ensure that the callback is called |
- * correctly. |
- * |
- * @param {Node} target |
- * @param {function(MutationObserverInit):MutationRecord} callback |
- */ |
- function forEachAncestorAndObserverEnqueueRecord(target, callback) { |
- for (var node = target; node; node = node.parentNode) { |
- var registrations = registrationsTable.get(node); |
- |
- if (registrations) { |
- for (var j = 0; j < registrations.length; j++) { |
- var registration = registrations[j]; |
- var options = registration.options; |
- |
- // Only target ignores subtree. |
- if (node !== target && !options.subtree) |
- continue; |
- |
- var record = callback(options); |
- if (record) |
- registration.enqueue(record); |
- } |
- } |
- } |
- } |
- |
- var uidCounter = 0; |
- |
- /** |
- * The class that maps to the DOM MutationObserver interface. |
- * @param {Function} callback. |
- * @constructor |
- */ |
- function JsMutationObserver(callback) { |
- this.callback_ = callback; |
- this.nodes_ = []; |
- this.records_ = []; |
- this.uid_ = ++uidCounter; |
- } |
- |
- JsMutationObserver.prototype = { |
- observe: function(target, options) { |
- target = wrapIfNeeded(target); |
- |
- // 1.1 |
- if (!options.childList && !options.attributes && !options.characterData || |
- |
- // 1.2 |
- options.attributeOldValue && !options.attributes || |
- |
- // 1.3 |
- options.attributeFilter && options.attributeFilter.length && |
- !options.attributes || |
- |
- // 1.4 |
- options.characterDataOldValue && !options.characterData) { |
- |
- throw new SyntaxError(); |
- } |
- |
- var registrations = registrationsTable.get(target); |
- if (!registrations) |
- registrationsTable.set(target, registrations = []); |
- |
- // 2 |
- // If target's list of registered observers already includes a registered |
- // observer associated with the context object, replace that registered |
- // observer's options with options. |
- var registration; |
- for (var i = 0; i < registrations.length; i++) { |
- if (registrations[i].observer === this) { |
- registration = registrations[i]; |
- registration.removeListeners(); |
- registration.options = options; |
- break; |
- } |
- } |
- |
- // 3. |
- // Otherwise, add a new registered observer to target's list of registered |
- // observers with the context object as the observer and options as the |
- // options, and add target to context object's list of nodes on which it |
- // is registered. |
- if (!registration) { |
- registration = new Registration(this, target, options); |
- registrations.push(registration); |
- this.nodes_.push(target); |
- } |
- |
- registration.addListeners(); |
- }, |
- |
- 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) { |
- registration.removeListeners(); |
- 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; |
- } |
- }; |
- |
- /** |
- * @param {string} type |
- * @param {Node} target |
- * @constructor |
- */ |
- function MutationRecord(type, target) { |
- this.type = type; |
- this.target = target; |
- this.addedNodes = []; |
- this.removedNodes = []; |
- this.previousSibling = null; |
- this.nextSibling = null; |
- this.attributeName = null; |
- this.attributeNamespace = null; |
- this.oldValue = null; |
- } |
- |
- function copyMutationRecord(original) { |
- var record = new MutationRecord(original.type, original.target); |
- record.addedNodes = original.addedNodes.slice(); |
- record.removedNodes = original.removedNodes.slice(); |
- record.previousSibling = original.previousSibling; |
- record.nextSibling = original.nextSibling; |
- record.attributeName = original.attributeName; |
- record.attributeNamespace = original.attributeNamespace; |
- record.oldValue = original.oldValue; |
- return record; |
- }; |
- |
- // We keep track of the two (possibly one) records used in a single mutation. |
- var currentRecord, recordWithOldValue; |
- |
- /** |
- * Creates a record without |oldValue| and caches it as |currentRecord| for |
- * later use. |
- * @param {string} oldValue |
- * @return {MutationRecord} |
- */ |
- function getRecord(type, target) { |
- return currentRecord = new MutationRecord(type, target); |
- } |
- |
- /** |
- * Gets or creates a record with |oldValue| based in the |currentRecord| |
- * @param {string} oldValue |
- * @return {MutationRecord} |
- */ |
- function getRecordWithOldValue(oldValue) { |
- if (recordWithOldValue) |
- return recordWithOldValue; |
- recordWithOldValue = copyMutationRecord(currentRecord); |
- recordWithOldValue.oldValue = oldValue; |
- return recordWithOldValue; |
- } |
- |
- function clearRecords() { |
- currentRecord = recordWithOldValue = undefined; |
- } |
- |
- /** |
- * @param {MutationRecord} record |
- * @return {boolean} Whether the record represents a record from the current |
- * mutation event. |
- */ |
- function recordRepresentsCurrentMutation(record) { |
- return record === recordWithOldValue || record === currentRecord; |
- } |
- |
- /** |
- * Selects which record, if any, to replace the last record in the queue. |
- * This returns |null| if no record should be replaced. |
- * |
- * @param {MutationRecord} lastRecord |
- * @param {MutationRecord} newRecord |
- * @param {MutationRecord} |
- */ |
- function selectRecord(lastRecord, newRecord) { |
- if (lastRecord === newRecord) |
- return lastRecord; |
- |
- // Check if the the record we are adding represents the same record. If |
- // so, we keep the one with the oldValue in it. |
- if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) |
- return recordWithOldValue; |
- |
- return null; |
- } |
- |
- /** |
- * Class used to represent a registered observer. |
- * @param {MutationObserver} observer |
- * @param {Node} target |
- * @param {MutationObserverInit} options |
- * @constructor |
- */ |
- function Registration(observer, target, options) { |
- this.observer = observer; |
- this.target = target; |
- this.options = options; |
- this.transientObservedNodes = []; |
- } |
- |
- Registration.prototype = { |
- enqueue: function(record) { |
- var records = this.observer.records_; |
- var length = records.length; |
- |
- // There are cases where we replace the last record with the new record. |
- // For example if the record represents the same mutation we need to use |
- // the one with the oldValue. If we get same record (this can happen as we |
- // walk up the tree) we ignore the new record. |
- if (records.length > 0) { |
- var lastRecord = records[length - 1]; |
- var recordToReplaceLast = selectRecord(lastRecord, record); |
- if (recordToReplaceLast) { |
- records[length - 1] = recordToReplaceLast; |
- return; |
- } |
- } else { |
- scheduleCallback(this.observer); |
- } |
- |
- records[length] = record; |
- }, |
- |
- addListeners: function() { |
- this.addListeners_(this.target); |
- }, |
- |
- addListeners_: function(node) { |
- var options = this.options; |
- if (options.attributes) |
- node.addEventListener('DOMAttrModified', this, true); |
- |
- if (options.characterData) |
- node.addEventListener('DOMCharacterDataModified', this, true); |
- |
- if (options.childList) |
- node.addEventListener('DOMNodeInserted', this, true); |
- |
- if (options.childList || options.subtree) |
- node.addEventListener('DOMNodeRemoved', this, true); |
- }, |
- |
- removeListeners: function() { |
- this.removeListeners_(this.target); |
- }, |
- |
- removeListeners_: function(node) { |
- var options = this.options; |
- if (options.attributes) |
- node.removeEventListener('DOMAttrModified', this, true); |
- |
- if (options.characterData) |
- node.removeEventListener('DOMCharacterDataModified', this, true); |
- |
- if (options.childList) |
- node.removeEventListener('DOMNodeInserted', this, true); |
- |
- if (options.childList || options.subtree) |
- node.removeEventListener('DOMNodeRemoved', this, true); |
- }, |
- |
- /** |
- * 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.addListeners_(node); |
- 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 = []; |
- |
- transientObservedNodes.forEach(function(node) { |
- // Transient observers are never added to the target. |
- this.removeListeners_(node); |
- |
- var registrations = registrationsTable.get(node); |
- for (var i = 0; i < registrations.length; i++) { |
- if (registrations[i] === this) { |
- registrations.splice(i, 1); |
- // Each node can only have one registered observer associated with |
- // this observer. |
- break; |
- } |
- } |
- }, this); |
- }, |
- |
- handleEvent: function(e) { |
- // Stop propagation since we are managing the propagation manually. |
- // This means that other mutation events on the page will not work |
- // correctly but that is by design. |
- e.stopImmediatePropagation(); |
- |
- switch (e.type) { |
- case 'DOMAttrModified': |
- // http://dom.spec.whatwg.org/#concept-mo-queue-attributes |
- |
- var name = e.attrName; |
- var namespace = e.relatedNode.namespaceURI; |
- var target = e.target; |
- |
- // 1. |
- var record = new getRecord('attributes', target); |
- record.attributeName = name; |
- record.attributeNamespace = namespace; |
- |
- // 2. |
- var oldValue = |
- e.attrChange === MutationEvent.ADDITION ? null : e.prevValue; |
- |
- forEachAncestorAndObserverEnqueueRecord(target, function(options) { |
- // 3.1, 4.2 |
- if (!options.attributes) |
- return; |
- |
- // 3.2, 4.3 |
- if (options.attributeFilter && options.attributeFilter.length && |
- options.attributeFilter.indexOf(name) === -1 && |
- options.attributeFilter.indexOf(namespace) === -1) { |
- return; |
- } |
- // 3.3, 4.4 |
- if (options.attributeOldValue) |
- return getRecordWithOldValue(oldValue); |
- |
- // 3.4, 4.5 |
- return record; |
- }); |
- |
- break; |
- |
- case 'DOMCharacterDataModified': |
- // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata |
- var target = e.target; |
- |
- // 1. |
- var record = getRecord('characterData', target); |
- |
- // 2. |
- var oldValue = e.prevValue; |
- |
- |
- forEachAncestorAndObserverEnqueueRecord(target, function(options) { |
- // 3.1, 4.2 |
- if (!options.characterData) |
- return; |
- |
- // 3.2, 4.3 |
- if (options.characterDataOldValue) |
- return getRecordWithOldValue(oldValue); |
- |
- // 3.3, 4.4 |
- return record; |
- }); |
- |
- break; |
- |
- case 'DOMNodeRemoved': |
- this.addTransientObserver(e.target); |
- // Fall through. |
- case 'DOMNodeInserted': |
- // http://dom.spec.whatwg.org/#concept-mo-queue-childlist |
- var target = e.relatedNode; |
- var changedNode = e.target; |
- var addedNodes, removedNodes; |
- if (e.type === 'DOMNodeInserted') { |
- addedNodes = [changedNode]; |
- removedNodes = []; |
- } else { |
- |
- addedNodes = []; |
- removedNodes = [changedNode]; |
- } |
- var previousSibling = changedNode.previousSibling; |
- var nextSibling = changedNode.nextSibling; |
- |
- // 1. |
- var record = getRecord('childList', target); |
- record.addedNodes = addedNodes; |
- record.removedNodes = removedNodes; |
- record.previousSibling = previousSibling; |
- record.nextSibling = nextSibling; |
- |
- forEachAncestorAndObserverEnqueueRecord(target, function(options) { |
- // 2.1, 3.2 |
- if (!options.childList) |
- return; |
- |
- // 2.2, 3.3 |
- return record; |
- }); |
- |
- } |
- |
- clearRecords(); |
- } |
- }; |
- |
- global.JsMutationObserver = JsMutationObserver; |
- |
- if (!global.MutationObserver) |
- global.MutationObserver = JsMutationObserver; |
- |
- |
-})(this); |
- |
-/* |
- * 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. |
- */ |
-window.HTMLImports = window.HTMLImports || {flags:{}}; |
-/* |
- * 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) { |
- |
- // imports |
- var path = scope.path; |
- var xhr = scope.xhr; |
- var flags = scope.flags; |
- |
- // TODO(sorvell): this loader supports a dynamic list of urls |
- // and an oncomplete callback that is called when the loader is done. |
- // The polyfill currently does *not* need this dynamism or the onComplete |
- // concept. Because of this, the loader could be simplified quite a bit. |
- var Loader = function(onLoad, onComplete) { |
- this.cache = {}; |
- this.onload = onLoad; |
- this.oncomplete = onComplete; |
- this.inflight = 0; |
- this.pending = {}; |
- }; |
- |
- Loader.prototype = { |
- addNodes: function(nodes) { |
- // number of transactions to complete |
- this.inflight += nodes.length; |
- // commence transactions |
- for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { |
- this.require(n); |
- } |
- // anything to do? |
- this.checkDone(); |
- }, |
- addNode: function(node) { |
- // number of transactions to complete |
- this.inflight++; |
- // commence transactions |
- this.require(node); |
- // anything to do? |
- this.checkDone(); |
- }, |
- require: function(elt) { |
- var url = elt.src || elt.href; |
- // ensure we have a standard url that can be used |
- // reliably for deduping. |
- // TODO(sjmiles): ad-hoc |
- elt.__nodeUrl = url; |
- // deduplication |
- if (!this.dedupe(url, elt)) { |
- // fetch this resource |
- this.fetch(url, elt); |
- } |
- }, |
- dedupe: function(url, elt) { |
- if (this.pending[url]) { |
- // add to list of nodes waiting for inUrl |
- this.pending[url].push(elt); |
- // don't need fetch |
- return true; |
- } |
- var resource; |
- if (this.cache[url]) { |
- this.onload(url, elt, this.cache[url]); |
- // finished this transaction |
- this.tail(); |
- // don't need fetch |
- return true; |
- } |
- // first node waiting for inUrl |
- this.pending[url] = [elt]; |
- // need fetch (not a dupe) |
- return false; |
- }, |
- fetch: function(url, elt) { |
- flags.load && console.log('fetch', url, elt); |
- if (url.match(/^data:/)) { |
- // Handle Data URI Scheme |
- var pieces = url.split(','); |
- var header = pieces[0]; |
- var body = pieces[1]; |
- if(header.indexOf(';base64') > -1) { |
- body = atob(body); |
- } else { |
- body = decodeURIComponent(body); |
- } |
- setTimeout(function() { |
- this.receive(url, elt, null, body); |
- }.bind(this), 0); |
- } else { |
- var receiveXhr = function(err, resource) { |
- this.receive(url, elt, err, resource); |
- }.bind(this); |
- xhr.load(url, receiveXhr); |
- // TODO(sorvell): blocked on) |
- // https://code.google.com/p/chromium/issues/detail?id=257221 |
- // xhr'ing for a document makes scripts in imports runnable; otherwise |
- // they are not; however, it requires that we have doctype=html in |
- // the import which is unacceptable. This is only needed on Chrome |
- // to avoid the bug above. |
- /* |
- if (isDocumentLink(elt)) { |
- xhr.loadDocument(url, receiveXhr); |
- } else { |
- xhr.load(url, receiveXhr); |
- } |
- */ |
- } |
- }, |
- receive: function(url, elt, err, resource) { |
- this.cache[url] = resource; |
- var $p = this.pending[url]; |
- for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) { |
- //if (!err) { |
- this.onload(url, p, resource); |
- //} |
- this.tail(); |
- } |
- this.pending[url] = null; |
- }, |
- tail: function() { |
- --this.inflight; |
- this.checkDone(); |
- }, |
- checkDone: function() { |
- if (!this.inflight) { |
- this.oncomplete(); |
- } |
- } |
- }; |
- |
- xhr = xhr || { |
- async: true, |
- ok: function(request) { |
- return (request.status >= 200 && request.status < 300) |
- || (request.status === 304) |
- || (request.status === 0); |
- }, |
- load: function(url, next, nextContext) { |
- var request = new XMLHttpRequest(); |
- if (scope.flags.debug || scope.flags.bust) { |
- url += '?' + Math.random(); |
- } |
- request.open('GET', url, xhr.async); |
- request.addEventListener('readystatechange', function(e) { |
- if (request.readyState === 4) { |
- next.call(nextContext, !xhr.ok(request) && request, |
- request.response || request.responseText, url); |
- } |
- }); |
- request.send(); |
- return request; |
- }, |
- loadDocument: function(url, next, nextContext) { |
- this.load(url, next, nextContext).responseType = 'document'; |
- } |
- }; |
- |
- // exports |
- scope.xhr = xhr; |
- scope.Loader = Loader; |
- |
-})(window.HTMLImports); |
- |
-/* |
- * 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) { |
- |
-var IMPORT_LINK_TYPE = 'import'; |
-var flags = scope.flags; |
-var isIe = /Trident/.test(navigator.userAgent); |
-// TODO(sorvell): SD polyfill intrusion |
-var mainDoc = window.ShadowDOMPolyfill ? |
- window.ShadowDOMPolyfill.wrapIfNeeded(document) : document; |
- |
-// importParser |
-// highlander object to manage parsing of imports |
-// parses import related elements |
-// and ensures proper parse order |
-// parse order is enforced by crawling the tree and monitoring which elements |
-// have been parsed; async parsing is also supported. |
- |
-// highlander object for parsing a document tree |
-var importParser = { |
- // parse selectors for main document elements |
- documentSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']', |
- // parse selectors for import document elements |
- importsSelectors: [ |
- 'link[rel=' + IMPORT_LINK_TYPE + ']', |
- 'link[rel=stylesheet]', |
- 'style', |
- 'script:not([type])', |
- 'script[type="text/javascript"]' |
- ].join(','), |
- map: { |
- link: 'parseLink', |
- script: 'parseScript', |
- style: 'parseStyle' |
- }, |
- // try to parse the next import in the tree |
- parseNext: function() { |
- var next = this.nextToParse(); |
- if (next) { |
- this.parse(next); |
- } |
- }, |
- parse: function(elt) { |
- if (this.isParsed(elt)) { |
- flags.parse && console.log('[%s] is already parsed', elt.localName); |
- return; |
- } |
- var fn = this[this.map[elt.localName]]; |
- if (fn) { |
- this.markParsing(elt); |
- fn.call(this, elt); |
- } |
- }, |
- // only 1 element may be parsed at a time; parsing is async so, each |
- // parsing implementation must inform the system that parsing is complete |
- // via markParsingComplete. |
- markParsing: function(elt) { |
- flags.parse && console.log('parsing', elt); |
- this.parsingElement = elt; |
- }, |
- markParsingComplete: function(elt) { |
- elt.__importParsed = true; |
- if (elt.__importElement) { |
- elt.__importElement.__importParsed = true; |
- } |
- this.parsingElement = null; |
- flags.parse && console.log('completed', elt); |
- this.parseNext(); |
- }, |
- parseImport: function(elt) { |
- elt.import.__importParsed = true; |
- // TODO(sorvell): consider if there's a better way to do this; |
- // expose an imports parsing hook; this is needed, for example, by the |
- // CustomElements polyfill. |
- if (HTMLImports.__importsParsingHook) { |
- HTMLImports.__importsParsingHook(elt); |
- } |
- // fire load event |
- if (elt.__resource) { |
- elt.dispatchEvent(new CustomEvent('load', {bubbles: false})); |
- } else { |
- elt.dispatchEvent(new CustomEvent('error', {bubbles: false})); |
- } |
- // TODO(sorvell): workaround for Safari addEventListener not working |
- // for elements not in the main document. |
- if (elt.__pending) { |
- var fn; |
- while (elt.__pending.length) { |
- fn = elt.__pending.shift(); |
- if (fn) { |
- fn({target: elt}); |
- } |
- } |
- } |
- this.markParsingComplete(elt); |
- }, |
- parseLink: function(linkElt) { |
- if (nodeIsImport(linkElt)) { |
- this.parseImport(linkElt); |
- } else { |
- // make href absolute |
- linkElt.href = linkElt.href; |
- this.parseGeneric(linkElt); |
- } |
- }, |
- parseStyle: function(elt) { |
- // TODO(sorvell): style element load event can just not fire so clone styles |
- var src = elt; |
- elt = cloneStyle(elt); |
- elt.__importElement = src; |
- this.parseGeneric(elt); |
- }, |
- parseGeneric: function(elt) { |
- this.trackElement(elt); |
- document.head.appendChild(elt); |
- }, |
- // tracks when a loadable element has loaded |
- trackElement: function(elt, callback) { |
- var self = this; |
- var done = function(e) { |
- if (callback) { |
- callback(e); |
- } |
- self.markParsingComplete(elt); |
- }; |
- elt.addEventListener('load', done); |
- elt.addEventListener('error', done); |
- |
- // NOTE: IE does not fire "load" event for styles that have already loaded |
- // This is in violation of the spec, so we try our hardest to work around it |
- if (isIe && elt.localName === 'style') { |
- var fakeLoad = false; |
- // If there's not @import in the textContent, assume it has loaded |
- if (elt.textContent.indexOf('@import') == -1) { |
- fakeLoad = true; |
- // if we have a sheet, we have been parsed |
- } else if (elt.sheet) { |
- fakeLoad = true; |
- var csr = elt.sheet.cssRules; |
- var len = csr ? csr.length : 0; |
- // search the rules for @import's |
- for (var i = 0, r; (i < len) && (r = csr[i]); i++) { |
- if (r.type === CSSRule.IMPORT_RULE) { |
- // if every @import has resolved, fake the load |
- fakeLoad = fakeLoad && Boolean(r.styleSheet); |
- } |
- } |
- } |
- // dispatch a fake load event and continue parsing |
- if (fakeLoad) { |
- elt.dispatchEvent(new CustomEvent('load', {bubbles: false})); |
- } |
- } |
- }, |
- // NOTE: execute scripts by injecting them and watching for the load/error |
- // event. Inline scripts are handled via dataURL's because browsers tend to |
- // provide correct parsing errors in this case. If this has any compatibility |
- // issues, we can switch to injecting the inline script with textContent. |
- // Scripts with dataURL's do not appear to generate load events and therefore |
- // we assume they execute synchronously. |
- parseScript: function(scriptElt) { |
- var script = document.createElement('script'); |
- script.__importElement = scriptElt; |
- script.src = scriptElt.src ? scriptElt.src : |
- generateScriptDataUrl(scriptElt); |
- scope.currentScript = scriptElt; |
- this.trackElement(script, function(e) { |
- script.parentNode.removeChild(script); |
- scope.currentScript = null; |
- }); |
- document.head.appendChild(script); |
- }, |
- // determine the next element in the tree which should be parsed |
- nextToParse: function() { |
- return !this.parsingElement && this.nextToParseInDoc(mainDoc); |
- }, |
- nextToParseInDoc: function(doc, link) { |
- var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc)); |
- for (var i=0, l=nodes.length, p=0, n; (i<l) && (n=nodes[i]); i++) { |
- if (!this.isParsed(n)) { |
- if (this.hasResource(n)) { |
- return nodeIsImport(n) ? this.nextToParseInDoc(n.import, n) : n; |
- } else { |
- return; |
- } |
- } |
- } |
- // all nodes have been parsed, ready to parse import, if any |
- return link; |
- }, |
- // return the set of parse selectors relevant for this node. |
- parseSelectorsForNode: function(node) { |
- var doc = node.ownerDocument || node; |
- return doc === mainDoc ? this.documentSelectors : this.importsSelectors; |
- }, |
- isParsed: function(node) { |
- return node.__importParsed; |
- }, |
- hasResource: function(node) { |
- if (nodeIsImport(node) && !node.import) { |
- return false; |
- } |
- return true; |
- } |
-}; |
- |
-function nodeIsImport(elt) { |
- return (elt.localName === 'link') && (elt.rel === IMPORT_LINK_TYPE); |
-} |
- |
-function generateScriptDataUrl(script) { |
- var scriptContent = generateScriptContent(script), b64; |
- try { |
- b64 = btoa(scriptContent); |
- } catch(e) { |
- b64 = btoa(unescape(encodeURIComponent(scriptContent))); |
- console.warn('Script contained non-latin characters that were forced ' + |
- 'to latin. Some characters may be wrong.', script); |
- } |
- return 'data:text/javascript;base64,' + b64; |
-} |
- |
-function generateScriptContent(script) { |
- return script.textContent + generateSourceMapHint(script); |
-} |
- |
-// calculate source map hint |
-function generateSourceMapHint(script) { |
- var moniker = script.__nodeUrl; |
- if (!moniker) { |
- moniker = script.ownerDocument.baseURI; |
- // there could be more than one script this url |
- var tag = '[' + Math.floor((Math.random()+1)*1000) + ']'; |
- // TODO(sjmiles): Polymer hack, should be pluggable if we need to allow |
- // this sort of thing |
- var matches = script.textContent.match(/Polymer\(['"]([^'"]*)/); |
- tag = matches && matches[1] || tag; |
- // tag the moniker |
- moniker += '/' + tag + '.js'; |
- } |
- return '\n//# sourceURL=' + moniker + '\n'; |
-} |
- |
-// style/stylesheet handling |
- |
-// clone style with proper path resolution for main document |
-// NOTE: styles are the only elements that require direct path fixup. |
-function cloneStyle(style) { |
- var clone = style.ownerDocument.createElement('style'); |
- clone.textContent = style.textContent; |
- path.resolveUrlsInStyle(clone); |
- return clone; |
-} |
- |
-// path fixup: style elements in imports must be made relative to the main |
-// document. We fixup url's in url() and @import. |
-var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; |
-var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; |
- |
-var path = { |
- resolveUrlsInStyle: function(style) { |
- var doc = style.ownerDocument; |
- var resolver = doc.createElement('a'); |
- style.textContent = this.resolveUrlsInCssText(style.textContent, resolver); |
- return style; |
- }, |
- resolveUrlsInCssText: function(cssText, urlObj) { |
- var r = this.replaceUrls(cssText, urlObj, CSS_URL_REGEXP); |
- r = this.replaceUrls(r, urlObj, CSS_IMPORT_REGEXP); |
- return r; |
- }, |
- replaceUrls: function(text, urlObj, regexp) { |
- return text.replace(regexp, function(m, pre, url, post) { |
- var urlPath = url.replace(/["']/g, ''); |
- urlObj.href = urlPath; |
- urlPath = urlObj.href; |
- return pre + '\'' + urlPath + '\'' + post; |
- }); |
- } |
-} |
- |
-// exports |
-scope.parser = importParser; |
-scope.path = path; |
-scope.isIE = isIe; |
- |
-})(HTMLImports); |
- |
-/* |
- * 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) { |
- |
-var hasNative = ('import' in document.createElement('link')); |
-var useNative = hasNative; |
-var flags = scope.flags; |
-var IMPORT_LINK_TYPE = 'import'; |
- |
-// TODO(sorvell): SD polyfill intrusion |
-var mainDoc = window.ShadowDOMPolyfill ? |
- ShadowDOMPolyfill.wrapIfNeeded(document) : document; |
- |
-if (!useNative) { |
- |
- // imports |
- var xhr = scope.xhr; |
- var Loader = scope.Loader; |
- var parser = scope.parser; |
- |
- // importer |
- // highlander object to manage loading of imports |
- |
- // for any document, importer: |
- // - loads any linked import documents (with deduping) |
- |
- var importer = { |
- documents: {}, |
- // nodes to load in the mian document |
- documentPreloadSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']', |
- // nodes to load in imports |
- importsPreloadSelectors: [ |
- 'link[rel=' + IMPORT_LINK_TYPE + ']' |
- ].join(','), |
- loadNode: function(node) { |
- importLoader.addNode(node); |
- }, |
- // load all loadable elements within the parent element |
- loadSubtree: function(parent) { |
- var nodes = this.marshalNodes(parent); |
- // add these nodes to loader's queue |
- importLoader.addNodes(nodes); |
- }, |
- marshalNodes: function(parent) { |
- // all preloadable nodes in inDocument |
- return parent.querySelectorAll(this.loadSelectorsForNode(parent)); |
- }, |
- // find the proper set of load selectors for a given node |
- loadSelectorsForNode: function(node) { |
- var doc = node.ownerDocument || node; |
- return doc === mainDoc ? this.documentPreloadSelectors : |
- this.importsPreloadSelectors; |
- }, |
- loaded: function(url, elt, resource) { |
- flags.load && console.log('loaded', url, elt); |
- // store generic resource |
- // TODO(sorvell): fails for nodes inside <template>.content |
- // see https://code.google.com/p/chromium/issues/detail?id=249381. |
- elt.__resource = resource; |
- if (isDocumentLink(elt)) { |
- var doc = this.documents[url]; |
- // if we've never seen a document at this url |
- if (!doc) { |
- // generate an HTMLDocument from data |
- doc = makeDocument(resource, url); |
- doc.__importLink = elt; |
- // TODO(sorvell): we cannot use MO to detect parsed nodes because |
- // SD polyfill does not report these as mutations. |
- this.bootDocument(doc); |
- // cache document |
- this.documents[url] = doc; |
- } |
- // don't store import record until we're actually loaded |
- // store document resource |
- elt.import = doc; |
- } |
- parser.parseNext(); |
- }, |
- bootDocument: function(doc) { |
- this.loadSubtree(doc); |
- this.observe(doc); |
- parser.parseNext(); |
- }, |
- loadedAll: function() { |
- parser.parseNext(); |
- } |
- }; |
- |
- // loader singleton |
- var importLoader = new Loader(importer.loaded.bind(importer), |
- importer.loadedAll.bind(importer)); |
- |
- function isDocumentLink(elt) { |
- return isLinkRel(elt, IMPORT_LINK_TYPE); |
- } |
- |
- function isLinkRel(elt, rel) { |
- return elt.localName === 'link' && elt.getAttribute('rel') === rel; |
- } |
- |
- function isScript(elt) { |
- return elt.localName === 'script'; |
- } |
- |
- function makeDocument(resource, url) { |
- // create a new HTML document |
- var doc = resource; |
- if (!(doc instanceof Document)) { |
- doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE); |
- } |
- // cache the new document's source url |
- doc._URL = url; |
- // establish a relative path via <base> |
- var base = doc.createElement('base'); |
- base.setAttribute('href', url); |
- // add baseURI support to browsers (IE) that lack it. |
- if (!doc.baseURI) { |
- doc.baseURI = url; |
- } |
- // ensure UTF-8 charset |
- var meta = doc.createElement('meta'); |
- meta.setAttribute('charset', 'utf-8'); |
- |
- doc.head.appendChild(meta); |
- doc.head.appendChild(base); |
- // install HTML last as it may trigger CustomElement upgrades |
- // TODO(sjmiles): problem wrt to template boostrapping below, |
- // template bootstrapping must (?) come before element upgrade |
- // but we cannot bootstrap templates until they are in a document |
- // which is too late |
- if (!(resource instanceof Document)) { |
- // install html |
- doc.body.innerHTML = resource; |
- } |
- // TODO(sorvell): ideally this code is not aware of Template polyfill, |
- // but for now the polyfill needs help to bootstrap these templates |
- if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) { |
- HTMLTemplateElement.bootstrap(doc); |
- } |
- return doc; |
- } |
-} else { |
- // do nothing if using native imports |
- var importer = {}; |
-} |
- |
-// NOTE: We cannot polyfill document.currentScript because it's not possible |
-// both to override and maintain the ability to capture the native value; |
-// therefore we choose to expose _currentScript both when native imports |
-// and the polyfill are in use. |
-var currentScriptDescriptor = { |
- get: function() { |
- return HTMLImports.currentScript || document.currentScript; |
- }, |
- configurable: true |
-}; |
- |
-Object.defineProperty(document, '_currentScript', currentScriptDescriptor); |
-Object.defineProperty(mainDoc, '_currentScript', currentScriptDescriptor); |
- |
-// Polyfill document.baseURI for browsers without it. |
-if (!document.baseURI) { |
- var baseURIDescriptor = { |
- get: function() { |
- return window.location.href; |
- }, |
- configurable: true |
- }; |
- |
- Object.defineProperty(document, 'baseURI', baseURIDescriptor); |
- Object.defineProperty(mainDoc, 'baseURI', baseURIDescriptor); |
-} |
- |
-// call a callback when all HTMLImports in the document at call (or at least |
-// document ready) time have loaded. |
-// 1. ensure the document is in a ready state (has dom), then |
-// 2. watch for loading of imports and call callback when done |
-function whenImportsReady(callback, doc) { |
- doc = doc || mainDoc; |
- // if document is loading, wait and try again |
- whenDocumentReady(function() { |
- watchImportsLoad(callback, doc); |
- }, doc); |
-} |
- |
-// call the callback when the document is in a ready state (has dom) |
-var requiredReadyState = HTMLImports.isIE ? 'complete' : 'interactive'; |
-var READY_EVENT = 'readystatechange'; |
-function isDocumentReady(doc) { |
- return (doc.readyState === 'complete' || |
- doc.readyState === requiredReadyState); |
-} |
- |
-// call <callback> when we ensure the document is in a ready state |
-function whenDocumentReady(callback, doc) { |
- if (!isDocumentReady(doc)) { |
- var checkReady = function() { |
- if (doc.readyState === 'complete' || |
- doc.readyState === requiredReadyState) { |
- doc.removeEventListener(READY_EVENT, checkReady); |
- whenDocumentReady(callback, doc); |
- } |
- } |
- doc.addEventListener(READY_EVENT, checkReady); |
- } else if (callback) { |
- callback(); |
- } |
-} |
- |
-// call <callback> when we ensure all imports have loaded |
-function watchImportsLoad(callback, doc) { |
- var imports = doc.querySelectorAll('link[rel=import]'); |
- var loaded = 0, l = imports.length; |
- function checkDone(d) { |
- if (loaded == l) { |
- // go async to ensure parser isn't stuck on a script tag |
- requestAnimationFrame(callback); |
- } |
- } |
- function loadedImport(e) { |
- loaded++; |
- checkDone(); |
- } |
- if (l) { |
- for (var i=0, imp; (i<l) && (imp=imports[i]); i++) { |
- if (isImportLoaded(imp)) { |
- loadedImport.call(imp); |
- } else { |
- imp.addEventListener('load', loadedImport); |
- imp.addEventListener('error', loadedImport); |
- } |
- } |
- } else { |
- checkDone(); |
- } |
-} |
- |
-function isImportLoaded(link) { |
- return useNative ? (link.import && (link.import.readyState !== 'loading')) : |
- link.__importParsed; |
-} |
- |
-// exports |
-scope.hasNative = hasNative; |
-scope.useNative = useNative; |
-scope.importer = importer; |
-scope.whenImportsReady = whenImportsReady; |
-scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |
-scope.isImportLoaded = isImportLoaded; |
-scope.importLoader = importLoader; |
- |
-})(window.HTMLImports); |
- |
- /* |
-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){ |
- |
-var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; |
-var importSelector = 'link[rel=' + IMPORT_LINK_TYPE + ']'; |
-var importer = scope.importer; |
- |
-// we track mutations for addedNodes, looking for imports |
-function handler(mutations) { |
- for (var i=0, l=mutations.length, m; (i<l) && (m=mutations[i]); i++) { |
- if (m.type === 'childList' && m.addedNodes.length) { |
- addedNodes(m.addedNodes); |
- } |
- } |
-} |
- |
-// find loadable elements and add them to the importer |
-function addedNodes(nodes) { |
- for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { |
- if (shouldLoadNode(n)) { |
- importer.loadNode(n); |
- } |
- if (n.children && n.children.length) { |
- addedNodes(n.children); |
- } |
- } |
-} |
- |
-function shouldLoadNode(node) { |
- return (node.nodeType === 1) && matches.call(node, |
- importer.loadSelectorsForNode(node)); |
-} |
- |
-// x-plat matches |
-var matches = HTMLElement.prototype.matches || |
- HTMLElement.prototype.matchesSelector || |
- HTMLElement.prototype.webkitMatchesSelector || |
- HTMLElement.prototype.mozMatchesSelector || |
- HTMLElement.prototype.msMatchesSelector; |
- |
-var observer = new MutationObserver(handler); |
- |
-// observe the given root for loadable elements |
-function observe(root) { |
- observer.observe(root, {childList: true, subtree: true}); |
-} |
- |
-// exports |
-// TODO(sorvell): factor so can put on scope |
-scope.observe = observe; |
-importer.observe = observe; |
- |
-})(HTMLImports); |
- |
-/* |
- * 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(){ |
- |
-// bootstrap |
- |
-// IE shim for CustomEvent |
-if (typeof window.CustomEvent !== 'function') { |
- window.CustomEvent = function(inType, dictionary) { |
- var e = document.createEvent('HTMLEvents'); |
- e.initEvent(inType, |
- dictionary.bubbles === false ? false : true, |
- dictionary.cancelable === false ? false : true, |
- dictionary.detail); |
- return e; |
- }; |
-} |
- |
-// TODO(sorvell): SD polyfill intrusion |
-var doc = window.ShadowDOMPolyfill ? |
- window.ShadowDOMPolyfill.wrapIfNeeded(document) : document; |
- |
-// Fire the 'HTMLImportsLoaded' event when imports in document at load time |
-// have loaded. This event is required to simulate the script blocking |
-// behavior of native imports. A main document script that needs to be sure |
-// imports have loaded should wait for this event. |
-HTMLImports.whenImportsReady(function() { |
- HTMLImports.ready = true; |
- HTMLImports.readyTime = new Date().getTime(); |
- doc.dispatchEvent( |
- new CustomEvent('HTMLImportsLoaded', {bubbles: true}) |
- ); |
-}); |
- |
- |
-// no need to bootstrap the polyfill when native imports is available. |
-if (!HTMLImports.useNative) { |
- function bootstrap() { |
- HTMLImports.importer.bootDocument(doc); |
- } |
- |
- // TODO(sorvell): SD polyfill does *not* generate mutations for nodes added |
- // by the parser. For this reason, we must wait until the dom exists to |
- // bootstrap. |
- if (document.readyState === 'complete' || |
- (document.readyState === 'interactive' && !window.attachEvent)) { |
- bootstrap(); |
- } else { |
- document.addEventListener('DOMContentLoaded', bootstrap); |
- } |
-} |
- |
-})(); |
- |
-/* |
- * 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. |
- */ |
-window.CustomElements = window.CustomElements || {flags:{}}; |
- /* |
-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){ |
- |
-var logFlags = window.logFlags || {}; |
-var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none'; |
- |
-// walk the subtree rooted at node, applying 'find(element, data)' function |
-// to each element |
-// if 'find' returns true for 'element', do not search element's subtree |
-function findAll(node, find, data) { |
- var e = node.firstElementChild; |
- if (!e) { |
- e = node.firstChild; |
- while (e && e.nodeType !== Node.ELEMENT_NODE) { |
- e = e.nextSibling; |
- } |
- } |
- while (e) { |
- if (find(e, data) !== true) { |
- findAll(e, find, data); |
- } |
- e = e.nextElementSibling; |
- } |
- return null; |
-} |
- |
-// walk all shadowRoots on a given node. |
-function forRoots(node, cb) { |
- var root = node.shadowRoot; |
- while(root) { |
- forSubtree(root, cb); |
- root = root.olderShadowRoot; |
- } |
-} |
- |
-// walk the subtree rooted at node, including descent into shadow-roots, |
-// applying 'cb' to each element |
-function forSubtree(node, cb) { |
- //logFlags.dom && node.childNodes && node.childNodes.length && console.group('subTree: ', node); |
- findAll(node, function(e) { |
- if (cb(e)) { |
- return true; |
- } |
- forRoots(e, cb); |
- }); |
- forRoots(node, cb); |
- //logFlags.dom && node.childNodes && node.childNodes.length && console.groupEnd(); |
-} |
- |
-// manage lifecycle on added node |
-function added(node) { |
- if (upgrade(node)) { |
- insertedNode(node); |
- return true; |
- } |
- inserted(node); |
-} |
- |
-// manage lifecycle on added node's subtree only |
-function addedSubtree(node) { |
- forSubtree(node, function(e) { |
- if (added(e)) { |
- return true; |
- } |
- }); |
-} |
- |
-// manage lifecycle on added node and it's subtree |
-function addedNode(node) { |
- return added(node) || addedSubtree(node); |
-} |
- |
-// upgrade custom elements at node, if applicable |
-function upgrade(node) { |
- if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) { |
- var type = node.getAttribute('is') || node.localName; |
- var definition = scope.registry[type]; |
- if (definition) { |
- logFlags.dom && console.group('upgrade:', node.localName); |
- scope.upgrade(node); |
- logFlags.dom && console.groupEnd(); |
- return true; |
- } |
- } |
-} |
- |
-function insertedNode(node) { |
- inserted(node); |
- if (inDocument(node)) { |
- forSubtree(node, function(e) { |
- inserted(e); |
- }); |
- } |
-} |
- |
-// TODO(sorvell): on platforms without MutationObserver, mutations may not be |
-// reliable and therefore attached/detached are not reliable. |
-// To make these callbacks less likely to fail, we defer all inserts and removes |
-// to give a chance for elements to be inserted into dom. |
-// This ensures attachedCallback fires for elements that are created and |
-// immediately added to dom. |
-var hasPolyfillMutations = (!window.MutationObserver || |
- (window.MutationObserver === window.JsMutationObserver)); |
-scope.hasPolyfillMutations = hasPolyfillMutations; |
- |
-var isPendingMutations = false; |
-var pendingMutations = []; |
-function deferMutation(fn) { |
- pendingMutations.push(fn); |
- if (!isPendingMutations) { |
- isPendingMutations = true; |
- var async = (window.Platform && window.Platform.endOfMicrotask) || |
- setTimeout; |
- async(takeMutations); |
- } |
-} |
- |
-function takeMutations() { |
- isPendingMutations = false; |
- var $p = pendingMutations; |
- for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) { |
- p(); |
- } |
- pendingMutations = []; |
-} |
- |
-function inserted(element) { |
- if (hasPolyfillMutations) { |
- deferMutation(function() { |
- _inserted(element); |
- }); |
- } else { |
- _inserted(element); |
- } |
-} |
- |
-// TODO(sjmiles): if there are descents into trees that can never have inDocument(*) true, fix this |
-function _inserted(element) { |
- // TODO(sjmiles): it's possible we were inserted and removed in the space |
- // of one microtask, in which case we won't be 'inDocument' here |
- // But there are other cases where we are testing for inserted without |
- // specific knowledge of mutations, and must test 'inDocument' to determine |
- // whether to call inserted |
- // If we can factor these cases into separate code paths we can have |
- // better diagnostics. |
- // TODO(sjmiles): when logging, do work on all custom elements so we can |
- // track behavior even when callbacks not defined |
- //console.log('inserted: ', element.localName); |
- if (element.attachedCallback || element.detachedCallback || (element.__upgraded__ && logFlags.dom)) { |
- logFlags.dom && console.group('inserted:', element.localName); |
- if (inDocument(element)) { |
- element.__inserted = (element.__inserted || 0) + 1; |
- // if we are in a 'removed' state, bluntly adjust to an 'inserted' state |
- if (element.__inserted < 1) { |
- element.__inserted = 1; |
- } |
- // if we are 'over inserted', squelch the callback |
- if (element.__inserted > 1) { |
- logFlags.dom && console.warn('inserted:', element.localName, |
- 'insert/remove count:', element.__inserted) |
- } else if (element.attachedCallback) { |
- logFlags.dom && console.log('inserted:', element.localName); |
- element.attachedCallback(); |
- } |
- } |
- logFlags.dom && console.groupEnd(); |
- } |
-} |
- |
-function removedNode(node) { |
- removed(node); |
- forSubtree(node, function(e) { |
- removed(e); |
- }); |
-} |
- |
-function removed(element) { |
- if (hasPolyfillMutations) { |
- deferMutation(function() { |
- _removed(element); |
- }); |
- } else { |
- _removed(element); |
- } |
-} |
- |
-function _removed(element) { |
- // TODO(sjmiles): temporary: do work on all custom elements so we can track |
- // behavior even when callbacks not defined |
- if (element.attachedCallback || element.detachedCallback || (element.__upgraded__ && logFlags.dom)) { |
- logFlags.dom && console.group('removed:', element.localName); |
- if (!inDocument(element)) { |
- element.__inserted = (element.__inserted || 0) - 1; |
- // if we are in a 'inserted' state, bluntly adjust to an 'removed' state |
- if (element.__inserted > 0) { |
- element.__inserted = 0; |
- } |
- // if we are 'over removed', squelch the callback |
- if (element.__inserted < 0) { |
- logFlags.dom && console.warn('removed:', element.localName, |
- 'insert/remove count:', element.__inserted) |
- } else if (element.detachedCallback) { |
- element.detachedCallback(); |
- } |
- } |
- logFlags.dom && console.groupEnd(); |
- } |
-} |
- |
-// SD polyfill intrustion due mainly to the fact that 'document' |
-// is not entirely wrapped |
-function wrapIfNeeded(node) { |
- return window.ShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) |
- : node; |
-} |
- |
-function inDocument(element) { |
- var p = element; |
- var doc = wrapIfNeeded(document); |
- while (p) { |
- if (p == doc) { |
- return true; |
- } |
- p = p.parentNode || p.host; |
- } |
-} |
- |
-function watchShadow(node) { |
- if (node.shadowRoot && !node.shadowRoot.__watched) { |
- logFlags.dom && console.log('watching shadow-root for: ', node.localName); |
- // watch all unwatched roots... |
- var root = node.shadowRoot; |
- while (root) { |
- watchRoot(root); |
- root = root.olderShadowRoot; |
- } |
- } |
-} |
- |
-function watchRoot(root) { |
- if (!root.__watched) { |
- observe(root); |
- root.__watched = true; |
- } |
-} |
- |
-function handler(mutations) { |
- // |
- if (logFlags.dom) { |
- var mx = mutations[0]; |
- if (mx && mx.type === 'childList' && mx.addedNodes) { |
- if (mx.addedNodes) { |
- var d = mx.addedNodes[0]; |
- while (d && d !== document && !d.host) { |
- d = d.parentNode; |
- } |
- var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || ''; |
- u = u.split('/?').shift().split('/').pop(); |
- } |
- } |
- console.group('mutations (%d) [%s]', mutations.length, u || ''); |
- } |
- // |
- mutations.forEach(function(mx) { |
- //logFlags.dom && console.group('mutation'); |
- if (mx.type === 'childList') { |
- forEach(mx.addedNodes, function(n) { |
- //logFlags.dom && console.log(n.localName); |
- if (!n.localName) { |
- return; |
- } |
- // nodes added may need lifecycle management |
- addedNode(n); |
- }); |
- // removed nodes may need lifecycle management |
- forEach(mx.removedNodes, function(n) { |
- //logFlags.dom && console.log(n.localName); |
- if (!n.localName) { |
- return; |
- } |
- removedNode(n); |
- }); |
- } |
- //logFlags.dom && console.groupEnd(); |
- }); |
- logFlags.dom && console.groupEnd(); |
-}; |
- |
-var observer = new MutationObserver(handler); |
- |
-function takeRecords() { |
- // TODO(sjmiles): ask Raf why we have to call handler ourselves |
- handler(observer.takeRecords()); |
- takeMutations(); |
-} |
- |
-var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |
- |
-function observe(inRoot) { |
- observer.observe(inRoot, {childList: true, subtree: true}); |
-} |
- |
-function observeDocument(doc) { |
- observe(doc); |
-} |
- |
-function upgradeDocument(doc) { |
- logFlags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').pop()); |
- addedNode(doc); |
- logFlags.dom && console.groupEnd(); |
-} |
- |
-function upgradeDocumentTree(doc) { |
- doc = wrapIfNeeded(doc); |
- //console.log('upgradeDocumentTree: ', (doc.baseURI).split('/').pop()); |
- // upgrade contained imported documents |
- var imports = doc.querySelectorAll('link[rel=' + IMPORT_LINK_TYPE + ']'); |
- for (var i=0, l=imports.length, n; (i<l) && (n=imports[i]); i++) { |
- if (n.import && n.import.__parsed) { |
- upgradeDocumentTree(n.import); |
- } |
- } |
- upgradeDocument(doc); |
-} |
- |
-// exports |
-scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |
-scope.watchShadow = watchShadow; |
-scope.upgradeDocumentTree = upgradeDocumentTree; |
-scope.upgradeAll = addedNode; |
-scope.upgradeSubtree = addedSubtree; |
-scope.insertedNode = insertedNode; |
- |
-scope.observeDocument = observeDocument; |
-scope.upgradeDocument = upgradeDocument; |
- |
-scope.takeRecords = takeRecords; |
- |
-})(window.CustomElements); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * Implements `document.register` |
- * @module CustomElements |
-*/ |
- |
-/** |
- * Polyfilled extensions to the `document` object. |
- * @class Document |
-*/ |
- |
-(function(scope) { |
- |
-// imports |
- |
-if (!scope) { |
- scope = window.CustomElements = {flags:{}}; |
-} |
-var flags = scope.flags; |
- |
-// native document.registerElement? |
- |
-var hasNative = Boolean(document.registerElement); |
-// TODO(sorvell): See https://github.com/Polymer/polymer/issues/399 |
-// we'll address this by defaulting to CE polyfill in the presence of the SD |
-// polyfill. This will avoid spamming excess attached/detached callbacks. |
-// If there is a compelling need to run CE native with SD polyfill, |
-// we'll need to fix this issue. |
-var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill; |
- |
-if (useNative) { |
- |
- // stub |
- var nop = function() {}; |
- |
- // exports |
- scope.registry = {}; |
- scope.upgradeElement = nop; |
- |
- scope.watchShadow = nop; |
- scope.upgrade = nop; |
- scope.upgradeAll = nop; |
- scope.upgradeSubtree = nop; |
- scope.observeDocument = nop; |
- scope.upgradeDocument = nop; |
- scope.upgradeDocumentTree = nop; |
- scope.takeRecords = nop; |
- scope.reservedTagList = []; |
- |
-} else { |
- |
- /** |
- * Registers a custom tag name with the document. |
- * |
- * When a registered element is created, a `readyCallback` method is called |
- * in the scope of the element. The `readyCallback` method can be specified on |
- * either `options.prototype` or `options.lifecycle` with the latter taking |
- * precedence. |
- * |
- * @method register |
- * @param {String} name The tag name to register. Must include a dash ('-'), |
- * for example 'x-component'. |
- * @param {Object} options |
- * @param {String} [options.extends] |
- * (_off spec_) Tag name of an element to extend (or blank for a new |
- * element). This parameter is not part of the specification, but instead |
- * is a hint for the polyfill because the extendee is difficult to infer. |
- * Remember that the input prototype must chain to the extended element's |
- * prototype (or HTMLElement.prototype) regardless of the value of |
- * `extends`. |
- * @param {Object} options.prototype The prototype to use for the new |
- * element. The prototype must inherit from HTMLElement. |
- * @param {Object} [options.lifecycle] |
- * Callbacks that fire at important phases in the life of the custom |
- * element. |
- * |
- * @example |
- * FancyButton = document.registerElement("fancy-button", { |
- * extends: 'button', |
- * prototype: Object.create(HTMLButtonElement.prototype, { |
- * readyCallback: { |
- * value: function() { |
- * console.log("a fancy-button was created", |
- * } |
- * } |
- * }) |
- * }); |
- * @return {Function} Constructor for the newly registered type. |
- */ |
- function register(name, options) { |
- //console.warn('document.registerElement("' + name + '", ', options, ')'); |
- // construct a defintion out of options |
- // TODO(sjmiles): probably should clone options instead of mutating it |
- var definition = options || {}; |
- if (!name) { |
- // TODO(sjmiles): replace with more appropriate error (EricB can probably |
- // offer guidance) |
- throw new Error('document.registerElement: first argument `name` must not be empty'); |
- } |
- if (name.indexOf('-') < 0) { |
- // TODO(sjmiles): replace with more appropriate error (EricB can probably |
- // offer guidance) |
- throw new Error('document.registerElement: first argument (\'name\') must contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); |
- } |
- // prevent registering reserved names |
- if (isReservedTag(name)) { |
- throw new Error('Failed to execute \'registerElement\' on \'Document\': Registration failed for type \'' + String(name) + '\'. The type name is invalid.'); |
- } |
- // elements may only be registered once |
- if (getRegisteredDefinition(name)) { |
- throw new Error('DuplicateDefinitionError: a type with name \'' + String(name) + '\' is already registered'); |
- } |
- // must have a prototype, default to an extension of HTMLElement |
- // TODO(sjmiles): probably should throw if no prototype, check spec |
- if (!definition.prototype) { |
- // TODO(sjmiles): replace with more appropriate error (EricB can probably |
- // offer guidance) |
- throw new Error('Options missing required prototype property'); |
- } |
- // record name |
- definition.__name = name.toLowerCase(); |
- // ensure a lifecycle object so we don't have to null test it |
- definition.lifecycle = definition.lifecycle || {}; |
- // build a list of ancestral custom elements (for native base detection) |
- // TODO(sjmiles): we used to need to store this, but current code only |
- // uses it in 'resolveTagName': it should probably be inlined |
- definition.ancestry = ancestry(definition.extends); |
- // extensions of native specializations of HTMLElement require localName |
- // to remain native, and use secondary 'is' specifier for extension type |
- resolveTagName(definition); |
- // some platforms require modifications to the user-supplied prototype |
- // chain |
- resolvePrototypeChain(definition); |
- // overrides to implement attributeChanged callback |
- overrideAttributeApi(definition.prototype); |
- // 7.1.5: Register the DEFINITION with DOCUMENT |
- registerDefinition(definition.__name, definition); |
- // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE |
- // 7.1.8. Return the output of the previous step. |
- definition.ctor = generateConstructor(definition); |
- definition.ctor.prototype = definition.prototype; |
- // force our .constructor to be our actual constructor |
- definition.prototype.constructor = definition.ctor; |
- // if initial parsing is complete |
- if (scope.ready) { |
- // upgrade any pre-existing nodes of this type |
- scope.upgradeDocumentTree(document); |
- } |
- return definition.ctor; |
- } |
- |
- function isReservedTag(name) { |
- for (var i = 0; i < reservedTagList.length; i++) { |
- if (name === reservedTagList[i]) { |
- return true; |
- } |
- } |
- } |
- |
- var reservedTagList = [ |
- 'annotation-xml', 'color-profile', 'font-face', 'font-face-src', |
- 'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph' |
- ]; |
- |
- function ancestry(extnds) { |
- var extendee = getRegisteredDefinition(extnds); |
- if (extendee) { |
- return ancestry(extendee.extends).concat([extendee]); |
- } |
- return []; |
- } |
- |
- function resolveTagName(definition) { |
- // if we are explicitly extending something, that thing is our |
- // baseTag, unless it represents a custom component |
- var baseTag = definition.extends; |
- // if our ancestry includes custom components, we only have a |
- // baseTag if one of them does |
- for (var i=0, a; (a=definition.ancestry[i]); i++) { |
- baseTag = a.is && a.tag; |
- } |
- // our tag is our baseTag, if it exists, and otherwise just our name |
- definition.tag = baseTag || definition.__name; |
- if (baseTag) { |
- // if there is a base tag, use secondary 'is' specifier |
- definition.is = definition.__name; |
- } |
- } |
- |
- function resolvePrototypeChain(definition) { |
- // if we don't support __proto__ we need to locate the native level |
- // prototype for precise mixing in |
- if (!Object.__proto__) { |
- // default prototype |
- var nativePrototype = HTMLElement.prototype; |
- // work out prototype when using type-extension |
- if (definition.is) { |
- var inst = document.createElement(definition.tag); |
- nativePrototype = Object.getPrototypeOf(inst); |
- } |
- // ensure __proto__ reference is installed at each point on the prototype |
- // chain. |
- // NOTE: On platforms without __proto__, a mixin strategy is used instead |
- // of prototype swizzling. In this case, this generated __proto__ provides |
- // limited support for prototype traversal. |
- var proto = definition.prototype, ancestor; |
- while (proto && (proto !== nativePrototype)) { |
- var ancestor = Object.getPrototypeOf(proto); |
- proto.__proto__ = ancestor; |
- proto = ancestor; |
- } |
- } |
- // cache this in case of mixin |
- definition.native = nativePrototype; |
- } |
- |
- // SECTION 4 |
- |
- function instantiate(definition) { |
- // 4.a.1. Create a new object that implements PROTOTYPE |
- // 4.a.2. Let ELEMENT by this new object |
- // |
- // the custom element instantiation algorithm must also ensure that the |
- // output is a valid DOM element with the proper wrapper in place. |
- // |
- return upgrade(domCreateElement(definition.tag), definition); |
- } |
- |
- function upgrade(element, definition) { |
- // some definitions specify an 'is' attribute |
- if (definition.is) { |
- element.setAttribute('is', definition.is); |
- } |
- // remove 'unresolved' attr, which is a standin for :unresolved. |
- element.removeAttribute('unresolved'); |
- // make 'element' implement definition.prototype |
- implement(element, definition); |
- // flag as upgraded |
- element.__upgraded__ = true; |
- // lifecycle management |
- created(element); |
- // attachedCallback fires in tree order, call before recursing |
- scope.insertedNode(element); |
- // there should never be a shadow root on element at this point |
- scope.upgradeSubtree(element); |
- // OUTPUT |
- return element; |
- } |
- |
- function implement(element, definition) { |
- // prototype swizzling is best |
- if (Object.__proto__) { |
- element.__proto__ = definition.prototype; |
- } else { |
- // where above we can re-acquire inPrototype via |
- // getPrototypeOf(Element), we cannot do so when |
- // we use mixin, so we install a magic reference |
- customMixin(element, definition.prototype, definition.native); |
- element.__proto__ = definition.prototype; |
- } |
- } |
- |
- function customMixin(inTarget, inSrc, inNative) { |
- // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of |
- // any property. This set should be precalculated. We also need to |
- // consider this for supporting 'super'. |
- var used = {}; |
- // start with inSrc |
- var p = inSrc; |
- // The default is HTMLElement.prototype, so we add a test to avoid mixing in |
- // native prototypes |
- while (p !== inNative && p !== HTMLElement.prototype) { |
- var keys = Object.getOwnPropertyNames(p); |
- for (var i=0, k; k=keys[i]; i++) { |
- if (!used[k]) { |
- Object.defineProperty(inTarget, k, |
- Object.getOwnPropertyDescriptor(p, k)); |
- used[k] = 1; |
- } |
- } |
- p = Object.getPrototypeOf(p); |
- } |
- } |
- |
- function created(element) { |
- // invoke createdCallback |
- if (element.createdCallback) { |
- element.createdCallback(); |
- } |
- } |
- |
- // attribute watching |
- |
- function overrideAttributeApi(prototype) { |
- // overrides to implement callbacks |
- // TODO(sjmiles): should support access via .attributes NamedNodeMap |
- // TODO(sjmiles): preserves user defined overrides, if any |
- if (prototype.setAttribute._polyfilled) { |
- return; |
- } |
- var setAttribute = prototype.setAttribute; |
- prototype.setAttribute = function(name, value) { |
- changeAttribute.call(this, name, value, setAttribute); |
- } |
- var removeAttribute = prototype.removeAttribute; |
- prototype.removeAttribute = function(name) { |
- changeAttribute.call(this, name, null, removeAttribute); |
- } |
- prototype.setAttribute._polyfilled = true; |
- } |
- |
- // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/ |
- // index.html#dfn-attribute-changed-callback |
- function changeAttribute(name, value, operation) { |
- var oldValue = this.getAttribute(name); |
- operation.apply(this, arguments); |
- var newValue = this.getAttribute(name); |
- if (this.attributeChangedCallback |
- && (newValue !== oldValue)) { |
- this.attributeChangedCallback(name, oldValue, newValue); |
- } |
- } |
- |
- // element registry (maps tag names to definitions) |
- |
- var registry = {}; |
- |
- function getRegisteredDefinition(name) { |
- if (name) { |
- return registry[name.toLowerCase()]; |
- } |
- } |
- |
- function registerDefinition(name, definition) { |
- registry[name] = definition; |
- } |
- |
- function generateConstructor(definition) { |
- return function() { |
- return instantiate(definition); |
- }; |
- } |
- |
- var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; |
- function createElementNS(namespace, tag, typeExtension) { |
- // NOTE: we do not support non-HTML elements, |
- // just call createElementNS for non HTML Elements |
- if (namespace === HTML_NAMESPACE) { |
- return createElement(tag, typeExtension); |
- } else { |
- return domCreateElementNS(namespace, tag); |
- } |
- } |
- |
- function createElement(tag, typeExtension) { |
- // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could |
- // error check it, or perhaps there should only ever be one argument |
- var definition = getRegisteredDefinition(typeExtension || tag); |
- if (definition) { |
- if (tag == definition.tag && typeExtension == definition.is) { |
- return new definition.ctor(); |
- } |
- // Handle empty string for type extension. |
- if (!typeExtension && !definition.is) { |
- return new definition.ctor(); |
- } |
- } |
- |
- if (typeExtension) { |
- var element = createElement(tag); |
- element.setAttribute('is', typeExtension); |
- return element; |
- } |
- var element = domCreateElement(tag); |
- // Custom tags should be HTMLElements even if not upgraded. |
- if (tag.indexOf('-') >= 0) { |
- implement(element, HTMLElement); |
- } |
- return element; |
- } |
- |
- function upgradeElement(element) { |
- if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) { |
- var is = element.getAttribute('is'); |
- var definition = getRegisteredDefinition(is || element.localName); |
- if (definition) { |
- if (is && definition.tag == element.localName) { |
- return upgrade(element, definition); |
- } else if (!is && !definition.extends) { |
- return upgrade(element, definition); |
- } |
- } |
- } |
- } |
- |
- function cloneNode(deep) { |
- // call original clone |
- var n = domCloneNode.call(this, deep); |
- // upgrade the element and subtree |
- scope.upgradeAll(n); |
- // return the clone |
- return n; |
- } |
- // capture native createElement before we override it |
- |
- var domCreateElement = document.createElement.bind(document); |
- var domCreateElementNS = document.createElementNS.bind(document); |
- |
- // capture native cloneNode before we override it |
- |
- var domCloneNode = Node.prototype.cloneNode; |
- |
- // exports |
- |
- document.registerElement = register; |
- document.createElement = createElement; // override |
- document.createElementNS = createElementNS; // override |
- Node.prototype.cloneNode = cloneNode; // override |
- |
- scope.registry = registry; |
- |
- /** |
- * Upgrade an element to a custom element. Upgrading an element |
- * causes the custom prototype to be applied, an `is` attribute |
- * to be attached (as needed), and invocation of the `readyCallback`. |
- * `upgrade` does nothing if the element is already upgraded, or |
- * if it matches no registered custom tag name. |
- * |
- * @method ugprade |
- * @param {Element} element The element to upgrade. |
- * @return {Element} The upgraded element. |
- */ |
- scope.upgrade = upgradeElement; |
-} |
- |
-// Create a custom 'instanceof'. This is necessary when CustomElements |
-// are implemented via a mixin strategy, as for example on IE10. |
-var isInstance; |
-if (!Object.__proto__ && !useNative) { |
- isInstance = function(obj, ctor) { |
- var p = obj; |
- while (p) { |
- // NOTE: this is not technically correct since we're not checking if |
- // an object is an instance of a constructor; however, this should |
- // be good enough for the mixin strategy. |
- if (p === ctor.prototype) { |
- return true; |
- } |
- p = p.__proto__; |
- } |
- return false; |
- } |
-} else { |
- isInstance = function(obj, base) { |
- return obj instanceof base; |
- } |
-} |
- |
-// exports |
-scope.instanceof = isInstance; |
-scope.reservedTagList = reservedTagList; |
- |
-// bc |
-document.register = document.registerElement; |
- |
-scope.hasNative = hasNative; |
-scope.useNative = useNative; |
- |
-})(window.CustomElements); |
- |
-/* |
- * 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) { |
- |
-// import |
- |
-var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; |
- |
-// highlander object for parsing a document tree |
- |
-var parser = { |
- selectors: [ |
- 'link[rel=' + IMPORT_LINK_TYPE + ']' |
- ], |
- map: { |
- link: 'parseLink' |
- }, |
- parse: function(inDocument) { |
- if (!inDocument.__parsed) { |
- // only parse once |
- inDocument.__parsed = true; |
- // all parsable elements in inDocument (depth-first pre-order traversal) |
- var elts = inDocument.querySelectorAll(parser.selectors); |
- // for each parsable node type, call the mapped parsing method |
- forEach(elts, function(e) { |
- parser[parser.map[e.localName]](e); |
- }); |
- // upgrade all upgradeable static elements, anything dynamically |
- // created should be caught by observer |
- CustomElements.upgradeDocument(inDocument); |
- // observe document for dom changes |
- CustomElements.observeDocument(inDocument); |
- } |
- }, |
- parseLink: function(linkElt) { |
- // imports |
- if (isDocumentLink(linkElt)) { |
- this.parseImport(linkElt); |
- } |
- }, |
- parseImport: function(linkElt) { |
- if (linkElt.import) { |
- parser.parse(linkElt.import); |
- } |
- } |
-}; |
- |
-function isDocumentLink(inElt) { |
- return (inElt.localName === 'link' |
- && inElt.getAttribute('rel') === IMPORT_LINK_TYPE); |
-} |
- |
-var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |
- |
-// exports |
- |
-scope.parser = parser; |
-scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |
- |
-})(window.CustomElements); |
-/* |
- * 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){ |
- |
-// bootstrap parsing |
-function bootstrap() { |
- // parse document |
- CustomElements.parser.parse(document); |
- // one more pass before register is 'live' |
- CustomElements.upgradeDocument(document); |
- // choose async |
- var async = window.Platform && Platform.endOfMicrotask ? |
- Platform.endOfMicrotask : |
- setTimeout; |
- async(function() { |
- // set internal 'ready' flag, now document.registerElement will trigger |
- // synchronous upgrades |
- CustomElements.ready = true; |
- // capture blunt profiling data |
- CustomElements.readyTime = Date.now(); |
- if (window.HTMLImports) { |
- CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime; |
- } |
- // notify the system that we are bootstrapped |
- document.dispatchEvent( |
- new CustomEvent('WebComponentsReady', {bubbles: true}) |
- ); |
- |
- // install upgrade hook if HTMLImports are available |
- if (window.HTMLImports) { |
- HTMLImports.__importsParsingHook = function(elt) { |
- CustomElements.parser.parse(elt.import); |
- } |
- } |
- }); |
-} |
- |
-// CustomEvent shim for IE |
-if (typeof window.CustomEvent !== 'function') { |
- window.CustomEvent = function(inType) { |
- var e = document.createEvent('HTMLEvents'); |
- e.initEvent(inType, true, true); |
- return e; |
- }; |
-} |
- |
-// When loading at readyState complete time (or via flag), boot custom elements |
-// immediately. |
-// If relevant, HTMLImports must already be loaded. |
-if (document.readyState === 'complete' || scope.flags.eager) { |
- bootstrap(); |
-// When loading at readyState interactive time, bootstrap only if HTMLImports |
-// are not pending. Also avoid IE as the semantics of this state are unreliable. |
-} else if (document.readyState === 'interactive' && !window.attachEvent && |
- (!window.HTMLImports || window.HTMLImports.ready)) { |
- bootstrap(); |
-// When loading at other readyStates, wait for the appropriate DOM event to |
-// bootstrap. |
-} else { |
- var loadEvent = window.HTMLImports && !HTMLImports.ready ? |
- 'HTMLImportsLoaded' : 'DOMContentLoaded'; |
- window.addEventListener(loadEvent, bootstrap); |
-} |
- |
-})(window.CustomElements); |
- |
-/* |
- * 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() { |
- |
-if (window.ShadowDOMPolyfill) { |
- |
- // ensure wrapped inputs for these functions |
- var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument', |
- 'upgradeDocument']; |
- |
- // cache originals |
- var original = {}; |
- fns.forEach(function(fn) { |
- original[fn] = CustomElements[fn]; |
- }); |
- |
- // override |
- fns.forEach(function(fn) { |
- CustomElements[fn] = function(inNode) { |
- return original[fn](wrap(inNode)); |
- }; |
- }); |
- |
-} |
- |
-})(); |
- |
-/* |
- * Copyright 2014 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) { |
- var endOfMicrotask = scope.endOfMicrotask; |
- |
- // Generic url loader |
- function Loader(regex) { |
- this.regex = regex; |
- } |
- Loader.prototype = { |
- // TODO(dfreedm): there may be a better factoring here |
- // extract absolute urls from the text (full of relative urls) |
- extractUrls: function(text, base) { |
- var matches = []; |
- var matched, u; |
- while ((matched = this.regex.exec(text))) { |
- u = new URL(matched[1], base); |
- matches.push({matched: matched[0], url: u.href}); |
- } |
- return matches; |
- }, |
- // take a text blob, a root url, and a callback and load all the urls found within the text |
- // returns a map of absolute url to text |
- process: function(text, root, callback) { |
- var matches = this.extractUrls(text, root); |
- this.fetch(matches, {}, callback); |
- }, |
- // build a mapping of url -> text from matches |
- fetch: function(matches, map, callback) { |
- var inflight = matches.length; |
- |
- // return early if there is no fetching to be done |
- if (!inflight) { |
- return callback(map); |
- } |
- |
- var done = function() { |
- if (--inflight === 0) { |
- callback(map); |
- } |
- }; |
- |
- // map url -> responseText |
- var handleXhr = function(err, request) { |
- var match = request.match; |
- var key = match.url; |
- // handle errors with an empty string |
- if (err) { |
- map[key] = ''; |
- return done(); |
- } |
- var response = request.response || request.responseText; |
- map[key] = response; |
- this.fetch(this.extractUrls(response, key), map, done); |
- }; |
- |
- var m, req, url; |
- for (var i = 0; i < inflight; i++) { |
- m = matches[i]; |
- url = m.url; |
- // if this url has already been requested, skip requesting it again |
- if (map[url]) { |
- // Async call to done to simplify the inflight logic |
- endOfMicrotask(done); |
- continue; |
- } |
- req = this.xhr(url, handleXhr, this); |
- req.match = m; |
- // tag the map with an XHR request to deduplicate at the same level |
- map[url] = req; |
- } |
- }, |
- xhr: function(url, callback, scope) { |
- var request = new XMLHttpRequest(); |
- request.open('GET', url, true); |
- request.send(); |
- request.onload = function() { |
- callback.call(scope, null, request); |
- }; |
- request.onerror = function() { |
- callback.call(scope, null, request); |
- }; |
- return request; |
- } |
- }; |
- |
- scope.Loader = Loader; |
-})(window.Platform); |
- |
-/* |
- * Copyright 2014 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) { |
- |
-var urlResolver = scope.urlResolver; |
-var Loader = scope.Loader; |
- |
-function StyleResolver() { |
- this.loader = new Loader(this.regex); |
-} |
-StyleResolver.prototype = { |
- regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g, |
- // Recursively replace @imports with the text at that url |
- resolve: function(text, url, callback) { |
- var done = function(map) { |
- callback(this.flatten(text, url, map)); |
- }.bind(this); |
- this.loader.process(text, url, done); |
- }, |
- // resolve the textContent of a style node |
- resolveNode: function(style, callback) { |
- var text = style.textContent; |
- var url = style.ownerDocument.baseURI; |
- var done = function(text) { |
- style.textContent = text; |
- callback(style); |
- }; |
- this.resolve(text, url, done); |
- }, |
- // flatten all the @imports to text |
- flatten: function(text, base, map) { |
- var matches = this.loader.extractUrls(text, base); |
- var match, url, intermediate; |
- for (var i = 0; i < matches.length; i++) { |
- match = matches[i]; |
- url = match.url; |
- // resolve any css text to be relative to the importer |
- intermediate = urlResolver.resolveCssText(map[url], url); |
- // flatten intermediate @imports |
- intermediate = this.flatten(intermediate, url, map); |
- text = text.replace(match.matched, intermediate); |
- } |
- return text; |
- }, |
- loadStyles: function(styles, callback) { |
- var loaded=0, l = styles.length; |
- // called in the context of the style |
- function loadedStyle(style) { |
- loaded++; |
- if (loaded === l && callback) { |
- callback(); |
- } |
- } |
- for (var i=0, s; (i<l) && (s=styles[i]); i++) { |
- this.resolveNode(s, loadedStyle); |
- } |
- } |
-}; |
- |
-var styleResolver = new StyleResolver(); |
- |
-// exports |
-scope.styleResolver = styleResolver; |
- |
-})(window.Platform); |
- |
-/* |
- * 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) { |
- scope = scope || {}; |
- scope.external = scope.external || {}; |
- var target = { |
- shadow: function(inEl) { |
- if (inEl) { |
- return inEl.shadowRoot || inEl.webkitShadowRoot; |
- } |
- }, |
- canTarget: function(shadow) { |
- return shadow && Boolean(shadow.elementFromPoint); |
- }, |
- targetingShadow: function(inEl) { |
- var s = this.shadow(inEl); |
- if (this.canTarget(s)) { |
- return s; |
- } |
- }, |
- olderShadow: function(shadow) { |
- var os = shadow.olderShadowRoot; |
- if (!os) { |
- var se = shadow.querySelector('shadow'); |
- if (se) { |
- os = se.olderShadowRoot; |
- } |
- } |
- return os; |
- }, |
- allShadows: function(element) { |
- var shadows = [], s = this.shadow(element); |
- while(s) { |
- shadows.push(s); |
- s = this.olderShadow(s); |
- } |
- return shadows; |
- }, |
- searchRoot: function(inRoot, x, y) { |
- if (inRoot) { |
- var t = inRoot.elementFromPoint(x, y); |
- var st, sr, os; |
- // is element a shadow host? |
- sr = this.targetingShadow(t); |
- while (sr) { |
- // find the the element inside the shadow root |
- st = sr.elementFromPoint(x, y); |
- if (!st) { |
- // check for older shadows |
- sr = this.olderShadow(sr); |
- } else { |
- // shadowed element may contain a shadow root |
- var ssr = this.targetingShadow(st); |
- return this.searchRoot(ssr, x, y) || st; |
- } |
- } |
- // light dom element is the target |
- return t; |
- } |
- }, |
- owner: function(element) { |
- var s = element; |
- // walk up until you hit the shadow root or document |
- while (s.parentNode) { |
- s = s.parentNode; |
- } |
- // the owner element is expected to be a Document or ShadowRoot |
- if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGMENT_NODE) { |
- s = document; |
- } |
- return s; |
- }, |
- findTarget: function(inEvent) { |
- var x = inEvent.clientX, y = inEvent.clientY; |
- // if the listener is in the shadow root, it is much faster to start there |
- var s = this.owner(inEvent.target); |
- // if x, y is not in this root, fall back to document search |
- if (!s.elementFromPoint(x, y)) { |
- s = document; |
- } |
- return this.searchRoot(s, x, y); |
- } |
- }; |
- scope.targetFinding = target; |
- scope.findTarget = target.findTarget.bind(target); |
- |
- window.PointerEventsPolyfill = scope; |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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() { |
- function shadowSelector(v) { |
- return 'body /shadow-deep/ ' + selector(v); |
- } |
- function selector(v) { |
- return '[touch-action="' + v + '"]'; |
- } |
- function rule(v) { |
- return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + '; touch-action-delay: none; }'; |
- } |
- var attrib2css = [ |
- 'none', |
- 'auto', |
- 'pan-x', |
- 'pan-y', |
- { |
- rule: 'pan-x pan-y', |
- selectors: [ |
- 'pan-x pan-y', |
- 'pan-y pan-x' |
- ] |
- } |
- ]; |
- var styles = ''; |
- // only install stylesheet if the browser has touch action support |
- var head = document.head; |
- var hasNativePE = window.PointerEvent || window.MSPointerEvent; |
- // only add shadow selectors if shadowdom is supported |
- var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot; |
- |
- if (hasNativePE) { |
- attrib2css.forEach(function(r) { |
- if (String(r) === r) { |
- styles += selector(r) + rule(r) + '\n'; |
- if (hasShadowRoot) { |
- styles += shadowSelector(r) + rule(r) + '\n'; |
- } |
- } else { |
- styles += r.selectors.map(selector) + rule(r.rule) + '\n'; |
- if (hasShadowRoot) { |
- styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n'; |
- } |
- } |
- }); |
- |
- var el = document.createElement('style'); |
- el.textContent = styles; |
- document.head.appendChild(el); |
- } |
-})(); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This is the constructor for new PointerEvents. |
- * |
- * New Pointer Events must be given a type, and an optional dictionary of |
- * initialization properties. |
- * |
- * Due to certain platform requirements, events returned from the constructor |
- * identify as MouseEvents. |
- * |
- * @constructor |
- * @param {String} inType The type of the event to create. |
- * @param {Object} [inDict] An optional dictionary of initial event properties. |
- * @return {Event} A new PointerEvent of type `inType` and initialized with properties from `inDict`. |
- */ |
-(function(scope) { |
- |
- var MOUSE_PROPS = [ |
- 'bubbles', |
- 'cancelable', |
- 'view', |
- 'detail', |
- 'screenX', |
- 'screenY', |
- 'clientX', |
- 'clientY', |
- 'ctrlKey', |
- 'altKey', |
- 'shiftKey', |
- 'metaKey', |
- 'button', |
- 'relatedTarget', |
- 'pageX', |
- 'pageY' |
- ]; |
- |
- var MOUSE_DEFAULTS = [ |
- false, |
- false, |
- null, |
- null, |
- 0, |
- 0, |
- 0, |
- 0, |
- false, |
- false, |
- false, |
- false, |
- 0, |
- null, |
- 0, |
- 0 |
- ]; |
- |
- function PointerEvent(inType, inDict) { |
- inDict = inDict || Object.create(null); |
- |
- var e = document.createEvent('Event'); |
- e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false); |
- |
- // define inherited MouseEvent properties |
- for(var i = 0, p; i < MOUSE_PROPS.length; i++) { |
- p = MOUSE_PROPS[i]; |
- e[p] = inDict[p] || MOUSE_DEFAULTS[i]; |
- } |
- e.buttons = inDict.buttons || 0; |
- |
- // Spec requires that pointers without pressure specified use 0.5 for down |
- // state and 0 for up state. |
- var pressure = 0; |
- if (inDict.pressure) { |
- pressure = inDict.pressure; |
- } else { |
- pressure = e.buttons ? 0.5 : 0; |
- } |
- |
- // add x/y properties aliased to clientX/Y |
- e.x = e.clientX; |
- e.y = e.clientY; |
- |
- // define the properties of the PointerEvent interface |
- e.pointerId = inDict.pointerId || 0; |
- e.width = inDict.width || 0; |
- e.height = inDict.height || 0; |
- e.pressure = pressure; |
- e.tiltX = inDict.tiltX || 0; |
- e.tiltY = inDict.tiltY || 0; |
- e.pointerType = inDict.pointerType || ''; |
- e.hwTimestamp = inDict.hwTimestamp || 0; |
- e.isPrimary = inDict.isPrimary || false; |
- return e; |
- } |
- |
- // attach to window |
- if (!scope.PointerEvent) { |
- scope.PointerEvent = PointerEvent; |
- } |
-})(window); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This module implements an map of pointer states |
- */ |
-(function(scope) { |
- var USE_MAP = window.Map && window.Map.prototype.forEach; |
- var POINTERS_FN = function(){ return this.size; }; |
- function PointerMap() { |
- if (USE_MAP) { |
- var m = new Map(); |
- m.pointers = POINTERS_FN; |
- return m; |
- } else { |
- this.keys = []; |
- this.values = []; |
- } |
- } |
- |
- PointerMap.prototype = { |
- set: function(inId, inEvent) { |
- var i = this.keys.indexOf(inId); |
- if (i > -1) { |
- this.values[i] = inEvent; |
- } else { |
- this.keys.push(inId); |
- this.values.push(inEvent); |
- } |
- }, |
- has: function(inId) { |
- return this.keys.indexOf(inId) > -1; |
- }, |
- 'delete': function(inId) { |
- var i = this.keys.indexOf(inId); |
- if (i > -1) { |
- this.keys.splice(i, 1); |
- this.values.splice(i, 1); |
- } |
- }, |
- get: function(inId) { |
- var i = this.keys.indexOf(inId); |
- return this.values[i]; |
- }, |
- clear: function() { |
- this.keys.length = 0; |
- this.values.length = 0; |
- }, |
- // return value, key, map |
- forEach: function(callback, thisArg) { |
- this.values.forEach(function(v, i) { |
- callback.call(thisArg, v, this.keys[i], this); |
- }, this); |
- }, |
- pointers: function() { |
- return this.keys.length; |
- } |
- }; |
- |
- scope.PointerMap = PointerMap; |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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) { |
- var CLONE_PROPS = [ |
- // MouseEvent |
- 'bubbles', |
- 'cancelable', |
- 'view', |
- 'detail', |
- 'screenX', |
- 'screenY', |
- 'clientX', |
- 'clientY', |
- 'ctrlKey', |
- 'altKey', |
- 'shiftKey', |
- 'metaKey', |
- 'button', |
- 'relatedTarget', |
- // DOM Level 3 |
- 'buttons', |
- // PointerEvent |
- 'pointerId', |
- 'width', |
- 'height', |
- 'pressure', |
- 'tiltX', |
- 'tiltY', |
- 'pointerType', |
- 'hwTimestamp', |
- 'isPrimary', |
- // event instance |
- 'type', |
- 'target', |
- 'currentTarget', |
- 'which', |
- 'pageX', |
- 'pageY' |
- ]; |
- |
- var CLONE_DEFAULTS = [ |
- // MouseEvent |
- false, |
- false, |
- null, |
- null, |
- 0, |
- 0, |
- 0, |
- 0, |
- false, |
- false, |
- false, |
- false, |
- 0, |
- null, |
- // DOM Level 3 |
- 0, |
- // PointerEvent |
- 0, |
- 0, |
- 0, |
- 0, |
- 0, |
- 0, |
- '', |
- 0, |
- false, |
- // event instance |
- '', |
- null, |
- null, |
- 0, |
- 0, |
- 0 |
- ]; |
- |
- var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined'); |
- |
- /** |
- * This module is for normalizing events. Mouse and Touch events will be |
- * collected here, and fire PointerEvents that have the same semantics, no |
- * matter the source. |
- * Events fired: |
- * - pointerdown: a pointing is added |
- * - pointerup: a pointer is removed |
- * - pointermove: a pointer is moved |
- * - pointerover: a pointer crosses into an element |
- * - pointerout: a pointer leaves an element |
- * - pointercancel: a pointer will no longer generate events |
- */ |
- var dispatcher = { |
- pointermap: new scope.PointerMap(), |
- eventMap: Object.create(null), |
- captureInfo: Object.create(null), |
- // Scope objects for native events. |
- // This exists for ease of testing. |
- eventSources: Object.create(null), |
- eventSourceList: [], |
- /** |
- * Add a new event source that will generate pointer events. |
- * |
- * `inSource` must contain an array of event names named `events`, and |
- * functions with the names specified in the `events` array. |
- * @param {string} name A name for the event source |
- * @param {Object} source A new source of platform events. |
- */ |
- registerSource: function(name, source) { |
- var s = source; |
- var newEvents = s.events; |
- if (newEvents) { |
- newEvents.forEach(function(e) { |
- if (s[e]) { |
- this.eventMap[e] = s[e].bind(s); |
- } |
- }, this); |
- this.eventSources[name] = s; |
- this.eventSourceList.push(s); |
- } |
- }, |
- register: function(element) { |
- var l = this.eventSourceList.length; |
- for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) { |
- // call eventsource register |
- es.register.call(es, element); |
- } |
- }, |
- unregister: function(element) { |
- var l = this.eventSourceList.length; |
- for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) { |
- // call eventsource register |
- es.unregister.call(es, element); |
- } |
- }, |
- contains: scope.external.contains || function(container, contained) { |
- return container.contains(contained); |
- }, |
- // EVENTS |
- down: function(inEvent) { |
- inEvent.bubbles = true; |
- this.fireEvent('pointerdown', inEvent); |
- }, |
- move: function(inEvent) { |
- inEvent.bubbles = true; |
- this.fireEvent('pointermove', inEvent); |
- }, |
- up: function(inEvent) { |
- inEvent.bubbles = true; |
- this.fireEvent('pointerup', inEvent); |
- }, |
- enter: function(inEvent) { |
- inEvent.bubbles = false; |
- this.fireEvent('pointerenter', inEvent); |
- }, |
- leave: function(inEvent) { |
- inEvent.bubbles = false; |
- this.fireEvent('pointerleave', inEvent); |
- }, |
- over: function(inEvent) { |
- inEvent.bubbles = true; |
- this.fireEvent('pointerover', inEvent); |
- }, |
- out: function(inEvent) { |
- inEvent.bubbles = true; |
- this.fireEvent('pointerout', inEvent); |
- }, |
- cancel: function(inEvent) { |
- inEvent.bubbles = true; |
- this.fireEvent('pointercancel', inEvent); |
- }, |
- leaveOut: function(event) { |
- this.out(event); |
- if (!this.contains(event.target, event.relatedTarget)) { |
- this.leave(event); |
- } |
- }, |
- enterOver: function(event) { |
- this.over(event); |
- if (!this.contains(event.target, event.relatedTarget)) { |
- this.enter(event); |
- } |
- }, |
- // LISTENER LOGIC |
- eventHandler: function(inEvent) { |
- // This is used to prevent multiple dispatch of pointerevents from |
- // platform events. This can happen when two elements in different scopes |
- // are set up to create pointer events, which is relevant to Shadow DOM. |
- if (inEvent._handledByPE) { |
- return; |
- } |
- var type = inEvent.type; |
- var fn = this.eventMap && this.eventMap[type]; |
- if (fn) { |
- fn(inEvent); |
- } |
- inEvent._handledByPE = true; |
- }, |
- // set up event listeners |
- listen: function(target, events) { |
- events.forEach(function(e) { |
- this.addEvent(target, e); |
- }, this); |
- }, |
- // remove event listeners |
- unlisten: function(target, events) { |
- events.forEach(function(e) { |
- this.removeEvent(target, e); |
- }, this); |
- }, |
- addEvent: scope.external.addEvent || function(target, eventName) { |
- target.addEventListener(eventName, this.boundHandler); |
- }, |
- removeEvent: scope.external.removeEvent || function(target, eventName) { |
- target.removeEventListener(eventName, this.boundHandler); |
- }, |
- // EVENT CREATION AND TRACKING |
- /** |
- * Creates a new Event of type `inType`, based on the information in |
- * `inEvent`. |
- * |
- * @param {string} inType A string representing the type of event to create |
- * @param {Event} inEvent A platform event with a target |
- * @return {Event} A PointerEvent of type `inType` |
- */ |
- makeEvent: function(inType, inEvent) { |
- // relatedTarget must be null if pointer is captured |
- if (this.captureInfo[inEvent.pointerId]) { |
- inEvent.relatedTarget = null; |
- } |
- var e = new PointerEvent(inType, inEvent); |
- if (inEvent.preventDefault) { |
- e.preventDefault = inEvent.preventDefault; |
- } |
- e._target = e._target || inEvent.target; |
- return e; |
- }, |
- // make and dispatch an event in one call |
- fireEvent: function(inType, inEvent) { |
- var e = this.makeEvent(inType, inEvent); |
- return this.dispatchEvent(e); |
- }, |
- /** |
- * Returns a snapshot of inEvent, with writable properties. |
- * |
- * @param {Event} inEvent An event that contains properties to copy. |
- * @return {Object} An object containing shallow copies of `inEvent`'s |
- * properties. |
- */ |
- cloneEvent: function(inEvent) { |
- var eventCopy = Object.create(null), p; |
- for (var i = 0; i < CLONE_PROPS.length; i++) { |
- p = CLONE_PROPS[i]; |
- eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i]; |
- // Work around SVGInstanceElement shadow tree |
- // Return the <use> element that is represented by the instance for Safari, Chrome, IE. |
- // This is the behavior implemented by Firefox. |
- if (HAS_SVG_INSTANCE && (p === 'target' || p === 'relatedTarget')) { |
- if (eventCopy[p] instanceof SVGElementInstance) { |
- eventCopy[p] = eventCopy[p].correspondingUseElement; |
- } |
- } |
- } |
- // keep the semantics of preventDefault |
- if (inEvent.preventDefault) { |
- eventCopy.preventDefault = function() { |
- inEvent.preventDefault(); |
- }; |
- } |
- return eventCopy; |
- }, |
- getTarget: function(inEvent) { |
- // if pointer capture is set, route all events for the specified pointerId |
- // to the capture target |
- return this.captureInfo[inEvent.pointerId] || inEvent._target; |
- }, |
- setCapture: function(inPointerId, inTarget) { |
- if (this.captureInfo[inPointerId]) { |
- this.releaseCapture(inPointerId); |
- } |
- this.captureInfo[inPointerId] = inTarget; |
- var e = document.createEvent('Event'); |
- e.initEvent('gotpointercapture', true, false); |
- e.pointerId = inPointerId; |
- this.implicitRelease = this.releaseCapture.bind(this, inPointerId); |
- document.addEventListener('pointerup', this.implicitRelease); |
- document.addEventListener('pointercancel', this.implicitRelease); |
- e._target = inTarget; |
- this.asyncDispatchEvent(e); |
- }, |
- releaseCapture: function(inPointerId) { |
- var t = this.captureInfo[inPointerId]; |
- if (t) { |
- var e = document.createEvent('Event'); |
- e.initEvent('lostpointercapture', true, false); |
- e.pointerId = inPointerId; |
- this.captureInfo[inPointerId] = undefined; |
- document.removeEventListener('pointerup', this.implicitRelease); |
- document.removeEventListener('pointercancel', this.implicitRelease); |
- e._target = t; |
- this.asyncDispatchEvent(e); |
- } |
- }, |
- /** |
- * Dispatches the event to its target. |
- * |
- * @param {Event} inEvent The event to be dispatched. |
- * @return {Boolean} True if an event handler returns true, false otherwise. |
- */ |
- dispatchEvent: scope.external.dispatchEvent || function(inEvent) { |
- var t = this.getTarget(inEvent); |
- if (t) { |
- return t.dispatchEvent(inEvent); |
- } |
- }, |
- asyncDispatchEvent: function(inEvent) { |
- requestAnimationFrame(this.dispatchEvent.bind(this, inEvent)); |
- } |
- }; |
- dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher); |
- scope.dispatcher = dispatcher; |
- scope.register = dispatcher.register.bind(dispatcher); |
- scope.unregister = dispatcher.unregister.bind(dispatcher); |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This module uses Mutation Observers to dynamically adjust which nodes will |
- * generate Pointer Events. |
- * |
- * All nodes that wish to generate Pointer Events must have the attribute |
- * `touch-action` set to `none`. |
- */ |
-(function(scope) { |
- var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |
- var map = Array.prototype.map.call.bind(Array.prototype.map); |
- var toArray = Array.prototype.slice.call.bind(Array.prototype.slice); |
- var filter = Array.prototype.filter.call.bind(Array.prototype.filter); |
- var MO = window.MutationObserver || window.WebKitMutationObserver; |
- var SELECTOR = '[touch-action]'; |
- var OBSERVER_INIT = { |
- subtree: true, |
- childList: true, |
- attributes: true, |
- attributeOldValue: true, |
- attributeFilter: ['touch-action'] |
- }; |
- |
- function Installer(add, remove, changed, binder) { |
- this.addCallback = add.bind(binder); |
- this.removeCallback = remove.bind(binder); |
- this.changedCallback = changed.bind(binder); |
- if (MO) { |
- this.observer = new MO(this.mutationWatcher.bind(this)); |
- } |
- } |
- |
- Installer.prototype = { |
- watchSubtree: function(target) { |
- // Only watch scopes that can target find, as these are top-level. |
- // Otherwise we can see duplicate additions and removals that add noise. |
- // |
- // TODO(dfreedman): For some instances with ShadowDOMPolyfill, we can see |
- // a removal without an insertion when a node is redistributed among |
- // shadows. Since it all ends up correct in the document, watching only |
- // the document will yield the correct mutations to watch. |
- if (scope.targetFinding.canTarget(target)) { |
- this.observer.observe(target, OBSERVER_INIT); |
- } |
- }, |
- enableOnSubtree: function(target) { |
- this.watchSubtree(target); |
- if (target === document && document.readyState !== 'complete') { |
- this.installOnLoad(); |
- } else { |
- this.installNewSubtree(target); |
- } |
- }, |
- installNewSubtree: function(target) { |
- forEach(this.findElements(target), this.addElement, this); |
- }, |
- findElements: function(target) { |
- if (target.querySelectorAll) { |
- return target.querySelectorAll(SELECTOR); |
- } |
- return []; |
- }, |
- removeElement: function(el) { |
- this.removeCallback(el); |
- }, |
- addElement: function(el) { |
- this.addCallback(el); |
- }, |
- elementChanged: function(el, oldValue) { |
- this.changedCallback(el, oldValue); |
- }, |
- concatLists: function(accum, list) { |
- return accum.concat(toArray(list)); |
- }, |
- // register all touch-action = none nodes on document load |
- installOnLoad: function() { |
- document.addEventListener('readystatechange', function() { |
- if (document.readyState === 'complete') { |
- this.installNewSubtree(document); |
- } |
- }.bind(this)); |
- }, |
- isElement: function(n) { |
- return n.nodeType === Node.ELEMENT_NODE; |
- }, |
- flattenMutationTree: function(inNodes) { |
- // find children with touch-action |
- var tree = map(inNodes, this.findElements, this); |
- // make sure the added nodes are accounted for |
- tree.push(filter(inNodes, this.isElement)); |
- // flatten the list |
- return tree.reduce(this.concatLists, []); |
- }, |
- mutationWatcher: function(mutations) { |
- mutations.forEach(this.mutationHandler, this); |
- }, |
- mutationHandler: function(m) { |
- if (m.type === 'childList') { |
- var added = this.flattenMutationTree(m.addedNodes); |
- added.forEach(this.addElement, this); |
- var removed = this.flattenMutationTree(m.removedNodes); |
- removed.forEach(this.removeElement, this); |
- } else if (m.type === 'attributes') { |
- this.elementChanged(m.target, m.oldValue); |
- } |
- } |
- }; |
- |
- if (!MO) { |
- Installer.prototype.watchSubtree = function(){ |
- console.warn('PointerEventsPolyfill: MutationObservers not found, touch-action will not be dynamically detected'); |
- }; |
- } |
- |
- scope.Installer = Installer; |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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) { |
- var dispatcher = scope.dispatcher; |
- var pointermap = dispatcher.pointermap; |
- // radius around touchend that swallows mouse events |
- var DEDUP_DIST = 25; |
- |
- var WHICH_TO_BUTTONS = [0, 1, 4, 2]; |
- |
- var HAS_BUTTONS = false; |
- try { |
- HAS_BUTTONS = new MouseEvent('test', {buttons: 1}).buttons === 1; |
- } catch (e) {} |
- |
- // handler block for native mouse events |
- var mouseEvents = { |
- POINTER_ID: 1, |
- POINTER_TYPE: 'mouse', |
- events: [ |
- 'mousedown', |
- 'mousemove', |
- 'mouseup', |
- 'mouseover', |
- 'mouseout' |
- ], |
- register: function(target) { |
- dispatcher.listen(target, this.events); |
- }, |
- unregister: function(target) { |
- dispatcher.unlisten(target, this.events); |
- }, |
- lastTouches: [], |
- // collide with the global mouse listener |
- isEventSimulatedFromTouch: function(inEvent) { |
- var lts = this.lastTouches; |
- var x = inEvent.clientX, y = inEvent.clientY; |
- for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) { |
- // simulated mouse events will be swallowed near a primary touchend |
- var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); |
- if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) { |
- return true; |
- } |
- } |
- }, |
- prepareEvent: function(inEvent) { |
- var e = dispatcher.cloneEvent(inEvent); |
- // forward mouse preventDefault |
- var pd = e.preventDefault; |
- e.preventDefault = function() { |
- inEvent.preventDefault(); |
- pd(); |
- }; |
- e.pointerId = this.POINTER_ID; |
- e.isPrimary = true; |
- e.pointerType = this.POINTER_TYPE; |
- if (!HAS_BUTTONS) { |
- e.buttons = WHICH_TO_BUTTONS[e.which] || 0; |
- } |
- return e; |
- }, |
- mousedown: function(inEvent) { |
- if (!this.isEventSimulatedFromTouch(inEvent)) { |
- var p = pointermap.has(this.POINTER_ID); |
- // TODO(dfreedman) workaround for some elements not sending mouseup |
- // http://crbug/149091 |
- if (p) { |
- this.cancel(inEvent); |
- } |
- var e = this.prepareEvent(inEvent); |
- pointermap.set(this.POINTER_ID, inEvent); |
- dispatcher.down(e); |
- } |
- }, |
- mousemove: function(inEvent) { |
- if (!this.isEventSimulatedFromTouch(inEvent)) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.move(e); |
- } |
- }, |
- mouseup: function(inEvent) { |
- if (!this.isEventSimulatedFromTouch(inEvent)) { |
- var p = pointermap.get(this.POINTER_ID); |
- if (p && p.button === inEvent.button) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.up(e); |
- this.cleanupMouse(); |
- } |
- } |
- }, |
- mouseover: function(inEvent) { |
- if (!this.isEventSimulatedFromTouch(inEvent)) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.enterOver(e); |
- } |
- }, |
- mouseout: function(inEvent) { |
- if (!this.isEventSimulatedFromTouch(inEvent)) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.leaveOut(e); |
- } |
- }, |
- cancel: function(inEvent) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.cancel(e); |
- this.cleanupMouse(); |
- }, |
- cleanupMouse: function() { |
- pointermap['delete'](this.POINTER_ID); |
- } |
- }; |
- |
- scope.mouseEvents = mouseEvents; |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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) { |
- var dispatcher = scope.dispatcher; |
- var captureInfo = dispatcher.captureInfo; |
- var findTarget = scope.findTarget; |
- var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding); |
- var pointermap = dispatcher.pointermap; |
- var touchMap = Array.prototype.map.call.bind(Array.prototype.map); |
- // This should be long enough to ignore compat mouse events made by touch |
- var DEDUP_TIMEOUT = 2500; |
- var CLICK_COUNT_TIMEOUT = 200; |
- var ATTRIB = 'touch-action'; |
- var INSTALLER; |
- // The presence of touch event handlers blocks scrolling, and so we must be careful to |
- // avoid adding handlers unnecessarily. Chrome plans to add a touch-action-delay property |
- // (crbug.com/329559) to address this, and once we have that we can opt-in to a simpler |
- // handler registration mechanism. Rather than try to predict how exactly to opt-in to |
- // that we'll just leave this disabled until there is a build of Chrome to test. |
- var HAS_TOUCH_ACTION_DELAY = false; |
- |
- // handler block for native touch events |
- var touchEvents = { |
- events: [ |
- 'touchstart', |
- 'touchmove', |
- 'touchend', |
- 'touchcancel' |
- ], |
- register: function(target) { |
- if (HAS_TOUCH_ACTION_DELAY) { |
- dispatcher.listen(target, this.events); |
- } else { |
- INSTALLER.enableOnSubtree(target); |
- } |
- }, |
- unregister: function(target) { |
- if (HAS_TOUCH_ACTION_DELAY) { |
- dispatcher.unlisten(target, this.events); |
- } else { |
- // TODO(dfreedman): is it worth it to disconnect the MO? |
- } |
- }, |
- elementAdded: function(el) { |
- var a = el.getAttribute(ATTRIB); |
- var st = this.touchActionToScrollType(a); |
- if (st) { |
- el._scrollType = st; |
- dispatcher.listen(el, this.events); |
- // set touch-action on shadows as well |
- allShadows(el).forEach(function(s) { |
- s._scrollType = st; |
- dispatcher.listen(s, this.events); |
- }, this); |
- } |
- }, |
- elementRemoved: function(el) { |
- el._scrollType = undefined; |
- dispatcher.unlisten(el, this.events); |
- // remove touch-action from shadow |
- allShadows(el).forEach(function(s) { |
- s._scrollType = undefined; |
- dispatcher.unlisten(s, this.events); |
- }, this); |
- }, |
- elementChanged: function(el, oldValue) { |
- var a = el.getAttribute(ATTRIB); |
- var st = this.touchActionToScrollType(a); |
- var oldSt = this.touchActionToScrollType(oldValue); |
- // simply update scrollType if listeners are already established |
- if (st && oldSt) { |
- el._scrollType = st; |
- allShadows(el).forEach(function(s) { |
- s._scrollType = st; |
- }, this); |
- } else if (oldSt) { |
- this.elementRemoved(el); |
- } else if (st) { |
- this.elementAdded(el); |
- } |
- }, |
- scrollTypes: { |
- EMITTER: 'none', |
- XSCROLLER: 'pan-x', |
- YSCROLLER: 'pan-y', |
- SCROLLER: /^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/ |
- }, |
- touchActionToScrollType: function(touchAction) { |
- var t = touchAction; |
- var st = this.scrollTypes; |
- if (t === 'none') { |
- return 'none'; |
- } else if (t === st.XSCROLLER) { |
- return 'X'; |
- } else if (t === st.YSCROLLER) { |
- return 'Y'; |
- } else if (st.SCROLLER.exec(t)) { |
- return 'XY'; |
- } |
- }, |
- POINTER_TYPE: 'touch', |
- firstTouch: null, |
- isPrimaryTouch: function(inTouch) { |
- return this.firstTouch === inTouch.identifier; |
- }, |
- setPrimaryTouch: function(inTouch) { |
- // set primary touch if there no pointers, or the only pointer is the mouse |
- if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointermap.has(1))) { |
- this.firstTouch = inTouch.identifier; |
- this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY}; |
- this.scrolling = false; |
- this.cancelResetClickCount(); |
- } |
- }, |
- removePrimaryPointer: function(inPointer) { |
- if (inPointer.isPrimary) { |
- this.firstTouch = null; |
- this.firstXY = null; |
- this.resetClickCount(); |
- } |
- }, |
- clickCount: 0, |
- resetId: null, |
- resetClickCount: function() { |
- var fn = function() { |
- this.clickCount = 0; |
- this.resetId = null; |
- }.bind(this); |
- this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT); |
- }, |
- cancelResetClickCount: function() { |
- if (this.resetId) { |
- clearTimeout(this.resetId); |
- } |
- }, |
- typeToButtons: function(type) { |
- var ret = 0; |
- if (type === 'touchstart' || type === 'touchmove') { |
- ret = 1; |
- } |
- return ret; |
- }, |
- touchToPointer: function(inTouch) { |
- var cte = this.currentTouchEvent; |
- var e = dispatcher.cloneEvent(inTouch); |
- // Spec specifies that pointerId 1 is reserved for Mouse. |
- // Touch identifiers can start at 0. |
- // Add 2 to the touch identifier for compatibility. |
- var id = e.pointerId = inTouch.identifier + 2; |
- e.target = captureInfo[id] || findTarget(e); |
- e.bubbles = true; |
- e.cancelable = true; |
- e.detail = this.clickCount; |
- e.button = 0; |
- e.buttons = this.typeToButtons(cte.type); |
- e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0; |
- e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0; |
- e.pressure = inTouch.webkitForce || inTouch.force || 0.5; |
- e.isPrimary = this.isPrimaryTouch(inTouch); |
- e.pointerType = this.POINTER_TYPE; |
- // forward touch preventDefaults |
- var self = this; |
- e.preventDefault = function() { |
- self.scrolling = false; |
- self.firstXY = null; |
- cte.preventDefault(); |
- }; |
- return e; |
- }, |
- processTouches: function(inEvent, inFunction) { |
- var tl = inEvent.changedTouches; |
- this.currentTouchEvent = inEvent; |
- for (var i = 0, t; i < tl.length; i++) { |
- t = tl[i]; |
- inFunction.call(this, this.touchToPointer(t)); |
- } |
- }, |
- // For single axis scrollers, determines whether the element should emit |
- // pointer events or behave as a scroller |
- shouldScroll: function(inEvent) { |
- if (this.firstXY) { |
- var ret; |
- var scrollAxis = inEvent.currentTarget._scrollType; |
- if (scrollAxis === 'none') { |
- // this element is a touch-action: none, should never scroll |
- ret = false; |
- } else if (scrollAxis === 'XY') { |
- // this element should always scroll |
- ret = true; |
- } else { |
- var t = inEvent.changedTouches[0]; |
- // check the intended scroll axis, and other axis |
- var a = scrollAxis; |
- var oa = scrollAxis === 'Y' ? 'X' : 'Y'; |
- var da = Math.abs(t['client' + a] - this.firstXY[a]); |
- var doa = Math.abs(t['client' + oa] - this.firstXY[oa]); |
- // if delta in the scroll axis > delta other axis, scroll instead of |
- // making events |
- ret = da >= doa; |
- } |
- this.firstXY = null; |
- return ret; |
- } |
- }, |
- findTouch: function(inTL, inId) { |
- for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) { |
- if (t.identifier === inId) { |
- return true; |
- } |
- } |
- }, |
- // In some instances, a touchstart can happen without a touchend. This |
- // leaves the pointermap in a broken state. |
- // Therefore, on every touchstart, we remove the touches that did not fire a |
- // touchend event. |
- // To keep state globally consistent, we fire a |
- // pointercancel for this "abandoned" touch |
- vacuumTouches: function(inEvent) { |
- var tl = inEvent.touches; |
- // pointermap.pointers() should be < tl.length here, as the touchstart has not |
- // been processed yet. |
- if (pointermap.pointers() >= tl.length) { |
- var d = []; |
- pointermap.forEach(function(value, key) { |
- // Never remove pointerId == 1, which is mouse. |
- // Touch identifiers are 2 smaller than their pointerId, which is the |
- // index in pointermap. |
- if (key !== 1 && !this.findTouch(tl, key - 2)) { |
- var p = value.out; |
- d.push(p); |
- } |
- }, this); |
- d.forEach(this.cancelOut, this); |
- } |
- }, |
- touchstart: function(inEvent) { |
- this.vacuumTouches(inEvent); |
- this.setPrimaryTouch(inEvent.changedTouches[0]); |
- this.dedupSynthMouse(inEvent); |
- if (!this.scrolling) { |
- this.clickCount++; |
- this.processTouches(inEvent, this.overDown); |
- } |
- }, |
- overDown: function(inPointer) { |
- var p = pointermap.set(inPointer.pointerId, { |
- target: inPointer.target, |
- out: inPointer, |
- outTarget: inPointer.target |
- }); |
- dispatcher.over(inPointer); |
- dispatcher.enter(inPointer); |
- dispatcher.down(inPointer); |
- }, |
- touchmove: function(inEvent) { |
- if (!this.scrolling) { |
- if (this.shouldScroll(inEvent)) { |
- this.scrolling = true; |
- this.touchcancel(inEvent); |
- } else { |
- inEvent.preventDefault(); |
- this.processTouches(inEvent, this.moveOverOut); |
- } |
- } |
- }, |
- moveOverOut: function(inPointer) { |
- var event = inPointer; |
- var pointer = pointermap.get(event.pointerId); |
- // a finger drifted off the screen, ignore it |
- if (!pointer) { |
- return; |
- } |
- var outEvent = pointer.out; |
- var outTarget = pointer.outTarget; |
- dispatcher.move(event); |
- if (outEvent && outTarget !== event.target) { |
- outEvent.relatedTarget = event.target; |
- event.relatedTarget = outTarget; |
- // recover from retargeting by shadow |
- outEvent.target = outTarget; |
- if (event.target) { |
- dispatcher.leaveOut(outEvent); |
- dispatcher.enterOver(event); |
- } else { |
- // clean up case when finger leaves the screen |
- event.target = outTarget; |
- event.relatedTarget = null; |
- this.cancelOut(event); |
- } |
- } |
- pointer.out = event; |
- pointer.outTarget = event.target; |
- }, |
- touchend: function(inEvent) { |
- this.dedupSynthMouse(inEvent); |
- this.processTouches(inEvent, this.upOut); |
- }, |
- upOut: function(inPointer) { |
- if (!this.scrolling) { |
- dispatcher.up(inPointer); |
- dispatcher.out(inPointer); |
- dispatcher.leave(inPointer); |
- } |
- this.cleanUpPointer(inPointer); |
- }, |
- touchcancel: function(inEvent) { |
- this.processTouches(inEvent, this.cancelOut); |
- }, |
- cancelOut: function(inPointer) { |
- dispatcher.cancel(inPointer); |
- dispatcher.out(inPointer); |
- dispatcher.leave(inPointer); |
- this.cleanUpPointer(inPointer); |
- }, |
- cleanUpPointer: function(inPointer) { |
- pointermap['delete'](inPointer.pointerId); |
- this.removePrimaryPointer(inPointer); |
- }, |
- // prevent synth mouse events from creating pointer events |
- dedupSynthMouse: function(inEvent) { |
- var lts = scope.mouseEvents.lastTouches; |
- var t = inEvent.changedTouches[0]; |
- // only the primary finger will synth mouse events |
- if (this.isPrimaryTouch(t)) { |
- // remember x/y of last touch |
- var lt = {x: t.clientX, y: t.clientY}; |
- lts.push(lt); |
- var fn = (function(lts, lt){ |
- var i = lts.indexOf(lt); |
- if (i > -1) { |
- lts.splice(i, 1); |
- } |
- }).bind(null, lts, lt); |
- setTimeout(fn, DEDUP_TIMEOUT); |
- } |
- } |
- }; |
- |
- if (!HAS_TOUCH_ACTION_DELAY) { |
- INSTALLER = new scope.Installer(touchEvents.elementAdded, touchEvents.elementRemoved, touchEvents.elementChanged, touchEvents); |
- } |
- |
- scope.touchEvents = touchEvents; |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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) { |
- var dispatcher = scope.dispatcher; |
- var pointermap = dispatcher.pointermap; |
- var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number'; |
- var msEvents = { |
- events: [ |
- 'MSPointerDown', |
- 'MSPointerMove', |
- 'MSPointerUp', |
- 'MSPointerOut', |
- 'MSPointerOver', |
- 'MSPointerCancel', |
- 'MSGotPointerCapture', |
- 'MSLostPointerCapture' |
- ], |
- register: function(target) { |
- dispatcher.listen(target, this.events); |
- }, |
- unregister: function(target) { |
- dispatcher.unlisten(target, this.events); |
- }, |
- POINTER_TYPES: [ |
- '', |
- 'unavailable', |
- 'touch', |
- 'pen', |
- 'mouse' |
- ], |
- prepareEvent: function(inEvent) { |
- var e = inEvent; |
- if (HAS_BITMAP_TYPE) { |
- e = dispatcher.cloneEvent(inEvent); |
- e.pointerType = this.POINTER_TYPES[inEvent.pointerType]; |
- } |
- return e; |
- }, |
- cleanup: function(id) { |
- pointermap['delete'](id); |
- }, |
- MSPointerDown: function(inEvent) { |
- pointermap.set(inEvent.pointerId, inEvent); |
- var e = this.prepareEvent(inEvent); |
- dispatcher.down(e); |
- }, |
- MSPointerMove: function(inEvent) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.move(e); |
- }, |
- MSPointerUp: function(inEvent) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.up(e); |
- this.cleanup(inEvent.pointerId); |
- }, |
- MSPointerOut: function(inEvent) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.leaveOut(e); |
- }, |
- MSPointerOver: function(inEvent) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.enterOver(e); |
- }, |
- MSPointerCancel: function(inEvent) { |
- var e = this.prepareEvent(inEvent); |
- dispatcher.cancel(e); |
- this.cleanup(inEvent.pointerId); |
- }, |
- MSLostPointerCapture: function(inEvent) { |
- var e = dispatcher.makeEvent('lostpointercapture', inEvent); |
- dispatcher.dispatchEvent(e); |
- }, |
- MSGotPointerCapture: function(inEvent) { |
- var e = dispatcher.makeEvent('gotpointercapture', inEvent); |
- dispatcher.dispatchEvent(e); |
- } |
- }; |
- |
- scope.msEvents = msEvents; |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This module contains the handlers for native platform events. |
- * From here, the dispatcher is called to create unified pointer events. |
- * Included are touch events (v1), mouse events, and MSPointerEvents. |
- */ |
-(function(scope) { |
- var dispatcher = scope.dispatcher; |
- |
- // only activate if this platform does not have pointer events |
- if (window.PointerEvent !== scope.PointerEvent) { |
- |
- if (window.navigator.msPointerEnabled) { |
- var tp = window.navigator.msMaxTouchPoints; |
- Object.defineProperty(window.navigator, 'maxTouchPoints', { |
- value: tp, |
- enumerable: true |
- }); |
- dispatcher.registerSource('ms', scope.msEvents); |
- } else { |
- dispatcher.registerSource('mouse', scope.mouseEvents); |
- if (window.ontouchstart !== undefined) { |
- dispatcher.registerSource('touch', scope.touchEvents); |
- } |
- } |
- |
- dispatcher.register(document); |
- } |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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) { |
- var dispatcher = scope.dispatcher; |
- var n = window.navigator; |
- var s, r; |
- function assertDown(id) { |
- if (!dispatcher.pointermap.has(id)) { |
- throw new Error('InvalidPointerId'); |
- } |
- } |
- if (n.msPointerEnabled) { |
- s = function(pointerId) { |
- assertDown(pointerId); |
- this.msSetPointerCapture(pointerId); |
- }; |
- r = function(pointerId) { |
- assertDown(pointerId); |
- this.msReleasePointerCapture(pointerId); |
- }; |
- } else { |
- s = function setPointerCapture(pointerId) { |
- assertDown(pointerId); |
- dispatcher.setCapture(pointerId, this); |
- }; |
- r = function releasePointerCapture(pointerId) { |
- assertDown(pointerId); |
- dispatcher.releaseCapture(pointerId, this); |
- }; |
- } |
- if (window.Element && !Element.prototype.setPointerCapture) { |
- Object.defineProperties(Element.prototype, { |
- 'setPointerCapture': { |
- value: s |
- }, |
- 'releasePointerCapture': { |
- value: r |
- } |
- }); |
- } |
-})(window.PointerEventsPolyfill); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * PointerGestureEvent is the constructor for all PointerGesture events. |
- * |
- * @module PointerGestures |
- * @class PointerGestureEvent |
- * @extends UIEvent |
- * @constructor |
- * @param {String} inType Event type |
- * @param {Object} [inDict] Dictionary of properties to initialize on the event |
- */ |
- |
-function PointerGestureEvent(inType, inDict) { |
- var dict = inDict || {}; |
- var e = document.createEvent('Event'); |
- var props = { |
- bubbles: Boolean(dict.bubbles) === dict.bubbles || true, |
- cancelable: Boolean(dict.cancelable) === dict.cancelable || true |
- }; |
- |
- e.initEvent(inType, props.bubbles, props.cancelable); |
- |
- var keys = Object.keys(dict), k; |
- for (var i = 0; i < keys.length; i++) { |
- k = keys[i]; |
- e[k] = dict[k]; |
- } |
- |
- e.preventTap = this.preventTap; |
- |
- return e; |
-} |
- |
-/** |
- * Allows for any gesture to prevent the tap gesture. |
- * |
- * @method preventTap |
- */ |
-PointerGestureEvent.prototype.preventTap = function() { |
- this.tapPrevented = true; |
-}; |
- |
- |
-/* |
- * 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) { |
- /** |
- * This class contains the gesture recognizers that create the PointerGesture |
- * events. |
- * |
- * @class PointerGestures |
- * @static |
- */ |
- scope = scope || {}; |
- scope.utils = { |
- LCA: { |
- // Determines the lowest node in the ancestor chain of a and b |
- find: function(a, b) { |
- if (a === b) { |
- return a; |
- } |
- // fast case, a is a direct descendant of b or vice versa |
- if (a.contains) { |
- if (a.contains(b)) { |
- return a; |
- } |
- if (b.contains(a)) { |
- return b; |
- } |
- } |
- var adepth = this.depth(a); |
- var bdepth = this.depth(b); |
- var d = adepth - bdepth; |
- if (d > 0) { |
- a = this.walk(a, d); |
- } else { |
- b = this.walk(b, -d); |
- } |
- while(a && b && a !== b) { |
- a = this.walk(a, 1); |
- b = this.walk(b, 1); |
- } |
- return a; |
- }, |
- walk: function(n, u) { |
- for (var i = 0; i < u; i++) { |
- n = n.parentNode; |
- } |
- return n; |
- }, |
- depth: function(n) { |
- var d = 0; |
- while(n) { |
- d++; |
- n = n.parentNode; |
- } |
- return d; |
- } |
- } |
- }; |
- scope.findLCA = function(a, b) { |
- return scope.utils.LCA.find(a, b); |
- } |
- window.PointerGestures = scope; |
-})(window.PointerGestures); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This module implements an map of pointer states |
- */ |
-(function(scope) { |
- var USE_MAP = window.Map && window.Map.prototype.forEach; |
- var POINTERS_FN = function(){ return this.size; }; |
- function PointerMap() { |
- if (USE_MAP) { |
- var m = new Map(); |
- m.pointers = POINTERS_FN; |
- return m; |
- } else { |
- this.keys = []; |
- this.values = []; |
- } |
- } |
- |
- PointerMap.prototype = { |
- set: function(inId, inEvent) { |
- var i = this.keys.indexOf(inId); |
- if (i > -1) { |
- this.values[i] = inEvent; |
- } else { |
- this.keys.push(inId); |
- this.values.push(inEvent); |
- } |
- }, |
- has: function(inId) { |
- return this.keys.indexOf(inId) > -1; |
- }, |
- 'delete': function(inId) { |
- var i = this.keys.indexOf(inId); |
- if (i > -1) { |
- this.keys.splice(i, 1); |
- this.values.splice(i, 1); |
- } |
- }, |
- get: function(inId) { |
- var i = this.keys.indexOf(inId); |
- return this.values[i]; |
- }, |
- clear: function() { |
- this.keys.length = 0; |
- this.values.length = 0; |
- }, |
- // return value, key, map |
- forEach: function(callback, thisArg) { |
- this.values.forEach(function(v, i) { |
- callback.call(thisArg, v, this.keys[i], this); |
- }, this); |
- }, |
- pointers: function() { |
- return this.keys.length; |
- } |
- }; |
- |
- scope.PointerMap = PointerMap; |
-})(window.PointerGestures); |
- |
-/* |
- * 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) { |
- var CLONE_PROPS = [ |
- // MouseEvent |
- 'bubbles', |
- 'cancelable', |
- 'view', |
- 'detail', |
- 'screenX', |
- 'screenY', |
- 'clientX', |
- 'clientY', |
- 'ctrlKey', |
- 'altKey', |
- 'shiftKey', |
- 'metaKey', |
- 'button', |
- 'relatedTarget', |
- // DOM Level 3 |
- 'buttons', |
- // PointerEvent |
- 'pointerId', |
- 'width', |
- 'height', |
- 'pressure', |
- 'tiltX', |
- 'tiltY', |
- 'pointerType', |
- 'hwTimestamp', |
- 'isPrimary', |
- // event instance |
- 'type', |
- 'target', |
- 'currentTarget', |
- 'screenX', |
- 'screenY', |
- 'pageX', |
- 'pageY', |
- 'tapPrevented' |
- ]; |
- |
- var CLONE_DEFAULTS = [ |
- // MouseEvent |
- false, |
- false, |
- null, |
- null, |
- 0, |
- 0, |
- 0, |
- 0, |
- false, |
- false, |
- false, |
- false, |
- 0, |
- null, |
- // DOM Level 3 |
- 0, |
- // PointerEvent |
- 0, |
- 0, |
- 0, |
- 0, |
- 0, |
- 0, |
- '', |
- 0, |
- false, |
- // event instance |
- '', |
- null, |
- null, |
- 0, |
- 0, |
- 0, |
- 0 |
- ]; |
- |
- var dispatcher = { |
- handledEvents: new WeakMap(), |
- targets: new WeakMap(), |
- handlers: {}, |
- recognizers: {}, |
- events: {}, |
- // Add a new gesture recognizer to the event listeners. |
- // Recognizer needs an `events` property. |
- registerRecognizer: function(inName, inRecognizer) { |
- var r = inRecognizer; |
- this.recognizers[inName] = r; |
- r.events.forEach(function(e) { |
- if (r[e]) { |
- this.events[e] = true; |
- var f = r[e].bind(r); |
- this.addHandler(e, f); |
- } |
- }, this); |
- }, |
- addHandler: function(inEvent, inFn) { |
- var e = inEvent; |
- if (!this.handlers[e]) { |
- this.handlers[e] = []; |
- } |
- this.handlers[e].push(inFn); |
- }, |
- // add event listeners for inTarget |
- registerTarget: function(inTarget) { |
- this.listen(Object.keys(this.events), inTarget); |
- }, |
- // remove event listeners for inTarget |
- unregisterTarget: function(inTarget) { |
- this.unlisten(Object.keys(this.events), inTarget); |
- }, |
- // LISTENER LOGIC |
- eventHandler: function(inEvent) { |
- if (this.handledEvents.get(inEvent)) { |
- return; |
- } |
- var type = inEvent.type, fns = this.handlers[type]; |
- if (fns) { |
- this.makeQueue(fns, inEvent); |
- } |
- this.handledEvents.set(inEvent, true); |
- }, |
- // queue event for async dispatch |
- makeQueue: function(inHandlerFns, inEvent) { |
- // must clone events to keep the (possibly shadowed) target correct for |
- // async dispatching |
- var e = this.cloneEvent(inEvent); |
- requestAnimationFrame(this.runQueue.bind(this, inHandlerFns, e)); |
- }, |
- // Dispatch the queued events |
- runQueue: function(inHandlers, inEvent) { |
- this.currentPointerId = inEvent.pointerId; |
- for (var i = 0, f, l = inHandlers.length; (i < l) && (f = inHandlers[i]); i++) { |
- f(inEvent); |
- } |
- this.currentPointerId = 0; |
- }, |
- // set up event listeners |
- listen: function(inEvents, inTarget) { |
- inEvents.forEach(function(e) { |
- this.addEvent(e, this.boundHandler, false, inTarget); |
- }, this); |
- }, |
- // remove event listeners |
- unlisten: function(inEvents) { |
- inEvents.forEach(function(e) { |
- this.removeEvent(e, this.boundHandler, false, inTarget); |
- }, this); |
- }, |
- addEvent: function(inEventName, inEventHandler, inCapture, inTarget) { |
- inTarget.addEventListener(inEventName, inEventHandler, inCapture); |
- }, |
- removeEvent: function(inEventName, inEventHandler, inCapture, inTarget) { |
- inTarget.removeEventListener(inEventName, inEventHandler, inCapture); |
- }, |
- // EVENT CREATION AND TRACKING |
- // Creates a new Event of type `inType`, based on the information in |
- // `inEvent`. |
- makeEvent: function(inType, inDict) { |
- return new PointerGestureEvent(inType, inDict); |
- }, |
- /* |
- * Returns a snapshot of inEvent, with writable properties. |
- * |
- * @method cloneEvent |
- * @param {Event} inEvent An event that contains properties to copy. |
- * @return {Object} An object containing shallow copies of `inEvent`'s |
- * properties. |
- */ |
- cloneEvent: function(inEvent) { |
- var eventCopy = {}, p; |
- for (var i = 0; i < CLONE_PROPS.length; i++) { |
- p = CLONE_PROPS[i]; |
- eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i]; |
- } |
- return eventCopy; |
- }, |
- // Dispatches the event to its target. |
- dispatchEvent: function(inEvent, inTarget) { |
- var t = inTarget || this.targets.get(inEvent); |
- if (t) { |
- t.dispatchEvent(inEvent); |
- if (inEvent.tapPrevented) { |
- this.preventTap(this.currentPointerId); |
- } |
- } |
- }, |
- asyncDispatchEvent: function(inEvent, inTarget) { |
- requestAnimationFrame(this.dispatchEvent.bind(this, inEvent, inTarget)); |
- }, |
- preventTap: function(inPointerId) { |
- var t = this.recognizers.tap; |
- if (t){ |
- t.preventTap(inPointerId); |
- } |
- } |
- }; |
- dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher); |
- // recognizers call into the dispatcher and load later |
- // solve the chicken and egg problem by having registerScopes module run last |
- dispatcher.registerQueue = []; |
- dispatcher.immediateRegister = false; |
- scope.dispatcher = dispatcher; |
- /** |
- * Enable gesture events for a given scope, typically |
- * [ShadowRoots](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#shadow-root-object). |
- * |
- * @for PointerGestures |
- * @method register |
- * @param {ShadowRoot} scope A top level scope to enable gesture |
- * support on. |
- */ |
- scope.register = function(inScope) { |
- if (dispatcher.immediateRegister) { |
- var pe = window.PointerEventsPolyfill; |
- if (pe) { |
- pe.register(inScope); |
- } |
- scope.dispatcher.registerTarget(inScope); |
- } else { |
- dispatcher.registerQueue.push(inScope); |
- } |
- }; |
- scope.register(document); |
-})(window.PointerGestures); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This event is fired when a pointer is held down for 200ms. |
- * |
- * @module PointerGestures |
- * @submodule Events |
- * @class hold |
- */ |
-/** |
- * Type of pointer that made the holding event. |
- * @type String |
- * @property pointerType |
- */ |
-/** |
- * Screen X axis position of the held pointer |
- * @type Number |
- * @property clientX |
- */ |
-/** |
- * Screen Y axis position of the held pointer |
- * @type Number |
- * @property clientY |
- */ |
-/** |
- * Type of pointer that made the holding event. |
- * @type String |
- * @property pointerType |
- */ |
-/** |
- * This event is fired every 200ms while a pointer is held down. |
- * |
- * @class holdpulse |
- * @extends hold |
- */ |
-/** |
- * Milliseconds pointer has been held down. |
- * @type Number |
- * @property holdTime |
- */ |
-/** |
- * This event is fired when a held pointer is released or moved. |
- * |
- * @class released |
- */ |
- |
-(function(scope) { |
- var dispatcher = scope.dispatcher; |
- var hold = { |
- // wait at least HOLD_DELAY ms between hold and pulse events |
- HOLD_DELAY: 200, |
- // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold |
- WIGGLE_THRESHOLD: 16, |
- events: [ |
- 'pointerdown', |
- 'pointermove', |
- 'pointerup', |
- 'pointercancel' |
- ], |
- heldPointer: null, |
- holdJob: null, |
- pulse: function() { |
- var hold = Date.now() - this.heldPointer.timeStamp; |
- var type = this.held ? 'holdpulse' : 'hold'; |
- this.fireHold(type, hold); |
- this.held = true; |
- }, |
- cancel: function() { |
- clearInterval(this.holdJob); |
- if (this.held) { |
- this.fireHold('release'); |
- } |
- this.held = false; |
- this.heldPointer = null; |
- this.target = null; |
- this.holdJob = null; |
- }, |
- pointerdown: function(inEvent) { |
- if (inEvent.isPrimary && !this.heldPointer) { |
- this.heldPointer = inEvent; |
- this.target = inEvent.target; |
- this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY); |
- } |
- }, |
- pointerup: function(inEvent) { |
- if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) { |
- this.cancel(); |
- } |
- }, |
- pointercancel: function(inEvent) { |
- this.cancel(); |
- }, |
- pointermove: function(inEvent) { |
- if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) { |
- var x = inEvent.clientX - this.heldPointer.clientX; |
- var y = inEvent.clientY - this.heldPointer.clientY; |
- if ((x * x + y * y) > this.WIGGLE_THRESHOLD) { |
- this.cancel(); |
- } |
- } |
- }, |
- fireHold: function(inType, inHoldTime) { |
- var p = { |
- pointerType: this.heldPointer.pointerType, |
- clientX: this.heldPointer.clientX, |
- clientY: this.heldPointer.clientY |
- }; |
- if (inHoldTime) { |
- p.holdTime = inHoldTime; |
- } |
- var e = dispatcher.makeEvent(inType, p); |
- dispatcher.dispatchEvent(e, this.target); |
- if (e.tapPrevented) { |
- dispatcher.preventTap(this.heldPointer.pointerId); |
- } |
- } |
- }; |
- dispatcher.registerRecognizer('hold', hold); |
-})(window.PointerGestures); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This event denotes the beginning of a series of tracking events. |
- * |
- * @module PointerGestures |
- * @submodule Events |
- * @class trackstart |
- */ |
-/** |
- * Pixels moved in the x direction since trackstart. |
- * @type Number |
- * @property dx |
- */ |
-/** |
- * Pixes moved in the y direction since trackstart. |
- * @type Number |
- * @property dy |
- */ |
-/** |
- * Pixels moved in the x direction since the last track. |
- * @type Number |
- * @property ddx |
- */ |
-/** |
- * Pixles moved in the y direction since the last track. |
- * @type Number |
- * @property ddy |
- */ |
-/** |
- * The clientX position of the track gesture. |
- * @type Number |
- * @property clientX |
- */ |
-/** |
- * The clientY position of the track gesture. |
- * @type Number |
- * @property clientY |
- */ |
-/** |
- * The pageX position of the track gesture. |
- * @type Number |
- * @property pageX |
- */ |
-/** |
- * The pageY position of the track gesture. |
- * @type Number |
- * @property pageY |
- */ |
-/** |
- * The screenX position of the track gesture. |
- * @type Number |
- * @property screenX |
- */ |
-/** |
- * The screenY position of the track gesture. |
- * @type Number |
- * @property screenY |
- */ |
-/** |
- * The last x axis direction of the pointer. |
- * @type Number |
- * @property xDirection |
- */ |
-/** |
- * The last y axis direction of the pointer. |
- * @type Number |
- * @property yDirection |
- */ |
-/** |
- * A shared object between all tracking events. |
- * @type Object |
- * @property trackInfo |
- */ |
-/** |
- * The element currently under the pointer. |
- * @type Element |
- * @property relatedTarget |
- */ |
-/** |
- * The type of pointer that make the track gesture. |
- * @type String |
- * @property pointerType |
- */ |
-/** |
- * |
- * This event fires for all pointer movement being tracked. |
- * |
- * @class track |
- * @extends trackstart |
- */ |
-/** |
- * This event fires when the pointer is no longer being tracked. |
- * |
- * @class trackend |
- * @extends trackstart |
- */ |
- |
- (function(scope) { |
- var dispatcher = scope.dispatcher; |
- var pointermap = new scope.PointerMap(); |
- var track = { |
- events: [ |
- 'pointerdown', |
- 'pointermove', |
- 'pointerup', |
- 'pointercancel' |
- ], |
- WIGGLE_THRESHOLD: 4, |
- clampDir: function(inDelta) { |
- return inDelta > 0 ? 1 : -1; |
- }, |
- calcPositionDelta: function(inA, inB) { |
- var x = 0, y = 0; |
- if (inA && inB) { |
- x = inB.pageX - inA.pageX; |
- y = inB.pageY - inA.pageY; |
- } |
- return {x: x, y: y}; |
- }, |
- fireTrack: function(inType, inEvent, inTrackingData) { |
- var t = inTrackingData; |
- var d = this.calcPositionDelta(t.downEvent, inEvent); |
- var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent); |
- if (dd.x) { |
- t.xDirection = this.clampDir(dd.x); |
- } |
- if (dd.y) { |
- t.yDirection = this.clampDir(dd.y); |
- } |
- var trackData = { |
- dx: d.x, |
- dy: d.y, |
- ddx: dd.x, |
- ddy: dd.y, |
- clientX: inEvent.clientX, |
- clientY: inEvent.clientY, |
- pageX: inEvent.pageX, |
- pageY: inEvent.pageY, |
- screenX: inEvent.screenX, |
- screenY: inEvent.screenY, |
- xDirection: t.xDirection, |
- yDirection: t.yDirection, |
- trackInfo: t.trackInfo, |
- relatedTarget: inEvent.target, |
- pointerType: inEvent.pointerType |
- }; |
- var e = dispatcher.makeEvent(inType, trackData); |
- t.lastMoveEvent = inEvent; |
- dispatcher.dispatchEvent(e, t.downTarget); |
- }, |
- pointerdown: function(inEvent) { |
- if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.buttons === 1 : true)) { |
- var p = { |
- downEvent: inEvent, |
- downTarget: inEvent.target, |
- trackInfo: {}, |
- lastMoveEvent: null, |
- xDirection: 0, |
- yDirection: 0, |
- tracking: false |
- }; |
- pointermap.set(inEvent.pointerId, p); |
- } |
- }, |
- pointermove: function(inEvent) { |
- var p = pointermap.get(inEvent.pointerId); |
- if (p) { |
- if (!p.tracking) { |
- var d = this.calcPositionDelta(p.downEvent, inEvent); |
- var move = d.x * d.x + d.y * d.y; |
- // start tracking only if finger moves more than WIGGLE_THRESHOLD |
- if (move > this.WIGGLE_THRESHOLD) { |
- p.tracking = true; |
- this.fireTrack('trackstart', p.downEvent, p); |
- this.fireTrack('track', inEvent, p); |
- } |
- } else { |
- this.fireTrack('track', inEvent, p); |
- } |
- } |
- }, |
- pointerup: function(inEvent) { |
- var p = pointermap.get(inEvent.pointerId); |
- if (p) { |
- if (p.tracking) { |
- this.fireTrack('trackend', inEvent, p); |
- } |
- pointermap.delete(inEvent.pointerId); |
- } |
- }, |
- pointercancel: function(inEvent) { |
- this.pointerup(inEvent); |
- } |
- }; |
- dispatcher.registerRecognizer('track', track); |
- })(window.PointerGestures); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This event denotes a rapid down/move/up sequence from a pointer. |
- * |
- * The event is sent to the first element the pointer went down on. |
- * |
- * @module PointerGestures |
- * @submodule Events |
- * @class flick |
- */ |
-/** |
- * Signed velocity of the flick in the x direction. |
- * @property xVelocity |
- * @type Number |
- */ |
-/** |
- * Signed velocity of the flick in the y direction. |
- * @type Number |
- * @property yVelocity |
- */ |
-/** |
- * Unsigned total velocity of the flick. |
- * @type Number |
- * @property velocity |
- */ |
-/** |
- * Angle of the flick in degrees, with 0 along the |
- * positive x axis. |
- * @type Number |
- * @property angle |
- */ |
-/** |
- * Axis with the greatest absolute velocity. Denoted |
- * with 'x' or 'y'. |
- * @type String |
- * @property majorAxis |
- */ |
-/** |
- * Type of the pointer that made the flick. |
- * @type String |
- * @property pointerType |
- */ |
- |
-(function(scope) { |
- var dispatcher = scope.dispatcher; |
- var flick = { |
- // TODO(dfreedman): value should be low enough for low speed flicks, but |
- // high enough to remove accidental flicks |
- MIN_VELOCITY: 0.5 /* px/ms */, |
- MAX_QUEUE: 4, |
- moveQueue: [], |
- target: null, |
- pointerId: null, |
- events: [ |
- 'pointerdown', |
- 'pointermove', |
- 'pointerup', |
- 'pointercancel' |
- ], |
- pointerdown: function(inEvent) { |
- if (inEvent.isPrimary && !this.pointerId) { |
- this.pointerId = inEvent.pointerId; |
- this.target = inEvent.target; |
- this.addMove(inEvent); |
- } |
- }, |
- pointermove: function(inEvent) { |
- if (inEvent.pointerId === this.pointerId) { |
- this.addMove(inEvent); |
- } |
- }, |
- pointerup: function(inEvent) { |
- if (inEvent.pointerId === this.pointerId) { |
- this.fireFlick(inEvent); |
- } |
- this.cleanup(); |
- }, |
- pointercancel: function(inEvent) { |
- this.cleanup(); |
- }, |
- cleanup: function() { |
- this.moveQueue = []; |
- this.target = null; |
- this.pointerId = null; |
- }, |
- addMove: function(inEvent) { |
- if (this.moveQueue.length >= this.MAX_QUEUE) { |
- this.moveQueue.shift(); |
- } |
- this.moveQueue.push(inEvent); |
- }, |
- fireFlick: function(inEvent) { |
- var e = inEvent; |
- var l = this.moveQueue.length; |
- var dt, dx, dy, tx, ty, tv, x = 0, y = 0, v = 0; |
- // flick based off the fastest segment of movement |
- for (var i = 0, m; i < l && (m = this.moveQueue[i]); i++) { |
- dt = e.timeStamp - m.timeStamp; |
- dx = e.clientX - m.clientX, dy = e.clientY - m.clientY; |
- tx = dx / dt, ty = dy / dt, tv = Math.sqrt(tx * tx + ty * ty); |
- if (tv > v) { |
- x = tx, y = ty, v = tv; |
- } |
- } |
- var ma = Math.abs(x) > Math.abs(y) ? 'x' : 'y'; |
- var a = this.calcAngle(x, y); |
- if (Math.abs(v) >= this.MIN_VELOCITY) { |
- var ev = dispatcher.makeEvent('flick', { |
- xVelocity: x, |
- yVelocity: y, |
- velocity: v, |
- angle: a, |
- majorAxis: ma, |
- pointerType: inEvent.pointerType |
- }); |
- dispatcher.dispatchEvent(ev, this.target); |
- } |
- }, |
- calcAngle: function(inX, inY) { |
- return (Math.atan2(inY, inX) * 180 / Math.PI); |
- } |
- }; |
- dispatcher.registerRecognizer('flick', flick); |
-})(window.PointerGestures); |
- |
-/* |
- * 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. |
- */ |
- |
-/* |
- * Basic strategy: find the farthest apart points, use as diameter of circle |
- * react to size change and rotation of the chord |
- */ |
- |
-/** |
- * @module PointerGestures |
- * @submodule Events |
- * @class pinch |
- */ |
-/** |
- * Scale of the pinch zoom gesture |
- * @property scale |
- * @type Number |
- */ |
-/** |
- * Center X position of pointers causing pinch |
- * @property centerX |
- * @type Number |
- */ |
-/** |
- * Center Y position of pointers causing pinch |
- * @property centerY |
- * @type Number |
- */ |
- |
-/** |
- * @module PointerGestures |
- * @submodule Events |
- * @class rotate |
- */ |
-/** |
- * Angle (in degrees) of rotation. Measured from starting positions of pointers. |
- * @property angle |
- * @type Number |
- */ |
-/** |
- * Center X position of pointers causing rotation |
- * @property centerX |
- * @type Number |
- */ |
-/** |
- * Center Y position of pointers causing rotation |
- * @property centerY |
- * @type Number |
- */ |
-(function(scope) { |
- var dispatcher = scope.dispatcher; |
- var pointermap = new scope.PointerMap(); |
- var RAD_TO_DEG = 180 / Math.PI; |
- var pinch = { |
- events: [ |
- 'pointerdown', |
- 'pointermove', |
- 'pointerup', |
- 'pointercancel' |
- ], |
- reference: {}, |
- pointerdown: function(ev) { |
- pointermap.set(ev.pointerId, ev); |
- if (pointermap.pointers() == 2) { |
- var points = this.calcChord(); |
- var angle = this.calcAngle(points); |
- this.reference = { |
- angle: angle, |
- diameter: points.diameter, |
- target: scope.findLCA(points.a.target, points.b.target) |
- }; |
- } |
- }, |
- pointerup: function(ev) { |
- pointermap.delete(ev.pointerId); |
- }, |
- pointermove: function(ev) { |
- if (pointermap.has(ev.pointerId)) { |
- pointermap.set(ev.pointerId, ev); |
- if (pointermap.pointers() > 1) { |
- this.calcPinchRotate(); |
- } |
- } |
- }, |
- pointercancel: function(ev) { |
- this.pointerup(ev); |
- }, |
- dispatchPinch: function(diameter, points) { |
- var zoom = diameter / this.reference.diameter; |
- var ev = dispatcher.makeEvent('pinch', { |
- scale: zoom, |
- centerX: points.center.x, |
- centerY: points.center.y |
- }); |
- dispatcher.dispatchEvent(ev, this.reference.target); |
- }, |
- dispatchRotate: function(angle, points) { |
- var diff = Math.round((angle - this.reference.angle) % 360); |
- var ev = dispatcher.makeEvent('rotate', { |
- angle: diff, |
- centerX: points.center.x, |
- centerY: points.center.y |
- }); |
- dispatcher.dispatchEvent(ev, this.reference.target); |
- }, |
- calcPinchRotate: function() { |
- var points = this.calcChord(); |
- var diameter = points.diameter; |
- var angle = this.calcAngle(points); |
- if (diameter != this.reference.diameter) { |
- this.dispatchPinch(diameter, points); |
- } |
- if (angle != this.reference.angle) { |
- this.dispatchRotate(angle, points); |
- } |
- }, |
- calcChord: function() { |
- var pointers = []; |
- pointermap.forEach(function(p) { |
- pointers.push(p); |
- }); |
- var dist = 0; |
- // start with at least two pointers |
- var points = {a: pointers[0], b: pointers[1]}; |
- var x, y, d; |
- for (var i = 0; i < pointers.length; i++) { |
- var a = pointers[i]; |
- for (var j = i + 1; j < pointers.length; j++) { |
- var b = pointers[j]; |
- x = Math.abs(a.clientX - b.clientX); |
- y = Math.abs(a.clientY - b.clientY); |
- d = x + y; |
- if (d > dist) { |
- dist = d; |
- points = {a: a, b: b}; |
- } |
- } |
- } |
- x = Math.abs(points.a.clientX + points.b.clientX) / 2; |
- y = Math.abs(points.a.clientY + points.b.clientY) / 2; |
- points.center = { x: x, y: y }; |
- points.diameter = dist; |
- return points; |
- }, |
- calcAngle: function(points) { |
- var x = points.a.clientX - points.b.clientX; |
- var y = points.a.clientY - points.b.clientY; |
- return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360; |
- }, |
- }; |
- dispatcher.registerRecognizer('pinch', pinch); |
-})(window.PointerGestures); |
- |
-/* |
- * 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. |
- */ |
- |
-/** |
- * This event is fired when a pointer quickly goes down and up, and is used to |
- * denote activation. |
- * |
- * Any gesture event can prevent the tap event from being created by calling |
- * `event.preventTap`. |
- * |
- * Any pointer event can prevent the tap by setting the `tapPrevented` property |
- * on itself. |
- * |
- * @module PointerGestures |
- * @submodule Events |
- * @class tap |
- */ |
-/** |
- * X axis position of the tap. |
- * @property x |
- * @type Number |
- */ |
-/** |
- * Y axis position of the tap. |
- * @property y |
- * @type Number |
- */ |
-/** |
- * Type of the pointer that made the tap. |
- * @property pointerType |
- * @type String |
- */ |
-(function(scope) { |
- var dispatcher = scope.dispatcher; |
- var pointermap = new scope.PointerMap(); |
- var tap = { |
- events: [ |
- 'pointerdown', |
- 'pointermove', |
- 'pointerup', |
- 'pointercancel', |
- 'keyup' |
- ], |
- pointerdown: function(inEvent) { |
- if (inEvent.isPrimary && !inEvent.tapPrevented) { |
- pointermap.set(inEvent.pointerId, { |
- target: inEvent.target, |
- buttons: inEvent.buttons, |
- x: inEvent.clientX, |
- y: inEvent.clientY |
- }); |
- } |
- }, |
- pointermove: function(inEvent) { |
- if (inEvent.isPrimary) { |
- var start = pointermap.get(inEvent.pointerId); |
- if (start) { |
- if (inEvent.tapPrevented) { |
- pointermap.delete(inEvent.pointerId); |
- } |
- } |
- } |
- }, |
- shouldTap: function(e, downState) { |
- if (!e.tapPrevented) { |
- if (e.pointerType === 'mouse') { |
- // only allow left click to tap for mouse |
- return downState.buttons === 1; |
- } else { |
- return true; |
- } |
- } |
- }, |
- pointerup: function(inEvent) { |
- var start = pointermap.get(inEvent.pointerId); |
- if (start && this.shouldTap(inEvent, start)) { |
- var t = scope.findLCA(start.target, inEvent.target); |
- if (t) { |
- var e = dispatcher.makeEvent('tap', { |
- x: inEvent.clientX, |
- y: inEvent.clientY, |
- detail: inEvent.detail, |
- pointerType: inEvent.pointerType |
- }); |
- dispatcher.dispatchEvent(e, t); |
- } |
- } |
- pointermap.delete(inEvent.pointerId); |
- }, |
- pointercancel: function(inEvent) { |
- pointermap.delete(inEvent.pointerId); |
- }, |
- keyup: function(inEvent) { |
- var code = inEvent.keyCode; |
- // 32 == spacebar |
- if (code === 32) { |
- var t = inEvent.target; |
- if (!(t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement)) { |
- dispatcher.dispatchEvent(dispatcher.makeEvent('tap', { |
- x: 0, |
- y: 0, |
- detail: 0, |
- pointerType: 'unavailable' |
- }), t); |
- } |
- } |
- }, |
- preventTap: function(inPointerId) { |
- pointermap.delete(inPointerId); |
- } |
- }; |
- dispatcher.registerRecognizer('tap', tap); |
-})(window.PointerGestures); |
- |
-/* |
- * Copyright 2014 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. |
- */ |
- |
-/** |
- * Because recognizers are loaded after dispatcher, we have to wait to register |
- * scopes until after all the recognizers. |
- */ |
-(function(scope) { |
- var dispatcher = scope.dispatcher; |
- function registerScopes() { |
- dispatcher.immediateRegister = true; |
- var rq = dispatcher.registerQueue; |
- rq.forEach(scope.register); |
- rq.length = 0; |
- } |
- if (document.readyState === 'complete') { |
- registerScopes(); |
- } else { |
- // register scopes after a steadystate is reached |
- // less MutationObserver churn |
- document.addEventListener('readystatechange', function() { |
- if (document.readyState === 'complete') { |
- registerScopes(); |
- } |
- }); |
- } |
-})(window.PointerGestures); |
- |
-// Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
-// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
-// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
-// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
-// Code distributed by Google as part of the polymer project is also |
-// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
- |
-(function(global) { |
- 'use strict'; |
- |
- var filter = Array.prototype.filter.call.bind(Array.prototype.filter); |
- |
- function getTreeScope(node) { |
- while (node.parentNode) { |
- node = node.parentNode; |
- } |
- |
- return typeof node.getElementById === 'function' ? node : null; |
- } |
- |
- Node.prototype.bind = function(name, observable) { |
- console.error('Unhandled binding to Node: ', this, name, observable); |
- }; |
- |
- function updateBindings(node, name, binding) { |
- var bindings = node.bindings_; |
- if (!bindings) |
- bindings = node.bindings_ = {}; |
- |
- if (bindings[name]) |
- binding[name].close(); |
- |
- return bindings[name] = binding; |
- } |
- |
- function returnBinding(node, name, binding) { |
- return binding; |
- } |
- |
- function sanitizeValue(value) { |
- return value == null ? '' : value; |
- } |
- |
- function updateText(node, value) { |
- node.data = sanitizeValue(value); |
- } |
- |
- function textBinding(node) { |
- return function(value) { |
- return updateText(node, value); |
- }; |
- } |
- |
- var maybeUpdateBindings = returnBinding; |
- |
- Object.defineProperty(Platform, 'enableBindingsReflection', { |
- get: function() { |
- return maybeUpdateBindings === updateBindings; |
- }, |
- set: function(enable) { |
- maybeUpdateBindings = enable ? updateBindings : returnBinding; |
- return enable; |
- }, |
- configurable: true |
- }); |
- |
- Text.prototype.bind = function(name, value, oneTime) { |
- if (name !== 'textContent') |
- return Node.prototype.bind.call(this, name, value, oneTime); |
- |
- if (oneTime) |
- return updateText(this, value); |
- |
- var observable = value; |
- updateText(this, observable.open(textBinding(this))); |
- return maybeUpdateBindings(this, name, observable); |
- } |
- |
- function updateAttribute(el, name, conditional, value) { |
- if (conditional) { |
- if (value) |
- el.setAttribute(name, ''); |
- else |
- el.removeAttribute(name); |
- return; |
- } |
- |
- el.setAttribute(name, sanitizeValue(value)); |
- } |
- |
- function attributeBinding(el, name, conditional) { |
- return function(value) { |
- updateAttribute(el, name, conditional, value); |
- }; |
- } |
- |
- Element.prototype.bind = function(name, value, oneTime) { |
- var conditional = name[name.length - 1] == '?'; |
- if (conditional) { |
- this.removeAttribute(name); |
- name = name.slice(0, -1); |
- } |
- |
- if (oneTime) |
- return updateAttribute(this, name, conditional, value); |
- |
- |
- var observable = value; |
- updateAttribute(this, name, conditional, |
- observable.open(attributeBinding(this, name, conditional))); |
- |
- return maybeUpdateBindings(this, name, observable); |
- }; |
- |
- var checkboxEventType; |
- (function() { |
- // Attempt to feature-detect which event (change or click) is fired first |
- // for checkboxes. |
- var div = document.createElement('div'); |
- var checkbox = div.appendChild(document.createElement('input')); |
- checkbox.setAttribute('type', 'checkbox'); |
- var first; |
- var count = 0; |
- checkbox.addEventListener('click', function(e) { |
- count++; |
- first = first || 'click'; |
- }); |
- checkbox.addEventListener('change', function() { |
- count++; |
- first = first || 'change'; |
- }); |
- |
- var event = document.createEvent('MouseEvent'); |
- event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, |
- false, false, false, 0, null); |
- checkbox.dispatchEvent(event); |
- // WebKit/Blink don't fire the change event if the element is outside the |
- // document, so assume 'change' for that case. |
- checkboxEventType = count == 1 ? 'change' : first; |
- })(); |
- |
- function getEventForInputType(element) { |
- switch (element.type) { |
- case 'checkbox': |
- return checkboxEventType; |
- case 'radio': |
- case 'select-multiple': |
- case 'select-one': |
- return 'change'; |
- case 'range': |
- if (/Trident|MSIE/.test(navigator.userAgent)) |
- return 'change'; |
- default: |
- return 'input'; |
- } |
- } |
- |
- function updateInput(input, property, value, santizeFn) { |
- input[property] = (santizeFn || sanitizeValue)(value); |
- } |
- |
- function inputBinding(input, property, santizeFn) { |
- return function(value) { |
- return updateInput(input, property, value, santizeFn); |
- } |
- } |
- |
- function noop() {} |
- |
- function bindInputEvent(input, property, observable, postEventFn) { |
- var eventType = getEventForInputType(input); |
- |
- function eventHandler() { |
- observable.setValue(input[property]); |
- observable.discardChanges(); |
- (postEventFn || noop)(input); |
- Platform.performMicrotaskCheckpoint(); |
- } |
- input.addEventListener(eventType, eventHandler); |
- |
- return { |
- close: function() { |
- input.removeEventListener(eventType, eventHandler); |
- observable.close(); |
- }, |
- |
- observable_: observable |
- } |
- } |
- |
- function booleanSanitize(value) { |
- return Boolean(value); |
- } |
- |
- // |element| is assumed to be an HTMLInputElement with |type| == 'radio'. |
- // Returns an array containing all radio buttons other than |element| that |
- // have the same |name|, either in the form that |element| belongs to or, |
- // if no form, in the document tree to which |element| belongs. |
- // |
- // This implementation is based upon the HTML spec definition of a |
- // "radio button group": |
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group |
- // |
- function getAssociatedRadioButtons(element) { |
- if (element.form) { |
- return filter(element.form.elements, function(el) { |
- return el != element && |
- el.tagName == 'INPUT' && |
- el.type == 'radio' && |
- el.name == element.name; |
- }); |
- } else { |
- var treeScope = getTreeScope(element); |
- if (!treeScope) |
- return []; |
- var radios = treeScope.querySelectorAll( |
- 'input[type="radio"][name="' + element.name + '"]'); |
- return filter(radios, function(el) { |
- return el != element && !el.form; |
- }); |
- } |
- } |
- |
- function checkedPostEvent(input) { |
- // Only the radio button that is getting checked gets an event. We |
- // therefore find all the associated radio buttons and update their |
- // check binding manually. |
- if (input.tagName === 'INPUT' && |
- input.type === 'radio') { |
- getAssociatedRadioButtons(input).forEach(function(radio) { |
- var checkedBinding = radio.bindings_.checked; |
- if (checkedBinding) { |
- // Set the value directly to avoid an infinite call stack. |
- checkedBinding.observable_.setValue(false); |
- } |
- }); |
- } |
- } |
- |
- HTMLInputElement.prototype.bind = function(name, value, oneTime) { |
- if (name !== 'value' && name !== 'checked') |
- return HTMLElement.prototype.bind.call(this, name, value, oneTime); |
- |
- this.removeAttribute(name); |
- var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue; |
- var postEventFn = name == 'checked' ? checkedPostEvent : noop; |
- |
- if (oneTime) |
- return updateInput(this, name, value, sanitizeFn); |
- |
- |
- var observable = value; |
- var binding = bindInputEvent(this, name, observable, postEventFn); |
- updateInput(this, name, |
- observable.open(inputBinding(this, name, sanitizeFn)), |
- sanitizeFn); |
- |
- // Checkboxes may need to update bindings of other checkboxes. |
- return updateBindings(this, name, binding); |
- } |
- |
- HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) { |
- if (name !== 'value') |
- return HTMLElement.prototype.bind.call(this, name, value, oneTime); |
- |
- this.removeAttribute('value'); |
- |
- if (oneTime) |
- return updateInput(this, 'value', value); |
- |
- var observable = value; |
- var binding = bindInputEvent(this, 'value', observable); |
- updateInput(this, 'value', |
- observable.open(inputBinding(this, 'value', sanitizeValue))); |
- return maybeUpdateBindings(this, name, binding); |
- } |
- |
- function updateOption(option, value) { |
- var parentNode = option.parentNode;; |
- var select; |
- var selectBinding; |
- var oldValue; |
- if (parentNode instanceof HTMLSelectElement && |
- parentNode.bindings_ && |
- parentNode.bindings_.value) { |
- select = parentNode; |
- selectBinding = select.bindings_.value; |
- oldValue = select.value; |
- } |
- |
- option.value = sanitizeValue(value); |
- |
- if (select && select.value != oldValue) { |
- selectBinding.observable_.setValue(select.value); |
- selectBinding.observable_.discardChanges(); |
- Platform.performMicrotaskCheckpoint(); |
- } |
- } |
- |
- function optionBinding(option) { |
- return function(value) { |
- updateOption(option, value); |
- } |
- } |
- |
- HTMLOptionElement.prototype.bind = function(name, value, oneTime) { |
- if (name !== 'value') |
- return HTMLElement.prototype.bind.call(this, name, value, oneTime); |
- |
- this.removeAttribute('value'); |
- |
- if (oneTime) |
- return updateOption(this, value); |
- |
- var observable = value; |
- var binding = bindInputEvent(this, 'value', observable); |
- updateOption(this, observable.open(optionBinding(this))); |
- return maybeUpdateBindings(this, name, binding); |
- } |
- |
- HTMLSelectElement.prototype.bind = function(name, value, oneTime) { |
- if (name === 'selectedindex') |
- name = 'selectedIndex'; |
- |
- if (name !== 'selectedIndex' && name !== 'value') |
- return HTMLElement.prototype.bind.call(this, name, value, oneTime); |
- |
- this.removeAttribute(name); |
- |
- if (oneTime) |
- return updateInput(this, name, value); |
- |
- var observable = value; |
- var binding = bindInputEvent(this, name, observable); |
- updateInput(this, name, |
- observable.open(inputBinding(this, name))); |
- |
- // Option update events may need to access select bindings. |
- return updateBindings(this, name, binding); |
- } |
-})(this); |
- |
-// Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
-// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
-// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
-// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
-// Code distributed by Google as part of the polymer project is also |
-// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
- |
-(function(global) { |
- 'use strict'; |
- |
- function assert(v) { |
- if (!v) |
- throw new Error('Assertion failed'); |
- } |
- |
- var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |
- |
- function getFragmentRoot(node) { |
- var p; |
- while (p = node.parentNode) { |
- node = p; |
- } |
- |
- return node; |
- } |
- |
- function searchRefId(node, id) { |
- if (!id) |
- return; |
- |
- var ref; |
- var selector = '#' + id; |
- while (!ref) { |
- node = getFragmentRoot(node); |
- |
- if (node.protoContent_) |
- ref = node.protoContent_.querySelector(selector); |
- else if (node.getElementById) |
- ref = node.getElementById(id); |
- |
- if (ref || !node.templateCreator_) |
- break |
- |
- node = node.templateCreator_; |
- } |
- |
- return ref; |
- } |
- |
- function getInstanceRoot(node) { |
- while (node.parentNode) { |
- node = node.parentNode; |
- } |
- return node.templateCreator_ ? node : null; |
- } |
- |
- var Map; |
- if (global.Map && typeof global.Map.prototype.forEach === 'function') { |
- Map = global.Map; |
- } else { |
- Map = function() { |
- this.keys = []; |
- this.values = []; |
- }; |
- |
- Map.prototype = { |
- set: function(key, value) { |
- var index = this.keys.indexOf(key); |
- if (index < 0) { |
- this.keys.push(key); |
- this.values.push(value); |
- } else { |
- this.values[index] = value; |
- } |
- }, |
- |
- get: function(key) { |
- var index = this.keys.indexOf(key); |
- if (index < 0) |
- return; |
- |
- return this.values[index]; |
- }, |
- |
- delete: function(key, value) { |
- var index = this.keys.indexOf(key); |
- if (index < 0) |
- return false; |
- |
- this.keys.splice(index, 1); |
- this.values.splice(index, 1); |
- return true; |
- }, |
- |
- forEach: function(f, opt_this) { |
- for (var i = 0; i < this.keys.length; i++) |
- f.call(opt_this || this, this.values[i], this.keys[i], this); |
- } |
- }; |
- } |
- |
- // JScript does not have __proto__. We wrap all object literals with |
- // createObject which uses Object.create, Object.defineProperty and |
- // Object.getOwnPropertyDescriptor to create a new object that does the exact |
- // same thing. The main downside to this solution is that we have to extract |
- // all those property descriptors for IE. |
- 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; |
- }; |
- |
- // IE does not support have Document.prototype.contains. |
- if (typeof document.contains != 'function') { |
- Document.prototype.contains = function(node) { |
- if (node === this || node.parentNode === this) |
- return true; |
- return this.documentElement.contains(node); |
- } |
- } |
- |
- var BIND = 'bind'; |
- var REPEAT = 'repeat'; |
- var IF = 'if'; |
- |
- var templateAttributeDirectives = { |
- 'template': true, |
- 'repeat': true, |
- 'bind': true, |
- 'ref': true |
- }; |
- |
- var semanticTemplateElements = { |
- 'THEAD': true, |
- 'TBODY': true, |
- 'TFOOT': true, |
- 'TH': true, |
- 'TR': true, |
- 'TD': true, |
- 'COLGROUP': true, |
- 'COL': true, |
- 'CAPTION': true, |
- 'OPTION': true, |
- 'OPTGROUP': true |
- }; |
- |
- var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined'; |
- if (hasTemplateElement) { |
- // TODO(rafaelw): Remove when fix for |
- // https://codereview.chromium.org/164803002/ |
- // makes it to Chrome release. |
- (function() { |
- var t = document.createElement('template'); |
- var d = t.content.ownerDocument; |
- var html = d.appendChild(d.createElement('html')); |
- var head = html.appendChild(d.createElement('head')); |
- var base = d.createElement('base'); |
- base.href = document.baseURI; |
- head.appendChild(base); |
- })(); |
- } |
- |
- var allTemplatesSelectors = 'template, ' + |
- Object.keys(semanticTemplateElements).map(function(tagName) { |
- return tagName.toLowerCase() + '[template]'; |
- }).join(', '); |
- |
- function isSVGTemplate(el) { |
- return el.tagName == 'template' && |
- el.namespaceURI == 'http://www.w3.org/2000/svg'; |
- } |
- |
- function isHTMLTemplate(el) { |
- return el.tagName == 'TEMPLATE' && |
- el.namespaceURI == 'http://www.w3.org/1999/xhtml'; |
- } |
- |
- function isAttributeTemplate(el) { |
- return Boolean(semanticTemplateElements[el.tagName] && |
- el.hasAttribute('template')); |
- } |
- |
- function isTemplate(el) { |
- if (el.isTemplate_ === undefined) |
- el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el); |
- |
- return el.isTemplate_; |
- } |
- |
- // FIXME: Observe templates being added/removed from documents |
- // FIXME: Expose imperative API to decorate and observe templates in |
- // "disconnected tress" (e.g. ShadowRoot) |
- document.addEventListener('DOMContentLoaded', function(e) { |
- bootstrapTemplatesRecursivelyFrom(document); |
- // FIXME: Is this needed? Seems like it shouldn't be. |
- Platform.performMicrotaskCheckpoint(); |
- }, false); |
- |
- function forAllTemplatesFrom(node, fn) { |
- var subTemplates = node.querySelectorAll(allTemplatesSelectors); |
- |
- if (isTemplate(node)) |
- fn(node) |
- forEach(subTemplates, fn); |
- } |
- |
- function bootstrapTemplatesRecursivelyFrom(node) { |
- function bootstrap(template) { |
- if (!HTMLTemplateElement.decorate(template)) |
- bootstrapTemplatesRecursivelyFrom(template.content); |
- } |
- |
- forAllTemplatesFrom(node, bootstrap); |
- } |
- |
- if (!hasTemplateElement) { |
- /** |
- * This represents a <template> element. |
- * @constructor |
- * @extends {HTMLElement} |
- */ |
- global.HTMLTemplateElement = function() { |
- throw TypeError('Illegal constructor'); |
- }; |
- } |
- |
- var hasProto = '__proto__' in {}; |
- |
- function mixin(to, from) { |
- Object.getOwnPropertyNames(from).forEach(function(name) { |
- Object.defineProperty(to, name, |
- Object.getOwnPropertyDescriptor(from, name)); |
- }); |
- } |
- |
- // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner |
- function getOrCreateTemplateContentsOwner(template) { |
- var doc = template.ownerDocument |
- if (!doc.defaultView) |
- return doc; |
- var d = doc.templateContentsOwner_; |
- 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); |
- } |
- doc.templateContentsOwner_ = d; |
- } |
- return d; |
- } |
- |
- function getTemplateStagingDocument(template) { |
- if (!template.stagingDocument_) { |
- var owner = template.ownerDocument; |
- if (!owner.stagingDocument_) { |
- owner.stagingDocument_ = owner.implementation.createHTMLDocument(''); |
- |
- // TODO(rafaelw): Remove when fix for |
- // https://codereview.chromium.org/164803002/ |
- // makes it to Chrome release. |
- var base = owner.stagingDocument_.createElement('base'); |
- base.href = document.baseURI; |
- owner.stagingDocument_.head.appendChild(base); |
- |
- owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_; |
- } |
- |
- template.stagingDocument_ = owner.stagingDocument_; |
- } |
- |
- return template.stagingDocument_; |
- } |
- |
- // For non-template browsers, the parser will disallow <template> in certain |
- // locations, so we allow "attribute templates" which combine the template |
- // element with the top-level container node of the content, e.g. |
- // |
- // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr> |
- // |
- // becomes |
- // |
- // <template repeat="{{ foo }}"> |
- // + #document-fragment |
- // + <tr class="bar"> |
- // + <td>Bar</td> |
- // |
- function extractTemplateFromAttributeTemplate(el) { |
- var template = el.ownerDocument.createElement('template'); |
- el.parentNode.insertBefore(template, el); |
- |
- var attribs = el.attributes; |
- var count = attribs.length; |
- while (count-- > 0) { |
- var attrib = attribs[count]; |
- if (templateAttributeDirectives[attrib.name]) { |
- if (attrib.name !== 'template') |
- template.setAttribute(attrib.name, attrib.value); |
- el.removeAttribute(attrib.name); |
- } |
- } |
- |
- return template; |
- } |
- |
- function extractTemplateFromSVGTemplate(el) { |
- var template = el.ownerDocument.createElement('template'); |
- el.parentNode.insertBefore(template, el); |
- |
- var attribs = el.attributes; |
- var count = attribs.length; |
- while (count-- > 0) { |
- var attrib = attribs[count]; |
- template.setAttribute(attrib.name, attrib.value); |
- el.removeAttribute(attrib.name); |
- } |
- |
- el.parentNode.removeChild(el); |
- return template; |
- } |
- |
- function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) { |
- var content = template.content; |
- if (useRoot) { |
- content.appendChild(el); |
- return; |
- } |
- |
- var child; |
- while (child = el.firstChild) { |
- content.appendChild(child); |
- } |
- } |
- |
- var templateObserver; |
- if (typeof MutationObserver == 'function') { |
- templateObserver = new MutationObserver(function(records) { |
- for (var i = 0; i < records.length; i++) { |
- records[i].target.refChanged_(); |
- } |
- }); |
- } |
- |
- /** |
- * Ensures proper API and content model for template elements. |
- * @param {HTMLTemplateElement} opt_instanceRef The template element which |
- * |el| template element will return as the value of its ref(), and whose |
- * content will be used as source when createInstance() is invoked. |
- */ |
- HTMLTemplateElement.decorate = function(el, opt_instanceRef) { |
- if (el.templateIsDecorated_) |
- return false; |
- |
- var templateElement = el; |
- templateElement.templateIsDecorated_ = true; |
- |
- var isNativeHTMLTemplate = isHTMLTemplate(templateElement) && |
- hasTemplateElement; |
- var bootstrapContents = isNativeHTMLTemplate; |
- var liftContents = !isNativeHTMLTemplate; |
- var liftRoot = false; |
- |
- if (!isNativeHTMLTemplate) { |
- if (isAttributeTemplate(templateElement)) { |
- assert(!opt_instanceRef); |
- templateElement = extractTemplateFromAttributeTemplate(el); |
- templateElement.templateIsDecorated_ = true; |
- isNativeHTMLTemplate = hasTemplateElement; |
- liftRoot = true; |
- } else if (isSVGTemplate(templateElement)) { |
- templateElement = extractTemplateFromSVGTemplate(el); |
- templateElement.templateIsDecorated_ = true; |
- isNativeHTMLTemplate = hasTemplateElement; |
- } |
- } |
- |
- if (!isNativeHTMLTemplate) { |
- fixTemplateElementPrototype(templateElement); |
- var doc = getOrCreateTemplateContentsOwner(templateElement); |
- templateElement.content_ = doc.createDocumentFragment(); |
- } |
- |
- if (opt_instanceRef) { |
- // template is contained within an instance, its direct content must be |
- // empty |
- templateElement.instanceRef_ = opt_instanceRef; |
- } else if (liftContents) { |
- liftNonNativeTemplateChildrenIntoContent(templateElement, |
- el, |
- liftRoot); |
- } else if (bootstrapContents) { |
- bootstrapTemplatesRecursivelyFrom(templateElement.content); |
- } |
- |
- return true; |
- }; |
- |
- // TODO(rafaelw): This used to decorate recursively all templates from a given |
- // node. This happens by default on 'DOMContentLoaded', but may be needed |
- // in subtrees not descendent from document (e.g. ShadowRoot). |
- // Review whether this is the right public API. |
- HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom; |
- |
- var htmlElement = global.HTMLUnknownElement || HTMLElement; |
- |
- var contentDescriptor = { |
- get: function() { |
- return this.content_; |
- }, |
- enumerable: true, |
- configurable: true |
- }; |
- |
- if (!hasTemplateElement) { |
- // Gecko is more picky with the prototype than WebKit. Make sure to use the |
- // same prototype as created in the constructor. |
- HTMLTemplateElement.prototype = Object.create(htmlElement.prototype); |
- |
- Object.defineProperty(HTMLTemplateElement.prototype, 'content', |
- contentDescriptor); |
- } |
- |
- function fixTemplateElementPrototype(el) { |
- if (hasProto) |
- el.__proto__ = HTMLTemplateElement.prototype; |
- else |
- mixin(el, HTMLTemplateElement.prototype); |
- } |
- |
- function ensureSetModelScheduled(template) { |
- if (!template.setModelFn_) { |
- template.setModelFn_ = function() { |
- template.setModelFnScheduled_ = false; |
- var map = getBindings(template, |
- template.delegate_ && template.delegate_.prepareBinding); |
- processBindings(template, map, template.model_); |
- }; |
- } |
- |
- if (!template.setModelFnScheduled_) { |
- template.setModelFnScheduled_ = true; |
- Observer.runEOM_(template.setModelFn_); |
- } |
- } |
- |
- mixin(HTMLTemplateElement.prototype, { |
- bind: function(name, value, oneTime) { |
- if (name != 'ref') |
- return Element.prototype.bind.call(this, name, value, oneTime); |
- |
- var self = this; |
- var ref = oneTime ? value : value.open(function(ref) { |
- self.setAttribute('ref', ref); |
- self.refChanged_(); |
- }); |
- |
- this.setAttribute('ref', ref); |
- this.refChanged_(); |
- if (oneTime) |
- return; |
- |
- if (!this.bindings_) { |
- this.bindings_ = { ref: value }; |
- } else { |
- this.bindings_.ref = value; |
- } |
- |
- return value; |
- }, |
- |
- processBindingDirectives_: function(directives) { |
- if (this.iterator_) |
- this.iterator_.closeDeps(); |
- |
- if (!directives.if && !directives.bind && !directives.repeat) { |
- if (this.iterator_) { |
- this.iterator_.close(); |
- this.iterator_ = undefined; |
- } |
- |
- return; |
- } |
- |
- if (!this.iterator_) { |
- this.iterator_ = new TemplateIterator(this); |
- } |
- |
- this.iterator_.updateDependencies(directives, this.model_); |
- |
- if (templateObserver) { |
- templateObserver.observe(this, { attributes: true, |
- attributeFilter: ['ref'] }); |
- } |
- |
- return this.iterator_; |
- }, |
- |
- createInstance: function(model, bindingDelegate, delegate_) { |
- if (bindingDelegate) |
- delegate_ = this.newDelegate_(bindingDelegate); |
- |
- if (!this.refContent_) |
- this.refContent_ = this.ref_.content; |
- var content = this.refContent_; |
- if (content.firstChild === null) |
- return emptyInstance; |
- |
- var map = this.bindingMap_; |
- if (!map || map.content !== content) { |
- // TODO(rafaelw): Setup a MutationObserver on content to detect |
- // when the instanceMap is invalid. |
- map = createInstanceBindingMap(content, |
- delegate_ && delegate_.prepareBinding) || []; |
- map.content = content; |
- this.bindingMap_ = map; |
- } |
- |
- var stagingDocument = getTemplateStagingDocument(this); |
- var instance = stagingDocument.createDocumentFragment(); |
- instance.templateCreator_ = this; |
- instance.protoContent_ = content; |
- instance.bindings_ = []; |
- instance.terminator_ = null; |
- var instanceRecord = instance.templateInstance_ = { |
- firstNode: null, |
- lastNode: null, |
- model: model |
- }; |
- |
- var i = 0; |
- var collectTerminator = false; |
- for (var child = content.firstChild; child; child = child.nextSibling) { |
- // The terminator of the instance is the clone of the last child of the |
- // content. If the last child is an active template, it may produce |
- // instances as a result of production, so simply collecting the last |
- // child of the instance after it has finished producing may be wrong. |
- if (child.nextSibling === null) |
- collectTerminator = true; |
- |
- var clone = cloneAndBindInstance(child, instance, stagingDocument, |
- map.children[i++], |
- model, |
- delegate_, |
- instance.bindings_); |
- clone.templateInstance_ = instanceRecord; |
- if (collectTerminator) |
- instance.terminator_ = clone; |
- } |
- |
- instanceRecord.firstNode = instance.firstChild; |
- instanceRecord.lastNode = instance.lastChild; |
- instance.templateCreator_ = undefined; |
- instance.protoContent_ = undefined; |
- return instance; |
- }, |
- |
- get model() { |
- return this.model_; |
- }, |
- |
- set model(model) { |
- this.model_ = model; |
- ensureSetModelScheduled(this); |
- }, |
- |
- get bindingDelegate() { |
- return this.delegate_ && this.delegate_.raw; |
- }, |
- |
- refChanged_: function() { |
- if (!this.iterator_ || this.refContent_ === this.ref_.content) |
- return; |
- |
- this.refContent_ = undefined; |
- this.iterator_.valueChanged(); |
- this.iterator_.updateIteratedValue(); |
- }, |
- |
- clear: function() { |
- this.model_ = undefined; |
- this.delegate_ = undefined; |
- if (this.bindings_ && this.bindings_.ref) |
- this.bindings_.ref.close() |
- this.refContent_ = undefined; |
- if (!this.iterator_) |
- return; |
- this.iterator_.valueChanged(); |
- this.iterator_.close() |
- this.iterator_ = undefined; |
- }, |
- |
- setDelegate_: function(delegate) { |
- this.delegate_ = delegate; |
- this.bindingMap_ = undefined; |
- if (this.iterator_) { |
- this.iterator_.instancePositionChangedFn_ = undefined; |
- this.iterator_.instanceModelFn_ = undefined; |
- } |
- }, |
- |
- newDelegate_: function(bindingDelegate) { |
- if (!bindingDelegate) |
- return {}; |
- |
- function delegateFn(name) { |
- var fn = bindingDelegate && bindingDelegate[name]; |
- if (typeof fn != 'function') |
- return; |
- |
- return function() { |
- return fn.apply(bindingDelegate, arguments); |
- }; |
- } |
- |
- return { |
- raw: bindingDelegate, |
- prepareBinding: delegateFn('prepareBinding'), |
- prepareInstanceModel: delegateFn('prepareInstanceModel'), |
- prepareInstancePositionChanged: |
- delegateFn('prepareInstancePositionChanged') |
- }; |
- }, |
- |
- // TODO(rafaelw): Assigning .bindingDelegate always succeeds. It may |
- // make sense to issue a warning or even throw if the template is already |
- // "activated", since this would be a strange thing to do. |
- set bindingDelegate(bindingDelegate) { |
- if (this.delegate_) { |
- throw Error('Template must be cleared before a new bindingDelegate ' + |
- 'can be assigned'); |
- } |
- |
- this.setDelegate_(this.newDelegate_(bindingDelegate)); |
- }, |
- |
- get ref_() { |
- var ref = searchRefId(this, this.getAttribute('ref')); |
- if (!ref) |
- ref = this.instanceRef_; |
- |
- if (!ref) |
- return this; |
- |
- var nextRef = ref.ref_; |
- return nextRef ? nextRef : ref; |
- } |
- }); |
- |
- // Returns |
- // a) undefined if there are no mustaches. |
- // b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache. |
- function parseMustaches(s, name, node, prepareBindingFn) { |
- if (!s || !s.length) |
- return; |
- |
- var tokens; |
- var length = s.length; |
- var startIndex = 0, lastIndex = 0, endIndex = 0; |
- var onlyOneTime = true; |
- while (lastIndex < length) { |
- var startIndex = s.indexOf('{{', lastIndex); |
- var oneTimeStart = s.indexOf('[[', lastIndex); |
- var oneTime = false; |
- var terminator = '}}'; |
- |
- if (oneTimeStart >= 0 && |
- (startIndex < 0 || oneTimeStart < startIndex)) { |
- startIndex = oneTimeStart; |
- oneTime = true; |
- terminator = ']]'; |
- } |
- |
- endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2); |
- |
- if (endIndex < 0) { |
- if (!tokens) |
- return; |
- |
- tokens.push(s.slice(lastIndex)); // TEXT |
- break; |
- } |
- |
- tokens = tokens || []; |
- tokens.push(s.slice(lastIndex, startIndex)); // TEXT |
- var pathString = s.slice(startIndex + 2, endIndex).trim(); |
- tokens.push(oneTime); // ONE_TIME? |
- onlyOneTime = onlyOneTime && oneTime; |
- var delegateFn = prepareBindingFn && |
- prepareBindingFn(pathString, name, node); |
- // Don't try to parse the expression if there's a prepareBinding function |
- if (delegateFn == null) { |
- tokens.push(Path.get(pathString)); // PATH |
- } else { |
- tokens.push(null); |
- } |
- tokens.push(delegateFn); // DELEGATE_FN |
- lastIndex = endIndex + 2; |
- } |
- |
- if (lastIndex === length) |
- tokens.push(''); // TEXT |
- |
- tokens.hasOnePath = tokens.length === 5; |
- tokens.isSimplePath = tokens.hasOnePath && |
- tokens[0] == '' && |
- tokens[4] == ''; |
- tokens.onlyOneTime = onlyOneTime; |
- |
- tokens.combinator = function(values) { |
- var newValue = tokens[0]; |
- |
- for (var i = 1; i < tokens.length; i += 4) { |
- var value = tokens.hasOnePath ? values : values[(i - 1) / 4]; |
- if (value !== undefined) |
- newValue += value; |
- newValue += tokens[i + 3]; |
- } |
- |
- return newValue; |
- } |
- |
- return tokens; |
- }; |
- |
- function processOneTimeBinding(name, tokens, node, model) { |
- if (tokens.hasOnePath) { |
- var delegateFn = tokens[3]; |
- var value = delegateFn ? delegateFn(model, node, true) : |
- tokens[2].getValueFrom(model); |
- return tokens.isSimplePath ? value : tokens.combinator(value); |
- } |
- |
- var values = []; |
- for (var i = 1; i < tokens.length; i += 4) { |
- var delegateFn = tokens[i + 2]; |
- values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) : |
- tokens[i + 1].getValueFrom(model); |
- } |
- |
- return tokens.combinator(values); |
- } |
- |
- function processSinglePathBinding(name, tokens, node, model) { |
- var delegateFn = tokens[3]; |
- var observer = delegateFn ? delegateFn(model, node, false) : |
- new PathObserver(model, tokens[2]); |
- |
- return tokens.isSimplePath ? observer : |
- new ObserverTransform(observer, tokens.combinator); |
- } |
- |
- function processBinding(name, tokens, node, model) { |
- if (tokens.onlyOneTime) |
- return processOneTimeBinding(name, tokens, node, model); |
- |
- if (tokens.hasOnePath) |
- return processSinglePathBinding(name, tokens, node, model); |
- |
- var observer = new CompoundObserver(); |
- |
- for (var i = 1; i < tokens.length; i += 4) { |
- var oneTime = tokens[i]; |
- var delegateFn = tokens[i + 2]; |
- |
- if (delegateFn) { |
- var value = delegateFn(model, node, oneTime); |
- if (oneTime) |
- observer.addPath(value) |
- else |
- observer.addObserver(value); |
- continue; |
- } |
- |
- var path = tokens[i + 1]; |
- if (oneTime) |
- observer.addPath(path.getValueFrom(model)) |
- else |
- observer.addPath(model, path); |
- } |
- |
- return new ObserverTransform(observer, tokens.combinator); |
- } |
- |
- function processBindings(node, bindings, model, instanceBindings) { |
- for (var i = 0; i < bindings.length; i += 2) { |
- var name = bindings[i] |
- var tokens = bindings[i + 1]; |
- var value = processBinding(name, tokens, node, model); |
- var binding = node.bind(name, value, tokens.onlyOneTime); |
- if (binding && instanceBindings) |
- instanceBindings.push(binding); |
- } |
- |
- if (!bindings.isTemplate) |
- return; |
- |
- node.model_ = model; |
- var iter = node.processBindingDirectives_(bindings); |
- if (instanceBindings && iter) |
- instanceBindings.push(iter); |
- } |
- |
- function parseWithDefault(el, name, prepareBindingFn) { |
- var v = el.getAttribute(name); |
- return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn); |
- } |
- |
- function parseAttributeBindings(element, prepareBindingFn) { |
- assert(element); |
- |
- var bindings = []; |
- var ifFound = false; |
- var bindFound = false; |
- |
- for (var i = 0; i < element.attributes.length; i++) { |
- var attr = element.attributes[i]; |
- var name = attr.name; |
- var value = attr.value; |
- |
- // Allow bindings expressed in attributes to be prefixed with underbars. |
- // We do this to allow correct semantics for browsers that don't implement |
- // <template> where certain attributes might trigger side-effects -- and |
- // for IE which sanitizes certain attributes, disallowing mustache |
- // replacements in their text. |
- while (name[0] === '_') { |
- name = name.substring(1); |
- } |
- |
- if (isTemplate(element) && |
- (name === IF || name === BIND || name === REPEAT)) { |
- continue; |
- } |
- |
- var tokens = parseMustaches(value, name, element, |
- prepareBindingFn); |
- if (!tokens) |
- continue; |
- |
- bindings.push(name, tokens); |
- } |
- |
- if (isTemplate(element)) { |
- bindings.isTemplate = true; |
- bindings.if = parseWithDefault(element, IF, prepareBindingFn); |
- bindings.bind = parseWithDefault(element, BIND, prepareBindingFn); |
- bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn); |
- |
- if (bindings.if && !bindings.bind && !bindings.repeat) |
- bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn); |
- } |
- |
- return bindings; |
- } |
- |
- function getBindings(node, prepareBindingFn) { |
- if (node.nodeType === Node.ELEMENT_NODE) |
- return parseAttributeBindings(node, prepareBindingFn); |
- |
- if (node.nodeType === Node.TEXT_NODE) { |
- var tokens = parseMustaches(node.data, 'textContent', node, |
- prepareBindingFn); |
- if (tokens) |
- return ['textContent', tokens]; |
- } |
- |
- return []; |
- } |
- |
- function cloneAndBindInstance(node, parent, stagingDocument, bindings, model, |
- delegate, |
- instanceBindings, |
- instanceRecord) { |
- var clone = parent.appendChild(stagingDocument.importNode(node, false)); |
- |
- var i = 0; |
- for (var child = node.firstChild; child; child = child.nextSibling) { |
- cloneAndBindInstance(child, clone, stagingDocument, |
- bindings.children[i++], |
- model, |
- delegate, |
- instanceBindings); |
- } |
- |
- if (bindings.isTemplate) { |
- HTMLTemplateElement.decorate(clone, node); |
- if (delegate) |
- clone.setDelegate_(delegate); |
- } |
- |
- processBindings(clone, bindings, model, instanceBindings); |
- return clone; |
- } |
- |
- function createInstanceBindingMap(node, prepareBindingFn) { |
- var map = getBindings(node, prepareBindingFn); |
- map.children = {}; |
- var index = 0; |
- for (var child = node.firstChild; child; child = child.nextSibling) { |
- map.children[index++] = createInstanceBindingMap(child, prepareBindingFn); |
- } |
- |
- return map; |
- } |
- |
- Object.defineProperty(Node.prototype, 'templateInstance', { |
- get: function() { |
- var instance = this.templateInstance_; |
- return instance ? instance : |
- (this.parentNode ? this.parentNode.templateInstance : undefined); |
- } |
- }); |
- |
- var emptyInstance = document.createDocumentFragment(); |
- emptyInstance.bindings_ = []; |
- emptyInstance.terminator_ = null; |
- |
- function TemplateIterator(templateElement) { |
- this.closed = false; |
- this.templateElement_ = templateElement; |
- this.instances = []; |
- this.deps = undefined; |
- this.iteratedValue = []; |
- this.presentValue = undefined; |
- this.arrayObserver = undefined; |
- } |
- |
- TemplateIterator.prototype = { |
- closeDeps: function() { |
- var deps = this.deps; |
- if (deps) { |
- if (deps.ifOneTime === false) |
- deps.ifValue.close(); |
- if (deps.oneTime === false) |
- deps.value.close(); |
- } |
- }, |
- |
- updateDependencies: function(directives, model) { |
- this.closeDeps(); |
- |
- var deps = this.deps = {}; |
- var template = this.templateElement_; |
- |
- if (directives.if) { |
- deps.hasIf = true; |
- deps.ifOneTime = directives.if.onlyOneTime; |
- deps.ifValue = processBinding(IF, directives.if, template, model); |
- |
- // oneTime if & predicate is false. nothing else to do. |
- if (deps.ifOneTime && !deps.ifValue) { |
- this.updateIteratedValue(); |
- return; |
- } |
- |
- if (!deps.ifOneTime) |
- deps.ifValue.open(this.updateIteratedValue, this); |
- } |
- |
- if (directives.repeat) { |
- deps.repeat = true; |
- deps.oneTime = directives.repeat.onlyOneTime; |
- deps.value = processBinding(REPEAT, directives.repeat, template, model); |
- } else { |
- deps.repeat = false; |
- deps.oneTime = directives.bind.onlyOneTime; |
- deps.value = processBinding(BIND, directives.bind, template, model); |
- } |
- |
- if (!deps.oneTime) |
- deps.value.open(this.updateIteratedValue, this); |
- |
- this.updateIteratedValue(); |
- }, |
- |
- updateIteratedValue: function() { |
- if (this.deps.hasIf) { |
- var ifValue = this.deps.ifValue; |
- if (!this.deps.ifOneTime) |
- ifValue = ifValue.discardChanges(); |
- if (!ifValue) { |
- this.valueChanged(); |
- return; |
- } |
- } |
- |
- var value = this.deps.value; |
- if (!this.deps.oneTime) |
- value = value.discardChanges(); |
- if (!this.deps.repeat) |
- value = [value]; |
- var observe = this.deps.repeat && |
- !this.deps.oneTime && |
- Array.isArray(value); |
- this.valueChanged(value, observe); |
- }, |
- |
- valueChanged: function(value, observeValue) { |
- if (!Array.isArray(value)) |
- value = []; |
- |
- if (value === this.iteratedValue) |
- return; |
- |
- this.unobserve(); |
- this.presentValue = value; |
- if (observeValue) { |
- this.arrayObserver = new ArrayObserver(this.presentValue); |
- this.arrayObserver.open(this.handleSplices, this); |
- } |
- |
- this.handleSplices(ArrayObserver.calculateSplices(this.presentValue, |
- this.iteratedValue)); |
- }, |
- |
- getLastInstanceNode: function(index) { |
- if (index == -1) |
- return this.templateElement_; |
- var instance = this.instances[index]; |
- var terminator = instance.terminator_; |
- if (!terminator) |
- return this.getLastInstanceNode(index - 1); |
- |
- if (terminator.nodeType !== Node.ELEMENT_NODE || |
- this.templateElement_ === terminator) { |
- return terminator; |
- } |
- |
- var subtemplateIterator = terminator.iterator_; |
- if (!subtemplateIterator) |
- return terminator; |
- |
- return subtemplateIterator.getLastTemplateNode(); |
- }, |
- |
- getLastTemplateNode: function() { |
- return this.getLastInstanceNode(this.instances.length - 1); |
- }, |
- |
- insertInstanceAt: function(index, fragment) { |
- var previousInstanceLast = this.getLastInstanceNode(index - 1); |
- var parent = this.templateElement_.parentNode; |
- this.instances.splice(index, 0, fragment); |
- |
- parent.insertBefore(fragment, previousInstanceLast.nextSibling); |
- }, |
- |
- extractInstanceAt: function(index) { |
- var previousInstanceLast = this.getLastInstanceNode(index - 1); |
- var lastNode = this.getLastInstanceNode(index); |
- var parent = this.templateElement_.parentNode; |
- var instance = this.instances.splice(index, 1)[0]; |
- |
- while (lastNode !== previousInstanceLast) { |
- var node = previousInstanceLast.nextSibling; |
- if (node == lastNode) |
- lastNode = previousInstanceLast; |
- |
- instance.appendChild(parent.removeChild(node)); |
- } |
- |
- return instance; |
- }, |
- |
- getDelegateFn: function(fn) { |
- fn = fn && fn(this.templateElement_); |
- return typeof fn === 'function' ? fn : null; |
- }, |
- |
- handleSplices: function(splices) { |
- if (this.closed || !splices.length) |
- return; |
- |
- var template = this.templateElement_; |
- |
- if (!template.parentNode) { |
- this.close(); |
- return; |
- } |
- |
- ArrayObserver.applySplices(this.iteratedValue, this.presentValue, |
- splices); |
- |
- var delegate = template.delegate_; |
- if (this.instanceModelFn_ === undefined) { |
- this.instanceModelFn_ = |
- this.getDelegateFn(delegate && delegate.prepareInstanceModel); |
- } |
- |
- if (this.instancePositionChangedFn_ === undefined) { |
- this.instancePositionChangedFn_ = |
- this.getDelegateFn(delegate && |
- delegate.prepareInstancePositionChanged); |
- } |
- |
- // Instance Removals |
- var instanceCache = new Map; |
- var removeDelta = 0; |
- for (var i = 0; i < splices.length; i++) { |
- var splice = splices[i]; |
- var removed = splice.removed; |
- for (var j = 0; j < removed.length; j++) { |
- var model = removed[j]; |
- var instance = this.extractInstanceAt(splice.index + removeDelta); |
- if (instance !== emptyInstance) { |
- instanceCache.set(model, instance); |
- } |
- } |
- |
- removeDelta -= splice.addedCount; |
- } |
- |
- // Instance Insertions |
- for (var i = 0; i < splices.length; i++) { |
- var splice = splices[i]; |
- var addIndex = splice.index; |
- for (; addIndex < splice.index + splice.addedCount; addIndex++) { |
- var model = this.iteratedValue[addIndex]; |
- var instance = instanceCache.get(model); |
- if (instance) { |
- instanceCache.delete(model); |
- } else { |
- if (this.instanceModelFn_) { |
- model = this.instanceModelFn_(model); |
- } |
- |
- if (model === undefined) { |
- instance = emptyInstance; |
- } else { |
- instance = template.createInstance(model, undefined, delegate); |
- } |
- } |
- |
- this.insertInstanceAt(addIndex, instance); |
- } |
- } |
- |
- instanceCache.forEach(function(instance) { |
- this.closeInstanceBindings(instance); |
- }, this); |
- |
- if (this.instancePositionChangedFn_) |
- this.reportInstancesMoved(splices); |
- }, |
- |
- reportInstanceMoved: function(index) { |
- var instance = this.instances[index]; |
- if (instance === emptyInstance) |
- return; |
- |
- this.instancePositionChangedFn_(instance.templateInstance_, index); |
- }, |
- |
- reportInstancesMoved: function(splices) { |
- var index = 0; |
- var offset = 0; |
- for (var i = 0; i < splices.length; i++) { |
- var splice = splices[i]; |
- if (offset != 0) { |
- while (index < splice.index) { |
- this.reportInstanceMoved(index); |
- index++; |
- } |
- } else { |
- index = splice.index; |
- } |
- |
- while (index < splice.index + splice.addedCount) { |
- this.reportInstanceMoved(index); |
- index++; |
- } |
- |
- offset += splice.addedCount - splice.removed.length; |
- } |
- |
- if (offset == 0) |
- return; |
- |
- var length = this.instances.length; |
- while (index < length) { |
- this.reportInstanceMoved(index); |
- index++; |
- } |
- }, |
- |
- closeInstanceBindings: function(instance) { |
- var bindings = instance.bindings_; |
- for (var i = 0; i < bindings.length; i++) { |
- bindings[i].close(); |
- } |
- }, |
- |
- unobserve: function() { |
- if (!this.arrayObserver) |
- return; |
- |
- this.arrayObserver.close(); |
- this.arrayObserver = undefined; |
- }, |
- |
- close: function() { |
- if (this.closed) |
- return; |
- this.unobserve(); |
- for (var i = 0; i < this.instances.length; i++) { |
- this.closeInstanceBindings(this.instances[i]); |
- } |
- |
- this.instances.length = 0; |
- this.closeDeps(); |
- this.templateElement_.iterator_ = undefined; |
- this.closed = true; |
- } |
- }; |
- |
- // Polyfill-specific API. |
- HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom; |
-})(this); |
- |
-/* |
- Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com> |
- Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com> |
- Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com> |
- Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be> |
- Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl> |
- Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com> |
- Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com> |
- Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com> |
- Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com> |
- |
- Redistribution and use in source and binary forms, with or without |
- modification, are permitted provided that the following conditions are met: |
- |
- * Redistributions of source code must retain the above copyright |
- notice, this list of conditions and the following disclaimer. |
- * Redistributions in binary form must reproduce the above copyright |
- notice, this list of conditions and the following disclaimer in the |
- documentation and/or other materials provided with the distribution. |
- |
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
- ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY |
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
-*/ |
- |
-(function (global) { |
- 'use strict'; |
- |
- var Token, |
- TokenName, |
- Syntax, |
- Messages, |
- source, |
- index, |
- length, |
- delegate, |
- lookahead, |
- state; |
- |
- Token = { |
- BooleanLiteral: 1, |
- EOF: 2, |
- Identifier: 3, |
- Keyword: 4, |
- NullLiteral: 5, |
- NumericLiteral: 6, |
- Punctuator: 7, |
- StringLiteral: 8 |
- }; |
- |
- TokenName = {}; |
- TokenName[Token.BooleanLiteral] = 'Boolean'; |
- TokenName[Token.EOF] = '<end>'; |
- TokenName[Token.Identifier] = 'Identifier'; |
- TokenName[Token.Keyword] = 'Keyword'; |
- TokenName[Token.NullLiteral] = 'Null'; |
- TokenName[Token.NumericLiteral] = 'Numeric'; |
- TokenName[Token.Punctuator] = 'Punctuator'; |
- TokenName[Token.StringLiteral] = 'String'; |
- |
- Syntax = { |
- ArrayExpression: 'ArrayExpression', |
- BinaryExpression: 'BinaryExpression', |
- CallExpression: 'CallExpression', |
- ConditionalExpression: 'ConditionalExpression', |
- EmptyStatement: 'EmptyStatement', |
- ExpressionStatement: 'ExpressionStatement', |
- Identifier: 'Identifier', |
- Literal: 'Literal', |
- LabeledStatement: 'LabeledStatement', |
- LogicalExpression: 'LogicalExpression', |
- MemberExpression: 'MemberExpression', |
- ObjectExpression: 'ObjectExpression', |
- Program: 'Program', |
- Property: 'Property', |
- ThisExpression: 'ThisExpression', |
- UnaryExpression: 'UnaryExpression' |
- }; |
- |
- // Error messages should be identical to V8. |
- Messages = { |
- UnexpectedToken: 'Unexpected token %0', |
- UnknownLabel: 'Undefined label \'%0\'', |
- Redeclaration: '%0 \'%1\' has already been declared' |
- }; |
- |
- // Ensure the condition is true, otherwise throw an error. |
- // This is only to have a better contract semantic, i.e. another safety net |
- // to catch a logic error. The condition shall be fulfilled in normal case. |
- // Do NOT use this to enforce a certain condition on any user input. |
- |
- function assert(condition, message) { |
- if (!condition) { |
- throw new Error('ASSERT: ' + message); |
- } |
- } |
- |
- function isDecimalDigit(ch) { |
- return (ch >= 48 && ch <= 57); // 0..9 |
- } |
- |
- |
- // 7.2 White Space |
- |
- function isWhiteSpace(ch) { |
- return (ch === 32) || // space |
- (ch === 9) || // tab |
- (ch === 0xB) || |
- (ch === 0xC) || |
- (ch === 0xA0) || |
- (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0); |
- } |
- |
- // 7.3 Line Terminators |
- |
- function isLineTerminator(ch) { |
- return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029); |
- } |
- |
- // 7.6 Identifier Names and Identifiers |
- |
- function isIdentifierStart(ch) { |
- return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) |
- (ch >= 65 && ch <= 90) || // A..Z |
- (ch >= 97 && ch <= 122); // a..z |
- } |
- |
- function isIdentifierPart(ch) { |
- return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) |
- (ch >= 65 && ch <= 90) || // A..Z |
- (ch >= 97 && ch <= 122) || // a..z |
- (ch >= 48 && ch <= 57); // 0..9 |
- } |
- |
- // 7.6.1.1 Keywords |
- |
- function isKeyword(id) { |
- return (id === 'this') |
- } |
- |
- // 7.4 Comments |
- |
- function skipWhitespace() { |
- while (index < length && isWhiteSpace(source.charCodeAt(index))) { |
- ++index; |
- } |
- } |
- |
- function getIdentifier() { |
- var start, ch; |
- |
- start = index++; |
- while (index < length) { |
- ch = source.charCodeAt(index); |
- if (isIdentifierPart(ch)) { |
- ++index; |
- } else { |
- break; |
- } |
- } |
- |
- return source.slice(start, index); |
- } |
- |
- function scanIdentifier() { |
- var start, id, type; |
- |
- start = index; |
- |
- id = getIdentifier(); |
- |
- // There is no keyword or literal with only one character. |
- // Thus, it must be an identifier. |
- if (id.length === 1) { |
- type = Token.Identifier; |
- } else if (isKeyword(id)) { |
- type = Token.Keyword; |
- } else if (id === 'null') { |
- type = Token.NullLiteral; |
- } else if (id === 'true' || id === 'false') { |
- type = Token.BooleanLiteral; |
- } else { |
- type = Token.Identifier; |
- } |
- |
- return { |
- type: type, |
- value: id, |
- range: [start, index] |
- }; |
- } |
- |
- |
- // 7.7 Punctuators |
- |
- function scanPunctuator() { |
- var start = index, |
- code = source.charCodeAt(index), |
- code2, |
- ch1 = source[index], |
- ch2; |
- |
- switch (code) { |
- |
- // Check for most common single-character punctuators. |
- case 46: // . dot |
- case 40: // ( open bracket |
- case 41: // ) close bracket |
- case 59: // ; semicolon |
- case 44: // , comma |
- case 123: // { open curly brace |
- case 125: // } close curly brace |
- case 91: // [ |
- case 93: // ] |
- case 58: // : |
- case 63: // ? |
- ++index; |
- return { |
- type: Token.Punctuator, |
- value: String.fromCharCode(code), |
- range: [start, index] |
- }; |
- |
- default: |
- code2 = source.charCodeAt(index + 1); |
- |
- // '=' (char #61) marks an assignment or comparison operator. |
- if (code2 === 61) { |
- switch (code) { |
- case 37: // % |
- case 38: // & |
- case 42: // *: |
- case 43: // + |
- case 45: // - |
- case 47: // / |
- case 60: // < |
- case 62: // > |
- case 124: // | |
- index += 2; |
- return { |
- type: Token.Punctuator, |
- value: String.fromCharCode(code) + String.fromCharCode(code2), |
- range: [start, index] |
- }; |
- |
- case 33: // ! |
- case 61: // = |
- index += 2; |
- |
- // !== and === |
- if (source.charCodeAt(index) === 61) { |
- ++index; |
- } |
- return { |
- type: Token.Punctuator, |
- value: source.slice(start, index), |
- range: [start, index] |
- }; |
- default: |
- break; |
- } |
- } |
- break; |
- } |
- |
- // Peek more characters. |
- |
- ch2 = source[index + 1]; |
- |
- // Other 2-character punctuators: && || |
- |
- if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) { |
- index += 2; |
- return { |
- type: Token.Punctuator, |
- value: ch1 + ch2, |
- range: [start, index] |
- }; |
- } |
- |
- if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { |
- ++index; |
- return { |
- type: Token.Punctuator, |
- value: ch1, |
- range: [start, index] |
- }; |
- } |
- |
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
- } |
- |
- // 7.8.3 Numeric Literals |
- function scanNumericLiteral() { |
- var number, start, ch; |
- |
- ch = source[index]; |
- assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), |
- 'Numeric literal must start with a decimal digit or a decimal point'); |
- |
- start = index; |
- number = ''; |
- if (ch !== '.') { |
- number = source[index++]; |
- ch = source[index]; |
- |
- // Hex number starts with '0x'. |
- // Octal number starts with '0'. |
- if (number === '0') { |
- // decimal number starts with '0' such as '09' is illegal. |
- if (ch && isDecimalDigit(ch.charCodeAt(0))) { |
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
- } |
- } |
- |
- while (isDecimalDigit(source.charCodeAt(index))) { |
- number += source[index++]; |
- } |
- ch = source[index]; |
- } |
- |
- if (ch === '.') { |
- number += source[index++]; |
- while (isDecimalDigit(source.charCodeAt(index))) { |
- number += source[index++]; |
- } |
- ch = source[index]; |
- } |
- |
- if (ch === 'e' || ch === 'E') { |
- number += source[index++]; |
- |
- ch = source[index]; |
- if (ch === '+' || ch === '-') { |
- number += source[index++]; |
- } |
- if (isDecimalDigit(source.charCodeAt(index))) { |
- while (isDecimalDigit(source.charCodeAt(index))) { |
- number += source[index++]; |
- } |
- } else { |
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
- } |
- } |
- |
- if (isIdentifierStart(source.charCodeAt(index))) { |
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
- } |
- |
- return { |
- type: Token.NumericLiteral, |
- value: parseFloat(number), |
- range: [start, index] |
- }; |
- } |
- |
- // 7.8.4 String Literals |
- |
- function scanStringLiteral() { |
- var str = '', quote, start, ch, octal = false; |
- |
- quote = source[index]; |
- assert((quote === '\'' || quote === '"'), |
- 'String literal must starts with a quote'); |
- |
- start = index; |
- ++index; |
- |
- while (index < length) { |
- ch = source[index++]; |
- |
- if (ch === quote) { |
- quote = ''; |
- break; |
- } else if (ch === '\\') { |
- ch = source[index++]; |
- if (!ch || !isLineTerminator(ch.charCodeAt(0))) { |
- switch (ch) { |
- case 'n': |
- str += '\n'; |
- break; |
- case 'r': |
- str += '\r'; |
- break; |
- case 't': |
- str += '\t'; |
- break; |
- case 'b': |
- str += '\b'; |
- break; |
- case 'f': |
- str += '\f'; |
- break; |
- case 'v': |
- str += '\x0B'; |
- break; |
- |
- default: |
- str += ch; |
- break; |
- } |
- } else { |
- if (ch === '\r' && source[index] === '\n') { |
- ++index; |
- } |
- } |
- } else if (isLineTerminator(ch.charCodeAt(0))) { |
- break; |
- } else { |
- str += ch; |
- } |
- } |
- |
- if (quote !== '') { |
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
- } |
- |
- return { |
- type: Token.StringLiteral, |
- value: str, |
- octal: octal, |
- range: [start, index] |
- }; |
- } |
- |
- function isIdentifierName(token) { |
- return token.type === Token.Identifier || |
- token.type === Token.Keyword || |
- token.type === Token.BooleanLiteral || |
- token.type === Token.NullLiteral; |
- } |
- |
- function advance() { |
- var ch; |
- |
- skipWhitespace(); |
- |
- if (index >= length) { |
- return { |
- type: Token.EOF, |
- range: [index, index] |
- }; |
- } |
- |
- ch = source.charCodeAt(index); |
- |
- // Very common: ( and ) and ; |
- if (ch === 40 || ch === 41 || ch === 58) { |
- return scanPunctuator(); |
- } |
- |
- // String literal starts with single quote (#39) or double quote (#34). |
- if (ch === 39 || ch === 34) { |
- return scanStringLiteral(); |
- } |
- |
- if (isIdentifierStart(ch)) { |
- return scanIdentifier(); |
- } |
- |
- // Dot (.) char #46 can also start a floating-point number, hence the need |
- // to check the next character. |
- if (ch === 46) { |
- if (isDecimalDigit(source.charCodeAt(index + 1))) { |
- return scanNumericLiteral(); |
- } |
- return scanPunctuator(); |
- } |
- |
- if (isDecimalDigit(ch)) { |
- return scanNumericLiteral(); |
- } |
- |
- return scanPunctuator(); |
- } |
- |
- function lex() { |
- var token; |
- |
- token = lookahead; |
- index = token.range[1]; |
- |
- lookahead = advance(); |
- |
- index = token.range[1]; |
- |
- return token; |
- } |
- |
- function peek() { |
- var pos; |
- |
- pos = index; |
- lookahead = advance(); |
- index = pos; |
- } |
- |
- // Throw an exception |
- |
- function throwError(token, messageFormat) { |
- var error, |
- args = Array.prototype.slice.call(arguments, 2), |
- msg = messageFormat.replace( |
- /%(\d)/g, |
- function (whole, index) { |
- assert(index < args.length, 'Message reference must be in range'); |
- return args[index]; |
- } |
- ); |
- |
- error = new Error(msg); |
- error.index = index; |
- error.description = msg; |
- throw error; |
- } |
- |
- // Throw an exception because of the token. |
- |
- function throwUnexpected(token) { |
- throwError(token, Messages.UnexpectedToken, token.value); |
- } |
- |
- // Expect the next token to match the specified punctuator. |
- // If not, an exception will be thrown. |
- |
- function expect(value) { |
- var token = lex(); |
- if (token.type !== Token.Punctuator || token.value !== value) { |
- throwUnexpected(token); |
- } |
- } |
- |
- // Return true if the next token matches the specified punctuator. |
- |
- function match(value) { |
- return lookahead.type === Token.Punctuator && lookahead.value === value; |
- } |
- |
- // Return true if the next token matches the specified keyword |
- |
- function matchKeyword(keyword) { |
- return lookahead.type === Token.Keyword && lookahead.value === keyword; |
- } |
- |
- function consumeSemicolon() { |
- // Catch the very common case first: immediately a semicolon (char #59). |
- if (source.charCodeAt(index) === 59) { |
- lex(); |
- return; |
- } |
- |
- skipWhitespace(); |
- |
- if (match(';')) { |
- lex(); |
- return; |
- } |
- |
- if (lookahead.type !== Token.EOF && !match('}')) { |
- throwUnexpected(lookahead); |
- } |
- } |
- |
- // 11.1.4 Array Initialiser |
- |
- function parseArrayInitialiser() { |
- var elements = []; |
- |
- expect('['); |
- |
- while (!match(']')) { |
- if (match(',')) { |
- lex(); |
- elements.push(null); |
- } else { |
- elements.push(parseExpression()); |
- |
- if (!match(']')) { |
- expect(','); |
- } |
- } |
- } |
- |
- expect(']'); |
- |
- return delegate.createArrayExpression(elements); |
- } |
- |
- // 11.1.5 Object Initialiser |
- |
- function parseObjectPropertyKey() { |
- var token; |
- |
- skipWhitespace(); |
- token = lex(); |
- |
- // Note: This function is called only from parseObjectProperty(), where |
- // EOF and Punctuator tokens are already filtered out. |
- if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) { |
- return delegate.createLiteral(token); |
- } |
- |
- return delegate.createIdentifier(token.value); |
- } |
- |
- function parseObjectProperty() { |
- var token, key; |
- |
- token = lookahead; |
- skipWhitespace(); |
- |
- if (token.type === Token.EOF || token.type === Token.Punctuator) { |
- throwUnexpected(token); |
- } |
- |
- key = parseObjectPropertyKey(); |
- expect(':'); |
- return delegate.createProperty('init', key, parseExpression()); |
- } |
- |
- function parseObjectInitialiser() { |
- var properties = []; |
- |
- expect('{'); |
- |
- while (!match('}')) { |
- properties.push(parseObjectProperty()); |
- |
- if (!match('}')) { |
- expect(','); |
- } |
- } |
- |
- expect('}'); |
- |
- return delegate.createObjectExpression(properties); |
- } |
- |
- // 11.1.6 The Grouping Operator |
- |
- function parseGroupExpression() { |
- var expr; |
- |
- expect('('); |
- |
- expr = parseExpression(); |
- |
- expect(')'); |
- |
- return expr; |
- } |
- |
- |
- // 11.1 Primary Expressions |
- |
- function parsePrimaryExpression() { |
- var type, token, expr; |
- |
- if (match('(')) { |
- return parseGroupExpression(); |
- } |
- |
- type = lookahead.type; |
- |
- if (type === Token.Identifier) { |
- expr = delegate.createIdentifier(lex().value); |
- } else if (type === Token.StringLiteral || type === Token.NumericLiteral) { |
- expr = delegate.createLiteral(lex()); |
- } else if (type === Token.Keyword) { |
- if (matchKeyword('this')) { |
- lex(); |
- expr = delegate.createThisExpression(); |
- } |
- } else if (type === Token.BooleanLiteral) { |
- token = lex(); |
- token.value = (token.value === 'true'); |
- expr = delegate.createLiteral(token); |
- } else if (type === Token.NullLiteral) { |
- token = lex(); |
- token.value = null; |
- expr = delegate.createLiteral(token); |
- } else if (match('[')) { |
- expr = parseArrayInitialiser(); |
- } else if (match('{')) { |
- expr = parseObjectInitialiser(); |
- } |
- |
- if (expr) { |
- return expr; |
- } |
- |
- throwUnexpected(lex()); |
- } |
- |
- // 11.2 Left-Hand-Side Expressions |
- |
- function parseArguments() { |
- var args = []; |
- |
- expect('('); |
- |
- if (!match(')')) { |
- while (index < length) { |
- args.push(parseExpression()); |
- if (match(')')) { |
- break; |
- } |
- expect(','); |
- } |
- } |
- |
- expect(')'); |
- |
- return args; |
- } |
- |
- function parseNonComputedProperty() { |
- var token; |
- |
- token = lex(); |
- |
- if (!isIdentifierName(token)) { |
- throwUnexpected(token); |
- } |
- |
- return delegate.createIdentifier(token.value); |
- } |
- |
- function parseNonComputedMember() { |
- expect('.'); |
- |
- return parseNonComputedProperty(); |
- } |
- |
- function parseComputedMember() { |
- var expr; |
- |
- expect('['); |
- |
- expr = parseExpression(); |
- |
- expect(']'); |
- |
- return expr; |
- } |
- |
- function parseLeftHandSideExpression() { |
- var expr, property; |
- |
- expr = parsePrimaryExpression(); |
- |
- while (match('.') || match('[')) { |
- if (match('[')) { |
- property = parseComputedMember(); |
- expr = delegate.createMemberExpression('[', expr, property); |
- } else { |
- property = parseNonComputedMember(); |
- expr = delegate.createMemberExpression('.', expr, property); |
- } |
- } |
- |
- return expr; |
- } |
- |
- // 11.3 Postfix Expressions |
- |
- var parsePostfixExpression = parseLeftHandSideExpression; |
- |
- // 11.4 Unary Operators |
- |
- function parseUnaryExpression() { |
- var token, expr; |
- |
- if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { |
- expr = parsePostfixExpression(); |
- } else if (match('+') || match('-') || match('!')) { |
- token = lex(); |
- expr = parseUnaryExpression(); |
- expr = delegate.createUnaryExpression(token.value, expr); |
- } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { |
- throwError({}, Messages.UnexpectedToken); |
- } else { |
- expr = parsePostfixExpression(); |
- } |
- |
- return expr; |
- } |
- |
- function binaryPrecedence(token) { |
- var prec = 0; |
- |
- if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { |
- return 0; |
- } |
- |
- switch (token.value) { |
- case '||': |
- prec = 1; |
- break; |
- |
- case '&&': |
- prec = 2; |
- break; |
- |
- case '==': |
- case '!=': |
- case '===': |
- case '!==': |
- prec = 6; |
- break; |
- |
- case '<': |
- case '>': |
- case '<=': |
- case '>=': |
- case 'instanceof': |
- prec = 7; |
- break; |
- |
- case 'in': |
- prec = 7; |
- break; |
- |
- case '+': |
- case '-': |
- prec = 9; |
- break; |
- |
- case '*': |
- case '/': |
- case '%': |
- prec = 11; |
- break; |
- |
- default: |
- break; |
- } |
- |
- return prec; |
- } |
- |
- // 11.5 Multiplicative Operators |
- // 11.6 Additive Operators |
- // 11.7 Bitwise Shift Operators |
- // 11.8 Relational Operators |
- // 11.9 Equality Operators |
- // 11.10 Binary Bitwise Operators |
- // 11.11 Binary Logical Operators |
- |
- function parseBinaryExpression() { |
- var expr, token, prec, stack, right, operator, left, i; |
- |
- left = parseUnaryExpression(); |
- |
- token = lookahead; |
- prec = binaryPrecedence(token); |
- if (prec === 0) { |
- return left; |
- } |
- token.prec = prec; |
- lex(); |
- |
- right = parseUnaryExpression(); |
- |
- stack = [left, token, right]; |
- |
- while ((prec = binaryPrecedence(lookahead)) > 0) { |
- |
- // Reduce: make a binary expression from the three topmost entries. |
- while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { |
- right = stack.pop(); |
- operator = stack.pop().value; |
- left = stack.pop(); |
- expr = delegate.createBinaryExpression(operator, left, right); |
- stack.push(expr); |
- } |
- |
- // Shift. |
- token = lex(); |
- token.prec = prec; |
- stack.push(token); |
- expr = parseUnaryExpression(); |
- stack.push(expr); |
- } |
- |
- // Final reduce to clean-up the stack. |
- i = stack.length - 1; |
- expr = stack[i]; |
- while (i > 1) { |
- expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr); |
- i -= 2; |
- } |
- |
- return expr; |
- } |
- |
- |
- // 11.12 Conditional Operator |
- |
- function parseConditionalExpression() { |
- var expr, consequent, alternate; |
- |
- expr = parseBinaryExpression(); |
- |
- if (match('?')) { |
- lex(); |
- consequent = parseConditionalExpression(); |
- expect(':'); |
- alternate = parseConditionalExpression(); |
- |
- expr = delegate.createConditionalExpression(expr, consequent, alternate); |
- } |
- |
- return expr; |
- } |
- |
- // Simplification since we do not support AssignmentExpression. |
- var parseExpression = parseConditionalExpression; |
- |
- // Polymer Syntax extensions |
- |
- // Filter :: |
- // Identifier |
- // Identifier "(" ")" |
- // Identifier "(" FilterArguments ")" |
- |
- function parseFilter() { |
- var identifier, args; |
- |
- identifier = lex(); |
- |
- if (identifier.type !== Token.Identifier) { |
- throwUnexpected(identifier); |
- } |
- |
- args = match('(') ? parseArguments() : []; |
- |
- return delegate.createFilter(identifier.value, args); |
- } |
- |
- // Filters :: |
- // "|" Filter |
- // Filters "|" Filter |
- |
- function parseFilters() { |
- while (match('|')) { |
- lex(); |
- parseFilter(); |
- } |
- } |
- |
- // TopLevel :: |
- // LabelledExpressions |
- // AsExpression |
- // InExpression |
- // FilterExpression |
- |
- // AsExpression :: |
- // FilterExpression as Identifier |
- |
- // InExpression :: |
- // Identifier, Identifier in FilterExpression |
- // Identifier in FilterExpression |
- |
- // FilterExpression :: |
- // Expression |
- // Expression Filters |
- |
- function parseTopLevel() { |
- skipWhitespace(); |
- peek(); |
- |
- var expr = parseExpression(); |
- if (expr) { |
- if (lookahead.value === ',' || lookahead.value == 'in' && |
- expr.type === Syntax.Identifier) { |
- parseInExpression(expr); |
- } else { |
- parseFilters(); |
- if (lookahead.value === 'as') { |
- parseAsExpression(expr); |
- } else { |
- delegate.createTopLevel(expr); |
- } |
- } |
- } |
- |
- if (lookahead.type !== Token.EOF) { |
- throwUnexpected(lookahead); |
- } |
- } |
- |
- function parseAsExpression(expr) { |
- lex(); // as |
- var identifier = lex().value; |
- delegate.createAsExpression(expr, identifier); |
- } |
- |
- function parseInExpression(identifier) { |
- var indexName; |
- if (lookahead.value === ',') { |
- lex(); |
- if (lookahead.type !== Token.Identifier) |
- throwUnexpected(lookahead); |
- indexName = lex().value; |
- } |
- |
- lex(); // in |
- var expr = parseExpression(); |
- parseFilters(); |
- delegate.createInExpression(identifier.name, indexName, expr); |
- } |
- |
- function parse(code, inDelegate) { |
- delegate = inDelegate; |
- source = code; |
- index = 0; |
- length = source.length; |
- lookahead = null; |
- state = { |
- labelSet: {} |
- }; |
- |
- return parseTopLevel(); |
- } |
- |
- global.esprima = { |
- parse: parse |
- }; |
-})(this); |
- |
-// Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
-// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
-// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
-// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
-// Code distributed by Google as part of the polymer project is also |
-// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
- |
-(function (global) { |
- 'use strict'; |
- |
- // JScript does not have __proto__. We wrap all object literals with |
- // createObject which uses Object.create, Object.defineProperty and |
- // Object.getOwnPropertyDescriptor to create a new object that does the exact |
- // same thing. The main downside to this solution is that we have to extract |
- // all those property descriptors for IE. |
- 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; |
- }; |
- |
- function prepareBinding(expressionText, name, node, filterRegistry) { |
- var expression; |
- try { |
- expression = getExpression(expressionText); |
- if (expression.scopeIdent && |
- (node.nodeType !== Node.ELEMENT_NODE || |
- node.tagName !== 'TEMPLATE' || |
- (name !== 'bind' && name !== 'repeat'))) { |
- throw Error('as and in can only be used within <template bind/repeat>'); |
- } |
- } catch (ex) { |
- console.error('Invalid expression syntax: ' + expressionText, ex); |
- return; |
- } |
- |
- return function(model, node, oneTime) { |
- var binding = expression.getBinding(model, filterRegistry, oneTime); |
- if (expression.scopeIdent && binding) { |
- node.polymerExpressionScopeIdent_ = expression.scopeIdent; |
- if (expression.indexIdent) |
- node.polymerExpressionIndexIdent_ = expression.indexIdent; |
- } |
- |
- return binding; |
- } |
- } |
- |
- // TODO(rafaelw): Implement simple LRU. |
- var expressionParseCache = Object.create(null); |
- |
- function getExpression(expressionText) { |
- var expression = expressionParseCache[expressionText]; |
- if (!expression) { |
- var delegate = new ASTDelegate(); |
- esprima.parse(expressionText, delegate); |
- expression = new Expression(delegate); |
- expressionParseCache[expressionText] = expression; |
- } |
- return expression; |
- } |
- |
- function Literal(value) { |
- this.value = value; |
- this.valueFn_ = undefined; |
- } |
- |
- Literal.prototype = { |
- valueFn: function() { |
- if (!this.valueFn_) { |
- var value = this.value; |
- this.valueFn_ = function() { |
- return value; |
- } |
- } |
- |
- return this.valueFn_; |
- } |
- } |
- |
- function IdentPath(name) { |
- this.name = name; |
- this.path = Path.get(name); |
- } |
- |
- IdentPath.prototype = { |
- valueFn: function() { |
- if (!this.valueFn_) { |
- var name = this.name; |
- var path = this.path; |
- this.valueFn_ = function(model, observer) { |
- if (observer) |
- observer.addPath(model, path); |
- |
- return path.getValueFrom(model); |
- } |
- } |
- |
- return this.valueFn_; |
- }, |
- |
- setValue: function(model, newValue) { |
- if (this.path.length == 1); |
- model = findScope(model, this.path[0]); |
- |
- return this.path.setValueFrom(model, newValue); |
- } |
- }; |
- |
- function MemberExpression(object, property, accessor) { |
- // convert literal computed property access where literal value is a value |
- // path to ident dot-access. |
- if (accessor == '[' && |
- property instanceof Literal && |
- Path.get(property.value).valid) { |
- accessor = '.'; |
- property = new IdentPath(property.value); |
- } |
- |
- this.dynamicDeps = typeof object == 'function' || object.dynamic; |
- |
- this.dynamic = typeof property == 'function' || |
- property.dynamic || |
- accessor == '['; |
- |
- this.simplePath = |
- !this.dynamic && |
- !this.dynamicDeps && |
- property instanceof IdentPath && |
- (object instanceof MemberExpression || object instanceof IdentPath); |
- |
- this.object = this.simplePath ? object : getFn(object); |
- this.property = accessor == '.' ? property : getFn(property); |
- } |
- |
- MemberExpression.prototype = { |
- get fullPath() { |
- if (!this.fullPath_) { |
- var last = this.object instanceof IdentPath ? |
- this.object.name : this.object.fullPath; |
- this.fullPath_ = Path.get(last + '.' + this.property.name); |
- } |
- |
- return this.fullPath_; |
- }, |
- |
- valueFn: function() { |
- if (!this.valueFn_) { |
- var object = this.object; |
- |
- if (this.simplePath) { |
- var path = this.fullPath; |
- |
- this.valueFn_ = function(model, observer) { |
- if (observer) |
- observer.addPath(model, path); |
- |
- return path.getValueFrom(model); |
- }; |
- } else if (this.property instanceof IdentPath) { |
- var path = Path.get(this.property.name); |
- |
- this.valueFn_ = function(model, observer) { |
- var context = object(model, observer); |
- |
- if (observer) |
- observer.addPath(context, path); |
- |
- return path.getValueFrom(context); |
- } |
- } else { |
- // Computed property. |
- var property = this.property; |
- |
- this.valueFn_ = function(model, observer) { |
- var context = object(model, observer); |
- var propName = property(model, observer); |
- if (observer) |
- observer.addPath(context, propName); |
- |
- return context ? context[propName] : undefined; |
- }; |
- } |
- } |
- return this.valueFn_; |
- }, |
- |
- setValue: function(model, newValue) { |
- if (this.simplePath) { |
- this.fullPath.setValueFrom(model, newValue); |
- return newValue; |
- } |
- |
- var object = this.object(model); |
- var propName = this.property instanceof IdentPath ? this.property.name : |
- this.property(model); |
- return object[propName] = newValue; |
- } |
- }; |
- |
- function Filter(name, args) { |
- this.name = name; |
- this.args = []; |
- for (var i = 0; i < args.length; i++) { |
- this.args[i] = getFn(args[i]); |
- } |
- } |
- |
- Filter.prototype = { |
- transform: function(value, toModelDirection, filterRegistry, model, |
- observer) { |
- var fn = filterRegistry[this.name]; |
- var context = model; |
- if (fn) { |
- context = undefined; |
- } else { |
- fn = context[this.name]; |
- if (!fn) { |
- console.error('Cannot find filter: ' + this.name); |
- return; |
- } |
- } |
- |
- // If toModelDirection is falsey, then the "normal" (dom-bound) direction |
- // is used. Otherwise, it looks for a 'toModel' property function on the |
- // object. |
- if (toModelDirection) { |
- fn = fn.toModel; |
- } else if (typeof fn.toDOM == 'function') { |
- fn = fn.toDOM; |
- } |
- |
- if (typeof fn != 'function') { |
- console.error('No ' + (toModelDirection ? 'toModel' : 'toDOM') + |
- ' found on' + this.name); |
- return; |
- } |
- |
- var args = [value]; |
- for (var i = 0; i < this.args.length; i++) { |
- args[i + 1] = getFn(this.args[i])(model, observer); |
- } |
- |
- return fn.apply(context, args); |
- } |
- }; |
- |
- function notImplemented() { throw Error('Not Implemented'); } |
- |
- var unaryOperators = { |
- '+': function(v) { return +v; }, |
- '-': function(v) { return -v; }, |
- '!': function(v) { return !v; } |
- }; |
- |
- var binaryOperators = { |
- '+': function(l, r) { return l+r; }, |
- '-': function(l, r) { return l-r; }, |
- '*': function(l, r) { return l*r; }, |
- '/': function(l, r) { return l/r; }, |
- '%': function(l, r) { return l%r; }, |
- '<': function(l, r) { return l<r; }, |
- '>': function(l, r) { return l>r; }, |
- '<=': function(l, r) { return l<=r; }, |
- '>=': function(l, r) { return l>=r; }, |
- '==': function(l, r) { return l==r; }, |
- '!=': function(l, r) { return l!=r; }, |
- '===': function(l, r) { return l===r; }, |
- '!==': function(l, r) { return l!==r; }, |
- '&&': function(l, r) { return l&&r; }, |
- '||': function(l, r) { return l||r; }, |
- }; |
- |
- function getFn(arg) { |
- return typeof arg == 'function' ? arg : arg.valueFn(); |
- } |
- |
- function ASTDelegate() { |
- this.expression = null; |
- this.filters = []; |
- this.deps = {}; |
- this.currentPath = undefined; |
- this.scopeIdent = undefined; |
- this.indexIdent = undefined; |
- this.dynamicDeps = false; |
- } |
- |
- ASTDelegate.prototype = { |
- createUnaryExpression: function(op, argument) { |
- if (!unaryOperators[op]) |
- throw Error('Disallowed operator: ' + op); |
- |
- argument = getFn(argument); |
- |
- return function(model, observer) { |
- return unaryOperators[op](argument(model, observer)); |
- }; |
- }, |
- |
- createBinaryExpression: function(op, left, right) { |
- if (!binaryOperators[op]) |
- throw Error('Disallowed operator: ' + op); |
- |
- left = getFn(left); |
- right = getFn(right); |
- |
- return function(model, observer) { |
- return binaryOperators[op](left(model, observer), |
- right(model, observer)); |
- }; |
- }, |
- |
- createConditionalExpression: function(test, consequent, alternate) { |
- test = getFn(test); |
- consequent = getFn(consequent); |
- alternate = getFn(alternate); |
- |
- return function(model, observer) { |
- return test(model, observer) ? |
- consequent(model, observer) : alternate(model, observer); |
- } |
- }, |
- |
- createIdentifier: function(name) { |
- var ident = new IdentPath(name); |
- ident.type = 'Identifier'; |
- return ident; |
- }, |
- |
- createMemberExpression: function(accessor, object, property) { |
- var ex = new MemberExpression(object, property, accessor); |
- if (ex.dynamicDeps) |
- this.dynamicDeps = true; |
- return ex; |
- }, |
- |
- createLiteral: function(token) { |
- return new Literal(token.value); |
- }, |
- |
- createArrayExpression: function(elements) { |
- for (var i = 0; i < elements.length; i++) |
- elements[i] = getFn(elements[i]); |
- |
- return function(model, observer) { |
- var arr = [] |
- for (var i = 0; i < elements.length; i++) |
- arr.push(elements[i](model, observer)); |
- return arr; |
- } |
- }, |
- |
- createProperty: function(kind, key, value) { |
- return { |
- key: key instanceof IdentPath ? key.name : key.value, |
- value: value |
- }; |
- }, |
- |
- createObjectExpression: function(properties) { |
- for (var i = 0; i < properties.length; i++) |
- properties[i].value = getFn(properties[i].value); |
- |
- return function(model, observer) { |
- var obj = {}; |
- for (var i = 0; i < properties.length; i++) |
- obj[properties[i].key] = properties[i].value(model, observer); |
- return obj; |
- } |
- }, |
- |
- createFilter: function(name, args) { |
- this.filters.push(new Filter(name, args)); |
- }, |
- |
- createAsExpression: function(expression, scopeIdent) { |
- this.expression = expression; |
- this.scopeIdent = scopeIdent; |
- }, |
- |
- createInExpression: function(scopeIdent, indexIdent, expression) { |
- this.expression = expression; |
- this.scopeIdent = scopeIdent; |
- this.indexIdent = indexIdent; |
- }, |
- |
- createTopLevel: function(expression) { |
- this.expression = expression; |
- }, |
- |
- createThisExpression: notImplemented |
- } |
- |
- function ConstantObservable(value) { |
- this.value_ = value; |
- } |
- |
- ConstantObservable.prototype = { |
- open: function() { return this.value_; }, |
- discardChanges: function() { return this.value_; }, |
- deliver: function() {}, |
- close: function() {}, |
- } |
- |
- function Expression(delegate) { |
- this.scopeIdent = delegate.scopeIdent; |
- this.indexIdent = delegate.indexIdent; |
- |
- if (!delegate.expression) |
- throw Error('No expression found.'); |
- |
- this.expression = delegate.expression; |
- getFn(this.expression); // forces enumeration of path dependencies |
- |
- this.filters = delegate.filters; |
- this.dynamicDeps = delegate.dynamicDeps; |
- } |
- |
- Expression.prototype = { |
- getBinding: function(model, filterRegistry, oneTime) { |
- if (oneTime) |
- return this.getValue(model, undefined, filterRegistry); |
- |
- var observer = new CompoundObserver(); |
- // captures deps. |
- var firstValue = this.getValue(model, observer, filterRegistry); |
- var firstTime = true; |
- var self = this; |
- |
- function valueFn() { |
- // deps cannot have changed on first value retrieval. |
- if (firstTime) { |
- firstTime = false; |
- return firstValue; |
- } |
- |
- if (self.dynamicDeps) |
- observer.startReset(); |
- |
- var value = self.getValue(model, |
- self.dynamicDeps ? observer : undefined, |
- filterRegistry); |
- if (self.dynamicDeps) |
- observer.finishReset(); |
- |
- return value; |
- } |
- |
- function setValueFn(newValue) { |
- self.setValue(model, newValue, filterRegistry); |
- return newValue; |
- } |
- |
- return new ObserverTransform(observer, valueFn, setValueFn, true); |
- }, |
- |
- getValue: function(model, observer, filterRegistry) { |
- var value = getFn(this.expression)(model, observer); |
- for (var i = 0; i < this.filters.length; i++) { |
- value = this.filters[i].transform(value, false, filterRegistry, model, |
- observer); |
- } |
- |
- return value; |
- }, |
- |
- setValue: function(model, newValue, filterRegistry) { |
- var count = this.filters ? this.filters.length : 0; |
- while (count-- > 0) { |
- newValue = this.filters[count].transform(newValue, true, filterRegistry, |
- model); |
- } |
- |
- if (this.expression.setValue) |
- return this.expression.setValue(model, newValue); |
- } |
- } |
- |
- /** |
- * Converts a style property name to a css property name. For example: |
- * "WebkitUserSelect" to "-webkit-user-select" |
- */ |
- function convertStylePropertyName(name) { |
- return String(name).replace(/[A-Z]/g, function(c) { |
- return '-' + c.toLowerCase(); |
- }); |
- } |
- |
- function isEventHandler(name) { |
- return name[0] === 'o' && |
- name[1] === 'n' && |
- name[2] === '-'; |
- } |
- |
- var mixedCaseEventTypes = {}; |
- [ |
- 'webkitAnimationStart', |
- 'webkitAnimationEnd', |
- 'webkitTransitionEnd', |
- 'DOMFocusOut', |
- 'DOMFocusIn', |
- 'DOMMouseScroll' |
- ].forEach(function(e) { |
- mixedCaseEventTypes[e.toLowerCase()] = e; |
- }); |
- |
- var parentScopeName = '@' + Math.random().toString(36).slice(2); |
- |
- // Single ident paths must bind directly to the appropriate scope object. |
- // I.e. Pushed values in two-bindings need to be assigned to the actual model |
- // object. |
- function findScope(model, prop) { |
- while (model[parentScopeName] && |
- !Object.prototype.hasOwnProperty.call(model, prop)) { |
- model = model[parentScopeName]; |
- } |
- |
- return model; |
- } |
- |
- function resolveEventReceiver(model, path, node) { |
- if (path.length == 0) |
- return undefined; |
- |
- if (path.length == 1) |
- return findScope(model, path[0]); |
- |
- for (var i = 0; model != null && i < path.length - 1; i++) { |
- model = model[path[i]]; |
- } |
- |
- return model; |
- } |
- |
- function prepareEventBinding(path, name, polymerExpressions) { |
- var eventType = name.substring(3); |
- eventType = mixedCaseEventTypes[eventType] || eventType; |
- |
- return function(model, node, oneTime) { |
- var fn, receiver, handler; |
- if (typeof polymerExpressions.resolveEventHandler == 'function') { |
- handler = function(e) { |
- fn = fn || polymerExpressions.resolveEventHandler(model, path, node); |
- fn(e, e.detail, e.currentTarget); |
- |
- if (Platform && typeof Platform.flush == 'function') |
- Platform.flush(); |
- }; |
- } else { |
- handler = function(e) { |
- fn = fn || path.getValueFrom(model); |
- receiver = receiver || resolveEventReceiver(model, path, node); |
- |
- fn.apply(receiver, [e, e.detail, e.currentTarget]); |
- |
- if (Platform && typeof Platform.flush == 'function') |
- Platform.flush(); |
- }; |
- } |
- |
- node.addEventListener(eventType, handler); |
- |
- if (oneTime) |
- return; |
- |
- function bindingValue() { |
- return '{{ ' + path + ' }}'; |
- } |
- |
- return { |
- open: bindingValue, |
- discardChanges: bindingValue, |
- close: function() { |
- node.removeEventListener(eventType, handler); |
- } |
- }; |
- } |
- } |
- |
- function isLiteralExpression(pathString) { |
- switch (pathString) { |
- case '': |
- return false; |
- |
- case 'false': |
- case 'null': |
- case 'true': |
- return true; |
- } |
- |
- if (!isNaN(Number(pathString))) |
- return true; |
- |
- return false; |
- }; |
- |
- function PolymerExpressions() {} |
- |
- PolymerExpressions.prototype = { |
- // "built-in" filters |
- styleObject: function(value) { |
- var parts = []; |
- for (var key in value) { |
- parts.push(convertStylePropertyName(key) + ': ' + value[key]); |
- } |
- return parts.join('; '); |
- }, |
- |
- tokenList: function(value) { |
- var tokens = []; |
- for (var key in value) { |
- if (value[key]) |
- tokens.push(key); |
- } |
- return tokens.join(' '); |
- }, |
- |
- // binding delegate API |
- prepareInstancePositionChanged: function(template) { |
- var indexIdent = template.polymerExpressionIndexIdent_; |
- if (!indexIdent) |
- return; |
- |
- return function(templateInstance, index) { |
- templateInstance.model[indexIdent] = index; |
- }; |
- }, |
- |
- prepareBinding: function(pathString, name, node) { |
- var path = Path.get(pathString); |
- if (isEventHandler(name)) { |
- if (!path.valid) { |
- console.error('on-* bindings must be simple path expressions'); |
- return; |
- } |
- |
- return prepareEventBinding(path, name, this); |
- } |
- |
- if (!isLiteralExpression(pathString) && path.valid) { |
- if (path.length == 1) { |
- return function(model, node, oneTime) { |
- if (oneTime) |
- return path.getValueFrom(model); |
- |
- var scope = findScope(model, path[0]); |
- return new PathObserver(scope, path); |
- }; |
- } |
- return; // bail out early if pathString is simple path. |
- } |
- |
- return prepareBinding(pathString, name, node, this); |
- }, |
- |
- prepareInstanceModel: function(template) { |
- var scopeName = template.polymerExpressionScopeIdent_; |
- if (!scopeName) |
- return; |
- |
- var parentScope = template.templateInstance ? |
- template.templateInstance.model : |
- template.model; |
- |
- var indexName = template.polymerExpressionIndexIdent_; |
- |
- return function(model) { |
- var scope = Object.create(parentScope); |
- scope[scopeName] = model; |
- scope[indexName] = undefined; |
- scope[parentScopeName] = parentScope; |
- return scope; |
- }; |
- } |
- }; |
- |
- global.PolymerExpressions = PolymerExpressions; |
- if (global.exposeGetExpression) |
- global.getExpression_ = getExpression; |
- |
- global.PolymerExpressions.prepareEventBinding = prepareEventBinding; |
-})(this); |
- |
-/* |
- * 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) { |
- |
-// inject style sheet |
-var style = document.createElement('style'); |
-style.textContent = 'template {display: none !important;} /* injected by platform.js */'; |
-var head = document.querySelector('head'); |
-head.insertBefore(style, head.firstChild); |
- |
-// flush (with logging) |
-var flushing; |
-function flush() { |
- if (!flushing) { |
- flushing = true; |
- scope.endOfMicrotask(function() { |
- flushing = false; |
- logFlags.data && console.group('Platform.flush()'); |
- scope.performMicrotaskCheckpoint(); |
- logFlags.data && console.groupEnd(); |
- }); |
- } |
-}; |
- |
-// polling dirty checker |
-// flush periodically if platform does not have object observe. |
-if (!Observer.hasObjectObserve) { |
- var FLUSH_POLL_INTERVAL = 125; |
- window.addEventListener('WebComponentsReady', function() { |
- flush(); |
- scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL); |
- }); |
-} else { |
- // make flush a no-op when we have Object.observe |
- flush = function() {}; |
-} |
- |
-if (window.CustomElements && !CustomElements.useNative) { |
- var originalImportNode = Document.prototype.importNode; |
- Document.prototype.importNode = function(node, deep) { |
- var imported = originalImportNode.call(this, node, deep); |
- CustomElements.upgradeAll(imported); |
- return imported; |
- } |
-} |
- |
-// exports |
-scope.flush = flush; |
- |
-})(window.Platform); |
- |
- |
-//# sourceMappingURL=platform.concat.js.map |