summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordbeam <dbeam@chromium.org>2016-02-09 12:29:16 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-09 20:30:46 +0000
commit82518f51af928f359dc51011a56ae221b9ff9508 (patch)
treec53b45d2c4402ad4fbf0f376fab680550e72c336
parent27a62f7974d680eb378bc23ef961eddcc19875b6 (diff)
downloadchromium_src-82518f51af928f359dc51011a56ae221b9ff9508.zip
chromium_src-82518f51af928f359dc51011a56ae221b9ff9508.tar.gz
chromium_src-82518f51af928f359dc51011a56ae221b9ff9508.tar.bz2
Unrestrict version of PolymerElements/iron-list and update it
Also pulls in whatever happened in the last few day in Polymer R=dpapad@chromium.org BUG=none NOPRESUBMIT=true # crisper.js Review URL: https://codereview.chromium.org/1681053002 Cr-Commit-Position: refs/heads/master@{#374455}
-rw-r--r--chrome/browser/resources/md_downloads/crisper.js1611
-rw-r--r--chrome/browser/resources/md_downloads/vulcanized.html19
-rw-r--r--chrome/browser/resources/md_history/compiled_resources.gyp2
-rw-r--r--chrome/test/data/webui/md_history/md_history_browsertest.js3
-rw-r--r--third_party/polymer/v1_0/bower.json2
-rw-r--r--third_party/polymer/v1_0/components-chromium/iron-list/.bower.json26
-rw-r--r--third_party/polymer/v1_0/components-chromium/iron-list/README.md44
-rw-r--r--third_party/polymer/v1_0/components-chromium/iron-list/bower.json18
-rw-r--r--third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js438
-rw-r--r--third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html54
-rw-r--r--third_party/polymer/v1_0/components-chromium/paper-dialog/.bower.json18
-rw-r--r--third_party/polymer/v1_0/components-chromium/paper-dialog/CONTRIBUTING.md11
-rw-r--r--third_party/polymer/v1_0/components-chromium/paper-dialog/README.md46
-rw-r--r--third_party/polymer/v1_0/components-chromium/paper-dialog/bower.json12
-rw-r--r--third_party/polymer/v1_0/components-chromium/paper-dialog/paper-dialog-extracted.js2
-rw-r--r--third_party/polymer/v1_0/components_summary.txt16
16 files changed, 1490 insertions, 832 deletions
diff --git a/chrome/browser/resources/md_downloads/crisper.js b/chrome/browser/resources/md_downloads/crisper.js
index 1c61c9f..0c4f256 100644
--- a/chrome/browser/resources/md_downloads/crisper.js
+++ b/chrome/browser/resources/md_downloads/crisper.js
@@ -9,6 +9,9 @@
*/
var global = this;
+/** @typedef {{eventName: string, uid: number}} */
+var WebUIListener;
+
/** Platform, package, object property, and Event support. **/
var cr = function() {
'use strict';
@@ -357,11 +360,13 @@ var cr = function() {
}
/**
- * A registry of callbacks keyed by event name. Used by addWebUIListener to
- * register listeners.
- * @type {!Object<Array<Function>>}
+ * A map of maps associating event names with listeners. The 2nd level map
+ * associates a listener ID with the callback function, such that individual
+ * listeners can be removed from an event without affecting other listeners of
+ * the same event.
+ * @type {!Object<!Object<!Function>>}
*/
- var webUIListenerMap = Object.create(null);
+ var webUIListenerMap = {};
/**
* The named method the WebUI handler calls directly when an event occurs.
@@ -369,26 +374,52 @@ var cr = function() {
* of the JS invocation; additionally, the handler may supply any number of
* other arguments that will be forwarded to the listener callbacks.
* @param {string} event The name of the event that has occurred.
+ * @param {...*} var_args Additional arguments passed from C++.
*/
- function webUIListenerCallback(event) {
- var listenerCallbacks = webUIListenerMap[event];
- for (var i = 0; i < listenerCallbacks.length; i++) {
- var callback = listenerCallbacks[i];
- callback.apply(null, Array.prototype.slice.call(arguments, 1));
+ function webUIListenerCallback(event, var_args) {
+ var eventListenersMap = webUIListenerMap[event];
+ if (!eventListenersMap) {
+ // C++ event sent for an event that has no listeners.
+ // TODO(dpapad): Should a warning be displayed here?
+ return;
+ }
+
+ var args = Array.prototype.slice.call(arguments, 1);
+ for (var listenerId in eventListenersMap) {
+ eventListenersMap[listenerId].apply(null, args);
}
}
/**
* Registers a listener for an event fired from WebUI handlers. Any number of
* listeners may register for a single event.
- * @param {string} event The event to listen to.
- * @param {Function} callback The callback run when the event is fired.
+ * @param {string} eventName The event to listen to.
+ * @param {!Function} callback The callback run when the event is fired.
+ * @return {!WebUIListener} An object to be used for removing a listener via
+ * cr.removeWebUIListener. Should be treated as read-only.
*/
- function addWebUIListener(event, callback) {
- if (event in webUIListenerMap)
- webUIListenerMap[event].push(callback);
- else
- webUIListenerMap[event] = [callback];
+ function addWebUIListener(eventName, callback) {
+ webUIListenerMap[eventName] = webUIListenerMap[eventName] || {};
+ var uid = createUid();
+ webUIListenerMap[eventName][uid] = callback;
+ return {eventName: eventName, uid: uid};
+ }
+
+ /**
+ * Removes a listener. Does nothing if the specified listener is not found.
+ * @param {!WebUIListener} listener The listener to be removed (as returned by
+ * addWebUIListener).
+ * @return {boolean} Whether the given listener was found and actually
+ * removed.
+ */
+ function removeWebUIListener(listener) {
+ var listenerExists = webUIListenerMap[listener.eventName] &&
+ webUIListenerMap[listener.eventName][listener.uid];
+ if (listenerExists) {
+ delete webUIListenerMap[listener.eventName][listener.uid];
+ return true;
+ }
+ return false;
}
return {
@@ -401,11 +432,14 @@ var cr = function() {
exportPath: exportPath,
getUid: getUid,
makePublic: makePublic,
- webUIResponse: webUIResponse,
+ PropertyKind: PropertyKind,
+
+ // C++ <-> JS communication related methods.
+ addWebUIListener: addWebUIListener,
+ removeWebUIListener: removeWebUIListener,
sendWithPromise: sendWithPromise,
webUIListenerCallback: webUIListenerCallback,
- addWebUIListener: addWebUIListener,
- PropertyKind: PropertyKind,
+ webUIResponse: webUIResponse,
get doc() {
return document;
@@ -430,6 +464,11 @@ var cr = function() {
get isLinux() {
return /Linux/.test(navigator.userAgent);
},
+
+ /** Whether this is on Android. */
+ get isAndroid() {
+ return /Android/.test(navigator.userAgent);
+ }
};
}();
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
@@ -987,9 +1026,19 @@ cr.define('cr.ui', function() {
*/
function $(id) {
var el = document.getElementById(id);
- var message =
- 'Element ' + el + ' with id "' + id + '" is not an HTMLElement.';
- return el ? assertInstanceof(el, HTMLElement, message) : null;
+ return el ? assertInstanceof(el, HTMLElement) : null;
+}
+
+// TODO(devlin): This should return SVGElement, but closure compiler is missing
+// those externs.
+/**
+ * Alias for document.getElementById. Found elements must be SVGElements.
+ * @param {string} id The ID of the element to find.
+ * @return {Element} The found element or null if not found.
+ */
+function getSVGElement(id) {
+ var el = document.getElementById(id);
+ return el ? assertInstanceof(el, Element) : null;
}
/**
@@ -1505,8 +1554,12 @@ function assertNotReached(opt_message) {
* @template T
*/
function assertInstanceof(value, type, opt_message) {
- assert(value instanceof type,
- opt_message || value + ' is not a[n] ' + (type.name || typeof type));
+ // We don't use assert immediately here so that we avoid constructing an error
+ // message if we don't have to.
+ if (!(value instanceof type)) {
+ assertNotReached(opt_message || 'Value ' + value +
+ ' is not a[n] ' + (type.name || typeof type));
+ }
return value;
};
// Copyright 2015 The Chromium Authors. All rights reserved.
@@ -1979,11 +2032,654 @@ i18nTemplate.process(document, loadTimeData);
}
};
(function() {
+ 'use strict';
+
+ /**
+ * Chrome uses an older version of DOM Level 3 Keyboard Events
+ *
+ * Most keys are labeled as text, but some are Unicode codepoints.
+ * Values taken from: http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html#KeySet-Set
+ */
+ var KEY_IDENTIFIER = {
+ 'U+0008': 'backspace',
+ 'U+0009': 'tab',
+ 'U+001B': 'esc',
+ 'U+0020': 'space',
+ 'U+007F': 'del'
+ };
+
+ /**
+ * Special table for KeyboardEvent.keyCode.
+ * KeyboardEvent.keyIdentifier is better, and KeyBoardEvent.key is even better
+ * than that.
+ *
+ * Values from: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode#Value_of_keyCode
+ */
+ var KEY_CODE = {
+ 8: 'backspace',
+ 9: 'tab',
+ 13: 'enter',
+ 27: 'esc',
+ 33: 'pageup',
+ 34: 'pagedown',
+ 35: 'end',
+ 36: 'home',
+ 32: 'space',
+ 37: 'left',
+ 38: 'up',
+ 39: 'right',
+ 40: 'down',
+ 46: 'del',
+ 106: '*'
+ };
+
+ /**
+ * MODIFIER_KEYS maps the short name for modifier keys used in a key
+ * combo string to the property name that references those same keys
+ * in a KeyboardEvent instance.
+ */
+ var MODIFIER_KEYS = {
+ 'shift': 'shiftKey',
+ 'ctrl': 'ctrlKey',
+ 'alt': 'altKey',
+ 'meta': 'metaKey'
+ };
+
+ /**
+ * KeyboardEvent.key is mostly represented by printable character made by
+ * the keyboard, with unprintable keys labeled nicely.
+ *
+ * However, on OS X, Alt+char can make a Unicode character that follows an
+ * Apple-specific mapping. In this case, we fall back to .keyCode.
+ */
+ var KEY_CHAR = /[a-z0-9*]/;
+
+ /**
+ * Matches a keyIdentifier string.
+ */
+ var IDENT_CHAR = /U\+/;
+
+ /**
+ * Matches arrow keys in Gecko 27.0+
+ */
+ var ARROW_KEY = /^arrow/;
+
+ /**
+ * Matches space keys everywhere (notably including IE10's exceptional name
+ * `spacebar`).
+ */
+ var SPACE_KEY = /^space(bar)?/;
+
+ /**
+ * Transforms the key.
+ * @param {string} key The KeyBoardEvent.key
+ * @param {Boolean} [noSpecialChars] Limits the transformation to
+ * alpha-numeric characters.
+ */
+ function transformKey(key, noSpecialChars) {
+ var validKey = '';
+ if (key) {
+ var lKey = key.toLowerCase();
+ if (lKey === ' ' || SPACE_KEY.test(lKey)) {
+ validKey = 'space';
+ } else if (lKey.length == 1) {
+ if (!noSpecialChars || KEY_CHAR.test(lKey)) {
+ validKey = lKey;
+ }
+ } else if (ARROW_KEY.test(lKey)) {
+ validKey = lKey.replace('arrow', '');
+ } else if (lKey == 'multiply') {
+ // numpad '*' can map to Multiply on IE/Windows
+ validKey = '*';
+ } else {
+ validKey = lKey;
+ }
+ }
+ return validKey;
+ }
+
+ function transformKeyIdentifier(keyIdent) {
+ var validKey = '';
+ if (keyIdent) {
+ if (keyIdent in KEY_IDENTIFIER) {
+ validKey = KEY_IDENTIFIER[keyIdent];
+ } else if (IDENT_CHAR.test(keyIdent)) {
+ keyIdent = parseInt(keyIdent.replace('U+', '0x'), 16);
+ validKey = String.fromCharCode(keyIdent).toLowerCase();
+ } else {
+ validKey = keyIdent.toLowerCase();
+ }
+ }
+ return validKey;
+ }
+
+ function transformKeyCode(keyCode) {
+ var validKey = '';
+ if (Number(keyCode)) {
+ if (keyCode >= 65 && keyCode <= 90) {
+ // ascii a-z
+ // lowercase is 32 offset from uppercase
+ validKey = String.fromCharCode(32 + keyCode);
+ } else if (keyCode >= 112 && keyCode <= 123) {
+ // function keys f1-f12
+ validKey = 'f' + (keyCode - 112);
+ } else if (keyCode >= 48 && keyCode <= 57) {
+ // top 0-9 keys
+ validKey = String(48 - keyCode);
+ } else if (keyCode >= 96 && keyCode <= 105) {
+ // num pad 0-9
+ validKey = String(96 - keyCode);
+ } else {
+ validKey = KEY_CODE[keyCode];
+ }
+ }
+ return validKey;
+ }
+
+ /**
+ * Calculates the normalized key for a KeyboardEvent.
+ * @param {KeyboardEvent} keyEvent
+ * @param {Boolean} [noSpecialChars] Set to true to limit keyEvent.key
+ * transformation to alpha-numeric chars. This is useful with key
+ * combinations like shift + 2, which on FF for MacOS produces
+ * keyEvent.key = @
+ * To get 2 returned, set noSpecialChars = true
+ * To get @ returned, set noSpecialChars = false
+ */
+ function normalizedKeyForEvent(keyEvent, noSpecialChars) {
+ // Fall back from .key, to .keyIdentifier, to .keyCode, and then to
+ // .detail.key to support artificial keyboard events.
+ return transformKey(keyEvent.key, noSpecialChars) ||
+ transformKeyIdentifier(keyEvent.keyIdentifier) ||
+ transformKeyCode(keyEvent.keyCode) ||
+ transformKey(keyEvent.detail.key, noSpecialChars) || '';
+ }
+
+ function keyComboMatchesEvent(keyCombo, event) {
+ // For combos with modifiers we support only alpha-numeric keys
+ var keyEvent = normalizedKeyForEvent(event, keyCombo.hasModifiers);
+ return keyEvent === keyCombo.key &&
+ (!keyCombo.hasModifiers || (
+ !!event.shiftKey === !!keyCombo.shiftKey &&
+ !!event.ctrlKey === !!keyCombo.ctrlKey &&
+ !!event.altKey === !!keyCombo.altKey &&
+ !!event.metaKey === !!keyCombo.metaKey)
+ );
+ }
+
+ function parseKeyComboString(keyComboString) {
+ if (keyComboString.length === 1) {
+ return {
+ combo: keyComboString,
+ key: keyComboString,
+ event: 'keydown'
+ };
+ }
+ return keyComboString.split('+').reduce(function(parsedKeyCombo, keyComboPart) {
+ var eventParts = keyComboPart.split(':');
+ var keyName = eventParts[0];
+ var event = eventParts[1];
+
+ if (keyName in MODIFIER_KEYS) {
+ parsedKeyCombo[MODIFIER_KEYS[keyName]] = true;
+ parsedKeyCombo.hasModifiers = true;
+ } else {
+ parsedKeyCombo.key = keyName;
+ parsedKeyCombo.event = event || 'keydown';
+ }
+
+ return parsedKeyCombo;
+ }, {
+ combo: keyComboString.split(':').shift()
+ });
+ }
+
+ function parseEventString(eventString) {
+ return eventString.trim().split(' ').map(function(keyComboString) {
+ return parseKeyComboString(keyComboString);
+ });
+ }
+
+ /**
+ * `Polymer.IronA11yKeysBehavior` provides a normalized interface for processing
+ * keyboard commands that pertain to [WAI-ARIA best practices](http://www.w3.org/TR/wai-aria-practices/#kbd_general_binding).
+ * The element takes care of browser differences with respect to Keyboard events
+ * and uses an expressive syntax to filter key presses.
+ *
+ * Use the `keyBindings` prototype property to express what combination of keys
+ * will trigger the event to fire.
+ *
+ * Use the `key-event-target` attribute to set up event handlers on a specific
+ * node.
+ * The `keys-pressed` event will fire when one of the key combinations set with the
+ * `keys` property is pressed.
+ *
+ * @demo demo/index.html
+ * @polymerBehavior
+ */
+ Polymer.IronA11yKeysBehavior = {
+ properties: {
+ /**
+ * The HTMLElement that will be firing relevant KeyboardEvents.
+ */
+ keyEventTarget: {
+ type: Object,
+ value: function() {
+ return this;
+ }
+ },
+
+ /**
+ * If true, this property will cause the implementing element to
+ * automatically stop propagation on any handled KeyboardEvents.
+ */
+ stopKeyboardEventPropagation: {
+ type: Boolean,
+ value: false
+ },
+
+ _boundKeyHandlers: {
+ type: Array,
+ value: function() {
+ return [];
+ }
+ },
+
+ // We use this due to a limitation in IE10 where instances will have
+ // own properties of everything on the "prototype".
+ _imperativeKeyBindings: {
+ type: Object,
+ value: function() {
+ return {};
+ }
+ }
+ },
+
+ observers: [
+ '_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)'
+ ],
+
+ keyBindings: {},
+
+ registered: function() {
+ this._prepKeyBindings();
+ },
+
+ attached: function() {
+ this._listenKeyEventListeners();
+ },
+
+ detached: function() {
+ this._unlistenKeyEventListeners();
+ },
+
+ /**
+ * Can be used to imperatively add a key binding to the implementing
+ * element. This is the imperative equivalent of declaring a keybinding
+ * in the `keyBindings` prototype property.
+ */
+ addOwnKeyBinding: function(eventString, handlerName) {
+ this._imperativeKeyBindings[eventString] = handlerName;
+ this._prepKeyBindings();
+ this._resetKeyEventListeners();
+ },
+
+ /**
+ * When called, will remove all imperatively-added key bindings.
+ */
+ removeOwnKeyBindings: function() {
+ this._imperativeKeyBindings = {};
+ this._prepKeyBindings();
+ this._resetKeyEventListeners();
+ },
+
+ keyboardEventMatchesKeys: function(event, eventString) {
+ var keyCombos = parseEventString(eventString);
+ for (var i = 0; i < keyCombos.length; ++i) {
+ if (keyComboMatchesEvent(keyCombos[i], event)) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ _collectKeyBindings: function() {
+ var keyBindings = this.behaviors.map(function(behavior) {
+ return behavior.keyBindings;
+ });
+
+ if (keyBindings.indexOf(this.keyBindings) === -1) {
+ keyBindings.push(this.keyBindings);
+ }
+
+ return keyBindings;
+ },
+
+ _prepKeyBindings: function() {
+ this._keyBindings = {};
+
+ this._collectKeyBindings().forEach(function(keyBindings) {
+ for (var eventString in keyBindings) {
+ this._addKeyBinding(eventString, keyBindings[eventString]);
+ }
+ }, this);
+
+ for (var eventString in this._imperativeKeyBindings) {
+ this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString]);
+ }
+
+ // Give precedence to combos with modifiers to be checked first.
+ for (var eventName in this._keyBindings) {
+ this._keyBindings[eventName].sort(function (kb1, kb2) {
+ var b1 = kb1[0].hasModifiers;
+ var b2 = kb2[0].hasModifiers;
+ return (b1 === b2) ? 0 : b1 ? -1 : 1;
+ })
+ }
+ },
+
+ _addKeyBinding: function(eventString, handlerName) {
+ parseEventString(eventString).forEach(function(keyCombo) {
+ this._keyBindings[keyCombo.event] =
+ this._keyBindings[keyCombo.event] || [];
+
+ this._keyBindings[keyCombo.event].push([
+ keyCombo,
+ handlerName
+ ]);
+ }, this);
+ },
+
+ _resetKeyEventListeners: function() {
+ this._unlistenKeyEventListeners();
+
+ if (this.isAttached) {
+ this._listenKeyEventListeners();
+ }
+ },
+
+ _listenKeyEventListeners: function() {
+ Object.keys(this._keyBindings).forEach(function(eventName) {
+ var keyBindings = this._keyBindings[eventName];
+ var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings);
+
+ this._boundKeyHandlers.push([this.keyEventTarget, eventName, boundKeyHandler]);
+
+ this.keyEventTarget.addEventListener(eventName, boundKeyHandler);
+ }, this);
+ },
+
+ _unlistenKeyEventListeners: function() {
+ var keyHandlerTuple;
+ var keyEventTarget;
+ var eventName;
+ var boundKeyHandler;
+
+ while (this._boundKeyHandlers.length) {
+ // My kingdom for block-scope binding and destructuring assignment..
+ keyHandlerTuple = this._boundKeyHandlers.pop();
+ keyEventTarget = keyHandlerTuple[0];
+ eventName = keyHandlerTuple[1];
+ boundKeyHandler = keyHandlerTuple[2];
+
+ keyEventTarget.removeEventListener(eventName, boundKeyHandler);
+ }
+ },
+
+ _onKeyBindingEvent: function(keyBindings, event) {
+ if (this.stopKeyboardEventPropagation) {
+ event.stopPropagation();
+ }
+
+ // if event has been already prevented, don't do anything
+ if (event.defaultPrevented) {
+ return;
+ }
+
+ for (var i = 0; i < keyBindings.length; i++) {
+ var keyCombo = keyBindings[i][0];
+ var handlerName = keyBindings[i][1];
+ if (keyComboMatchesEvent(keyCombo, event)) {
+ this._triggerKeyHandler(keyCombo, handlerName, event);
+ // exit the loop if eventDefault was prevented
+ if (event.defaultPrevented) {
+ return;
+ }
+ }
+ }
+ },
+
+ _triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) {
+ var detail = Object.create(keyCombo);
+ detail.keyboardEvent = keyboardEvent;
+ var event = new CustomEvent(keyCombo.event, {
+ detail: detail,
+ cancelable: true
+ });
+ this[handlerName].call(this, event);
+ if (event.defaultPrevented) {
+ keyboardEvent.preventDefault();
+ }
+ }
+ };
+ })();
+/**
+ * `Polymer.IronScrollTargetBehavior` allows an element to respond to scroll events from a
+ * designated scroll target.
+ *
+ * Elements that consume this behavior can override the `_scrollHandler`
+ * method to add logic on the scroll event.
+ *
+ * @demo demo/scrolling-region.html Scrolling Region
+ * @demo demo/document.html Document Element
+ * @polymerBehavior
+ */
+ Polymer.IronScrollTargetBehavior = {
+
+ properties: {
+
+ /**
+ * Specifies the element that will handle the scroll event
+ * on the behalf of the current element. This is typically a reference to an `Element`,
+ * but there are a few more posibilities:
+ *
+ * ### Elements id
+ *
+ *```html
+ * <div id="scrollable-element" style="overflow-y: auto;">
+ * <x-element scroll-target="scrollable-element">
+ * Content
+ * </x-element>
+ * </div>
+ *```
+ * In this case, `scrollTarget` will point to the outer div element. Alternatively,
+ * you can set the property programatically:
+ *
+ *```js
+ * appHeader.scrollTarget = document.querySelector('#scrollable-element');
+ *```
+ *
+ * @type {HTMLElement}
+ */
+ scrollTarget: {
+ type: HTMLElement,
+ value: function() {
+ return this._defaultScrollTarget;
+ }
+ }
+ },
+
+ observers: [
+ '_scrollTargetChanged(scrollTarget, isAttached)'
+ ],
+
+ _scrollTargetChanged: function(scrollTarget, isAttached) {
+ // Remove lister to the current scroll target
+ if (this._oldScrollTarget) {
+ if (this._oldScrollTarget === this._doc) {
+ window.removeEventListener('scroll', this._boundScrollHandler);
+ } else if (this._oldScrollTarget.removeEventListener) {
+ this._oldScrollTarget.removeEventListener('scroll', this._boundScrollHandler);
+ }
+ this._oldScrollTarget = null;
+ }
+ if (isAttached) {
+ // Support element id references
+ if (typeof scrollTarget === 'string') {
+
+ var ownerRoot = Polymer.dom(this).getOwnerRoot();
+ this.scrollTarget = (ownerRoot && ownerRoot.$) ?
+ ownerRoot.$[scrollTarget] : Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget);
+
+ } else if (this._scrollHandler) {
+
+ this._boundScrollHandler = this._boundScrollHandler || this._scrollHandler.bind(this);
+ // Add a new listener
+ if (scrollTarget === this._doc) {
+ window.addEventListener('scroll', this._boundScrollHandler);
+ if (this._scrollTop !== 0 || this._scrollLeft !== 0) {
+ this._scrollHandler();
+ }
+ } else if (scrollTarget && scrollTarget.addEventListener) {
+ scrollTarget.addEventListener('scroll', this._boundScrollHandler);
+ }
+ this._oldScrollTarget = scrollTarget;
+ }
+ }
+ },
+
+ /**
+ * Runs on every scroll event. Consumer of this behavior may want to override this method.
+ *
+ * @protected
+ */
+ _scrollHandler: function scrollHandler() {},
+
+ /**
+ * The default scroll target. Consumers of this behavior may want to customize
+ * the default scroll target.
+ *
+ * @type {Element}
+ */
+ get _defaultScrollTarget() {
+ return this._doc;
+ },
+
+ /**
+ * Shortcut for the document element
+ *
+ * @type {Element}
+ */
+ get _doc() {
+ return this.ownerDocument.documentElement;
+ },
+
+ /**
+ * Gets the number of pixels that the content of an element is scrolled upward.
+ *
+ * @type {number}
+ */
+ get _scrollTop() {
+ if (this._isValidScrollTarget()) {
+ return this.scrollTarget === this._doc ? window.pageYOffset : this.scrollTarget.scrollTop;
+ }
+ return 0;
+ },
+
+ /**
+ * Gets the number of pixels that the content of an element is scrolled to the left.
+ *
+ * @type {number}
+ */
+ get _scrollLeft() {
+ if (this._isValidScrollTarget()) {
+ return this.scrollTarget === this._doc ? window.pageXOffset : this.scrollTarget.scrollLeft;
+ }
+ return 0;
+ },
+
+ /**
+ * Sets the number of pixels that the content of an element is scrolled upward.
+ *
+ * @type {number}
+ */
+ set _scrollTop(top) {
+ if (this.scrollTarget === this._doc) {
+ window.scrollTo(window.pageXOffset, top);
+ } else if (this._isValidScrollTarget()) {
+ this.scrollTarget.scrollTop = top;
+ }
+ },
+
+ /**
+ * Sets the number of pixels that the content of an element is scrolled to the left.
+ *
+ * @type {number}
+ */
+ set _scrollLeft(left) {
+ if (this.scrollTarget === this._doc) {
+ window.scrollTo(left, window.pageYOffset);
+ } else if (this._isValidScrollTarget()) {
+ this.scrollTarget.scrollLeft = left;
+ }
+ },
+
+ /**
+ * Scrolls the content to a particular place.
+ *
+ * @method scroll
+ * @param {number} top The top position
+ * @param {number} left The left position
+ */
+ scroll: function(top, left) {
+ if (this.scrollTarget === this._doc) {
+ window.scrollTo(top, left);
+ } else if (this._isValidScrollTarget()) {
+ this.scrollTarget.scrollTop = top;
+ this.scrollTarget.scrollLeft = left;
+ }
+ },
+
+ /**
+ * Gets the width of the scroll target.
+ *
+ * @type {number}
+ */
+ get _scrollTargetWidth() {
+ if (this._isValidScrollTarget()) {
+ return this.scrollTarget === this._doc ? window.innerWidth : this.scrollTarget.offsetWidth;
+ }
+ return 0;
+ },
+
+ /**
+ * Gets the height of the scroll target.
+ *
+ * @type {number}
+ */
+ get _scrollTargetHeight() {
+ if (this._isValidScrollTarget()) {
+ return this.scrollTarget === this._doc ? window.innerHeight : this.scrollTarget.offsetHeight;
+ }
+ return 0;
+ },
+
+ /**
+ * Returns true if the scroll target is a valid HTMLElement.
+ *
+ * @return {boolean}
+ */
+ _isValidScrollTarget: function() {
+ return this.scrollTarget instanceof HTMLElement;
+ }
+ };
+(function() {
var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/);
var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
var DEFAULT_PHYSICAL_COUNT = 3;
var MAX_PHYSICAL_COUNT = 500;
+ var HIDDEN_Y = '-10000px';
Polymer({
@@ -2069,18 +2765,27 @@ i18nTemplate.process(document, loadTimeData);
observers: [
'_itemsChanged(items.*)',
'_selectionEnabledChanged(selectionEnabled)',
- '_multiSelectionChanged(multiSelection)'
+ '_multiSelectionChanged(multiSelection)',
+ '_setOverflow(scrollTarget)'
],
behaviors: [
Polymer.Templatizer,
- Polymer.IronResizableBehavior
+ Polymer.IronResizableBehavior,
+ Polymer.IronA11yKeysBehavior,
+ Polymer.IronScrollTargetBehavior
],
listeners: {
'iron-resize': '_resizeHandler'
},
+ keyBindings: {
+ 'up': '_didMoveUp',
+ 'down': '_didMoveDown',
+ 'enter': '_didEnter'
+ },
+
/**
* The ratio of hidden tiles that should remain in the scroll direction.
* Recommended value ~0.5, so it will distribute tiles evely in both directions.
@@ -2088,12 +2793,6 @@ i18nTemplate.process(document, loadTimeData);
_ratio: 0.5,
/**
- * The element that controls the scroll
- * @type {?Element}
- */
- _scroller: null,
-
- /**
* The padding-top value of the `scroller` element
*/
_scrollerPaddingTop: 0,
@@ -2124,7 +2823,7 @@ i18nTemplate.process(document, loadTimeData);
_physicalSize: 0,
/**
- * The average `offsetHeight` of the tiles observed till now.
+ * The average `F` of the tiles observed till now.
*/
_physicalAverage: 0,
@@ -2182,13 +2881,21 @@ i18nTemplate.process(document, loadTimeData);
_physicalSizes: null,
/**
- * A cached value for the visible index.
+ * A cached value for the first visible index.
* See `firstVisibleIndex`
* @type {?number}
*/
_firstVisibleIndexVal: null,
/**
+ * A cached value for the last visible index.
+ * See `lastVisibleIndex`
+ * @type {?number}
+ */
+ _lastVisibleIndexVal: null,
+
+
+ /**
* A Polymer collection for the items.
* @type {?Polymer.Collection}
*/
@@ -2211,6 +2918,23 @@ i18nTemplate.process(document, loadTimeData);
_maxPages: 3,
/**
+ * The currently focused item index.
+ */
+ _focusedIndex: 0,
+
+ /**
+ * The the item that is focused if it is moved offscreen.
+ * @private {?TemplatizerNode}
+ */
+ _offscreenFocusedItem: null,
+
+ /**
+ * The item that backfills the `_offscreenFocusedItem` in the physical items
+ * list when that item is moved offscreen.
+ */
+ _focusBackfillItem: null,
+
+ /**
* The bottom of the physical content.
*/
get _physicalBottom() {
@@ -2228,7 +2952,7 @@ i18nTemplate.process(document, loadTimeData);
* The n-th item rendered in the last physical item.
*/
get _virtualEnd() {
- return this._virtualStartVal + this._physicalCount - 1;
+ return this._virtualStart + this._physicalCount - 1;
},
/**
@@ -2263,8 +2987,13 @@ i18nTemplate.process(document, loadTimeData);
set _virtualStart(val) {
// clamp the value so that _minVirtualStart <= val <= _maxVirtualStart
this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val));
- this._physicalStart = this._virtualStartVal % this._physicalCount;
- this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount;
+ if (this._physicalCount === 0) {
+ this._physicalStart = 0;
+ this._physicalEnd = 0;
+ } else {
+ this._physicalStart = this._virtualStartVal % this._physicalCount;
+ this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount;
+ }
},
/**
@@ -2289,7 +3018,7 @@ i18nTemplate.process(document, loadTimeData);
* True if the current list is visible.
*/
get _isVisible() {
- return this._scroller && Boolean(this._scroller.offsetWidth || this._scroller.offsetHeight);
+ return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.scrollTarget.offsetHeight);
},
/**
@@ -2298,10 +3027,8 @@ i18nTemplate.process(document, loadTimeData);
* @type {number}
*/
get firstVisibleIndex() {
- var physicalOffset;
-
if (this._firstVisibleIndexVal === null) {
- physicalOffset = this._physicalTop;
+ var physicalOffset = this._physicalTop;
this._firstVisibleIndexVal = this._iterateItems(
function(pidx, vidx) {
@@ -2312,54 +3039,52 @@ i18nTemplate.process(document, loadTimeData);
}
}) || 0;
}
-
return this._firstVisibleIndexVal;
},
- ready: function() {
- if (IOS_TOUCH_SCROLLING) {
- this._scrollListener = function() {
- requestAnimationFrame(this._scrollHandler.bind(this));
- }.bind(this);
- } else {
- this._scrollListener = this._scrollHandler.bind(this);
- }
- },
-
/**
- * When the element has been attached to the DOM tree.
+ * Gets the index of the last visible item in the viewport.
+ *
+ * @type {number}
*/
- attached: function() {
- // delegate to the parent's scroller
- // e.g. paper-scroll-header-panel
- var el = Polymer.dom(this);
+ get lastVisibleIndex() {
+ if (this._lastVisibleIndexVal === null) {
+ var physicalOffset = this._physicalTop;
- var parentNode = /** @type {?{scroller: ?Element}} */ (el.parentNode);
- if (parentNode && parentNode.scroller) {
- this._scroller = parentNode.scroller;
- } else {
- this._scroller = this;
- this.classList.add('has-scroller');
- }
+ this._iterateItems(function(pidx, vidx) {
+ physicalOffset += this._physicalSizes[pidx];
- if (IOS_TOUCH_SCROLLING) {
- this._scroller.style.webkitOverflowScrolling = 'touch';
+ if(physicalOffset <= this._scrollBottom) {
+ this._lastVisibleIndexVal = vidx;
+ }
+ });
}
+ return this._lastVisibleIndexVal;
+ },
- this._scroller.addEventListener('scroll', this._scrollListener);
+ ready: function() {
+ this.addEventListener('focus', this._didFocus.bind(this), true);
+ },
+ attached: function() {
this.updateViewportBoundaries();
this._render();
},
- /**
- * When the element has been removed from the DOM tree.
- */
detached: function() {
this._itemsRendered = false;
- if (this._scroller) {
- this._scroller.removeEventListener('scroll', this._scrollListener);
- }
+ },
+
+ get _defaultScrollTarget() {
+ return this;
+ },
+
+ /**
+ * Set the overflow property if this element has its own scrolling region
+ */
+ _setOverflow: function(scrollTarget) {
+ this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : '';
+ this.style.overflow = scrollTarget === this ? 'auto' : '';
},
/**
@@ -2369,20 +3094,20 @@ i18nTemplate.process(document, loadTimeData);
* @method updateViewportBoundaries
*/
updateViewportBoundaries: function() {
- var scrollerStyle = window.getComputedStyle(this._scroller);
+ var scrollerStyle = window.getComputedStyle(this.scrollTarget);
this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10);
- this._viewportSize = this._scroller.offsetHeight;
+ this._viewportSize = this._scrollTargetHeight;
},
/**
* Update the models, the position of the
* items in the viewport and recycle tiles as needed.
*/
- _refresh: function() {
+ _scrollHandler: function() {
// clamp the `scrollTop` value
// IE 10|11 scrollTop may go above `_maxScrollTop`
// iOS `scrollTop` may go below 0 and above `_maxScrollTop`
- var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.scrollTop));
+ var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop));
var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBottom;
var ratio = this._ratio;
var delta = scrollTop - this._scrollPosition;
@@ -2396,6 +3121,7 @@ i18nTemplate.process(document, loadTimeData);
// clear cached visible index
this._firstVisibleIndexVal = null;
+ this._lastVisibleIndexVal = null;
scrollBottom = this._scrollBottom;
physicalBottom = this._physicalBottom;
@@ -2485,17 +3211,21 @@ i18nTemplate.process(document, loadTimeData);
},
/**
- * Update the list of items, starting from the `_virtualStartVal` item.
+ * Update the list of items, starting from the `_virtualStart` item.
* @param {!Array<number>=} itemSet
* @param {!Array<number>=} movingUp
*/
_update: function(itemSet, movingUp) {
+ // manage focus
+ if (this._isIndexRendered(this._focusedIndex)) {
+ this._restoreFocusedItem();
+ } else {
+ this._createFocusBackfillItem();
+ }
// update models
this._assignModels(itemSet);
-
// measure heights
this._updateMetrics(itemSet);
-
// adjust offset after measuring
if (movingUp) {
while (movingUp.length) {
@@ -2504,10 +3234,8 @@ i18nTemplate.process(document, loadTimeData);
}
// update the position of the items
this._positionItems();
-
// set the scroller size
this._updateScrollerSize();
-
// increase the pool of physical items
this._increasePoolIfNeeded();
},
@@ -2527,7 +3255,6 @@ i18nTemplate.process(document, loadTimeData);
physicalItems[i] = inst.root.querySelector('*');
Polymer.dom(this).appendChild(inst.root);
}
-
return physicalItems;
},
@@ -2537,24 +3264,24 @@ i18nTemplate.process(document, loadTimeData);
* if the physical size is shorter than `_optPhysicalSize`
*/
_increasePoolIfNeeded: function() {
- if (this._viewportSize !== 0 && this._physicalSize < this._optPhysicalSize) {
- // 0 <= `currentPage` <= `_maxPages`
- var currentPage = Math.floor(this._physicalSize / this._viewportSize);
-
- if (currentPage === 0) {
- // fill the first page
- this.async(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5)));
- } else if (this._lastPage !== currentPage) {
- // once a page is filled up, paint it and defer the next increase
- requestAnimationFrame(this._increasePool.bind(this, 1));
- } else {
- // fill the rest of the pages
- this.async(this._increasePool.bind(this, 1));
- }
- this._lastPage = currentPage;
- return true;
+ if (this._viewportSize === 0 || this._physicalSize >= this._optPhysicalSize) {
+ return false;
}
- return false;
+ // 0 <= `currentPage` <= `_maxPages`
+ var currentPage = Math.floor(this._physicalSize / this._viewportSize);
+ if (currentPage === 0) {
+ // fill the first page
+ this._debounceTemplate(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5)));
+ } else if (this._lastPage !== currentPage) {
+ // paint the page and defer the next increase
+ // wait 16ms which is rough enough to get paint cycle.
+ Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increasePool.bind(this, 1), 16));
+ } else {
+ // fill the rest of the pages
+ this._debounceTemplate(this._increasePool.bind(this, 1));
+ }
+ this._lastPage = currentPage;
+ return true;
},
/**
@@ -2564,7 +3291,7 @@ i18nTemplate.process(document, loadTimeData);
// limit the size
var nextPhysicalCount = Math.min(
this._physicalCount + missingItems,
- this._virtualCount,
+ this._virtualCount - this._virtualStart,
MAX_PHYSICAL_COUNT
);
var prevPhysicalCount = this._physicalCount;
@@ -2590,6 +3317,7 @@ i18nTemplate.process(document, loadTimeData);
if (this.isAttached && !this._itemsRendered && this._isVisible && requiresUpdate) {
this._lastPage = 0;
this._update();
+ this._scrollHandler();
this._itemsRendered = true;
}
},
@@ -2601,11 +3329,11 @@ i18nTemplate.process(document, loadTimeData);
if (!this.ctor) {
// Template instance props that should be excluded from forwarding
var props = {};
-
props.__key__ = true;
props[this.as] = true;
props[this.indexAs] = true;
props[this.selectedAs] = true;
+ props.tabIndex = true;
this._instanceProps = props;
this._userTemplate = Polymer.dom(this).querySelector('template');
@@ -2673,6 +3401,10 @@ i18nTemplate.process(document, loadTimeData);
var key = path.substring(0, dot < 0 ? path.length : dot);
var idx = this._physicalIndexForKey[key];
var row = this._physicalItems[idx];
+
+ if (idx === this._focusedIndex && this._offscreenFocusedItem) {
+ row = this._offscreenFocusedItem;
+ }
if (row) {
var inst = row._templateInstance;
if (dot >= 0) {
@@ -2691,17 +3423,18 @@ i18nTemplate.process(document, loadTimeData);
*/
_itemsChanged: function(change) {
if (change.path === 'items') {
+
+ this._restoreFocusedItem();
// render the new set
this._itemsRendered = false;
-
// update the whole set
- this._virtualStartVal = 0;
+ this._virtualStart = 0;
this._physicalTop = 0;
this._virtualCount = this.items ? this.items.length : 0;
+ this._focusedIndex = 0;
this._collection = this.items ? Polymer.Collection.get(this.items) : null;
this._physicalIndexForKey = {};
- // scroll to the top
this._resetScrollPosition(0);
// create the initial physical items
@@ -2710,17 +3443,20 @@ i18nTemplate.process(document, loadTimeData);
this._physicalItems = this._createPool(this._physicalCount);
this._physicalSizes = new Array(this._physicalCount);
}
-
- this.debounce('refresh', this._render);
+ this._debounceTemplate(this._render);
} else if (change.path === 'items.splices') {
// render the new set
this._itemsRendered = false;
-
this._adjustVirtualIndex(change.value.indexSplices);
this._virtualCount = this.items ? this.items.length : 0;
- this.debounce('refresh', this._render);
+ this._debounceTemplate(this._render);
+
+ if (this._focusedIndex < 0 || this._focusedIndex >= this._virtualCount) {
+ this._focusedIndex = 0;
+ }
+ this._debounceTemplate(this._render);
} else {
// update a single item
@@ -2742,19 +3478,15 @@ i18nTemplate.process(document, loadTimeData);
idx = splice.index;
// We only need to care about changes happening above the current position
- if (idx >= this._virtualStartVal) {
+ if (idx >= this._virtualStart) {
break;
}
this._virtualStart = this._virtualStart +
- Math.max(splice.addedCount - splice.removed.length, idx - this._virtualStartVal);
+ Math.max(splice.addedCount - splice.removed.length, idx - this._virtualStart);
}
},
- _scrollHandler: function() {
- this._refresh();
- },
-
/**
* Executes a provided function per every physical index in `itemSet`
* `itemSet` default value is equivalent to the entire set of physical indexes.
@@ -2769,9 +3501,9 @@ i18nTemplate.process(document, loadTimeData);
for (i = 0; i < itemSet.length; i++) {
pidx = itemSet[i];
if (pidx >= this._physicalStart) {
- vidx = this._virtualStartVal + (pidx - this._physicalStart);
+ vidx = this._virtualStart + (pidx - this._physicalStart);
} else {
- vidx = this._virtualStartVal + (this._physicalCount - this._physicalStart) + pidx;
+ vidx = this._virtualStart + (this._physicalCount - this._physicalStart) + pidx;
}
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
@@ -2779,17 +3511,14 @@ i18nTemplate.process(document, loadTimeData);
}
} else {
pidx = this._physicalStart;
- vidx = this._virtualStartVal;
+ vidx = this._virtualStart;
for (; pidx < this._physicalCount; pidx++, vidx++) {
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
}
}
-
- pidx = 0;
-
- for (; pidx < this._physicalStart; pidx++, vidx++) {
+ for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) {
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
}
@@ -2807,12 +3536,12 @@ i18nTemplate.process(document, loadTimeData);
var inst = el._templateInstance;
var item = this.items && this.items[vidx];
- if (item) {
+ if (item !== undefined && item !== null) {
inst[this.as] = item;
inst.__key__ = this._collection.getKey(item);
- inst[this.selectedAs] =
- /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item);
+ inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item);
inst[this.indexAs] = vidx;
+ inst.tabIndex = vidx === this._focusedIndex ? 0 : -1;
el.removeAttribute('hidden');
this._physicalIndexForKey[inst.__key__] = pidx;
} else {
@@ -2829,23 +3558,26 @@ i18nTemplate.process(document, loadTimeData);
* @param {!Array<number>=} itemSet
*/
_updateMetrics: function(itemSet) {
+ // Make sure we distributed all the physical items
+ // so we can measure them
+ Polymer.dom.flush();
+
var newPhysicalSize = 0;
var oldPhysicalSize = 0;
var prevAvgCount = this._physicalAverageCount;
var prevPhysicalAvg = this._physicalAverage;
- // Make sure we distributed all the physical items
- // so we can measure them
- Polymer.dom.flush();
this._iterateItems(function(pidx, vidx) {
+
oldPhysicalSize += this._physicalSizes[pidx] || 0;
this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
newPhysicalSize += this._physicalSizes[pidx];
this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
+
}, itemSet);
this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize;
- this._viewportSize = this._scroller.offsetHeight;
+ this._viewportSize = this._scrollTargetHeight;
// update the average if we measured something
if (this._physicalAverageCount !== prevAvgCount) {
@@ -2865,7 +3597,7 @@ i18nTemplate.process(document, loadTimeData);
this._iterateItems(function(pidx) {
- this.transform('translate3d(0, ' + y + 'px, 0)', this._physicalItems[pidx]);
+ this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]);
y += this._physicalSizes[pidx];
});
@@ -2875,15 +3607,14 @@ i18nTemplate.process(document, loadTimeData);
* Adjusts the scroll position when it was overestimated.
*/
_adjustScrollPosition: function() {
- var deltaHeight = this._virtualStartVal === 0 ? this._physicalTop :
+ var deltaHeight = this._virtualStart === 0 ? this._physicalTop :
Math.min(this._scrollPosition + this._physicalTop, 0);
if (deltaHeight) {
this._physicalTop = this._physicalTop - deltaHeight;
-
// juking scroll position during interial scrolling on iOS is no bueno
if (!IOS_TOUCH_SCROLLING) {
- this._resetScrollPosition(this._scroller.scrollTop - deltaHeight);
+ this._resetScrollPosition(this._scrollTop - deltaHeight);
}
}
},
@@ -2892,9 +3623,9 @@ i18nTemplate.process(document, loadTimeData);
* Sets the position of the scroll.
*/
_resetScrollPosition: function(pos) {
- if (this._scroller) {
- this._scroller.scrollTop = pos;
- this._scrollPosition = this._scroller.scrollTop;
+ if (this.scrollTarget) {
+ this._scrollTop = pos;
+ this._scrollPosition = this._scrollTop;
}
},
@@ -2905,7 +3636,7 @@ i18nTemplate.process(document, loadTimeData);
*/
_updateScrollerSize: function(forceUpdate) {
this._estScrollHeight = (this._physicalBottom +
- Math.max(this._virtualCount - this._physicalCount - this._virtualStartVal, 0) * this._physicalAverage);
+ Math.max(this._virtualCount - this._physicalCount - this._virtualStart, 0) * this._physicalAverage);
forceUpdate = forceUpdate || this._scrollHeight === 0;
forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize;
@@ -2929,20 +3660,18 @@ i18nTemplate.process(document, loadTimeData);
return;
}
- var firstVisible = this.firstVisibleIndex;
+ Polymer.dom.flush();
+ var firstVisible = this.firstVisibleIndex;
idx = Math.min(Math.max(idx, 0), this._virtualCount-1);
// start at the previous virtual item
// so we have a item above the first visible item
this._virtualStart = idx - 1;
-
// assign new models
this._assignModels();
-
// measure the new sizes
this._updateMetrics();
-
// estimate new physical offset
this._physicalTop = this._virtualStart * this._physicalAverage;
@@ -2957,21 +3686,17 @@ i18nTemplate.process(document, loadTimeData);
currentTopItem = (currentTopItem + 1) % this._physicalCount;
currentVirtualItem++;
}
-
// update the scroller size
this._updateScrollerSize(true);
-
// update the position of the items
this._positionItems();
-
// set the new scroll position
- this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1);
-
+ this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + targetOffsetTop + 1);
// increase the pool of physical items if needed
this._increasePoolIfNeeded();
-
// clear cached visible index
this._firstVisibleIndexVal = null;
+ this._lastVisibleIndexVal = null;
},
/**
@@ -2987,7 +3712,11 @@ i18nTemplate.process(document, loadTimeData);
* when the element is resized.
*/
_resizeHandler: function() {
- this.debounce('resize', function() {
+ // iOS fires the resize event when the address bar slides up
+ if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100) {
+ return;
+ }
+ this._debounceTemplate(function() {
this._render();
if (this._itemsRendered && this._physicalItems && this._isVisible) {
this._resetAverage();
@@ -3013,12 +3742,14 @@ i18nTemplate.process(document, loadTimeData);
* @param {(Object|number)} item The item object or its index
*/
_getNormalizedItem: function(item) {
- if (typeof item === 'number') {
- item = this.items[item];
- if (!item) {
- throw new RangeError('<item> not found');
+ if (this._collection.getKey(item) === undefined) {
+ if (typeof item === 'number') {
+ item = this.items[item];
+ if (!item) {
+ throw new RangeError('<item> not found');
+ }
+ return item;
}
- } else if (this._collection.getKey(item) === undefined) {
throw new TypeError('<item> should be a valid item');
}
return item;
@@ -3041,6 +3772,7 @@ i18nTemplate.process(document, loadTimeData);
model[this.selectedAs] = true;
}
this.$.selector.select(item);
+ this.updateSizeForItem(item);
},
/**
@@ -3058,6 +3790,7 @@ i18nTemplate.process(document, loadTimeData);
model[this.selectedAs] = false;
}
this.$.selector.deselect(item);
+ this.updateSizeForItem(item);
},
/**
@@ -3103,20 +3836,15 @@ i18nTemplate.process(document, loadTimeData);
* it will remove the listener otherwise.
*/
_selectionEnabledChanged: function(selectionEnabled) {
- if (selectionEnabled) {
- this.listen(this, 'tap', '_selectionHandler');
- this.listen(this, 'keypress', '_selectionHandler');
- } else {
- this.unlisten(this, 'tap', '_selectionHandler');
- this.unlisten(this, 'keypress', '_selectionHandler');
- }
+ var handler = selectionEnabled ? this.listen : this.unlisten;
+ handler.call(this, this, 'tap', '_selectionHandler');
},
/**
* Select an item from an event object.
*/
_selectionHandler: function(e) {
- if (e.type !== 'keypress' || e.keyCode === 13) {
+ if (this.selectionEnabled) {
var model = this.modelForElement(e.target);
if (model) {
this.toggleSelectionForItem(model[this.as]);
@@ -3144,6 +3872,135 @@ i18nTemplate.process(document, loadTimeData);
this._updateMetrics([pidx]);
this._positionItems();
}
+ },
+
+ _isIndexRendered: function(idx) {
+ return idx >= this._virtualStart && idx <= this._virtualEnd;
+ },
+
+ _getPhysicalItemForIndex: function(idx, force) {
+ if (!this._collection) {
+ return null;
+ }
+ if (!this._isIndexRendered(idx)) {
+ if (force) {
+ this.scrollToIndex(idx);
+ return this._getPhysicalItemForIndex(idx, false);
+ }
+ return null;
+ }
+ var item = this._getNormalizedItem(idx);
+ var physicalItem = this._physicalItems[this._physicalIndexForKey[this._collection.getKey(item)]];
+
+ return physicalItem || null;
+ },
+
+ _focusPhysicalItem: function(idx) {
+ this._restoreFocusedItem();
+
+ var physicalItem = this._getPhysicalItemForIndex(idx, true);
+ if (!physicalItem) {
+ return;
+ }
+ var SECRET = ~(Math.random() * 100);
+ var model = physicalItem._templateInstance;
+ var focusable;
+
+ model.tabIndex = SECRET;
+ // the focusable element could be the entire physical item
+ if (physicalItem.tabIndex === SECRET) {
+ focusable = physicalItem;
+ }
+ // the focusable element could be somewhere within the physical item
+ if (!focusable) {
+ focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECRET + '"]');
+ }
+ // restore the tab index
+ model.tabIndex = 0;
+ focusable && focusable.focus();
+ },
+
+ _restoreFocusedItem: function() {
+ if (!this._offscreenFocusedItem) {
+ return;
+ }
+ var item = this._getNormalizedItem(this._focusedIndex);
+ var pidx = this._physicalIndexForKey[this._collection.getKey(item)];
+
+ if (pidx !== undefined) {
+ this.translate3d(0, HIDDEN_Y, 0, this._physicalItems[pidx]);
+ this._physicalItems[pidx] = this._offscreenFocusedItem;
+ }
+ this._offscreenFocusedItem = null;
+ },
+
+ _removeFocusedItem: function() {
+ if (!this._offscreenFocusedItem) {
+ return;
+ }
+ Polymer.dom(this).removeChild(this._offscreenFocusedItem);
+ this._offscreenFocusedItem = null;
+ this._focusBackfillItem = null;
+ },
+
+ _createFocusBackfillItem: function() {
+ if (this._offscreenFocusedItem) {
+ return;
+ }
+ var item = this._getNormalizedItem(this._focusedIndex);
+ var pidx = this._physicalIndexForKey[this._collection.getKey(item)];
+
+ this._offscreenFocusedItem = this._physicalItems[pidx];
+ this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem);
+
+ if (!this._focusBackfillItem) {
+ var stampedTemplate = this.stamp(null);
+ this._focusBackfillItem = stampedTemplate.root.querySelector('*');
+ Polymer.dom(this).appendChild(stampedTemplate.root);
+ }
+ this._physicalItems[pidx] = this._focusBackfillItem;
+ },
+
+ _didFocus: function(e) {
+ var targetModel = this.modelForElement(e.target);
+ var fidx = this._focusedIndex;
+
+ if (!targetModel) {
+ return;
+ }
+ this._restoreFocusedItem();
+
+ if (this.modelForElement(this._offscreenFocusedItem) === targetModel) {
+ this.scrollToIndex(fidx);
+ } else {
+ // restore tabIndex for the currently focused item
+ this._getModelFromItem(this._getNormalizedItem(fidx)).tabIndex = -1;
+ // set the tabIndex for the next focused item
+ targetModel.tabIndex = 0;
+ fidx = /** @type {{index: number}} */(targetModel).index;
+ this._focusedIndex = fidx;
+ // bring the item into view
+ if (fidx < this.firstVisibleIndex || fidx > this.lastVisibleIndex) {
+ this.scrollToIndex(fidx);
+ } else {
+ this._update();
+ }
+ }
+ },
+
+ _didMoveUp: function() {
+ this._focusPhysicalItem(Math.max(0, this._focusedIndex - 1));
+ },
+
+ _didMoveDown: function() {
+ this._focusPhysicalItem(Math.min(this._virtualCount, this._focusedIndex + 1));
+ },
+
+ _didEnter: function(e) {
+ // focus the currently focused physical item
+ this._focusPhysicalItem(this._focusedIndex);
+ // toggle selection
+ this._selectionHandler(/** @type {{keyboardEvent: Event}} */(e.detail).keyboardEvent);
}
});
@@ -3690,438 +4547,6 @@ Polymer({
}
});
-(function() {
- 'use strict';
-
- /**
- * Chrome uses an older version of DOM Level 3 Keyboard Events
- *
- * Most keys are labeled as text, but some are Unicode codepoints.
- * Values taken from: http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html#KeySet-Set
- */
- var KEY_IDENTIFIER = {
- 'U+0008': 'backspace',
- 'U+0009': 'tab',
- 'U+001B': 'esc',
- 'U+0020': 'space',
- 'U+007F': 'del'
- };
-
- /**
- * Special table for KeyboardEvent.keyCode.
- * KeyboardEvent.keyIdentifier is better, and KeyBoardEvent.key is even better
- * than that.
- *
- * Values from: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode#Value_of_keyCode
- */
- var KEY_CODE = {
- 8: 'backspace',
- 9: 'tab',
- 13: 'enter',
- 27: 'esc',
- 33: 'pageup',
- 34: 'pagedown',
- 35: 'end',
- 36: 'home',
- 32: 'space',
- 37: 'left',
- 38: 'up',
- 39: 'right',
- 40: 'down',
- 46: 'del',
- 106: '*'
- };
-
- /**
- * MODIFIER_KEYS maps the short name for modifier keys used in a key
- * combo string to the property name that references those same keys
- * in a KeyboardEvent instance.
- */
- var MODIFIER_KEYS = {
- 'shift': 'shiftKey',
- 'ctrl': 'ctrlKey',
- 'alt': 'altKey',
- 'meta': 'metaKey'
- };
-
- /**
- * KeyboardEvent.key is mostly represented by printable character made by
- * the keyboard, with unprintable keys labeled nicely.
- *
- * However, on OS X, Alt+char can make a Unicode character that follows an
- * Apple-specific mapping. In this case, we fall back to .keyCode.
- */
- var KEY_CHAR = /[a-z0-9*]/;
-
- /**
- * Matches a keyIdentifier string.
- */
- var IDENT_CHAR = /U\+/;
-
- /**
- * Matches arrow keys in Gecko 27.0+
- */
- var ARROW_KEY = /^arrow/;
-
- /**
- * Matches space keys everywhere (notably including IE10's exceptional name
- * `spacebar`).
- */
- var SPACE_KEY = /^space(bar)?/;
-
- /**
- * Transforms the key.
- * @param {string} key The KeyBoardEvent.key
- * @param {Boolean} [noSpecialChars] Limits the transformation to
- * alpha-numeric characters.
- */
- function transformKey(key, noSpecialChars) {
- var validKey = '';
- if (key) {
- var lKey = key.toLowerCase();
- if (lKey === ' ' || SPACE_KEY.test(lKey)) {
- validKey = 'space';
- } else if (lKey.length == 1) {
- if (!noSpecialChars || KEY_CHAR.test(lKey)) {
- validKey = lKey;
- }
- } else if (ARROW_KEY.test(lKey)) {
- validKey = lKey.replace('arrow', '');
- } else if (lKey == 'multiply') {
- // numpad '*' can map to Multiply on IE/Windows
- validKey = '*';
- } else {
- validKey = lKey;
- }
- }
- return validKey;
- }
-
- function transformKeyIdentifier(keyIdent) {
- var validKey = '';
- if (keyIdent) {
- if (keyIdent in KEY_IDENTIFIER) {
- validKey = KEY_IDENTIFIER[keyIdent];
- } else if (IDENT_CHAR.test(keyIdent)) {
- keyIdent = parseInt(keyIdent.replace('U+', '0x'), 16);
- validKey = String.fromCharCode(keyIdent).toLowerCase();
- } else {
- validKey = keyIdent.toLowerCase();
- }
- }
- return validKey;
- }
-
- function transformKeyCode(keyCode) {
- var validKey = '';
- if (Number(keyCode)) {
- if (keyCode >= 65 && keyCode <= 90) {
- // ascii a-z
- // lowercase is 32 offset from uppercase
- validKey = String.fromCharCode(32 + keyCode);
- } else if (keyCode >= 112 && keyCode <= 123) {
- // function keys f1-f12
- validKey = 'f' + (keyCode - 112);
- } else if (keyCode >= 48 && keyCode <= 57) {
- // top 0-9 keys
- validKey = String(48 - keyCode);
- } else if (keyCode >= 96 && keyCode <= 105) {
- // num pad 0-9
- validKey = String(96 - keyCode);
- } else {
- validKey = KEY_CODE[keyCode];
- }
- }
- return validKey;
- }
-
- /**
- * Calculates the normalized key for a KeyboardEvent.
- * @param {KeyboardEvent} keyEvent
- * @param {Boolean} [noSpecialChars] Set to true to limit keyEvent.key
- * transformation to alpha-numeric chars. This is useful with key
- * combinations like shift + 2, which on FF for MacOS produces
- * keyEvent.key = @
- * To get 2 returned, set noSpecialChars = true
- * To get @ returned, set noSpecialChars = false
- */
- function normalizedKeyForEvent(keyEvent, noSpecialChars) {
- // Fall back from .key, to .keyIdentifier, to .keyCode, and then to
- // .detail.key to support artificial keyboard events.
- return transformKey(keyEvent.key, noSpecialChars) ||
- transformKeyIdentifier(keyEvent.keyIdentifier) ||
- transformKeyCode(keyEvent.keyCode) ||
- transformKey(keyEvent.detail.key, noSpecialChars) || '';
- }
-
- function keyComboMatchesEvent(keyCombo, event) {
- // For combos with modifiers we support only alpha-numeric keys
- var keyEvent = normalizedKeyForEvent(event, keyCombo.hasModifiers);
- return keyEvent === keyCombo.key &&
- (!keyCombo.hasModifiers || (
- !!event.shiftKey === !!keyCombo.shiftKey &&
- !!event.ctrlKey === !!keyCombo.ctrlKey &&
- !!event.altKey === !!keyCombo.altKey &&
- !!event.metaKey === !!keyCombo.metaKey)
- );
- }
-
- function parseKeyComboString(keyComboString) {
- if (keyComboString.length === 1) {
- return {
- combo: keyComboString,
- key: keyComboString,
- event: 'keydown'
- };
- }
- return keyComboString.split('+').reduce(function(parsedKeyCombo, keyComboPart) {
- var eventParts = keyComboPart.split(':');
- var keyName = eventParts[0];
- var event = eventParts[1];
-
- if (keyName in MODIFIER_KEYS) {
- parsedKeyCombo[MODIFIER_KEYS[keyName]] = true;
- parsedKeyCombo.hasModifiers = true;
- } else {
- parsedKeyCombo.key = keyName;
- parsedKeyCombo.event = event || 'keydown';
- }
-
- return parsedKeyCombo;
- }, {
- combo: keyComboString.split(':').shift()
- });
- }
-
- function parseEventString(eventString) {
- return eventString.trim().split(' ').map(function(keyComboString) {
- return parseKeyComboString(keyComboString);
- });
- }
-
- /**
- * `Polymer.IronA11yKeysBehavior` provides a normalized interface for processing
- * keyboard commands that pertain to [WAI-ARIA best practices](http://www.w3.org/TR/wai-aria-practices/#kbd_general_binding).
- * The element takes care of browser differences with respect to Keyboard events
- * and uses an expressive syntax to filter key presses.
- *
- * Use the `keyBindings` prototype property to express what combination of keys
- * will trigger the event to fire.
- *
- * Use the `key-event-target` attribute to set up event handlers on a specific
- * node.
- * The `keys-pressed` event will fire when one of the key combinations set with the
- * `keys` property is pressed.
- *
- * @demo demo/index.html
- * @polymerBehavior
- */
- Polymer.IronA11yKeysBehavior = {
- properties: {
- /**
- * The HTMLElement that will be firing relevant KeyboardEvents.
- */
- keyEventTarget: {
- type: Object,
- value: function() {
- return this;
- }
- },
-
- /**
- * If true, this property will cause the implementing element to
- * automatically stop propagation on any handled KeyboardEvents.
- */
- stopKeyboardEventPropagation: {
- type: Boolean,
- value: false
- },
-
- _boundKeyHandlers: {
- type: Array,
- value: function() {
- return [];
- }
- },
-
- // We use this due to a limitation in IE10 where instances will have
- // own properties of everything on the "prototype".
- _imperativeKeyBindings: {
- type: Object,
- value: function() {
- return {};
- }
- }
- },
-
- observers: [
- '_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)'
- ],
-
- keyBindings: {},
-
- registered: function() {
- this._prepKeyBindings();
- },
-
- attached: function() {
- this._listenKeyEventListeners();
- },
-
- detached: function() {
- this._unlistenKeyEventListeners();
- },
-
- /**
- * Can be used to imperatively add a key binding to the implementing
- * element. This is the imperative equivalent of declaring a keybinding
- * in the `keyBindings` prototype property.
- */
- addOwnKeyBinding: function(eventString, handlerName) {
- this._imperativeKeyBindings[eventString] = handlerName;
- this._prepKeyBindings();
- this._resetKeyEventListeners();
- },
-
- /**
- * When called, will remove all imperatively-added key bindings.
- */
- removeOwnKeyBindings: function() {
- this._imperativeKeyBindings = {};
- this._prepKeyBindings();
- this._resetKeyEventListeners();
- },
-
- keyboardEventMatchesKeys: function(event, eventString) {
- var keyCombos = parseEventString(eventString);
- for (var i = 0; i < keyCombos.length; ++i) {
- if (keyComboMatchesEvent(keyCombos[i], event)) {
- return true;
- }
- }
- return false;
- },
-
- _collectKeyBindings: function() {
- var keyBindings = this.behaviors.map(function(behavior) {
- return behavior.keyBindings;
- });
-
- if (keyBindings.indexOf(this.keyBindings) === -1) {
- keyBindings.push(this.keyBindings);
- }
-
- return keyBindings;
- },
-
- _prepKeyBindings: function() {
- this._keyBindings = {};
-
- this._collectKeyBindings().forEach(function(keyBindings) {
- for (var eventString in keyBindings) {
- this._addKeyBinding(eventString, keyBindings[eventString]);
- }
- }, this);
-
- for (var eventString in this._imperativeKeyBindings) {
- this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString]);
- }
-
- // Give precedence to combos with modifiers to be checked first.
- for (var eventName in this._keyBindings) {
- this._keyBindings[eventName].sort(function (kb1, kb2) {
- var b1 = kb1[0].hasModifiers;
- var b2 = kb2[0].hasModifiers;
- return (b1 === b2) ? 0 : b1 ? -1 : 1;
- })
- }
- },
-
- _addKeyBinding: function(eventString, handlerName) {
- parseEventString(eventString).forEach(function(keyCombo) {
- this._keyBindings[keyCombo.event] =
- this._keyBindings[keyCombo.event] || [];
-
- this._keyBindings[keyCombo.event].push([
- keyCombo,
- handlerName
- ]);
- }, this);
- },
-
- _resetKeyEventListeners: function() {
- this._unlistenKeyEventListeners();
-
- if (this.isAttached) {
- this._listenKeyEventListeners();
- }
- },
-
- _listenKeyEventListeners: function() {
- Object.keys(this._keyBindings).forEach(function(eventName) {
- var keyBindings = this._keyBindings[eventName];
- var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings);
-
- this._boundKeyHandlers.push([this.keyEventTarget, eventName, boundKeyHandler]);
-
- this.keyEventTarget.addEventListener(eventName, boundKeyHandler);
- }, this);
- },
-
- _unlistenKeyEventListeners: function() {
- var keyHandlerTuple;
- var keyEventTarget;
- var eventName;
- var boundKeyHandler;
-
- while (this._boundKeyHandlers.length) {
- // My kingdom for block-scope binding and destructuring assignment..
- keyHandlerTuple = this._boundKeyHandlers.pop();
- keyEventTarget = keyHandlerTuple[0];
- eventName = keyHandlerTuple[1];
- boundKeyHandler = keyHandlerTuple[2];
-
- keyEventTarget.removeEventListener(eventName, boundKeyHandler);
- }
- },
-
- _onKeyBindingEvent: function(keyBindings, event) {
- if (this.stopKeyboardEventPropagation) {
- event.stopPropagation();
- }
-
- // if event has been already prevented, don't do anything
- if (event.defaultPrevented) {
- return;
- }
-
- for (var i = 0; i < keyBindings.length; i++) {
- var keyCombo = keyBindings[i][0];
- var handlerName = keyBindings[i][1];
- if (keyComboMatchesEvent(keyCombo, event)) {
- this._triggerKeyHandler(keyCombo, handlerName, event);
- // exit the loop if eventDefault was prevented
- if (event.defaultPrevented) {
- return;
- }
- }
- }
- },
-
- _triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) {
- var detail = Object.create(keyCombo);
- detail.keyboardEvent = keyboardEvent;
- var event = new CustomEvent(keyCombo.event, {
- detail: detail,
- cancelable: true
- });
- this[handlerName].call(this, event);
- if (event.defaultPrevented) {
- keyboardEvent.preventDefault();
- }
- }
- };
- })();
/**
* @demo demo/index.html
* @polymerBehavior
diff --git a/chrome/browser/resources/md_downloads/vulcanized.html b/chrome/browser/resources/md_downloads/vulcanized.html
index 85123ff..1aa9e4e 100644
--- a/chrome/browser/resources/md_downloads/vulcanized.html
+++ b/chrome/browser/resources/md_downloads/vulcanized.html
@@ -8,6 +8,14 @@ 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
--><!--
@license
+Copyright (c) 2016 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
+--><!--
+@license
Copyright (c) 2015 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
@@ -1032,14 +1040,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<style>
:host {
display: block;
+ position: relative;
}
- :host(.has-scroller) {
- overflow: auto;
- }
-
- :host(:not(.has-scroller)) {
- position: relative;
+ @media only screen and (-webkit-max-device-pixel-ratio: 1) {
+ :host {
+ will-change: transform;
+ }
}
#items {
diff --git a/chrome/browser/resources/md_history/compiled_resources.gyp b/chrome/browser/resources/md_history/compiled_resources.gyp
index 463c350..c23dccf 100644
--- a/chrome/browser/resources/md_history/compiled_resources.gyp
+++ b/chrome/browser/resources/md_history/compiled_resources.gyp
@@ -10,8 +10,10 @@
'variables': {
'depends': [
+ '../../../../third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior-extracted.js',
'../../../../third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js',
'../../../../third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior-extracted.js',
+ '../../../../third_party/polymer/v1_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior-extracted.js',
'../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data',
'../../../../ui/webui/resources/js/cr.js',
'../../../../ui/webui/resources/js/cr/ui/position_util.js',
diff --git a/chrome/test/data/webui/md_history/md_history_browsertest.js b/chrome/test/data/webui/md_history/md_history_browsertest.js
index 1c4ea3e..bb6c676 100644
--- a/chrome/test/data/webui/md_history/md_history_browsertest.js
+++ b/chrome/test/data/webui/md_history/md_history_browsertest.js
@@ -22,6 +22,9 @@ MaterialHistoryBrowserTest.prototype = {
commandLineSwitches: [{switchName: 'enable-md-history'}],
+ /** @override */
+ runAccessibilityChecks: false,
+
extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
'test_util.js',
'history_card_manager_test.js',
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json
index 4329d2f..771cc74 100644
--- a/third_party/polymer/v1_0/bower.json
+++ b/third_party/polymer/v1_0/bower.json
@@ -20,7 +20,7 @@
"iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
"iron-image": "PolymerElements/iron-image#^1.0.0",
"iron-input": "PolymerElements/iron-input#^1.0.0",
- "iron-list": "PolymerElements/iron-list#1.1.7",
+ "iron-list": "PolymerElements/iron-list#^1.0.0",
"iron-media-query": "PolymerElements/iron-media-query#^1.0.0",
"iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.0.0",
"iron-meta": "PolymerElements/iron-meta#^1.0.0",
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/.bower.json b/third_party/polymer/v1_0/components-chromium/iron-list/.bower.json
index 841812c..6340b3c 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/.bower.json
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/.bower.json
@@ -7,7 +7,7 @@
"list",
"virtual-list"
],
- "version": "1.1.7",
+ "version": "1.2.3",
"homepage": "https://github.com/PolymerElements/iron-list",
"authors": [
"The Polymer Authors"
@@ -21,34 +21,36 @@
"ignore": [],
"dependencies": {
"polymer": "Polymer/polymer#^1.1.0",
- "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0"
+ "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0",
+ "iron-a11y-keys-behavior": "polymerelements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-scroll-target-behavior": "PolymerElements/iron-scroll-target-behavior#^1.0.0"
},
"devDependencies": {
+ "app-layout": "PolymerLabs/app-layout#master",
"iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
"iron-component-page": "polymerelements/iron-component-page#^1.0.0",
- "paper-scroll-header-panel": "polymerelements/paper-scroll-header-panel#^1.0.0",
+ "iron-ajax": "polymerelements/iron-ajax#^1.0.0",
+ "iron-icon": "polymerelements/iron-icon#^1.0.0",
+ "iron-icons": "polymerelements/iron-icons#^1.0.0",
+ "iron-scroll-threshold": "polymerelements/iron-scroll-threshold#^1.0.0",
"paper-menu": "polymerelements/paper-menu#^1.0.0",
"paper-item": "polymerelements/paper-item#^1.0.0",
"paper-icon-button": "polymerelements/paper-icon-button#^1.0.0",
"paper-button": "polymerelements/paper-button#^1.0.0",
- "iron-ajax": "polymerelements/iron-ajax#^1.0.0",
- "iron-icon": "polymerelements/iron-icon#^1.0.0",
- "iron-icons": "polymerelements/iron-icons#^1.0.0",
"paper-badge": "polymerelements/paper-badge#^1.0.0",
- "paper-toolbar": "polymerelements/paper-toolbar#^1.0.0",
"paper-spinner": "polymerelements/paper-spinner#^1.0.0",
"test-fixture": "polymerelements/test-fixture#^1.0.0",
"iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.2",
- "web-component-tester": "polymer/web-component-tester#^3.4.0"
+ "web-component-tester": "^4.0.0"
},
- "_release": "1.1.7",
+ "_release": "1.2.3",
"_resolution": {
"type": "version",
- "tag": "v1.1.7",
- "commit": "a00edace6cac13c16ba6e7a930d29a14b98e44c7"
+ "tag": "v1.2.3",
+ "commit": "07957dc10616341606ae030d78bb41492fb7c0cc"
},
"_source": "git://github.com/PolymerElements/iron-list.git",
- "_target": "1.1.7",
+ "_target": "^1.0.0",
"_originalSource": "PolymerElements/iron-list"
} \ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/README.md b/third_party/polymer/v1_0/components-chromium/iron-list/README.md
index 1b31769..45dba70 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/README.md
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/README.md
@@ -7,6 +7,9 @@ iron-list.html
Edit those files, and our readme bot will duplicate them over here!
Edit this file, and the bot will squash your changes :)
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
-->
[![Build Status](https://travis-ci.org/PolymerElements/iron-list.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-list)
@@ -16,8 +19,6 @@ _[Demo and API Docs](https://elements.polymer-project.org/elements/iron-list)_
##&lt;iron-list&gt;
-
-
`iron-list` displays a virtual, 'infinite' list. The template inside
the iron-list element represents the DOM to create for each list item.
The `items` property specifies an array of list item data.
@@ -39,12 +40,14 @@ layout means (e.g. the `flex` or `fit` classes).
List item templates should bind to template models of the following structure:
- {
- index: 0, // data index for this item
- item: { // user data corresponding to items[index]
- /* user item data */
- }
- }
+```js
+{
+ index: 0, // index in the item array
+ selected: false, // true if the current item is selected
+ tabIndex: -1, // a dynamically generated tabIndex for focus management
+ item: {} // user data corresponding to items[index]
+}
+```
Alternatively, you can change the property name used as data index by changing the
`indexAs` property. The `as` property defines the name of the variable to add to the binding
@@ -71,16 +74,33 @@ bound from the model object provided to the template scope):
<iron-list items="[[data]]" as="item">
<template>
<div>
- Name: <span>[[item.name]]</span>
+ Name: [[item.name]]
</div>
</template>
</iron-list>
</template>
```
+### Accessibility
+
+`iron-list` automatically manages the focus state for the items. It also provides
+a `tabIndex` property within the template scope that can be used for keyboard navigation.
+For example, users can press the up and down keys to move to previous and next
+items in the list:
+
+```html
+<iron-list items="[[data]]" as="item">
+ <template>
+ <div tabindex$="[[tabIndex]]">
+ Name: [[item.name]]
+ </div>
+ </template>
+</iron-list>
+```
+
### Styling
-Use the `--iron-list-items-container` mixin to style the container of items, e.g.
+You can use the `--iron-list-items-container` mixin to style the container of items:
```css
iron-list {
@@ -98,7 +118,7 @@ This event is fired by any element that implements `IronResizableBehavior`.
By default, elements such as `iron-pages`, `paper-tabs` or `paper-dialog` will trigger
this event automatically. If you hide the list manually (e.g. you use `display: none`)
you might want to implement `IronResizableBehavior` or fire this event manually right
-after the list became visible again. e.g.
+after the list became visible again. For example:
```js
document.querySelector('iron-list').fire('iron-resize');
@@ -111,7 +131,7 @@ visible on the screen. e.g. the page has 500 nodes, but only 20 are visible at t
This is why we refer to it as a `virtual` list. In this case, a `dom-repeat` will still
create 500 nodes which could slow down the web app, but `iron-list` will only create 20.
-However, having an `iron-list` does not mean that you can load all the data at once.
+However, having an `iron-list` does not mean that you can load all the data at once.
Say, you have a million records in the database, you want to split the data into pages
so you can bring a page at the time. The page could contain 500 items, and iron-list
will only render 20.
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/bower.json b/third_party/polymer/v1_0/components-chromium/iron-list/bower.json
index 607bd606..50bb130 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/bower.json
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/bower.json
@@ -7,7 +7,7 @@
"list",
"virtual-list"
],
- "version": "1.1.7",
+ "version": "1.2.3",
"homepage": "https://github.com/PolymerElements/iron-list",
"authors": [
"The Polymer Authors"
@@ -21,25 +21,27 @@
"ignore": [],
"dependencies": {
"polymer": "Polymer/polymer#^1.1.0",
- "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0"
+ "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0",
+ "iron-a11y-keys-behavior": "polymerelements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-scroll-target-behavior": "PolymerElements/iron-scroll-target-behavior#^1.0.0"
},
"devDependencies": {
+ "app-layout": "PolymerLabs/app-layout#master",
"iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
"iron-component-page": "polymerelements/iron-component-page#^1.0.0",
- "paper-scroll-header-panel": "polymerelements/paper-scroll-header-panel#^1.0.0",
+ "iron-ajax": "polymerelements/iron-ajax#^1.0.0",
+ "iron-icon": "polymerelements/iron-icon#^1.0.0",
+ "iron-icons": "polymerelements/iron-icons#^1.0.0",
+ "iron-scroll-threshold": "polymerelements/iron-scroll-threshold#^1.0.0",
"paper-menu": "polymerelements/paper-menu#^1.0.0",
"paper-item": "polymerelements/paper-item#^1.0.0",
"paper-icon-button": "polymerelements/paper-icon-button#^1.0.0",
"paper-button": "polymerelements/paper-button#^1.0.0",
- "iron-ajax": "polymerelements/iron-ajax#^1.0.0",
- "iron-icon": "polymerelements/iron-icon#^1.0.0",
- "iron-icons": "polymerelements/iron-icons#^1.0.0",
"paper-badge": "polymerelements/paper-badge#^1.0.0",
- "paper-toolbar": "polymerelements/paper-toolbar#^1.0.0",
"paper-spinner": "polymerelements/paper-spinner#^1.0.0",
"test-fixture": "polymerelements/test-fixture#^1.0.0",
"iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.2",
- "web-component-tester": "polymer/web-component-tester#^3.4.0"
+ "web-component-tester": "^4.0.0"
}
}
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
index a7c75b8..8a0ee87 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
@@ -4,6 +4,7 @@
var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
var DEFAULT_PHYSICAL_COUNT = 3;
var MAX_PHYSICAL_COUNT = 500;
+ var HIDDEN_Y = '-10000px';
Polymer({
@@ -89,18 +90,27 @@
observers: [
'_itemsChanged(items.*)',
'_selectionEnabledChanged(selectionEnabled)',
- '_multiSelectionChanged(multiSelection)'
+ '_multiSelectionChanged(multiSelection)',
+ '_setOverflow(scrollTarget)'
],
behaviors: [
Polymer.Templatizer,
- Polymer.IronResizableBehavior
+ Polymer.IronResizableBehavior,
+ Polymer.IronA11yKeysBehavior,
+ Polymer.IronScrollTargetBehavior
],
listeners: {
'iron-resize': '_resizeHandler'
},
+ keyBindings: {
+ 'up': '_didMoveUp',
+ 'down': '_didMoveDown',
+ 'enter': '_didEnter'
+ },
+
/**
* The ratio of hidden tiles that should remain in the scroll direction.
* Recommended value ~0.5, so it will distribute tiles evely in both directions.
@@ -108,12 +118,6 @@
_ratio: 0.5,
/**
- * The element that controls the scroll
- * @type {?Element}
- */
- _scroller: null,
-
- /**
* The padding-top value of the `scroller` element
*/
_scrollerPaddingTop: 0,
@@ -144,7 +148,7 @@
_physicalSize: 0,
/**
- * The average `offsetHeight` of the tiles observed till now.
+ * The average `F` of the tiles observed till now.
*/
_physicalAverage: 0,
@@ -202,13 +206,21 @@
_physicalSizes: null,
/**
- * A cached value for the visible index.
+ * A cached value for the first visible index.
* See `firstVisibleIndex`
* @type {?number}
*/
_firstVisibleIndexVal: null,
/**
+ * A cached value for the last visible index.
+ * See `lastVisibleIndex`
+ * @type {?number}
+ */
+ _lastVisibleIndexVal: null,
+
+
+ /**
* A Polymer collection for the items.
* @type {?Polymer.Collection}
*/
@@ -231,6 +243,23 @@
_maxPages: 3,
/**
+ * The currently focused item index.
+ */
+ _focusedIndex: 0,
+
+ /**
+ * The the item that is focused if it is moved offscreen.
+ * @private {?TemplatizerNode}
+ */
+ _offscreenFocusedItem: null,
+
+ /**
+ * The item that backfills the `_offscreenFocusedItem` in the physical items
+ * list when that item is moved offscreen.
+ */
+ _focusBackfillItem: null,
+
+ /**
* The bottom of the physical content.
*/
get _physicalBottom() {
@@ -248,7 +277,7 @@
* The n-th item rendered in the last physical item.
*/
get _virtualEnd() {
- return this._virtualStartVal + this._physicalCount - 1;
+ return this._virtualStart + this._physicalCount - 1;
},
/**
@@ -283,8 +312,13 @@
set _virtualStart(val) {
// clamp the value so that _minVirtualStart <= val <= _maxVirtualStart
this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val));
- this._physicalStart = this._virtualStartVal % this._physicalCount;
- this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount;
+ if (this._physicalCount === 0) {
+ this._physicalStart = 0;
+ this._physicalEnd = 0;
+ } else {
+ this._physicalStart = this._virtualStartVal % this._physicalCount;
+ this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount;
+ }
},
/**
@@ -309,7 +343,7 @@
* True if the current list is visible.
*/
get _isVisible() {
- return this._scroller && Boolean(this._scroller.offsetWidth || this._scroller.offsetHeight);
+ return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.scrollTarget.offsetHeight);
},
/**
@@ -318,10 +352,8 @@
* @type {number}
*/
get firstVisibleIndex() {
- var physicalOffset;
-
if (this._firstVisibleIndexVal === null) {
- physicalOffset = this._physicalTop;
+ var physicalOffset = this._physicalTop;
this._firstVisibleIndexVal = this._iterateItems(
function(pidx, vidx) {
@@ -332,54 +364,52 @@
}
}) || 0;
}
-
return this._firstVisibleIndexVal;
},
- ready: function() {
- if (IOS_TOUCH_SCROLLING) {
- this._scrollListener = function() {
- requestAnimationFrame(this._scrollHandler.bind(this));
- }.bind(this);
- } else {
- this._scrollListener = this._scrollHandler.bind(this);
- }
- },
-
/**
- * When the element has been attached to the DOM tree.
+ * Gets the index of the last visible item in the viewport.
+ *
+ * @type {number}
*/
- attached: function() {
- // delegate to the parent's scroller
- // e.g. paper-scroll-header-panel
- var el = Polymer.dom(this);
+ get lastVisibleIndex() {
+ if (this._lastVisibleIndexVal === null) {
+ var physicalOffset = this._physicalTop;
- var parentNode = /** @type {?{scroller: ?Element}} */ (el.parentNode);
- if (parentNode && parentNode.scroller) {
- this._scroller = parentNode.scroller;
- } else {
- this._scroller = this;
- this.classList.add('has-scroller');
- }
+ this._iterateItems(function(pidx, vidx) {
+ physicalOffset += this._physicalSizes[pidx];
- if (IOS_TOUCH_SCROLLING) {
- this._scroller.style.webkitOverflowScrolling = 'touch';
+ if(physicalOffset <= this._scrollBottom) {
+ this._lastVisibleIndexVal = vidx;
+ }
+ });
}
+ return this._lastVisibleIndexVal;
+ },
- this._scroller.addEventListener('scroll', this._scrollListener);
+ ready: function() {
+ this.addEventListener('focus', this._didFocus.bind(this), true);
+ },
+ attached: function() {
this.updateViewportBoundaries();
this._render();
},
- /**
- * When the element has been removed from the DOM tree.
- */
detached: function() {
this._itemsRendered = false;
- if (this._scroller) {
- this._scroller.removeEventListener('scroll', this._scrollListener);
- }
+ },
+
+ get _defaultScrollTarget() {
+ return this;
+ },
+
+ /**
+ * Set the overflow property if this element has its own scrolling region
+ */
+ _setOverflow: function(scrollTarget) {
+ this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : '';
+ this.style.overflow = scrollTarget === this ? 'auto' : '';
},
/**
@@ -389,20 +419,20 @@
* @method updateViewportBoundaries
*/
updateViewportBoundaries: function() {
- var scrollerStyle = window.getComputedStyle(this._scroller);
+ var scrollerStyle = window.getComputedStyle(this.scrollTarget);
this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10);
- this._viewportSize = this._scroller.offsetHeight;
+ this._viewportSize = this._scrollTargetHeight;
},
/**
* Update the models, the position of the
* items in the viewport and recycle tiles as needed.
*/
- _refresh: function() {
+ _scrollHandler: function() {
// clamp the `scrollTop` value
// IE 10|11 scrollTop may go above `_maxScrollTop`
// iOS `scrollTop` may go below 0 and above `_maxScrollTop`
- var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.scrollTop));
+ var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop));
var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBottom;
var ratio = this._ratio;
var delta = scrollTop - this._scrollPosition;
@@ -416,6 +446,7 @@
// clear cached visible index
this._firstVisibleIndexVal = null;
+ this._lastVisibleIndexVal = null;
scrollBottom = this._scrollBottom;
physicalBottom = this._physicalBottom;
@@ -505,17 +536,21 @@
},
/**
- * Update the list of items, starting from the `_virtualStartVal` item.
+ * Update the list of items, starting from the `_virtualStart` item.
* @param {!Array<number>=} itemSet
* @param {!Array<number>=} movingUp
*/
_update: function(itemSet, movingUp) {
+ // manage focus
+ if (this._isIndexRendered(this._focusedIndex)) {
+ this._restoreFocusedItem();
+ } else {
+ this._createFocusBackfillItem();
+ }
// update models
this._assignModels(itemSet);
-
// measure heights
this._updateMetrics(itemSet);
-
// adjust offset after measuring
if (movingUp) {
while (movingUp.length) {
@@ -524,10 +559,8 @@
}
// update the position of the items
this._positionItems();
-
// set the scroller size
this._updateScrollerSize();
-
// increase the pool of physical items
this._increasePoolIfNeeded();
},
@@ -547,7 +580,6 @@
physicalItems[i] = inst.root.querySelector('*');
Polymer.dom(this).appendChild(inst.root);
}
-
return physicalItems;
},
@@ -557,24 +589,24 @@
* if the physical size is shorter than `_optPhysicalSize`
*/
_increasePoolIfNeeded: function() {
- if (this._viewportSize !== 0 && this._physicalSize < this._optPhysicalSize) {
- // 0 <= `currentPage` <= `_maxPages`
- var currentPage = Math.floor(this._physicalSize / this._viewportSize);
-
- if (currentPage === 0) {
- // fill the first page
- this.async(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5)));
- } else if (this._lastPage !== currentPage) {
- // once a page is filled up, paint it and defer the next increase
- requestAnimationFrame(this._increasePool.bind(this, 1));
- } else {
- // fill the rest of the pages
- this.async(this._increasePool.bind(this, 1));
- }
- this._lastPage = currentPage;
- return true;
+ if (this._viewportSize === 0 || this._physicalSize >= this._optPhysicalSize) {
+ return false;
+ }
+ // 0 <= `currentPage` <= `_maxPages`
+ var currentPage = Math.floor(this._physicalSize / this._viewportSize);
+ if (currentPage === 0) {
+ // fill the first page
+ this._debounceTemplate(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5)));
+ } else if (this._lastPage !== currentPage) {
+ // paint the page and defer the next increase
+ // wait 16ms which is rough enough to get paint cycle.
+ Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increasePool.bind(this, 1), 16));
+ } else {
+ // fill the rest of the pages
+ this._debounceTemplate(this._increasePool.bind(this, 1));
}
- return false;
+ this._lastPage = currentPage;
+ return true;
},
/**
@@ -584,7 +616,7 @@
// limit the size
var nextPhysicalCount = Math.min(
this._physicalCount + missingItems,
- this._virtualCount,
+ this._virtualCount - this._virtualStart,
MAX_PHYSICAL_COUNT
);
var prevPhysicalCount = this._physicalCount;
@@ -610,6 +642,7 @@
if (this.isAttached && !this._itemsRendered && this._isVisible && requiresUpdate) {
this._lastPage = 0;
this._update();
+ this._scrollHandler();
this._itemsRendered = true;
}
},
@@ -621,11 +654,11 @@
if (!this.ctor) {
// Template instance props that should be excluded from forwarding
var props = {};
-
props.__key__ = true;
props[this.as] = true;
props[this.indexAs] = true;
props[this.selectedAs] = true;
+ props.tabIndex = true;
this._instanceProps = props;
this._userTemplate = Polymer.dom(this).querySelector('template');
@@ -693,6 +726,10 @@
var key = path.substring(0, dot < 0 ? path.length : dot);
var idx = this._physicalIndexForKey[key];
var row = this._physicalItems[idx];
+
+ if (idx === this._focusedIndex && this._offscreenFocusedItem) {
+ row = this._offscreenFocusedItem;
+ }
if (row) {
var inst = row._templateInstance;
if (dot >= 0) {
@@ -711,17 +748,18 @@
*/
_itemsChanged: function(change) {
if (change.path === 'items') {
+
+ this._restoreFocusedItem();
// render the new set
this._itemsRendered = false;
-
// update the whole set
- this._virtualStartVal = 0;
+ this._virtualStart = 0;
this._physicalTop = 0;
this._virtualCount = this.items ? this.items.length : 0;
+ this._focusedIndex = 0;
this._collection = this.items ? Polymer.Collection.get(this.items) : null;
this._physicalIndexForKey = {};
- // scroll to the top
this._resetScrollPosition(0);
// create the initial physical items
@@ -730,17 +768,20 @@
this._physicalItems = this._createPool(this._physicalCount);
this._physicalSizes = new Array(this._physicalCount);
}
-
- this.debounce('refresh', this._render);
+ this._debounceTemplate(this._render);
} else if (change.path === 'items.splices') {
// render the new set
this._itemsRendered = false;
-
this._adjustVirtualIndex(change.value.indexSplices);
this._virtualCount = this.items ? this.items.length : 0;
- this.debounce('refresh', this._render);
+ this._debounceTemplate(this._render);
+
+ if (this._focusedIndex < 0 || this._focusedIndex >= this._virtualCount) {
+ this._focusedIndex = 0;
+ }
+ this._debounceTemplate(this._render);
} else {
// update a single item
@@ -762,19 +803,15 @@
idx = splice.index;
// We only need to care about changes happening above the current position
- if (idx >= this._virtualStartVal) {
+ if (idx >= this._virtualStart) {
break;
}
this._virtualStart = this._virtualStart +
- Math.max(splice.addedCount - splice.removed.length, idx - this._virtualStartVal);
+ Math.max(splice.addedCount - splice.removed.length, idx - this._virtualStart);
}
},
- _scrollHandler: function() {
- this._refresh();
- },
-
/**
* Executes a provided function per every physical index in `itemSet`
* `itemSet` default value is equivalent to the entire set of physical indexes.
@@ -789,9 +826,9 @@
for (i = 0; i < itemSet.length; i++) {
pidx = itemSet[i];
if (pidx >= this._physicalStart) {
- vidx = this._virtualStartVal + (pidx - this._physicalStart);
+ vidx = this._virtualStart + (pidx - this._physicalStart);
} else {
- vidx = this._virtualStartVal + (this._physicalCount - this._physicalStart) + pidx;
+ vidx = this._virtualStart + (this._physicalCount - this._physicalStart) + pidx;
}
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
@@ -799,17 +836,14 @@
}
} else {
pidx = this._physicalStart;
- vidx = this._virtualStartVal;
+ vidx = this._virtualStart;
for (; pidx < this._physicalCount; pidx++, vidx++) {
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
}
}
-
- pidx = 0;
-
- for (; pidx < this._physicalStart; pidx++, vidx++) {
+ for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) {
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
}
@@ -827,12 +861,12 @@
var inst = el._templateInstance;
var item = this.items && this.items[vidx];
- if (item) {
+ if (item !== undefined && item !== null) {
inst[this.as] = item;
inst.__key__ = this._collection.getKey(item);
- inst[this.selectedAs] =
- /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item);
+ inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item);
inst[this.indexAs] = vidx;
+ inst.tabIndex = vidx === this._focusedIndex ? 0 : -1;
el.removeAttribute('hidden');
this._physicalIndexForKey[inst.__key__] = pidx;
} else {
@@ -849,23 +883,26 @@
* @param {!Array<number>=} itemSet
*/
_updateMetrics: function(itemSet) {
+ // Make sure we distributed all the physical items
+ // so we can measure them
+ Polymer.dom.flush();
+
var newPhysicalSize = 0;
var oldPhysicalSize = 0;
var prevAvgCount = this._physicalAverageCount;
var prevPhysicalAvg = this._physicalAverage;
- // Make sure we distributed all the physical items
- // so we can measure them
- Polymer.dom.flush();
this._iterateItems(function(pidx, vidx) {
+
oldPhysicalSize += this._physicalSizes[pidx] || 0;
this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
newPhysicalSize += this._physicalSizes[pidx];
this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
+
}, itemSet);
this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize;
- this._viewportSize = this._scroller.offsetHeight;
+ this._viewportSize = this._scrollTargetHeight;
// update the average if we measured something
if (this._physicalAverageCount !== prevAvgCount) {
@@ -885,7 +922,7 @@
this._iterateItems(function(pidx) {
- this.transform('translate3d(0, ' + y + 'px, 0)', this._physicalItems[pidx]);
+ this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]);
y += this._physicalSizes[pidx];
});
@@ -895,15 +932,14 @@
* Adjusts the scroll position when it was overestimated.
*/
_adjustScrollPosition: function() {
- var deltaHeight = this._virtualStartVal === 0 ? this._physicalTop :
+ var deltaHeight = this._virtualStart === 0 ? this._physicalTop :
Math.min(this._scrollPosition + this._physicalTop, 0);
if (deltaHeight) {
this._physicalTop = this._physicalTop - deltaHeight;
-
// juking scroll position during interial scrolling on iOS is no bueno
if (!IOS_TOUCH_SCROLLING) {
- this._resetScrollPosition(this._scroller.scrollTop - deltaHeight);
+ this._resetScrollPosition(this._scrollTop - deltaHeight);
}
}
},
@@ -912,9 +948,9 @@
* Sets the position of the scroll.
*/
_resetScrollPosition: function(pos) {
- if (this._scroller) {
- this._scroller.scrollTop = pos;
- this._scrollPosition = this._scroller.scrollTop;
+ if (this.scrollTarget) {
+ this._scrollTop = pos;
+ this._scrollPosition = this._scrollTop;
}
},
@@ -925,7 +961,7 @@
*/
_updateScrollerSize: function(forceUpdate) {
this._estScrollHeight = (this._physicalBottom +
- Math.max(this._virtualCount - this._physicalCount - this._virtualStartVal, 0) * this._physicalAverage);
+ Math.max(this._virtualCount - this._physicalCount - this._virtualStart, 0) * this._physicalAverage);
forceUpdate = forceUpdate || this._scrollHeight === 0;
forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize;
@@ -949,20 +985,18 @@
return;
}
- var firstVisible = this.firstVisibleIndex;
+ Polymer.dom.flush();
+ var firstVisible = this.firstVisibleIndex;
idx = Math.min(Math.max(idx, 0), this._virtualCount-1);
// start at the previous virtual item
// so we have a item above the first visible item
this._virtualStart = idx - 1;
-
// assign new models
this._assignModels();
-
// measure the new sizes
this._updateMetrics();
-
// estimate new physical offset
this._physicalTop = this._virtualStart * this._physicalAverage;
@@ -977,21 +1011,17 @@
currentTopItem = (currentTopItem + 1) % this._physicalCount;
currentVirtualItem++;
}
-
// update the scroller size
this._updateScrollerSize(true);
-
// update the position of the items
this._positionItems();
-
// set the new scroll position
- this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1);
-
+ this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + targetOffsetTop + 1);
// increase the pool of physical items if needed
this._increasePoolIfNeeded();
-
// clear cached visible index
this._firstVisibleIndexVal = null;
+ this._lastVisibleIndexVal = null;
},
/**
@@ -1007,7 +1037,11 @@
* when the element is resized.
*/
_resizeHandler: function() {
- this.debounce('resize', function() {
+ // iOS fires the resize event when the address bar slides up
+ if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100) {
+ return;
+ }
+ this._debounceTemplate(function() {
this._render();
if (this._itemsRendered && this._physicalItems && this._isVisible) {
this._resetAverage();
@@ -1033,12 +1067,14 @@
* @param {(Object|number)} item The item object or its index
*/
_getNormalizedItem: function(item) {
- if (typeof item === 'number') {
- item = this.items[item];
- if (!item) {
- throw new RangeError('<item> not found');
+ if (this._collection.getKey(item) === undefined) {
+ if (typeof item === 'number') {
+ item = this.items[item];
+ if (!item) {
+ throw new RangeError('<item> not found');
+ }
+ return item;
}
- } else if (this._collection.getKey(item) === undefined) {
throw new TypeError('<item> should be a valid item');
}
return item;
@@ -1061,6 +1097,7 @@
model[this.selectedAs] = true;
}
this.$.selector.select(item);
+ this.updateSizeForItem(item);
},
/**
@@ -1078,6 +1115,7 @@
model[this.selectedAs] = false;
}
this.$.selector.deselect(item);
+ this.updateSizeForItem(item);
},
/**
@@ -1123,20 +1161,15 @@
* it will remove the listener otherwise.
*/
_selectionEnabledChanged: function(selectionEnabled) {
- if (selectionEnabled) {
- this.listen(this, 'tap', '_selectionHandler');
- this.listen(this, 'keypress', '_selectionHandler');
- } else {
- this.unlisten(this, 'tap', '_selectionHandler');
- this.unlisten(this, 'keypress', '_selectionHandler');
- }
+ var handler = selectionEnabled ? this.listen : this.unlisten;
+ handler.call(this, this, 'tap', '_selectionHandler');
},
/**
* Select an item from an event object.
*/
_selectionHandler: function(e) {
- if (e.type !== 'keypress' || e.keyCode === 13) {
+ if (this.selectionEnabled) {
var model = this.modelForElement(e.target);
if (model) {
this.toggleSelectionForItem(model[this.as]);
@@ -1164,7 +1197,136 @@
this._updateMetrics([pidx]);
this._positionItems();
}
+ },
+
+ _isIndexRendered: function(idx) {
+ return idx >= this._virtualStart && idx <= this._virtualEnd;
+ },
+
+ _getPhysicalItemForIndex: function(idx, force) {
+ if (!this._collection) {
+ return null;
+ }
+ if (!this._isIndexRendered(idx)) {
+ if (force) {
+ this.scrollToIndex(idx);
+ return this._getPhysicalItemForIndex(idx, false);
+ }
+ return null;
+ }
+ var item = this._getNormalizedItem(idx);
+ var physicalItem = this._physicalItems[this._physicalIndexForKey[this._collection.getKey(item)]];
+
+ return physicalItem || null;
+ },
+
+ _focusPhysicalItem: function(idx) {
+ this._restoreFocusedItem();
+
+ var physicalItem = this._getPhysicalItemForIndex(idx, true);
+ if (!physicalItem) {
+ return;
+ }
+ var SECRET = ~(Math.random() * 100);
+ var model = physicalItem._templateInstance;
+ var focusable;
+
+ model.tabIndex = SECRET;
+ // the focusable element could be the entire physical item
+ if (physicalItem.tabIndex === SECRET) {
+ focusable = physicalItem;
+ }
+ // the focusable element could be somewhere within the physical item
+ if (!focusable) {
+ focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECRET + '"]');
+ }
+ // restore the tab index
+ model.tabIndex = 0;
+ focusable && focusable.focus();
+ },
+
+ _restoreFocusedItem: function() {
+ if (!this._offscreenFocusedItem) {
+ return;
+ }
+ var item = this._getNormalizedItem(this._focusedIndex);
+ var pidx = this._physicalIndexForKey[this._collection.getKey(item)];
+
+ if (pidx !== undefined) {
+ this.translate3d(0, HIDDEN_Y, 0, this._physicalItems[pidx]);
+ this._physicalItems[pidx] = this._offscreenFocusedItem;
+ }
+ this._offscreenFocusedItem = null;
+ },
+
+ _removeFocusedItem: function() {
+ if (!this._offscreenFocusedItem) {
+ return;
+ }
+ Polymer.dom(this).removeChild(this._offscreenFocusedItem);
+ this._offscreenFocusedItem = null;
+ this._focusBackfillItem = null;
+ },
+
+ _createFocusBackfillItem: function() {
+ if (this._offscreenFocusedItem) {
+ return;
+ }
+ var item = this._getNormalizedItem(this._focusedIndex);
+ var pidx = this._physicalIndexForKey[this._collection.getKey(item)];
+
+ this._offscreenFocusedItem = this._physicalItems[pidx];
+ this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem);
+
+ if (!this._focusBackfillItem) {
+ var stampedTemplate = this.stamp(null);
+ this._focusBackfillItem = stampedTemplate.root.querySelector('*');
+ Polymer.dom(this).appendChild(stampedTemplate.root);
+ }
+ this._physicalItems[pidx] = this._focusBackfillItem;
+ },
+
+ _didFocus: function(e) {
+ var targetModel = this.modelForElement(e.target);
+ var fidx = this._focusedIndex;
+
+ if (!targetModel) {
+ return;
+ }
+ this._restoreFocusedItem();
+
+ if (this.modelForElement(this._offscreenFocusedItem) === targetModel) {
+ this.scrollToIndex(fidx);
+ } else {
+ // restore tabIndex for the currently focused item
+ this._getModelFromItem(this._getNormalizedItem(fidx)).tabIndex = -1;
+ // set the tabIndex for the next focused item
+ targetModel.tabIndex = 0;
+ fidx = /** @type {{index: number}} */(targetModel).index;
+ this._focusedIndex = fidx;
+ // bring the item into view
+ if (fidx < this.firstVisibleIndex || fidx > this.lastVisibleIndex) {
+ this.scrollToIndex(fidx);
+ } else {
+ this._update();
+ }
+ }
+ },
+
+ _didMoveUp: function() {
+ this._focusPhysicalItem(Math.max(0, this._focusedIndex - 1));
+ },
+
+ _didMoveDown: function() {
+ this._focusPhysicalItem(Math.min(this._virtualCount, this._focusedIndex + 1));
+ },
+
+ _didEnter: function(e) {
+ // focus the currently focused physical item
+ this._focusPhysicalItem(this._focusedIndex);
+ // toggle selection
+ this._selectionHandler(/** @type {{keyboardEvent: Event}} */(e.detail).keyboardEvent);
}
});
-})(); \ No newline at end of file
+})();
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html
index 9b3148a..6737eea 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html
@@ -8,6 +8,8 @@ Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--><html><head><link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="../iron-scroll-target-behavior/iron-scroll-target-behavior.html">
<!--
@@ -32,12 +34,14 @@ layout means (e.g. the `flex` or `fit` classes).
List item templates should bind to template models of the following structure:
- {
- index: 0, // data index for this item
- item: { // user data corresponding to items[index]
- /* user item data */
- }
- }
+```js
+{
+ index: 0, // index in the item array
+ selected: false, // true if the current item is selected
+ tabIndex: -1, // a dynamically generated tabIndex for focus management
+ item: {} // user data corresponding to items[index]
+}
+```
Alternatively, you can change the property name used as data index by changing the
`indexAs` property. The `as` property defines the name of the variable to add to the binding
@@ -64,16 +68,33 @@ bound from the model object provided to the template scope):
<iron-list items="[[data]]" as="item">
<template>
<div>
- Name: <span>[[item.name]]</span>
+ Name: [[item.name]]
</div>
</template>
</iron-list>
</template>
```
+### Accessibility
+
+`iron-list` automatically manages the focus state for the items. It also provides
+a `tabIndex` property within the template scope that can be used for keyboard navigation.
+For example, users can press the up and down keys to move to previous and next
+items in the list:
+
+```html
+<iron-list items="[[data]]" as="item">
+ <template>
+ <div tabindex$="[[tabIndex]]">
+ Name: [[item.name]]
+ </div>
+ </template>
+</iron-list>
+```
+
### Styling
-Use the `--iron-list-items-container` mixin to style the container of items, e.g.
+You can use the `--iron-list-items-container` mixin to style the container of items:
```css
iron-list {
@@ -91,7 +112,7 @@ This event is fired by any element that implements `IronResizableBehavior`.
By default, elements such as `iron-pages`, `paper-tabs` or `paper-dialog` will trigger
this event automatically. If you hide the list manually (e.g. you use `display: none`)
you might want to implement `IronResizableBehavior` or fire this event manually right
-after the list became visible again. e.g.
+after the list became visible again. For example:
```js
document.querySelector('iron-list').fire('iron-resize');
@@ -104,7 +125,7 @@ visible on the screen. e.g. the page has 500 nodes, but only 20 are visible at t
This is why we refer to it as a `virtual` list. In this case, a `dom-repeat` will still
create 500 nodes which could slow down the web app, but `iron-list` will only create 20.
-However, having an `iron-list` does not mean that you can load all the data at once.
+However, having an `iron-list` does not mean that you can load all the data at once.
Say, you have a million records in the database, you want to split the data into pages
so you can bring a page at the time. The page could contain 500 items, and iron-list
will only render 20.
@@ -114,6 +135,8 @@ will only render 20.
@demo demo/index.html Simple list
@demo demo/selection.html Selection of items
@demo demo/collapse.html Collapsable items
+@demo demo/scroll-threshold.html Scroll thesholds
+
-->
</head><body><dom-module id="iron-list">
@@ -121,14 +144,13 @@ will only render 20.
<style>
:host {
display: block;
+ position: relative;
}
- :host(.has-scroller) {
- overflow: auto;
- }
-
- :host(:not(.has-scroller)) {
- position: relative;
+ @media only screen and (-webkit-max-device-pixel-ratio: 1) {
+ :host {
+ will-change: transform;
+ }
}
#items {
diff --git a/third_party/polymer/v1_0/components-chromium/paper-dialog/.bower.json b/third_party/polymer/v1_0/components-chromium/paper-dialog/.bower.json
index 90f438f..2882e47 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-dialog/.bower.json
+++ b/third_party/polymer/v1_0/components-chromium/paper-dialog/.bower.json
@@ -1,7 +1,7 @@
{
"name": "paper-dialog",
"description": "A Material Design dialog",
- "version": "1.0.3",
+ "version": "1.0.4",
"authors": "The Polymer Authors",
"keywords": [
"web-components",
@@ -19,27 +19,27 @@
"homepage": "https://github.com/PolymerElements/paper-dialog",
"ignore": [],
"dependencies": {
- "polymer": "Polymer/polymer#^1.1.0",
+ "neon-animation": "PolymerElements/neon-animation#^1.0.0",
"paper-dialog-behavior": "PolymerElements/paper-dialog-behavior#^1.0.0",
"paper-styles": "PolymerElements/paper-styles#^1.0.0",
- "neon-animation": "PolymerElements/neon-animation#^1.0.0"
+ "polymer": "Polymer/polymer#^1.1.0"
},
"devDependencies": {
"iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
"paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.0.0",
"paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
- "paper-menu": "PolymerElements/paper-menu#^1.0.0",
"paper-item": "PolymerElements/paper-item#^1.0.0",
- "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.0.0",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
"test-fixture": "PolymerElements/test-fixture#^1.0.0",
- "web-component-tester": "polymer/web-component-tester#^3.4.0",
+ "web-component-tester": "^4.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
- "_release": "1.0.3",
+ "_release": "1.0.4",
"_resolution": {
"type": "version",
- "tag": "v1.0.3",
- "commit": "ec85b110e11e9d390f601e1c88ebf97984b53d82"
+ "tag": "v1.0.4",
+ "commit": "53b099bed06bbbab7cb0f82c8209328c1a82aee6"
},
"_source": "git://github.com/PolymerElements/paper-dialog.git",
"_target": "^1.0.0",
diff --git a/third_party/polymer/v1_0/components-chromium/paper-dialog/CONTRIBUTING.md b/third_party/polymer/v1_0/components-chromium/paper-dialog/CONTRIBUTING.md
index 7b10141..f147978a 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-dialog/CONTRIBUTING.md
+++ b/third_party/polymer/v1_0/components-chromium/paper-dialog/CONTRIBUTING.md
@@ -5,6 +5,11 @@ https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
If you edit that file, it will get updated everywhere else.
If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
-->
# Polymer Elements
## Guide for Contributors
@@ -41,7 +46,7 @@ Polymer Elements are built in the open, and the Polymer authors eagerly encourag
3. Click the `paper-foo` element.
```
- 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [http://jsbin.com/cagaye](http://jsbin.com/cagaye/edit?html,output).
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
@@ -51,14 +56,14 @@ Polymer Elements are built in the open, and the Polymer authors eagerly encourag
When submitting pull requests, please provide:
- 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues using the following syntax:
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
```markdown
(For a single issue)
Fixes #20
(For multiple issues)
- Fixes #32, #40
+ Fixes #32, fixes #40
```
2. **A succinct description of the design** used to fix any related issues. For example:
diff --git a/third_party/polymer/v1_0/components-chromium/paper-dialog/README.md b/third_party/polymer/v1_0/components-chromium/paper-dialog/README.md
index 4bcd5d4..787cb7e 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-dialog/README.md
+++ b/third_party/polymer/v1_0/components-chromium/paper-dialog/README.md
@@ -7,16 +7,18 @@ paper-dialog.html
Edit those files, and our readme bot will duplicate them over here!
Edit this file, and the bot will squash your changes :)
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
-->
-[![Build Status](https://travis-ci.org/PolymerElements/paper-dialog.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-dialog)
+[![Build status](https://travis-ci.org/PolymerElements/paper-dialog.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-dialog)
-_[Demo and API Docs](https://elements.polymer-project.org/elements/paper-dialog)_
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-dialog)_
##&lt;paper-dialog&gt;
-
Material design: [Dialogs](https://www.google.com/design/spec/components/dialogs.html)
`<paper-dialog>` is a dialog with Material Design styling and optional animations when it is
@@ -27,16 +29,18 @@ content area. See `Polymer.PaperDialogBehavior` for specifics.
For example, the following code implements a dialog with a header, scrolling content area and
buttons.
- <paper-dialog>
- <h2>Header</h2>
- <paper-dialog-scrollable>
- Lorem ipsum...
- </paper-dialog-scrollable>
- <div class="buttons">
- <paper-button dialog-dismiss>Cancel</paper-button>
- <paper-button dialog-confirm>Accept</paper-button>
- </div>
- </paper-dialog>
+```html
+<paper-dialog>
+ <h2>Header</h2>
+ <paper-dialog-scrollable>
+ Lorem ipsum...
+ </paper-dialog-scrollable>
+ <div class="buttons">
+ <paper-button dialog-dismiss>Cancel</paper-button>
+ <paper-button dialog-confirm>Accept</paper-button>
+ </div>
+</paper-dialog>
+```
### Styling
@@ -51,14 +55,16 @@ is opened or closed. See the documentation in
For example:
- <link rel="import" href="components/neon-animation/animations/scale-up-animation.html">
- <link rel="import" href="components/neon-animation/animations/fade-out-animation.html">
+```html
+<link rel="import" href="components/neon-animation/animations/scale-up-animation.html">
+<link rel="import" href="components/neon-animation/animations/fade-out-animation.html">
- <paper-dialog entry-animation="scale-up-animation"
- exit-animation="fade-out-animation">
- <h2>Header</h2>
- <div>Dialog body</div>
- </paper-dialog>
+<paper-dialog entry-animation="scale-up-animation"
+ exit-animation="fade-out-animation">
+ <h2>Header</h2>
+ <div>Dialog body</div>
+</paper-dialog>
+```
### Accessibility
diff --git a/third_party/polymer/v1_0/components-chromium/paper-dialog/bower.json b/third_party/polymer/v1_0/components-chromium/paper-dialog/bower.json
index ae1aa53..ab3be84 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-dialog/bower.json
+++ b/third_party/polymer/v1_0/components-chromium/paper-dialog/bower.json
@@ -1,7 +1,7 @@
{
"name": "paper-dialog",
"description": "A Material Design dialog",
- "version": "1.0.3",
+ "version": "1.0.4",
"authors": "The Polymer Authors",
"keywords": [
"web-components",
@@ -19,20 +19,20 @@
"homepage": "https://github.com/PolymerElements/paper-dialog",
"ignore": [],
"dependencies": {
- "polymer": "Polymer/polymer#^1.1.0",
+ "neon-animation": "PolymerElements/neon-animation#^1.0.0",
"paper-dialog-behavior": "PolymerElements/paper-dialog-behavior#^1.0.0",
"paper-styles": "PolymerElements/paper-styles#^1.0.0",
- "neon-animation": "PolymerElements/neon-animation#^1.0.0"
+ "polymer": "Polymer/polymer#^1.1.0"
},
"devDependencies": {
"iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
"paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.0.0",
"paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
- "paper-menu": "PolymerElements/paper-menu#^1.0.0",
"paper-item": "PolymerElements/paper-item#^1.0.0",
- "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.0.0",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
"test-fixture": "PolymerElements/test-fixture#^1.0.0",
- "web-component-tester": "polymer/web-component-tester#^3.4.0",
+ "web-component-tester": "^4.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
}
}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-dialog/paper-dialog-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-dialog/paper-dialog-extracted.js
index a91d6ce..1b3570a 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-dialog/paper-dialog-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/paper-dialog/paper-dialog-extracted.js
@@ -14,6 +14,7 @@
},
_renderOpened: function() {
+ this.cancelAnimation();
if (this.withBackdrop) {
this.backdropElement.open();
}
@@ -21,6 +22,7 @@
},
_renderClosed: function() {
+ this.cancelAnimation();
if (this.withBackdrop) {
this.backdropElement.close();
}
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt
index 0bf0492..3560018 100644
--- a/third_party/polymer/v1_0/components_summary.txt
+++ b/third_party/polymer/v1_0/components_summary.txt
@@ -111,11 +111,11 @@ Revision: 55d2b39ead32b8d90da538daa1a6681fd9ae89d9
Tree link: https://github.com/PolymerElements/iron-input/tree/1.0.8
Name: iron-list
-Version: 1.1.7
+Version: 1.2.3
Repository: git://github.com/PolymerElements/iron-list.git
-Tag: v1.1.7
-Revision: a00edace6cac13c16ba6e7a930d29a14b98e44c7
-Tree link: https://github.com/PolymerElements/iron-list/tree/v1.1.7
+Tag: v1.2.3
+Revision: 07957dc10616341606ae030d78bb41492fb7c0cc
+Tree link: https://github.com/PolymerElements/iron-list/tree/v1.2.3
Name: iron-media-query
Version: 1.0.8
@@ -230,11 +230,11 @@ Revision: ce52f51c537d27f414fec7298b9ebff4248044eb
Tree link: https://github.com/PolymerElements/paper-checkbox/tree/v1.1.1
Name: paper-dialog
-Version: 1.0.3
+Version: 1.0.4
Repository: git://github.com/PolymerElements/paper-dialog.git
-Tag: v1.0.3
-Revision: ec85b110e11e9d390f601e1c88ebf97984b53d82
-Tree link: https://github.com/PolymerElements/paper-dialog/tree/v1.0.3
+Tag: v1.0.4
+Revision: 53b099bed06bbbab7cb0f82c8209328c1a82aee6
+Tree link: https://github.com/PolymerElements/paper-dialog/tree/v1.0.4
Name: paper-dialog-behavior
Version: 1.1.1