Index: chrome/browser/resources/md_history/app.crisper.js |
diff --git a/chrome/browser/resources/md_history/app.crisper.js b/chrome/browser/resources/md_history/app.crisper.js |
index 3e75862e14094e32974a703a7d4959977c019372..d0f753b089bacd0c235ac3ac8bc9a1b322fd6887 100644 |
--- a/chrome/browser/resources/md_history/app.crisper.js |
+++ b/chrome/browser/resources/md_history/app.crisper.js |
@@ -2,24 +2,7 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * @fileoverview PromiseResolver is a helper class that allows creating a |
- * Promise that will be fulfilled (resolved or rejected) some time later. |
- * |
- * Example: |
- * var resolver = new PromiseResolver(); |
- * resolver.promise.then(function(result) { |
- * console.log('resolved with', result); |
- * }); |
- * ... |
- * ... |
- * resolver.resolve({hello: 'world'}); |
- */ |
- |
-/** |
- * @constructor @struct |
- * @template T |
- */ |
+ |
function PromiseResolver() { |
/** @private {function(T=): void} */ |
this.resolve_; |
@@ -51,11 +34,6 @@ PromiseResolver.prototype = { |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * The global object. |
- * @type {!Object} |
- * @const |
- */ |
var global = this; |
/** @typedef {{eventName: string, uid: number}} */ |
@@ -65,26 +43,12 @@ var WebUIListener; |
var cr = cr || function() { |
'use strict'; |
- /** |
- * Builds an object structure for the provided namespace path, |
- * ensuring that names that already exist are not overwritten. For |
- * example: |
- * "a.b.c" -> a = {};a.b={};a.b.c={}; |
- * @param {string} name Name of the object that this file defines. |
- * @param {*=} opt_object The object to expose at the end of the path. |
- * @param {Object=} opt_objectToExportTo The object to add the path to; |
- * default is {@code global}. |
- * @return {!Object} The last object exported (i.e. exportPath('cr.ui') |
- * returns a reference to the ui property of window.cr). |
- * @private |
- */ |
function exportPath(name, opt_object, opt_objectToExportTo) { |
var parts = name.split('.'); |
var cur = opt_objectToExportTo || global; |
for (var part; parts.length && (part = parts.shift());) { |
if (!parts.length && opt_object !== undefined) { |
- // last part and we have an object; use it |
cur[part] = opt_object; |
} else if (part in cur) { |
cur = cur[part]; |
@@ -95,13 +59,6 @@ var cr = cr || function() { |
return cur; |
} |
- /** |
- * Fires a property change event on the target. |
- * @param {EventTarget} target The target to dispatch the event on. |
- * @param {string} propertyName The name of the property that changed. |
- * @param {*} newValue The new value for the property. |
- * @param {*} oldValue The old value for the property. |
- */ |
function dispatchPropertyChange(target, propertyName, newValue, oldValue) { |
var e = new Event(propertyName + 'Change'); |
e.propertyName = propertyName; |
@@ -110,50 +67,18 @@ var cr = cr || function() { |
target.dispatchEvent(e); |
} |
- /** |
- * Converts a camelCase javascript property name to a hyphenated-lower-case |
- * attribute name. |
- * @param {string} jsName The javascript camelCase property name. |
- * @return {string} The equivalent hyphenated-lower-case attribute name. |
- */ |
function getAttributeName(jsName) { |
return jsName.replace(/([A-Z])/g, '-$1').toLowerCase(); |
} |
- /** |
- * The kind of property to define in {@code defineProperty}. |
- * @enum {string} |
- * @const |
- */ |
var PropertyKind = { |
- /** |
- * Plain old JS property where the backing data is stored as a "private" |
- * field on the object. |
- * Use for properties of any type. Type will not be checked. |
- */ |
JS: 'js', |
- /** |
- * The property backing data is stored as an attribute on an element. |
- * Use only for properties of type {string}. |
- */ |
ATTR: 'attr', |
- /** |
- * The property backing data is stored as an attribute on an element. If the |
- * element has the attribute then the value is true. |
- * Use only for properties of type {boolean}. |
- */ |
BOOL_ATTR: 'boolAttr' |
}; |
- /** |
- * Helper function for defineProperty that returns the getter to use for the |
- * property. |
- * @param {string} name The name of the property. |
- * @param {PropertyKind} kind The kind of the property. |
- * @return {function():*} The getter for the property. |
- */ |
function getGetter(name, kind) { |
switch (kind) { |
case PropertyKind.JS: |
@@ -173,22 +98,9 @@ var cr = cr || function() { |
}; |
} |
- // TODO(dbeam): replace with assertNotReached() in assert.js when I can coax |
- // the browser/unit tests to preprocess this file through grit. |
throw 'not reached'; |
} |
- /** |
- * Helper function for defineProperty that returns the setter of the right |
- * kind. |
- * @param {string} name The name of the property we are defining the setter |
- * for. |
- * @param {PropertyKind} kind The kind of property we are getting the |
- * setter for. |
- * @param {function(*, *):void=} opt_setHook A function to run after the |
- * property is set, but before the propertyChange event is fired. |
- * @return {function(*):void} The function to use as a setter. |
- */ |
function getSetter(name, kind, opt_setHook) { |
switch (kind) { |
case PropertyKind.JS: |
@@ -234,20 +146,9 @@ var cr = cr || function() { |
}; |
} |
- // TODO(dbeam): replace with assertNotReached() in assert.js when I can coax |
- // the browser/unit tests to preprocess this file through grit. |
throw 'not reached'; |
} |
- /** |
- * Defines a property on an object. When the setter changes the value a |
- * property change event with the type {@code name + 'Change'} is fired. |
- * @param {!Object} obj The object to define the property for. |
- * @param {string} name The name of the property. |
- * @param {PropertyKind=} opt_kind What kind of underlying storage to use. |
- * @param {function(*, *):void=} opt_setHook A function to run after the |
- * property is set, but before the propertyChange event is fired. |
- */ |
function defineProperty(obj, name, opt_kind, opt_setHook) { |
if (typeof obj == 'function') |
obj = obj.prototype; |
@@ -261,40 +162,18 @@ var cr = cr || function() { |
obj.__defineSetter__(name, getSetter(name, kind, opt_setHook)); |
} |
- /** |
- * Counter for use with createUid |
- */ |
var uidCounter = 1; |
- /** |
- * @return {number} A new unique ID. |
- */ |
function createUid() { |
return uidCounter++; |
} |
- /** |
- * Returns a unique ID for the item. This mutates the item so it needs to be |
- * an object |
- * @param {!Object} item The item to get the unique ID for. |
- * @return {number} The unique ID for the item. |
- */ |
function getUid(item) { |
if (item.hasOwnProperty('uid')) |
return item.uid; |
return item.uid = createUid(); |
} |
- /** |
- * Dispatches a simple event on an event target. |
- * @param {!EventTarget} target The event target to dispatch the event on. |
- * @param {string} type The type of the event. |
- * @param {boolean=} opt_bubbles Whether the event bubbles or not. |
- * @param {boolean=} opt_cancelable Whether the default action of the event |
- * can be prevented. Default is true. |
- * @return {boolean} If any of the listeners called {@code preventDefault} |
- * during the dispatch this will return false. |
- */ |
function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) { |
var e = new Event(type, { |
bubbles: opt_bubbles, |
@@ -303,32 +182,10 @@ var cr = cr || function() { |
return target.dispatchEvent(e); |
} |
- /** |
- * Calls |fun| and adds all the fields of the returned object to the object |
- * named by |name|. For example, cr.define('cr.ui', function() { |
- * function List() { |
- * ... |
- * } |
- * function ListItem() { |
- * ... |
- * } |
- * return { |
- * List: List, |
- * ListItem: ListItem, |
- * }; |
- * }); |
- * defines the functions cr.ui.List and cr.ui.ListItem. |
- * @param {string} name The name of the object that we are adding fields to. |
- * @param {!Function} fun The function that will return an object containing |
- * the names and values of the new fields. |
- */ |
function define(name, fun) { |
var obj = exportPath(name); |
var exports = fun(); |
for (var propertyName in exports) { |
- // Maybe we should check the prototype chain here? The current usage |
- // pattern is always using an object literal so we only care about own |
- // properties. |
var propertyDescriptor = Object.getOwnPropertyDescriptor(exports, |
propertyName); |
if (propertyDescriptor) |
@@ -336,26 +193,12 @@ var cr = cr || function() { |
} |
} |
- /** |
- * Adds a {@code getInstance} static method that always return the same |
- * instance object. |
- * @param {!Function} ctor The constructor for the class to add the static |
- * method to. |
- */ |
function addSingletonGetter(ctor) { |
ctor.getInstance = function() { |
return ctor.instance_ || (ctor.instance_ = new ctor()); |
}; |
} |
- /** |
- * Forwards public APIs to private implementations. |
- * @param {Function} ctor Constructor that have private implementations in its |
- * prototype. |
- * @param {Array<string>} methods List of public method names that have their |
- * underscored counterparts in constructor's prototype. |
- * @param {string=} opt_target Selector for target node. |
- */ |
function makePublic(ctor, methods, opt_target) { |
methods.forEach(function(method) { |
ctor[method] = function() { |
@@ -366,28 +209,8 @@ var cr = cr || function() { |
}); |
} |
- /** |
- * The mapping used by the sendWithPromise mechanism to tie the Promise |
- * returned to callers with the corresponding WebUI response. The mapping is |
- * from ID to the PromiseResolver helper; the ID is generated by |
- * sendWithPromise and is unique across all invocations of said method. |
- * @type {!Object<!PromiseResolver>} |
- */ |
var chromeSendResolverMap = {}; |
- /** |
- * The named method the WebUI handler calls directly in response to a |
- * chrome.send call that expects a response. The handler requires no knowledge |
- * of the specific name of this method, as the name is passed to the handler |
- * as the first argument in the arguments list of chrome.send. The handler |
- * must pass the ID, also sent via the chrome.send arguments list, as the |
- * first argument of the JS invocation; additionally, the handler may |
- * supply any number of other arguments that will be included in the response. |
- * @param {string} id The unique ID identifying the Promise this response is |
- * tied to. |
- * @param {boolean} isSuccess Whether the request was successful. |
- * @param {*} response The response as sent from C++. |
- */ |
function webUIResponse(id, isSuccess, response) { |
var resolver = chromeSendResolverMap[id]; |
delete chromeSendResolverMap[id]; |
@@ -398,14 +221,6 @@ var cr = cr || function() { |
resolver.reject(response); |
} |
- /** |
- * A variation of chrome.send, suitable for messages that expect a single |
- * response from C++. |
- * @param {string} methodName The name of the WebUI handler API. |
- * @param {...*} var_args Varibale number of arguments to be forwarded to the |
- * C++ call. |
- * @return {!Promise} |
- */ |
function sendWithPromise(methodName, var_args) { |
var args = Array.prototype.slice.call(arguments, 1); |
var promiseResolver = new PromiseResolver(); |
@@ -415,28 +230,11 @@ var cr = cr || function() { |
return promiseResolver.promise; |
} |
- /** |
- * 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 = {}; |
- /** |
- * The named method the WebUI handler calls directly when an event occurs. |
- * The WebUI handler must supply the name of the event as the first argument |
- * 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_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; |
} |
@@ -446,14 +244,6 @@ var cr = cr || function() { |
} |
} |
- /** |
- * Registers a listener for an event fired from WebUI handlers. Any number of |
- * listeners may register for a single event. |
- * @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(eventName, callback) { |
webUIListenerMap[eventName] = webUIListenerMap[eventName] || {}; |
var uid = createUid(); |
@@ -461,13 +251,6 @@ var cr = cr || function() { |
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]; |
@@ -490,7 +273,6 @@ var cr = cr || function() { |
makePublic: makePublic, |
PropertyKind: PropertyKind, |
- // C++ <-> JS communication related methods. |
addWebUIListener: addWebUIListener, |
removeWebUIListener: removeWebUIListener, |
sendWithPromise: sendWithPromise, |
@@ -538,14 +320,6 @@ var cr = cr || function() { |
cr.define('cr.ui', function() { |
- /** |
- * Decorates elements as an instance of a class. |
- * @param {string|!Element} source The way to find the element(s) to decorate. |
- * If this is a string then {@code querySeletorAll} is used to find the |
- * elements to decorate. |
- * @param {!Function} constr The constructor to decorate with. The constr |
- * needs to have a {@code decorate} function. |
- */ |
function decorate(source, constr) { |
var elements; |
if (typeof source == 'string') |
@@ -559,11 +333,7 @@ cr.define('cr.ui', function() { |
} |
} |
- /** |
- * Helper function for creating new element for define. |
- */ |
function createElementHelper(tagName, opt_bag) { |
- // Allow passing in ownerDocument to create in a different document. |
var doc; |
if (opt_bag && opt_bag.ownerDocument) |
doc = opt_bag.ownerDocument; |
@@ -572,28 +342,6 @@ cr.define('cr.ui', function() { |
return doc.createElement(tagName); |
} |
- /** |
- * Creates the constructor for a UI element class. |
- * |
- * Usage: |
- * <pre> |
- * var List = cr.ui.define('list'); |
- * List.prototype = { |
- * __proto__: HTMLUListElement.prototype, |
- * decorate: function() { |
- * ... |
- * }, |
- * ... |
- * }; |
- * </pre> |
- * |
- * @param {string|Function} tagNameOrFunction The tagName or |
- * function to use for newly created elements. If this is a function it |
- * needs to return a new element when called. |
- * @return {function(Object=):Element} The constructor function which takes |
- * an optional property bag. The function also has a static |
- * {@code decorate} method added to it. |
- */ |
function define(tagNameOrFunction) { |
var createFunction, tagName; |
if (typeof tagNameOrFunction == 'function') { |
@@ -604,14 +352,6 @@ cr.define('cr.ui', function() { |
tagName = tagNameOrFunction; |
} |
- /** |
- * Creates a new UI element constructor. |
- * @param {Object=} opt_propertyBag Optional bag of properties to set on the |
- * object after created. The property {@code ownerDocument} is special |
- * cased and it allows you to create the element in a different |
- * document than the default. |
- * @constructor |
- */ |
function f(opt_propertyBag) { |
var el = createFunction(tagName, opt_propertyBag); |
f.decorate(el); |
@@ -621,10 +361,6 @@ cr.define('cr.ui', function() { |
return el; |
} |
- /** |
- * Decorates an element as a UI element class. |
- * @param {!Element} el The element to decorate. |
- */ |
f.decorate = function(el) { |
el.__proto__ = f.prototype; |
el.decorate(); |
@@ -633,18 +369,7 @@ cr.define('cr.ui', function() { |
return f; |
} |
- /** |
- * Input elements do not grow and shrink with their content. This is a simple |
- * (and not very efficient) way of handling shrinking to content with support |
- * for min width and limited by the width of the parent element. |
- * @param {!HTMLElement} el The element to limit the width for. |
- * @param {!HTMLElement} parentEl The parent element that should limit the |
- * size. |
- * @param {number} min The minimum width. |
- * @param {number=} opt_scale Optional scale factor to apply to the width. |
- */ |
function limitInputWidth(el, parentEl, min, opt_scale) { |
- // Needs a size larger than borders |
el.style.width = '10px'; |
var doc = el.ownerDocument; |
var win = doc.defaultView; |
@@ -652,20 +377,16 @@ cr.define('cr.ui', function() { |
var parentComputedStyle = win.getComputedStyle(parentEl); |
var rtl = computedStyle.direction == 'rtl'; |
- // To get the max width we get the width of the treeItem minus the position |
- // of the input. |
var inputRect = el.getBoundingClientRect(); // box-sizing |
var parentRect = parentEl.getBoundingClientRect(); |
var startPos = rtl ? parentRect.right - inputRect.right : |
inputRect.left - parentRect.left; |
- // Add up border and padding of the input. |
var inner = parseInt(computedStyle.borderLeftWidth, 10) + |
parseInt(computedStyle.paddingLeft, 10) + |
parseInt(computedStyle.paddingRight, 10) + |
parseInt(computedStyle.borderRightWidth, 10); |
- // We also need to subtract the padding of parent to prevent it to overflow. |
var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) : |
parseInt(parentComputedStyle.paddingRight, 10); |
@@ -691,24 +412,12 @@ cr.define('cr.ui', function() { |
limit(); |
} |
- /** |
- * Takes a number and spits out a value CSS will be happy with. To avoid |
- * subpixel layout issues, the value is rounded to the nearest integral value. |
- * @param {number} pixels The number of pixels. |
- * @return {string} e.g. '16px'. |
- */ |
function toCssPx(pixels) { |
if (!window.isFinite(pixels)) |
console.error('Pixel value is not a number: ' + pixels); |
return Math.round(pixels) + 'px'; |
} |
- /** |
- * Users complain they occasionaly use doubleclicks instead of clicks |
- * (http://crbug.com/140364). To fix it we freeze click handling for |
- * the doubleclick time interval. |
- * @param {MouseEvent} e Initial click event. |
- */ |
function swallowDoubleClick(e) { |
var doc = e.target.ownerDocument; |
var counter = Math.min(1, e.detail); |
@@ -719,17 +428,12 @@ cr.define('cr.ui', function() { |
function onclick(e) { |
if (e.detail > counter) { |
counter = e.detail; |
- // Swallow the click since it's a click inside the doubleclick timeout. |
swallow(e); |
} else { |
- // Stop tracking clicks and let regular handling. |
doc.removeEventListener('dblclick', swallow, true); |
doc.removeEventListener('click', onclick, true); |
} |
} |
- // The following 'click' event (if e.type == 'mouseup') mustn't be taken |
- // into account (it mustn't stop tracking clicks). Start event listening |
- // after zero timeout. |
setTimeout(function() { |
doc.addEventListener('click', onclick, true); |
doc.addEventListener('dblclick', swallow, true); |
@@ -748,27 +452,9 @@ cr.define('cr.ui', function() { |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * @fileoverview A command is an abstraction of an action a user can do in the |
- * UI. |
- * |
- * When the focus changes in the document for each command a canExecute event |
- * is dispatched on the active element. By listening to this event you can |
- * enable and disable the command by setting the event.canExecute property. |
- * |
- * When a command is executed a command event is dispatched on the active |
- * element. Note that you should stop the propagation after you have handled the |
- * command if there might be other command listeners higher up in the DOM tree. |
- */ |
cr.define('cr.ui', function() { |
- /** |
- * This is used to identify keyboard shortcuts. |
- * @param {string} shortcut The text used to describe the keys for this |
- * keyboard shortcut. |
- * @constructor |
- */ |
function KeyboardShortcut(shortcut) { |
var mods = {}; |
var ident = ''; |
@@ -793,14 +479,8 @@ cr.define('cr.ui', function() { |
} |
KeyboardShortcut.prototype = { |
- /** |
- * Whether the keyboard shortcut object matches a keyboard event. |
- * @param {!Event} e The keyboard event object. |
- * @return {boolean} Whether we found a match or not. |
- */ |
matchesEvent: function(e) { |
if (e.key == this.ident_) { |
- // All keyboard modifiers needs to match. |
var mods = this.mods_; |
return ['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].every(function(k) { |
return e[k] == !!mods[k]; |
@@ -810,19 +490,11 @@ cr.define('cr.ui', function() { |
} |
}; |
- /** |
- * Creates a new command element. |
- * @constructor |
- * @extends {HTMLElement} |
- */ |
var Command = cr.ui.define('command'); |
Command.prototype = { |
__proto__: HTMLElement.prototype, |
- /** |
- * Initializes the command. |
- */ |
decorate: function() { |
CommandManager.init(assert(this.ownerDocument)); |
@@ -830,12 +502,6 @@ cr.define('cr.ui', function() { |
this.shortcut = this.getAttribute('shortcut'); |
}, |
- /** |
- * Executes the command by dispatching a command event on the given element. |
- * If |element| isn't given, the active element is used instead. |
- * If the command is {@code disabled} this does nothing. |
- * @param {HTMLElement=} opt_element Optional element to dispatch event on. |
- */ |
execute: function(opt_element) { |
if (this.disabled) |
return; |
@@ -848,32 +514,11 @@ cr.define('cr.ui', function() { |
} |
}, |
- /** |
- * Call this when there have been changes that might change whether the |
- * command can be executed or not. |
- * @param {Node=} opt_node Node for which to actuate command state. |
- */ |
canExecuteChange: function(opt_node) { |
dispatchCanExecuteEvent(this, |
opt_node || this.ownerDocument.activeElement); |
}, |
- /** |
- * The keyboard shortcut that triggers the command. This is a string |
- * consisting of a key (as reported by WebKit in keydown) as |
- * well as optional key modifiers joinded with a '|'. |
- * |
- * Multiple keyboard shortcuts can be provided by separating them by |
- * whitespace. |
- * |
- * For example: |
- * "F1" |
- * "Backspace|Meta" for Apple command backspace. |
- * "a|Ctrl" for Control A |
- * "Delete Backspace|Meta" for Delete and Command Backspace |
- * |
- * @type {string} |
- */ |
shortcut_: '', |
get shortcut() { |
return this.shortcut_; |
@@ -885,18 +530,12 @@ cr.define('cr.ui', function() { |
return new KeyboardShortcut(shortcut); |
}); |
- // Set this after the keyboardShortcuts_ since that might throw. |
this.shortcut_ = shortcut; |
cr.dispatchPropertyChange(this, 'shortcut', this.shortcut_, |
oldShortcut); |
} |
}, |
- /** |
- * Whether the event object matches the shortcut for this command. |
- * @param {!Event} e The key event object. |
- * @return {boolean} Whether it matched or not. |
- */ |
matchesEvent: function(e) { |
if (!this.keyboardShortcuts_) |
return false; |
@@ -907,68 +546,29 @@ cr.define('cr.ui', function() { |
}, |
}; |
- /** |
- * The label of the command. |
- */ |
cr.defineProperty(Command, 'label', cr.PropertyKind.ATTR); |
- /** |
- * Whether the command is disabled or not. |
- */ |
cr.defineProperty(Command, 'disabled', cr.PropertyKind.BOOL_ATTR); |
- /** |
- * Whether the command is hidden or not. |
- */ |
cr.defineProperty(Command, 'hidden', cr.PropertyKind.BOOL_ATTR); |
- /** |
- * Whether the command is checked or not. |
- */ |
cr.defineProperty(Command, 'checked', cr.PropertyKind.BOOL_ATTR); |
- /** |
- * The flag that prevents the shortcut text from being displayed on menu. |
- * |
- * If false, the keyboard shortcut text (eg. "Ctrl+X" for the cut command) |
- * is displayed in menu when the command is assosiated with a menu item. |
- * Otherwise, no text is displayed. |
- */ |
cr.defineProperty(Command, 'hideShortcutText', cr.PropertyKind.BOOL_ATTR); |
- /** |
- * Dispatches a canExecute event on the target. |
- * @param {!cr.ui.Command} command The command that we are testing for. |
- * @param {EventTarget} target The target element to dispatch the event on. |
- */ |
function dispatchCanExecuteEvent(command, target) { |
var e = new CanExecuteEvent(command); |
target.dispatchEvent(e); |
command.disabled = !e.canExecute; |
} |
- /** |
- * The command managers for different documents. |
- */ |
var commandManagers = {}; |
- /** |
- * Keeps track of the focused element and updates the commands when the focus |
- * changes. |
- * @param {!Document} doc The document that we are managing the commands for. |
- * @constructor |
- */ |
function CommandManager(doc) { |
doc.addEventListener('focus', this.handleFocus_.bind(this), true); |
- // Make sure we add the listener to the bubbling phase so that elements can |
- // prevent the command. |
doc.addEventListener('keydown', this.handleKeyDown_.bind(this), false); |
} |
- /** |
- * Initializes a command manager for the document as needed. |
- * @param {!Document} doc The document to manage the commands for. |
- */ |
CommandManager.init = function(doc) { |
var uid = cr.getUid(doc); |
if (!(uid in commandManagers)) { |
@@ -978,17 +578,9 @@ cr.define('cr.ui', function() { |
CommandManager.prototype = { |
- /** |
- * Handles focus changes on the document. |
- * @param {Event} e The focus event object. |
- * @private |
- * @suppress {checkTypes} |
- * TODO(vitalyp): remove the suppression. |
- */ |
handleFocus_: function(e) { |
var target = e.target; |
- // Ignore focus on a menu button or command item. |
if (target.menu || target.command) |
return; |
@@ -1000,10 +592,6 @@ cr.define('cr.ui', function() { |
}); |
}, |
- /** |
- * Handles the keydown event and routes it to the right command. |
- * @param {!Event} e The keydown event. |
- */ |
handleKeyDown_: function(e) { |
var target = e.target; |
var commands = Array.prototype.slice.call( |
@@ -1011,14 +599,10 @@ cr.define('cr.ui', function() { |
for (var i = 0, command; command = commands[i]; i++) { |
if (command.matchesEvent(e)) { |
- // When invoking a command via a shortcut, we have to manually check |
- // if it can be executed, since focus might not have been changed |
- // what would have updated the command's state. |
command.canExecuteChange(); |
if (!command.disabled) { |
e.preventDefault(); |
- // We do not want any other element to handle this. |
e.stopPropagation(); |
command.execute(); |
return; |
@@ -1028,13 +612,6 @@ cr.define('cr.ui', function() { |
} |
}; |
- /** |
- * The event type used for canExecute events. |
- * @param {!cr.ui.Command} command The command that we are evaluating. |
- * @extends {Event} |
- * @constructor |
- * @class |
- */ |
function CanExecuteEvent(command) { |
var e = new Event('canExecute', {bubbles: true, cancelable: true}); |
e.__proto__ = CanExecuteEvent.prototype; |
@@ -1045,18 +622,8 @@ cr.define('cr.ui', function() { |
CanExecuteEvent.prototype = { |
__proto__: Event.prototype, |
- /** |
- * The current command |
- * @type {cr.ui.Command} |
- */ |
command: null, |
- /** |
- * Whether the target can execute the command. Setting this also stops the |
- * propagation and prevents the default. Callers can tell if an event has |
- * been handled via |this.defaultPrevented|. |
- * @type {boolean} |
- */ |
canExecute_: false, |
get canExecute() { |
return this.canExecute_; |
@@ -1068,7 +635,6 @@ cr.define('cr.ui', function() { |
} |
}; |
- // Export |
return { |
Command: Command, |
CanExecuteEvent: CanExecuteEvent |
@@ -1078,9 +644,6 @@ Polymer({ |
is: 'app-drawer', |
properties: { |
- /** |
- * The opened state of the drawer. |
- */ |
opened: { |
type: Boolean, |
value: false, |
@@ -1088,28 +651,17 @@ Polymer({ |
reflectToAttribute: true |
}, |
- /** |
- * The drawer does not have a scrim and cannot be swiped close. |
- */ |
persistent: { |
type: Boolean, |
value: false, |
reflectToAttribute: true |
}, |
- /** |
- * The alignment of the drawer on the screen ('left', 'right', 'start' or 'end'). |
- * 'start' computes to left and 'end' to right in LTR layout and vice versa in RTL |
- * layout. |
- */ |
align: { |
type: String, |
value: 'left' |
}, |
- /** |
- * The computed, read-only position of the drawer on the screen ('left' or 'right'). |
- */ |
position: { |
type: String, |
readOnly: true, |
@@ -1117,18 +669,12 @@ Polymer({ |
reflectToAttribute: true |
}, |
- /** |
- * Create an area at the edge of the screen to swipe open the drawer. |
- */ |
swipeOpen: { |
type: Boolean, |
value: false, |
reflectToAttribute: true |
}, |
- /** |
- * Trap keyboard focus when the drawer is opened and not persistent. |
- */ |
noFocusTrap: { |
type: Boolean, |
value: false |
@@ -1153,17 +699,12 @@ Polymer({ |
_lastTabStop: null, |
ready: function() { |
- // Set the scroll direction so you can vertically scroll inside the drawer. |
this.setScrollDirection('y'); |
- // Only transition the drawer after its first render (e.g. app-drawer-layout |
- // may need to set the initial opened state which should not be transitioned). |
this._setTransitionDuration('0s'); |
}, |
attached: function() { |
- // Only transition the drawer after its first render (e.g. app-drawer-layout |
- // may need to set the initial opened state which should not be transitioned). |
Polymer.RenderStatus.afterNextRender(this, function() { |
this._setTransitionDuration(''); |
this._boundEscKeydownHandler = this._escKeydownHandler.bind(this); |
@@ -1179,43 +720,22 @@ Polymer({ |
document.removeEventListener('keydown', this._boundEscKeydownHandler); |
}, |
- /** |
- * Opens the drawer. |
- */ |
open: function() { |
this.opened = true; |
}, |
- /** |
- * Closes the drawer. |
- */ |
close: function() { |
this.opened = false; |
}, |
- /** |
- * Toggles the drawer open and close. |
- */ |
toggle: function() { |
this.opened = !this.opened; |
}, |
- /** |
- * Gets the width of the drawer. |
- * |
- * @return {number} The width of the drawer in pixels. |
- */ |
getWidth: function() { |
return this.$.contentContainer.offsetWidth; |
}, |
- /** |
- * Resets the layout. If you changed the size of app-header via CSS |
- * you can notify the changes by either firing the `iron-resize` event |
- * or calling `resetLayout` directly. |
- * |
- * @method resetLayout |
- */ |
resetLayout: function() { |
this.debounce('_resetLayout', function() { |
this.fire('app-drawer-reset-layout'); |
@@ -1241,7 +761,6 @@ Polymer({ |
_escKeydownHandler: function(event) { |
var ESC_KEYCODE = 27; |
if (event.keyCode === ESC_KEYCODE) { |
- // Prevent any side effects if app-drawer closes. |
event.preventDefault(); |
this.close(); |
} |
@@ -1252,7 +771,6 @@ Polymer({ |
return; |
} |
- // Disable user selection on desktop. |
event.preventDefault(); |
switch (event.detail.state) { |
@@ -1271,7 +789,6 @@ Polymer({ |
_trackStart: function(event) { |
this._drawerState = this._DRAWER_STATE.TRACKING; |
- // Disable transitions since style attributes will reflect user track events. |
this._setTransitionDuration('0s'); |
this.style.visibility = 'visible'; |
@@ -1288,8 +805,6 @@ Polymer({ |
_trackMove: function(event) { |
this._translateDrawer(event.detail.dx + this._translateOffset); |
- // Use Date.now() since event.timeStamp is inconsistent across browsers (e.g. most |
- // browsers use milliseconds but FF 44 uses microseconds). |
this._trackDetails.push({ |
dx: event.detail.dx, |
timeStamp: Date.now() |
@@ -1304,7 +819,6 @@ Polymer({ |
(x <= 0 || x >= drawerWidth); |
if (!isInEndState) { |
- // No longer need the track events after this method returns - allow them to be GC'd. |
var trackDetails = this._trackDetails; |
this._trackDetails = null; |
@@ -1314,8 +828,6 @@ Polymer({ |
} |
} |
- // If the drawer is not flinging, toggle the opened state based on the position of |
- // the drawer. |
var halfWidth = drawerWidth / 2; |
if (event.detail.dx < -halfWidth) { |
this.opened = this.position === 'right'; |
@@ -1323,7 +835,6 @@ Polymer({ |
this.opened = this.position === 'left'; |
} |
- // Trigger app-drawer-transitioned now since there will be no transitionend event. |
if (isInEndState) { |
this._resetDrawerState(); |
} |
@@ -1334,7 +845,6 @@ Polymer({ |
}, |
_calculateVelocity: function(event, trackDetails) { |
- // Find the oldest track event that is within 100ms using binary search. |
var now = Date.now(); |
var timeLowerBound = now - 100; |
var trackDetail; |
@@ -1342,7 +852,6 @@ Polymer({ |
var max = trackDetails.length - 1; |
while (min <= max) { |
- // Floor of average of min and max. |
var mid = (min + max) >> 1; |
var d = trackDetails[mid]; |
if (d.timeStamp >= timeLowerBound) { |
@@ -1364,7 +873,6 @@ Polymer({ |
_flingDrawer: function(event, trackDetails) { |
var velocity = this._calculateVelocity(event, trackDetails); |
- // Do not fling if velocity is not above a threshold. |
if (Math.abs(velocity) < this._MIN_FLING_THRESHOLD) { |
return; |
} |
@@ -1386,7 +894,6 @@ Polymer({ |
dx = -x; |
} |
- // Enforce a minimum transition velocity to make the drawer feel snappy. |
if (isVelocityPositive) { |
velocity = Math.max(velocity, this._MIN_TRANSITION_VELOCITY); |
this.opened = this.position === 'left'; |
@@ -1395,8 +902,6 @@ Polymer({ |
this.opened = this.position === 'right'; |
} |
- // Calculate the amount of time needed to finish the transition based on the |
- // initial slope of the timing function. |
this._setTransitionDuration((this._FLING_INITIAL_SLOPE * dx / velocity) + 'ms'); |
this._setTransitionTimingFunction(this._FLING_TIMING_FUNCTION); |
@@ -1404,13 +909,9 @@ Polymer({ |
}, |
_transitionend: function(event) { |
- // contentContainer will transition on opened state changed, and scrim will |
- // transition on persistent state changed when opened - these are the |
- // transitions we are interested in. |
var target = Polymer.dom(event).rootTarget; |
if (target === this.$.contentContainer || target === this.$.scrim) { |
- // If the drawer was flinging, we need to reset the style attributes. |
if (this._drawerState === this._DRAWER_STATE.FLINGING) { |
this._setTransitionDuration(''); |
this._setTransitionTimingFunction(''); |
@@ -1469,7 +970,6 @@ Polymer({ |
document.body.style.overflow = ''; |
} |
- // Don't fire the event on initial load. |
if (oldState !== this._DRAWER_STATE.INIT) { |
this.fire('app-drawer-transitioned'); |
} |
@@ -1481,8 +981,6 @@ Polymer({ |
return; |
} |
- // NOTE: Unless we use /deep/ (which we shouldn't since it's deprecated), this will |
- // not select focusable elements inside shadow roots. |
var focusableElementsSelector = [ |
'a[href]:not([tabindex="-1"])', |
'area[href]:not([tabindex="-1"])', |
@@ -1500,14 +998,10 @@ Polymer({ |
this._firstTabStop = focusableElements[0]; |
this._lastTabStop = focusableElements[focusableElements.length - 1]; |
} else { |
- // Reset saved tab stops when there are no focusable elements in the drawer. |
this._firstTabStop = null; |
this._lastTabStop = null; |
} |
- // Focus on app-drawer if it has non-zero tabindex. Otherwise, focus the first focusable |
- // element in the drawer, if it exists. Use the tabindex attribute since the this.tabIndex |
- // property in IE/Edge returns 0 (instead of -1) when the attribute is not set. |
var tabindex = this.getAttribute('tabindex'); |
if (tabindex && parseInt(tabindex, 10) > -1) { |
this.focus(); |
@@ -1554,17 +1048,7 @@ Polymer({ |
FLINGING: 5 |
} |
- /** |
- * Fired when the layout of app-drawer has changed. |
- * |
- * @event app-drawer-reset-layout |
- */ |
- /** |
- * Fired when app-drawer has finished transitioning. |
- * |
- * @event app-drawer-transitioned |
- */ |
}); |
(function() { |
'use strict'; |
@@ -1572,9 +1056,6 @@ Polymer({ |
Polymer({ |
is: 'iron-location', |
properties: { |
- /** |
- * The pathname component of the URL. |
- */ |
path: { |
type: String, |
notify: true, |
@@ -1582,9 +1063,6 @@ Polymer({ |
return window.decodeURIComponent(window.location.pathname); |
} |
}, |
- /** |
- * The query string portion of the URL. |
- */ |
query: { |
type: String, |
notify: true, |
@@ -1592,9 +1070,6 @@ Polymer({ |
return window.decodeURIComponent(window.location.search.slice(1)); |
} |
}, |
- /** |
- * The hash component of the URL. |
- */ |
hash: { |
type: String, |
notify: true, |
@@ -1602,42 +1077,16 @@ Polymer({ |
return window.decodeURIComponent(window.location.hash.slice(1)); |
} |
}, |
- /** |
- * If the user was on a URL for less than `dwellTime` milliseconds, it |
- * won't be added to the browser's history, but instead will be replaced |
- * by the next entry. |
- * |
- * This is to prevent large numbers of entries from clogging up the user's |
- * browser history. Disable by setting to a negative number. |
- */ |
dwellTime: { |
type: Number, |
value: 2000 |
}, |
- /** |
- * A regexp that defines the set of URLs that should be considered part |
- * of this web app. |
- * |
- * Clicking on a link that matches this regex won't result in a full page |
- * navigation, but will instead just update the URL state in place. |
- * |
- * This regexp is given everything after the origin in an absolute |
- * URL. So to match just URLs that start with /search/ do: |
- * url-space-regex="^/search/" |
- * |
- * @type {string|RegExp} |
- */ |
urlSpaceRegex: { |
type: String, |
value: '' |
}, |
- /** |
- * urlSpaceRegex, but coerced into a regexp. |
- * |
- * @type {RegExp} |
- */ |
_urlSpaceRegExp: { |
computed: '_makeRegExp(urlSpaceRegex)' |
}, |
@@ -1662,8 +1111,6 @@ Polymer({ |
this.listen(window, 'location-changed', '_urlChanged'); |
this.listen(window, 'popstate', '_urlChanged'); |
this.listen(/** @type {!HTMLBodyElement} */(document.body), 'click', '_globalOnClick'); |
- // Give a 200ms grace period to make initial redirects without any |
- // additions to the user's history. |
this._lastChangedAt = window.performance.now() - (this.dwellTime - 200); |
this._initialized = true; |
@@ -1680,11 +1127,6 @@ Polymer({ |
this.hash = window.decodeURIComponent(window.location.hash.substring(1)); |
}, |
_urlChanged: function() { |
- // We want to extract all info out of the updated URL before we |
- // try to write anything back into it. |
- // |
- // i.e. without _dontUpdateUrl we'd overwrite the new path with the old |
- // one when we set this.hash. Likewise for query. |
this._dontUpdateUrl = true; |
this._hashChanged(); |
this.path = window.decodeURIComponent(window.location.pathname); |
@@ -1717,11 +1159,9 @@ Polymer({ |
window.location.search.substring(1)) && |
this.hash === window.decodeURIComponent( |
window.location.hash.substring(1))) { |
- // Nothing to do, the current URL is a representation of our properties. |
return; |
} |
var newUrl = this._getUrl(); |
- // Need to use a full URL in case the containing page has a base URI. |
var fullNewUrl = new URL( |
newUrl, window.location.protocol + '//' + window.location.host).href; |
var now = window.performance.now(); |
@@ -1735,16 +1175,7 @@ Polymer({ |
} |
this.fire('location-changed', {}, {node: window}); |
}, |
- /** |
- * A necessary evil so that links work as expected. Does its best to |
- * bail out early if possible. |
- * |
- * @param {MouseEvent} event . |
- */ |
_globalOnClick: function(event) { |
- // If another event handler has stopped this event then there's nothing |
- // for us to do. This can happen e.g. when there are multiple |
- // iron-location elements in a page. |
if (event.defaultPrevented) { |
return; |
} |
@@ -1753,29 +1184,16 @@ Polymer({ |
return; |
} |
event.preventDefault(); |
- // If the navigation is to the current page we shouldn't add a history |
- // entry or fire a change event. |
if (href === window.location.href) { |
return; |
} |
window.history.pushState({}, '', href); |
this.fire('location-changed', {}, {node: window}); |
}, |
- /** |
- * Returns the absolute URL of the link (if any) that this click event |
- * is clicking on, if we can and should override the resulting full |
- * page navigation. Returns null otherwise. |
- * |
- * @param {MouseEvent} event . |
- * @return {string?} . |
- */ |
_getSameOriginLinkHref: function(event) { |
- // We only care about left-clicks. |
if (event.button !== 0) { |
return null; |
} |
- // We don't want modified clicks, where the intent is to open the page |
- // in a new tab. |
if (event.metaKey || event.ctrlKey) { |
return null; |
} |
@@ -1789,16 +1207,13 @@ Polymer({ |
} |
} |
- // If there's no link there's nothing to do. |
if (!anchor) { |
return null; |
} |
- // Target blank is a new tab, don't intercept. |
if (anchor.target === '_blank') { |
return null; |
} |
- // If the link is for an existing parent frame, don't intercept. |
if ((anchor.target === '_top' || |
anchor.target === '_parent') && |
window.top !== window) { |
@@ -1807,8 +1222,6 @@ Polymer({ |
var href = anchor.href; |
- // It only makes sense for us to intercept same-origin navigations. |
- // pushState/replaceState don't work with cross-origin links. |
var url; |
if (document.baseURI != null) { |
url = new URL(href, /** @type {string} */(document.baseURI)); |
@@ -1818,7 +1231,6 @@ Polymer({ |
var origin; |
- // IE Polyfill |
if (window.location.origin) { |
origin = window.location.origin; |
} else { |
@@ -1834,12 +1246,10 @@ Polymer({ |
} |
var normalizedHref = url.pathname + url.search + url.hash; |
- // If we've been configured not to handle this url... don't handle it! |
if (this._urlSpaceRegExp && |
!this._urlSpaceRegExp.test(normalizedHref)) { |
return null; |
} |
- // Need to use a full URL in case the containing page has a base URI. |
var fullNormalizedHref = new URL( |
normalizedHref, window.location.href).href; |
return fullNormalizedHref; |
@@ -1907,8 +1317,6 @@ Polymer({ |
_decodeParams: function(paramString) { |
var params = {}; |
- // Work around a bug in decodeURIComponent where + is not |
- // converted to spaces: |
paramString = (paramString || '').replace(/\+/g, '%20'); |
var paramList = paramString.split('&'); |
@@ -1924,46 +1332,18 @@ Polymer({ |
}); |
'use strict'; |
- /** |
- * Provides bidirectional mapping between `path` and `queryParams` and a |
- * app-route compatible `route` object. |
- * |
- * For more information, see the docs for `app-route-converter`. |
- * |
- * @polymerBehavior |
- */ |
Polymer.AppRouteConverterBehavior = { |
properties: { |
- /** |
- * A model representing the deserialized path through the route tree, as |
- * well as the current queryParams. |
- * |
- * A route object is the kernel of the routing system. It is intended to |
- * be fed into consuming elements such as `app-route`. |
- * |
- * @type {?Object} |
- */ |
route: { |
type: Object, |
notify: true |
}, |
- /** |
- * A set of key/value pairs that are universally accessible to branches of |
- * the route tree. |
- * |
- * @type {?Object} |
- */ |
queryParams: { |
type: Object, |
notify: true |
}, |
- /** |
- * The serialized path through the route tree. This corresponds to the |
- * `window.location.pathname` value, and will update to reflect changes |
- * to that value. |
- */ |
path: { |
type: String, |
notify: true, |
@@ -1981,9 +1361,6 @@ Polymer({ |
this.linkPaths('queryParams', 'route.__queryParams'); |
}, |
- /** |
- * Handler called when the path or queryParams change. |
- */ |
_locationChanged: function() { |
if (this.route && |
this.route.path === this.path && |
@@ -1997,9 +1374,6 @@ Polymer({ |
}; |
}, |
- /** |
- * Handler called when the route prefix and route path change. |
- */ |
_routeChanged: function() { |
if (!this.route) { |
return; |
@@ -2008,12 +1382,6 @@ Polymer({ |
this.path = this.route.prefix + this.route.path; |
}, |
- /** |
- * Handler called when the route queryParams change. |
- * |
- * @param {Object} queryParams A set of key/value pairs that are |
- * universally accessible to branches of the route tree. |
- */ |
_routeQueryParamsChanged: function(queryParams) { |
if (!this.route) { |
return; |
@@ -2027,82 +1395,37 @@ Polymer({ |
is: 'app-location', |
properties: { |
- /** |
- * A model representing the deserialized path through the route tree, as |
- * well as the current queryParams. |
- */ |
route: { |
type: Object, |
notify: true |
}, |
- /** |
- * In many scenarios, it is convenient to treat the `hash` as a stand-in |
- * alternative to the `path`. For example, if deploying an app to a static |
- * web server (e.g., Github Pages) - where one does not have control over |
- * server-side routing - it is usually a better experience to use the hash |
- * to represent paths through one's app. |
- * |
- * When this property is set to true, the `hash` will be used in place of |
- |
- * the `path` for generating a `route`. |
- */ |
useHashAsPath: { |
type: Boolean, |
value: false |
}, |
- /** |
- * A regexp that defines the set of URLs that should be considered part |
- * of this web app. |
- * |
- * Clicking on a link that matches this regex won't result in a full page |
- * navigation, but will instead just update the URL state in place. |
- * |
- * This regexp is given everything after the origin in an absolute |
- * URL. So to match just URLs that start with /search/ do: |
- * url-space-regex="^/search/" |
- * |
- * @type {string|RegExp} |
- */ |
urlSpaceRegex: { |
type: String, |
notify: true |
}, |
- /** |
- * A set of key/value pairs that are universally accessible to branches |
- * of the route tree. |
- */ |
__queryParams: { |
type: Object |
}, |
- /** |
- * The pathname component of the current URL. |
- */ |
__path: { |
type: String |
}, |
- /** |
- * The query string portion of the current URL. |
- */ |
__query: { |
type: String |
}, |
- /** |
- * The hash portion of the current URL. |
- */ |
__hash: { |
type: String |
}, |
- /** |
- * The route path, which will be either the hash or the path, depending |
- * on useHashAsPath. |
- */ |
path: { |
type: String, |
observer: '__onPathChanged' |
@@ -2137,39 +1460,21 @@ Polymer({ |
is: 'app-route', |
properties: { |
- /** |
- * The URL component managed by this element. |
- */ |
route: { |
type: Object, |
notify: true |
}, |
- /** |
- * The pattern of slash-separated segments to match `path` against. |
- * |
- * For example the pattern "/foo" will match "/foo" or "/foo/bar" |
- * but not "/foobar". |
- * |
- * Path segments like `/:named` are mapped to properties on the `data` object. |
- */ |
pattern: { |
type: String |
}, |
- /** |
- * The parameterized values that are extracted from the route as |
- * described by `pattern`. |
- */ |
data: { |
type: Object, |
value: function() {return {};}, |
notify: true |
}, |
- /** |
- * @type {?Object} |
- */ |
queryParams: { |
type: Object, |
value: function() { |
@@ -2178,9 +1483,6 @@ Polymer({ |
notify: true |
}, |
- /** |
- * The part of `path` NOT consumed by `pattern`. |
- */ |
tail: { |
type: Object, |
value: function() {return {path: null, prefix: null, __queryParams: null};}, |
@@ -2197,9 +1499,6 @@ Polymer({ |
type: Boolean, |
value: false |
}, |
- /** |
- * @type {?string} |
- */ |
_matched: { |
type: String, |
value: '' |
@@ -2220,10 +1519,6 @@ Polymer({ |
this.linkPaths('tail.__queryParams', 'route.__queryParams'); |
}, |
- /** |
- * Deal with the query params object being assigned to wholesale. |
- * @export |
- */ |
__routeQueryParamsChanged: function(queryParams) { |
if (queryParams && this.tail) { |
this.set('tail.__queryParams', queryParams); |
@@ -2232,8 +1527,6 @@ Polymer({ |
return; |
} |
- // Copy queryParams and track whether there are any differences compared |
- // to the existing query params. |
var copyOfQueryParams = {}; |
var anythingChanged = false; |
for (var key in queryParams) { |
@@ -2244,7 +1537,6 @@ Polymer({ |
anythingChanged = true; |
} |
} |
- // Need to check whether any keys were deleted |
for (var key in this.queryParams) { |
if (anythingChanged || !(key in queryParams)) { |
anythingChanged = true; |
@@ -2261,18 +1553,12 @@ Polymer({ |
} |
}, |
- /** |
- * @export |
- */ |
__tailQueryParamsChanged: function(queryParams) { |
if (queryParams && this.route) { |
this.set('route.__queryParams', queryParams); |
} |
}, |
- /** |
- * @export |
- */ |
__queryParamsChanged: function(changes) { |
if (!this.active || this._queryParamsUpdating) { |
return; |
@@ -2284,13 +1570,8 @@ Polymer({ |
__resetProperties: function() { |
this._setActive(false); |
this._matched = null; |
- //this.tail = { path: null, prefix: null, queryParams: null }; |
- //this.data = {}; |
}, |
- /** |
- * @export |
- */ |
__tryToMatch: function() { |
if (!this.route) { |
return; |
@@ -2319,7 +1600,6 @@ Polymer({ |
} |
var pathPiece = remainingPieces.shift(); |
- // We don't match this path. |
if (!pathPiece && pathPiece !== '') { |
this.__resetProperties(); |
return; |
@@ -2336,15 +1616,12 @@ Polymer({ |
this._matched = matched.join('/'); |
- // Properties that must be updated atomically. |
var propertyUpdates = {}; |
- //this.active |
if (!this.active) { |
propertyUpdates.active = true; |
} |
- // this.tail |
var tailPrefix = this.route.prefix + this._matched; |
var tailPath = remainingPieces.join('/'); |
if (remainingPieces.length > 0) { |
@@ -2360,7 +1637,6 @@ Polymer({ |
}; |
} |
- // this.data |
propertyUpdates.data = namedMatches; |
this._dataInUrl = {}; |
for (var key in namedMatches) { |
@@ -2370,9 +1646,6 @@ Polymer({ |
this.__setMulti(propertyUpdates); |
}, |
- /** |
- * @export |
- */ |
__tailPathChanged: function() { |
if (!this.active) { |
return; |
@@ -2388,9 +1661,6 @@ Polymer({ |
this.set('route.path', newPath); |
}, |
- /** |
- * @export |
- */ |
__updatePathOnDataChange: function() { |
if (!this.route || !this.active) { |
return; |
@@ -2429,17 +1699,6 @@ Polymer({ |
}, |
__setMulti: function(setObj) { |
- // HACK(rictic): skirting around 1.0's lack of a setMulti by poking at |
- // internal data structures. I would not advise that you copy this |
- // example. |
- // |
- // In the future this will be a feature of Polymer itself. |
- // See: https://github.com/Polymer/polymer/issues/3640 |
- // |
- // Hacking around with private methods like this is juggling footguns, |
- // and is likely to have unexpected and unsupported rough edges. |
- // |
- // Be ye so warned. |
for (var property in setObj) { |
this._propertySetter(property, setObj[property]); |
} |
@@ -2456,9 +1715,6 @@ Polymer({ |
properties: { |
- /** |
- * The Boolean return value of the media query. |
- */ |
queryMatches: { |
type: Boolean, |
value: false, |
@@ -2466,35 +1722,22 @@ Polymer({ |
notify: true |
}, |
- /** |
- * The CSS media query to evaluate. |
- */ |
query: { |
type: String, |
observer: 'queryChanged' |
}, |
- /** |
- * If true, the query attribute is assumed to be a complete media query |
- * string rather than a single media feature. |
- */ |
full: { |
type: Boolean, |
value: false |
}, |
- /** |
- * @type {function(MediaQueryList)} |
- */ |
_boundMQHandler: { |
value: function() { |
return this.queryHandler.bind(this); |
} |
}, |
- /** |
- * @type {MediaQueryList} |
- */ |
_mq: { |
value: null |
} |
@@ -2541,38 +1784,13 @@ Polymer({ |
} |
}); |
-/** |
- * `IronResizableBehavior` is a behavior that can be used in Polymer elements to |
- * coordinate the flow of resize events between "resizers" (elements that control the |
- * size or hidden state of their children) and "resizables" (elements that need to be |
- * notified when they are resized or un-hidden by their parents in order to take |
- * action on their new measurements). |
- * |
- * Elements that perform measurement should add the `IronResizableBehavior` behavior to |
- * their element definition and listen for the `iron-resize` event on themselves. |
- * This event will be fired when they become showing after having been hidden, |
- * when they are resized explicitly by another resizable, or when the window has been |
- * resized. |
- * |
- * Note, the `iron-resize` event is non-bubbling. |
- * |
- * @polymerBehavior Polymer.IronResizableBehavior |
- * @demo demo/index.html |
- **/ |
Polymer.IronResizableBehavior = { |
properties: { |
- /** |
- * The closest ancestor element that implements `IronResizableBehavior`. |
- */ |
_parentResizable: { |
type: Object, |
observer: '_parentResizableChanged' |
}, |
- /** |
- * True if this element is currently notifying its descedant elements of |
- * resize. |
- */ |
_notifyingDescendant: { |
type: Boolean, |
value: false |
@@ -2584,8 +1802,6 @@ Polymer({ |
}, |
created: function() { |
- // We don't really need property effects on these, and also we want them |
- // to be created before the `_parentResizable` observer fires: |
this._interestedResizables = []; |
this._boundNotifyResize = this.notifyResize.bind(this); |
}, |
@@ -2613,10 +1829,6 @@ Polymer({ |
this._parentResizable = null; |
}, |
- /** |
- * Can be called to manually notify a resizable and its descendant |
- * resizables of a resize change. |
- */ |
notifyResize: function() { |
if (!this.isAttached) { |
return; |
@@ -2631,18 +1843,10 @@ Polymer({ |
this._fireResize(); |
}, |
- /** |
- * Used to assign the closest resizable ancestor to this resizable |
- * if the ancestor detects a request for notifications. |
- */ |
assignParentResizable: function(parentResizable) { |
this._parentResizable = parentResizable; |
}, |
- /** |
- * Used to remove a resizable descendant from the list of descendants |
- * that should be notified of a resize change. |
- */ |
stopResizeNotificationsFor: function(target) { |
var index = this._interestedResizables.indexOf(target); |
@@ -2652,15 +1856,6 @@ Polymer({ |
} |
}, |
- /** |
- * This method can be overridden to filter nested elements that should or |
- * should not be notified by the current element. Return true if an element |
- * should be notified, or false if it should not be notified. |
- * |
- * @param {HTMLElement} element A candidate descendant element that |
- * implements `IronResizableBehavior`. |
- * @return {boolean} True if the `element` should be notified of resize. |
- */ |
resizerShouldNotify: function(element) { return true; }, |
_onDescendantIronResize: function(event) { |
@@ -2669,9 +1864,6 @@ Polymer({ |
return; |
} |
- // NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the |
- // otherwise non-bubbling event "just work." We do it manually here for |
- // the case where Polymer is not using shadow roots for whatever reason: |
if (!Polymer.Settings.useShadow) { |
this._fireResize(); |
} |
@@ -2709,9 +1901,6 @@ Polymer({ |
}, |
_notifyDescendant: function(descendant) { |
- // NOTE(cdata): In IE10, attached is fired on children first, so it's |
- // important not to notify them if the parent is not attached yet (or |
- // else they will get redundantly notified when the parent attaches). |
if (!this.isAttached) { |
return; |
} |
@@ -2721,10 +1910,6 @@ Polymer({ |
this._notifyingDescendant = false; |
} |
}; |
-/** |
- * @param {!Function} selectCallback |
- * @constructor |
- */ |
Polymer.IronSelection = function(selectCallback) { |
this.selection = []; |
this.selectCallback = selectCallback; |
@@ -2732,24 +1917,10 @@ Polymer({ |
Polymer.IronSelection.prototype = { |
- /** |
- * Retrieves the selected item(s). |
- * |
- * @method get |
- * @returns Returns the selected item(s). If the multi property is true, |
- * `get` will return an array, otherwise it will return |
- * the selected item or undefined if there is no selection. |
- */ |
get: function() { |
return this.multi ? this.selection.slice() : this.selection[0]; |
}, |
- /** |
- * Clears all the selection except the ones indicated. |
- * |
- * @method clear |
- * @param {Array} excludes items to be excluded. |
- */ |
clear: function(excludes) { |
this.selection.slice().forEach(function(item) { |
if (!excludes || excludes.indexOf(item) < 0) { |
@@ -2758,28 +1929,13 @@ Polymer({ |
}, this); |
}, |
- /** |
- * Indicates if a given item is selected. |
- * |
- * @method isSelected |
- * @param {*} item The item whose selection state should be checked. |
- * @returns Returns true if `item` is selected. |
- */ |
isSelected: function(item) { |
return this.selection.indexOf(item) >= 0; |
}, |
- /** |
- * Sets the selection state for a given item to either selected or deselected. |
- * |
- * @method setItemSelected |
- * @param {*} item The item to select. |
- * @param {boolean} isSelected True for selected, false for deselected. |
- */ |
setItemSelected: function(item, isSelected) { |
if (item != null) { |
if (isSelected !== this.isSelected(item)) { |
- // proceed to update selection only if requested state differs from current |
if (isSelected) { |
this.selection.push(item); |
} else { |
@@ -2795,14 +1951,6 @@ Polymer({ |
} |
}, |
- /** |
- * Sets the selection state for a given item. If the `multi` property |
- * is true, then the selected state of `item` will be toggled; otherwise |
- * the `item` will be selected. |
- * |
- * @method select |
- * @param {*} item The item to select. |
- */ |
select: function(item) { |
if (this.multi) { |
this.toggle(item); |
@@ -2812,12 +1960,6 @@ Polymer({ |
} |
}, |
- /** |
- * Toggles the selection state for `item`. |
- * |
- * @method toggle |
- * @param {*} item The item to toggle. |
- */ |
toggle: function(item) { |
this.setItemSelected(item, !this.isSelected(item)); |
} |
@@ -2826,116 +1968,51 @@ Polymer({ |
/** @polymerBehavior */ |
Polymer.IronSelectableBehavior = { |
- /** |
- * Fired when iron-selector is activated (selected or deselected). |
- * It is fired before the selected items are changed. |
- * Cancel the event to abort selection. |
- * |
- * @event iron-activate |
- */ |
- |
- /** |
- * Fired when an item is selected |
- * |
- * @event iron-select |
- */ |
- |
- /** |
- * Fired when an item is deselected |
- * |
- * @event iron-deselect |
- */ |
- |
- /** |
- * Fired when the list of selectable items changes (e.g., items are |
- * added or removed). The detail of the event is a mutation record that |
- * describes what changed. |
- * |
- * @event iron-items-changed |
- */ |
+ |
+ |
+ |
properties: { |
- /** |
- * If you want to use an attribute value or property of an element for |
- * `selected` instead of the index, set this to the name of the attribute |
- * or property. Hyphenated values are converted to camel case when used to |
- * look up the property of a selectable element. Camel cased values are |
- * *not* converted to hyphenated values for attribute lookup. It's |
- * recommended that you provide the hyphenated form of the name so that |
- * selection works in both cases. (Use `attr-or-property-name` instead of |
- * `attrOrPropertyName`.) |
- */ |
attrForSelected: { |
type: String, |
value: null |
}, |
- /** |
- * Gets or sets the selected element. The default is to use the index of the item. |
- * @type {string|number} |
- */ |
selected: { |
type: String, |
notify: true |
}, |
- /** |
- * Returns the currently selected item. |
- * |
- * @type {?Object} |
- */ |
selectedItem: { |
type: Object, |
readOnly: true, |
notify: true |
}, |
- /** |
- * The event that fires from items when they are selected. Selectable |
- * will listen for this event from items and update the selection state. |
- * Set to empty string to listen to no events. |
- */ |
activateEvent: { |
type: String, |
value: 'tap', |
observer: '_activateEventChanged' |
}, |
- /** |
- * This is a CSS selector string. If this is set, only items that match the CSS selector |
- * are selectable. |
- */ |
selectable: String, |
- /** |
- * The class to set on elements when selected. |
- */ |
selectedClass: { |
type: String, |
value: 'iron-selected' |
}, |
- /** |
- * The attribute to set on elements when selected. |
- */ |
selectedAttribute: { |
type: String, |
value: null |
}, |
- /** |
- * Default fallback if the selection based on selected with `attrForSelected` |
- * is not found. |
- */ |
fallbackSelection: { |
type: String, |
value: null |
}, |
- /** |
- * The list of items from which a selection can be made. |
- */ |
items: { |
type: Array, |
readOnly: true, |
@@ -2945,12 +2022,6 @@ Polymer({ |
} |
}, |
- /** |
- * The set of excluded elements where the key is the `localName` |
- * of the element that will be ignored from the item list. |
- * |
- * @default {template: 1} |
- */ |
_excludedLocalNames: { |
type: Object, |
value: function() { |
@@ -2988,69 +2059,29 @@ Polymer({ |
this._removeListener(this.activateEvent); |
}, |
- /** |
- * Returns the index of the given item. |
- * |
- * @method indexOf |
- * @param {Object} item |
- * @returns Returns the index of the item |
- */ |
indexOf: function(item) { |
return this.items.indexOf(item); |
}, |
- /** |
- * Selects the given value. |
- * |
- * @method select |
- * @param {string|number} value the value to select. |
- */ |
select: function(value) { |
this.selected = value; |
}, |
- /** |
- * Selects the previous item. |
- * |
- * @method selectPrevious |
- */ |
selectPrevious: function() { |
var length = this.items.length; |
var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % length; |
this.selected = this._indexToValue(index); |
}, |
- /** |
- * Selects the next item. |
- * |
- * @method selectNext |
- */ |
selectNext: function() { |
var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.length; |
this.selected = this._indexToValue(index); |
}, |
- /** |
- * Selects the item at the given index. |
- * |
- * @method selectIndex |
- */ |
selectIndex: function(index) { |
this.select(this._indexToValue(index)); |
}, |
- /** |
- * Force a synchronous update of the `items` property. |
- * |
- * NOTE: Consider listening for the `iron-items-changed` event to respond to |
- * updates to the set of selectable items after updates to the DOM list and |
- * selection state have been made. |
- * |
- * WARNING: If you are using this method, you should probably consider an |
- * alternate approach. Synchronously querying for items is potentially |
- * slow for many use cases. The `items` property will update asynchronously |
- * on its own to reflect selectable items in the DOM. |
- */ |
forceSynchronousItemUpdate: function() { |
this._updateItems(); |
}, |
@@ -3096,8 +2127,6 @@ Polymer({ |
_selectSelected: function(selected) { |
this._selection.select(this._valueToItem(this.selected)); |
- // Check for items, since this array is populated only when attached |
- // Since Number(0) is falsy, explicitly check for undefined |
if (this.fallbackSelection && this.items.length && (this._selection.get() === undefined)) { |
this.selected = this.fallbackSelection; |
} |
@@ -3154,7 +2183,6 @@ Polymer({ |
this._setSelectedItem(this._selection.get()); |
}, |
- // observe items change under the given node. |
_observeItems: function(node) { |
return Polymer.dom(node).observeNodes(function(mutation) { |
this._updateItems(); |
@@ -3163,8 +2191,6 @@ Polymer({ |
this._updateSelected(); |
} |
- // Let other interested parties know about the change so that |
- // we don't have to recreate mutation observers everywhere. |
this.fire('iron-items-changed', mutation, { |
bubbles: false, |
cancelable: false |
@@ -3205,10 +2231,6 @@ Polymer({ |
properties: { |
- // as the selected page is the only one visible, activateEvent |
- // is both non-sensical and problematic; e.g. in cases where a user |
- // handler attempts to change the page and the activateEvent |
- // handler immediately changes it back |
activateEvent: { |
type: String, |
value: null |
@@ -3227,12 +2249,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', |
@@ -3241,13 +2257,6 @@ Polymer({ |
'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', |
@@ -3266,11 +2275,6 @@ Polymer({ |
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', |
@@ -3278,44 +2282,16 @@ Polymer({ |
'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)?/; |
- /** |
- * Matches ESC key. |
- * |
- * Value from: http://w3c.github.io/uievents-key/#key-Escape |
- */ |
var ESC_KEY = /^escape$/; |
- /** |
- * 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) { |
@@ -3331,7 +2307,6 @@ Polymer({ |
} 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; |
@@ -3359,17 +2334,12 @@ Polymer({ |
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(keyCode - 48); |
} else if (keyCode >= 96 && keyCode <= 105) { |
- // num pad 0-9 |
validKey = String(keyCode - 96); |
} else { |
validKey = KEY_CODE[keyCode]; |
@@ -3378,19 +2348,7 @@ Polymer({ |
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) || |
@@ -3398,7 +2356,6 @@ Polymer({ |
} |
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 || ( |
@@ -3442,49 +2399,8 @@ Polymer({ |
}); |
} |
- /** |
- * `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 callback. A key binding has the format |
- * `"KEY+MODIFIER:EVENT": "callback"` (`"KEY": "callback"` or |
- * `"KEY:EVENT": "callback"` are valid as well). Some examples: |
- * |
- * keyBindings: { |
- * 'space': '_onKeydown', // same as 'space:keydown' |
- * 'shift+tab': '_onKeydown', |
- * 'enter:keypress': '_onKeypress', |
- * 'esc:keyup': '_onKeyup' |
- * } |
- * |
- * The callback will receive with an event containing the following information in `event.detail`: |
- * |
- * _onKeydown: function(event) { |
- * console.log(event.detail.combo); // KEY+MODIFIER, e.g. "shift+tab" |
- * console.log(event.detail.key); // KEY only, e.g. "tab" |
- * console.log(event.detail.event); // EVENT, e.g. "keydown" |
- * console.log(event.detail.keyboardEvent); // the original KeyboardEvent |
- * } |
- * |
- * Use the `keyEventTarget` attribute to set up event handlers on a specific |
- * node. |
- * |
- * See the [demo source code](https://github.com/PolymerElements/iron-a11y-keys-behavior/blob/master/demo/x-key-aware.html) |
- * for an example. |
- * |
- * @demo demo/index.html |
- * @polymerBehavior |
- */ |
Polymer.IronA11yKeysBehavior = { |
properties: { |
- /** |
- * The EventTarget that will be firing relevant KeyboardEvents. Set it to |
- * `null` to disable the listeners. |
- * @type {?EventTarget} |
- */ |
keyEventTarget: { |
type: Object, |
value: function() { |
@@ -3492,10 +2408,6 @@ Polymer({ |
} |
}, |
- /** |
- * If true, this property will cause the implementing element to |
- * automatically stop propagation on any handled KeyboardEvents. |
- */ |
stopKeyboardEventPropagation: { |
type: Boolean, |
value: false |
@@ -3508,8 +2420,6 @@ Polymer({ |
} |
}, |
- // 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() { |
@@ -3523,11 +2433,6 @@ Polymer({ |
], |
- /** |
- * To be used to express what combination of keys will trigger the relative |
- * callback. e.g. `keyBindings: { 'esc': '_onEscPressed'}` |
- * @type {Object} |
- */ |
keyBindings: {}, |
registered: function() { |
@@ -3542,33 +2447,18 @@ Polymer({ |
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(); |
}, |
- /** |
- * Returns true if a keyboard event matches `eventString`. |
- * |
- * @param {KeyboardEvent} event |
- * @param {string} eventString |
- * @return {boolean} |
- */ |
keyboardEventMatchesKeys: function(event, eventString) { |
var keyCombos = parseEventString(eventString); |
for (var i = 0; i < keyCombos.length; ++i) { |
@@ -3604,7 +2494,6 @@ Polymer({ |
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; |
@@ -3655,7 +2544,6 @@ Polymer({ |
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]; |
@@ -3670,7 +2558,6 @@ Polymer({ |
event.stopPropagation(); |
} |
- // if event has been already prevented, don't do anything |
if (event.defaultPrevented) { |
return; |
} |
@@ -3680,7 +2567,6 @@ Polymer({ |
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; |
} |
@@ -3702,17 +2588,10 @@ Polymer({ |
} |
}; |
})(); |
-/** |
- * @demo demo/index.html |
- * @polymerBehavior |
- */ |
Polymer.IronControlState = { |
properties: { |
- /** |
- * If true, the element currently has focus. |
- */ |
focused: { |
type: Boolean, |
value: false, |
@@ -3721,9 +2600,6 @@ Polymer({ |
reflectToAttribute: true |
}, |
- /** |
- * If true, the user cannot interact with this element. |
- */ |
disabled: { |
type: Boolean, |
value: false, |
@@ -3755,11 +2631,6 @@ Polymer({ |
}, |
_focusBlurHandler: function(event) { |
- // NOTE(cdata): if we are in ShadowDOM land, `event.target` will |
- // eventually become `this` due to retargeting; if we are not in |
- // ShadowDOM land, `event.target` will eventually become `this` due |
- // to the second conditional which fires a synthetic event (that is also |
- // handled). In either case, we can disregard `event.path`. |
if (event.target === this) { |
this._setFocused(event.type === 'focus'); |
@@ -3789,24 +2660,16 @@ Polymer({ |
}, |
_changedControlState: function() { |
- // _controlStateChanged is abstract, follow-on behaviors may implement it |
if (this._controlStateChanged) { |
this._controlStateChanged(); |
} |
} |
}; |
-/** |
- * @demo demo/index.html |
- * @polymerBehavior Polymer.IronButtonState |
- */ |
Polymer.IronButtonStateImpl = { |
properties: { |
- /** |
- * If true, the user is currently holding down the button. |
- */ |
pressed: { |
type: Boolean, |
readOnly: true, |
@@ -3815,19 +2678,12 @@ Polymer({ |
observer: '_pressedChanged' |
}, |
- /** |
- * If true, the button toggles the active state with each tap or press |
- * of the spacebar. |
- */ |
toggles: { |
type: Boolean, |
value: false, |
reflectToAttribute: true |
}, |
- /** |
- * If true, the button is a toggle and is currently in the active state. |
- */ |
active: { |
type: Boolean, |
value: false, |
@@ -3835,30 +2691,17 @@ Polymer({ |
reflectToAttribute: true |
}, |
- /** |
- * True if the element is currently being pressed by a "pointer," which |
- * is loosely defined as mouse or touch input (but specifically excluding |
- * keyboard input). |
- */ |
pointerDown: { |
type: Boolean, |
readOnly: true, |
value: false |
}, |
- /** |
- * True if the input device that caused the element to receive focus |
- * was a keyboard. |
- */ |
receivedFocusFromKeyboard: { |
type: Boolean, |
readOnly: true |
}, |
- /** |
- * The aria attribute to be set if the button is a toggle and in the |
- * active state. |
- */ |
ariaActiveAttribute: { |
type: String, |
value: 'aria-pressed', |
@@ -3887,7 +2730,6 @@ Polymer({ |
_tapHandler: function() { |
if (this.toggles) { |
- // a tap is needed to toggle the active state |
this._userActivate(!this.active); |
} else { |
this.active = false; |
@@ -3898,8 +2740,6 @@ Polymer({ |
this._setReceivedFocusFromKeyboard(!this.pointerDown && focused); |
}, |
- // to emulate native checkbox, (de-)activations from a user interaction fire |
- // 'change' events |
_userActivate: function(active) { |
if (this.active !== active) { |
this.active = active; |
@@ -3918,15 +2758,10 @@ Polymer({ |
this._setPressed(false); |
}, |
- /** |
- * @param {!KeyboardEvent} event . |
- */ |
_spaceKeyDownHandler: function(event) { |
var keyboardEvent = event.detail.keyboardEvent; |
var target = Polymer.dom(keyboardEvent).localTarget; |
- // Ignore the event if this is coming from a focused light child, since that |
- // element will deal with it. |
if (this.isLightDescendant(/** @type {Node} */(target))) |
return; |
@@ -3935,15 +2770,10 @@ Polymer({ |
this._setPressed(true); |
}, |
- /** |
- * @param {!KeyboardEvent} event . |
- */ |
_spaceKeyUpHandler: function(event) { |
var keyboardEvent = event.detail.keyboardEvent; |
var target = Polymer.dom(keyboardEvent).localTarget; |
- // Ignore the event if this is coming from a focused light child, since that |
- // element will deal with it. |
if (this.isLightDescendant(/** @type {Node} */(target))) |
return; |
@@ -3953,15 +2783,12 @@ Polymer({ |
this._setPressed(false); |
}, |
- // trigger click asynchronously, the asynchrony is useful to allow one |
- // event handler to unwind before triggering another event |
_asyncClick: function() { |
this.async(function() { |
this.click(); |
}, 1); |
}, |
- // any of these changes are considered a change to button state |
_pressedChanged: function(pressed) { |
this._changedButtonState(); |
@@ -3991,7 +2818,6 @@ Polymer({ |
} |
}, |
- // provide hook for follow-on behaviors to react to button-state |
_changedButtonState: function() { |
if (this._buttonStateChanged) { |
@@ -4019,10 +2845,6 @@ Polymer({ |
window.performance.now.bind(window.performance) : Date.now |
}; |
- /** |
- * @param {HTMLElement} element |
- * @constructor |
- */ |
function ElementMetrics(element) { |
this.element = element; |
this.width = this.boundingRect.width; |
@@ -4046,10 +2868,6 @@ Polymer({ |
} |
}; |
- /** |
- * @param {HTMLElement} element |
- * @constructor |
- */ |
function Ripple(element) { |
this.element = element; |
this.color = window.getComputedStyle(element).color; |
@@ -4143,8 +2961,6 @@ Polymer({ |
}, |
get outerOpacity() { |
- // Linear increase in background opacity, capped at the opacity |
- // of the wavefront (waveOpacity). |
var outerOpacity = this.mouseUpElapsedSeconds * 0.3; |
var waveOpacity = this.opacity; |
@@ -4223,8 +3039,6 @@ Polymer({ |
dy = this.yNow - (this.containerMetrics.height / 2); |
- // 2d transform for safari because of border-radius and overflow:hidden clipping bug. |
- // https://bugs.webkit.org/show_bug.cgi?id=98538 |
this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' + dy + 'px)'; |
this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy + 'px, 0)'; |
this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')'; |
@@ -4300,62 +3114,26 @@ Polymer({ |
], |
properties: { |
- /** |
- * The initial opacity set on the wave. |
- * |
- * @attribute initialOpacity |
- * @type number |
- * @default 0.25 |
- */ |
initialOpacity: { |
type: Number, |
value: 0.25 |
}, |
- /** |
- * How fast (opacity per second) the wave fades out. |
- * |
- * @attribute opacityDecayVelocity |
- * @type number |
- * @default 0.8 |
- */ |
opacityDecayVelocity: { |
type: Number, |
value: 0.8 |
}, |
- /** |
- * If true, ripples will exhibit a gravitational pull towards |
- * the center of their container as they fade away. |
- * |
- * @attribute recenters |
- * @type boolean |
- * @default false |
- */ |
recenters: { |
type: Boolean, |
value: false |
}, |
- /** |
- * If true, ripples will center inside its container |
- * |
- * @attribute recenters |
- * @type boolean |
- * @default false |
- */ |
center: { |
type: Boolean, |
value: false |
}, |
- /** |
- * A list of the visual ripples. |
- * |
- * @attribute ripples |
- * @type Array |
- * @default [] |
- */ |
ripples: { |
type: Array, |
value: function() { |
@@ -4363,10 +3141,6 @@ Polymer({ |
} |
}, |
- /** |
- * True when there are visible ripples animating within the |
- * element. |
- */ |
animating: { |
type: Boolean, |
readOnly: true, |
@@ -4374,22 +3148,12 @@ Polymer({ |
value: false |
}, |
- /** |
- * If true, the ripple will remain in the "down" state until `holdDown` |
- * is set to false again. |
- */ |
holdDown: { |
type: Boolean, |
value: false, |
observer: '_holdDownChanged' |
}, |
- /** |
- * If true, the ripple will not generate a ripple effect |
- * via pointer interaction. |
- * Calling ripple's imperative api like `simulatedRipple` will |
- * still generate the ripple effect. |
- */ |
noink: { |
type: Boolean, |
value: false |
@@ -4418,9 +3182,6 @@ Polymer({ |
}, |
attached: function() { |
- // Set up a11yKeysBehavior to listen to key events on the target, |
- // so that space and enter activate the ripple even if the target doesn't |
- // handle key events. The key handlers deal with `noink` themselves. |
if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE |
this.keyEventTarget = Polymer.dom(this).getOwnerRoot().host; |
} else { |
@@ -4450,28 +3211,17 @@ Polymer({ |
simulatedRipple: function() { |
this.downAction(null); |
- // Please see polymer/polymer#1305 |
this.async(function() { |
this.upAction(); |
}, 1); |
}, |
- /** |
- * Provokes a ripple down effect via a UI event, |
- * respecting the `noink` property. |
- * @param {Event=} event |
- */ |
uiDownAction: function(event) { |
if (!this.noink) { |
this.downAction(event); |
} |
}, |
- /** |
- * Provokes a ripple down effect via a UI event, |
- * *not* respecting the `noink` property. |
- * @param {Event=} event |
- */ |
downAction: function(event) { |
if (this.holdDown && this.ripples.length > 0) { |
return; |
@@ -4487,22 +3237,12 @@ Polymer({ |
} |
}, |
- /** |
- * Provokes a ripple up effect via a UI event, |
- * respecting the `noink` property. |
- * @param {Event=} event |
- */ |
uiUpAction: function(event) { |
if (!this.noink) { |
this.upAction(event); |
} |
}, |
- /** |
- * Provokes a ripple up effect via a UI event, |
- * *not* respecting the `noink` property. |
- * @param {Event=} event |
- */ |
upAction: function(event) { |
if (this.holdDown) { |
return; |
@@ -4589,8 +3329,6 @@ Polymer({ |
this.uiUpAction(); |
}, |
- // note: holdDown does not respect noink since it can be a focus based |
- // effect. |
_holdDownChanged: function(newVal, oldVal) { |
if (oldVal === undefined) { |
return; |
@@ -4602,58 +3340,26 @@ Polymer({ |
} |
} |
- /** |
- Fired when the animation finishes. |
- This is useful if you want to wait until |
- the ripple animation finishes to perform some action. |
- |
- @event transitionend |
- @param {{node: Object}} detail Contains the animated node. |
- */ |
}); |
})(); |
-/** |
- * `Polymer.PaperRippleBehavior` dynamically implements a ripple |
- * when the element has focus via pointer or keyboard. |
- * |
- * NOTE: This behavior is intended to be used in conjunction with and after |
- * `Polymer.IronButtonState` and `Polymer.IronControlState`. |
- * |
- * @polymerBehavior Polymer.PaperRippleBehavior |
- */ |
Polymer.PaperRippleBehavior = { |
properties: { |
- /** |
- * If true, the element will not produce a ripple effect when interacted |
- * with via the pointer. |
- */ |
noink: { |
type: Boolean, |
observer: '_noinkChanged' |
}, |
- /** |
- * @type {Element|undefined} |
- */ |
_rippleContainer: { |
type: Object, |
} |
}, |
- /** |
- * Ensures a `<paper-ripple>` element is available when the element is |
- * focused. |
- */ |
_buttonStateChanged: function() { |
if (this.focused) { |
this.ensureRipple(); |
} |
}, |
- /** |
- * In addition to the functionality provided in `IronButtonState`, ensures |
- * a ripple effect is created when the element is in a `pressed` state. |
- */ |
_downHandler: function(event) { |
Polymer.IronButtonStateImpl._downHandler.call(this, event); |
if (this.pressed) { |
@@ -4661,12 +3367,6 @@ Polymer({ |
} |
}, |
- /** |
- * Ensures this element contains a ripple effect. For startup efficiency |
- * the ripple effect is dynamically on demand when needed. |
- * @param {!Event=} optTriggeringEvent (optional) event that triggered the |
- * ripple. |
- */ |
ensureRipple: function(optTriggeringEvent) { |
if (!this.hasRipple()) { |
this._ripple = this._createRipple(); |
@@ -4676,9 +3376,6 @@ Polymer({ |
Polymer.dom(rippleContainer).appendChild(this._ripple); |
} |
if (optTriggeringEvent) { |
- // Check if the event happened inside of the ripple container |
- // Fall back to host instead of the root because distributed text |
- // nodes are not valid event targets |
var domContainer = Polymer.dom(this._rippleContainer || this); |
var target = Polymer.dom(optTriggeringEvent).rootTarget; |
if (domContainer.deepContains( /** @type {Node} */(target))) { |
@@ -4688,30 +3385,15 @@ Polymer({ |
} |
}, |
- /** |
- * Returns the `<paper-ripple>` element used by this element to create |
- * ripple effects. The element's ripple is created on demand, when |
- * necessary, and calling this method will force the |
- * ripple to be created. |
- */ |
getRipple: function() { |
this.ensureRipple(); |
return this._ripple; |
}, |
- /** |
- * Returns true if this element currently contains a ripple effect. |
- * @return {boolean} |
- */ |
hasRipple: function() { |
return Boolean(this._ripple); |
}, |
- /** |
- * Create the element's ripple effect via creating a `<paper-ripple>`. |
- * Override this method to customize the ripple element. |
- * @return {!PaperRippleElement} Returns a `<paper-ripple>` element. |
- */ |
_createRipple: function() { |
return /** @type {!PaperRippleElement} */ ( |
document.createElement('paper-ripple')); |
@@ -4726,15 +3408,6 @@ Polymer({ |
/** @polymerBehavior Polymer.PaperButtonBehavior */ |
Polymer.PaperButtonBehaviorImpl = { |
properties: { |
- /** |
- * The z-depth of this element, from 0-5. Setting to 0 will remove the |
- * shadow, and each increasing number greater than 0 will be "deeper" |
- * than the last. |
- * |
- * @attribute elevation |
- * @type number |
- * @default 1 |
- */ |
elevation: { |
type: Number, |
reflectToAttribute: true, |
@@ -4769,26 +3442,13 @@ Polymer({ |
this.toggleClass('keyboard-focus', receivedFocusFromKeyboard); |
}, |
- /** |
- * In addition to `IronButtonState` behavior, when space key goes down, |
- * create a ripple down effect. |
- * |
- * @param {!KeyboardEvent} event . |
- */ |
_spaceKeyDownHandler: function(event) { |
Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event); |
- // Ensure that there is at most one ripple when the space key is held down. |
if (this.hasRipple() && this.getRipple().ripples.length < 1) { |
this._ripple.uiDownAction(); |
} |
}, |
- /** |
- * In addition to `IronButtonState` behavior, when space key goes up, |
- * create a ripple up effect. |
- * |
- * @param {!KeyboardEvent} event . |
- */ |
_spaceKeyUpHandler: function(event) { |
Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event); |
if (this.hasRipple()) { |
@@ -4812,9 +3472,6 @@ Polymer({ |
], |
properties: { |
- /** |
- * If true, the button should be styled with a shadow. |
- */ |
raised: { |
type: Boolean, |
reflectToAttribute: true, |
@@ -4831,18 +3488,9 @@ Polymer({ |
} |
} |
- /** |
- Fired when the animation finishes. |
- This is useful if you want to wait until |
- the ripple animation finishes to perform some action. |
- |
- @event transitionend |
- Event param: {{node: Object}} detail Contains the animated node. |
- */ |
}); |
(function() { |
- // monostate data |
var metaDatas = {}; |
var metaArrays = {}; |
var singleton = null; |
@@ -4853,44 +3501,28 @@ Polymer({ |
properties: { |
- /** |
- * The type of meta-data. All meta-data of the same type is stored |
- * together. |
- */ |
type: { |
type: String, |
value: 'default', |
observer: '_typeChanged' |
}, |
- /** |
- * The key used to store `value` under the `type` namespace. |
- */ |
key: { |
type: String, |
observer: '_keyChanged' |
}, |
- /** |
- * The meta-data to store or retrieve. |
- */ |
value: { |
type: Object, |
notify: true, |
observer: '_valueChanged' |
}, |
- /** |
- * If true, `value` is set to the iron-meta instance itself. |
- */ |
self: { |
type: Boolean, |
observer: '_selfChanged' |
}, |
- /** |
- * Array of all meta-data values for the given type. |
- */ |
list: { |
type: Array, |
notify: true |
@@ -4902,12 +3534,6 @@ Polymer({ |
hidden: true |
}, |
- /** |
- * Only runs if someone invokes the factory/constructor directly |
- * e.g. `new Polymer.IronMeta()` |
- * |
- * @param {{type: (string|undefined), key: (string|undefined), value}=} config |
- */ |
factoryImpl: function(config) { |
if (config) { |
for (var n in config) { |
@@ -4923,7 +3549,6 @@ Polymer({ |
}, |
created: function() { |
- // TODO(sjmiles): good for debugging? |
this._metaDatas = metaDatas; |
this._metaArrays = metaArrays; |
}, |
@@ -4955,13 +3580,6 @@ Polymer({ |
this._registerKeyValue(this.key, this.value); |
}, |
- /** |
- * Retrieves meta data value by key. |
- * |
- * @method byKey |
- * @param {string} key The key of the meta-data to be returned. |
- * @return {*} |
- */ |
byKey: function(key) { |
return this._metaData && this._metaData[key]; |
}, |
@@ -5005,62 +3623,29 @@ Polymer({ |
return singleton; |
}; |
- /** |
- `iron-meta-query` can be used to access infomation stored in `iron-meta`. |
- |
- Examples: |
- |
- If I create an instance like this: |
- |
- <iron-meta key="info" value="foo/bar"></iron-meta> |
- |
- Note that value="foo/bar" is the metadata I've defined. I could define more |
- attributes or use child nodes to define additional metadata. |
- |
- Now I can access that element (and it's metadata) from any `iron-meta-query` instance: |
- |
- var value = new Polymer.IronMetaQuery({key: 'info'}).value; |
- |
- @group Polymer Iron Elements |
- @element iron-meta-query |
- */ |
Polymer.IronMetaQuery = Polymer({ |
is: 'iron-meta-query', |
properties: { |
- /** |
- * The type of meta-data. All meta-data of the same type is stored |
- * together. |
- */ |
type: { |
type: String, |
value: 'default', |
observer: '_typeChanged' |
}, |
- /** |
- * Specifies a key to use for retrieving `value` from the `type` |
- * namespace. |
- */ |
key: { |
type: String, |
observer: '_keyChanged' |
}, |
- /** |
- * The meta-data to store or retrieve. |
- */ |
value: { |
type: Object, |
notify: true, |
readOnly: true |
}, |
- /** |
- * Array of all meta-data values for the given type. |
- */ |
list: { |
type: Array, |
notify: true |
@@ -5068,12 +3653,6 @@ Polymer({ |
}, |
- /** |
- * Actually a factory method, not a true constructor. Only runs if |
- * someone invokes it directly (via `new Polymer.IronMeta()`); |
- * |
- * @param {{type: (string|undefined), key: (string|undefined)}=} config |
- */ |
factoryImpl: function(config) { |
if (config) { |
for (var n in config) { |
@@ -5088,7 +3667,6 @@ Polymer({ |
}, |
created: function() { |
- // TODO(sjmiles): good for debugging? |
this._metaDatas = metaDatas; |
this._metaArrays = metaArrays; |
}, |
@@ -5105,11 +3683,6 @@ Polymer({ |
} |
}, |
- /** |
- * Retrieves meta data value by key. |
- * @param {string} key The key of the meta-data to be returned. |
- * @return {*} |
- */ |
byKey: function(key) { |
return this._metaData && this._metaData[key]; |
} |
@@ -5123,37 +3696,21 @@ Polymer({ |
properties: { |
- /** |
- * The name of the icon to use. The name should be of the form: |
- * `iconset_name:icon_name`. |
- */ |
icon: { |
type: String, |
observer: '_iconChanged' |
}, |
- /** |
- * The name of the theme to used, if one is specified by the |
- * iconset. |
- */ |
theme: { |
type: String, |
observer: '_updateIcon' |
}, |
- /** |
- * If using iron-icon without an iconset, you can set the src to be |
- * the URL of an individual icon image file. Note that this will take |
- * precedence over a given icon attribute. |
- */ |
src: { |
type: String, |
observer: '_srcChanged' |
}, |
- /** |
- * @type {!Polymer.IronMeta} |
- */ |
_meta: { |
value: Polymer.Base.create('iron-meta', {type: 'iconset'}), |
observer: '_updateIcon' |
@@ -5214,11 +3771,6 @@ Polymer({ |
} |
}); |
-/** |
- * `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus. |
- * |
- * @polymerBehavior Polymer.PaperInkyFocusBehavior |
- */ |
Polymer.PaperInkyFocusBehaviorImpl = { |
observers: [ |
'_focusedChanged(receivedFocusFromKeyboard)' |
@@ -5262,26 +3814,14 @@ Polymer({ |
], |
properties: { |
- /** |
- * The URL of an image for the icon. If the src property is specified, |
- * the icon property should not be. |
- */ |
src: { |
type: String |
}, |
- /** |
- * Specifies the icon name or index in the set of icons available in |
- * the icon's icon set. If the icon property is specified, |
- * the src property should not be. |
- */ |
icon: { |
type: String |
}, |
- /** |
- * Specifies the alternate text for the button, for accessibility. |
- */ |
alt: { |
type: String, |
observer: "_altChanged" |
@@ -5291,7 +3831,6 @@ Polymer({ |
_altChanged: function(newValue, oldValue) { |
var label = this.getAttribute('aria-label'); |
- // Don't stomp over a user-set aria-label. |
if (!label || oldValue == label) { |
this.setAttribute('aria-label', newValue); |
} |
@@ -5308,10 +3847,6 @@ Polymer({ |
properties: { |
- /** |
- * If true, the tab will forward keyboard clicks (enter/space) to |
- * the first anchor element found in its descendants |
- */ |
link: { |
type: Boolean, |
value: false, |
@@ -5350,8 +3885,6 @@ Polymer({ |
return; |
} |
- // Don't get stuck in a loop delegating |
- // the listener from the child anchor |
if (event.target === anchor) { |
return; |
} |
@@ -5365,27 +3898,17 @@ Polymer({ |
Polymer.IronMultiSelectableBehaviorImpl = { |
properties: { |
- /** |
- * If true, multiple selections are allowed. |
- */ |
multi: { |
type: Boolean, |
value: false, |
observer: 'multiChanged' |
}, |
- /** |
- * Gets or sets the selected elements. This is used instead of `selected` when `multi` |
- * is true. |
- */ |
selectedValues: { |
type: Array, |
notify: true |
}, |
- /** |
- * Returns an array of currently selected items. |
- */ |
selectedItems: { |
type: Array, |
readOnly: true, |
@@ -5398,13 +3921,6 @@ Polymer({ |
'_updateSelected(selectedValues.splices)' |
], |
- /** |
- * Selects the given value. If the `multi` property is true, then the selected state of the |
- * `value` will be toggled; otherwise the `value` will be selected. |
- * |
- * @method select |
- * @param {string|number} value the value to select. |
- */ |
select: function(value) { |
if (this.multi) { |
if (this.selectedValues) { |
@@ -5449,13 +3965,10 @@ Polymer({ |
_selectMulti: function(values) { |
if (values) { |
var selectedItems = this._valuesToItems(values); |
- // clear all but the current selected items |
this._selection.clear(selectedItems); |
- // select only those not selected yet |
for (var i = 0; i < selectedItems.length; i++) { |
this._selection.setItemSelected(selectedItems[i], true); |
} |
- // Check for items, since this array is populated only when attached |
if (this.fallbackSelection && this.items.length && !this._selection.get().length) { |
var fallback = this._valueToItem(this.fallbackSelection); |
if (fallback) { |
@@ -5499,31 +4012,16 @@ Polymer({ |
Polymer.IronSelectableBehavior, |
Polymer.IronMultiSelectableBehaviorImpl |
]; |
-/** |
- * `Polymer.IronMenuBehavior` implements accessible menu behavior. |
- * |
- * @demo demo/index.html |
- * @polymerBehavior Polymer.IronMenuBehavior |
- */ |
Polymer.IronMenuBehaviorImpl = { |
properties: { |
- /** |
- * Returns the currently focused item. |
- * @type {?Object} |
- */ |
focusedItem: { |
observer: '_focusedItemChanged', |
readOnly: true, |
type: Object |
}, |
- /** |
- * The attribute to use on menu items to look up the item title. Typing the first |
- * letter of an item when the menu is open focuses that item. If unset, `textContent` |
- * will be used. |
- */ |
attrForItemTitle: { |
type: String |
} |
@@ -5555,15 +4053,7 @@ Polymer({ |
this._resetTabindices(); |
}, |
- /** |
- * Selects the given value. If the `multi` property is true, then the selected state of the |
- * `value` will be toggled; otherwise the `value` will be selected. |
- * |
- * @param {string|number} value the value to select. |
- */ |
select: function(value) { |
- // Cancel automatically focusing a default item if the menu received focus |
- // through a user action selecting a particular item. |
if (this._defaultFocusAsync) { |
this.cancelAsync(this._defaultFocusAsync); |
this._defaultFocusAsync = null; |
@@ -5574,12 +4064,6 @@ Polymer({ |
Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); |
}, |
- /** |
- * Resets all tabindex attributes to the appropriate value based on the |
- * current selection state. The appropriate value is `0` (focusable) for |
- * the default selected item, and `-1` (not keyboard focusable) for all |
- * other items. |
- */ |
_resetTabindices: function() { |
var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem; |
@@ -5588,12 +4072,6 @@ Polymer({ |
}, this); |
}, |
- /** |
- * Sets appropriate ARIA based on whether or not the menu is meant to be |
- * multi-selectable. |
- * |
- * @param {boolean} multi True if the menu should be multi-selectable. |
- */ |
_updateMultiselectable: function(multi) { |
if (multi) { |
this.setAttribute('aria-multiselectable', 'true'); |
@@ -5602,12 +4080,6 @@ Polymer({ |
} |
}, |
- /** |
- * Given a KeyboardEvent, this method will focus the appropriate item in the |
- * menu (if there is a relevant item, and it is possible to focus it). |
- * |
- * @param {KeyboardEvent} event A KeyboardEvent. |
- */ |
_focusWithKeyboardEvent: function(event) { |
for (var i = 0, item; item = this.items[i]; i++) { |
var attr = this.attrForItemTitle || 'textContent'; |
@@ -5621,11 +4093,6 @@ Polymer({ |
} |
}, |
- /** |
- * Focuses the previous item (relative to the currently focused item) in the |
- * menu, disabled items will be skipped. |
- * Loop until length + 1 to handle case of single item in menu. |
- */ |
_focusPrevious: function() { |
var length = this.items.length; |
var curFocusIndex = Number(this.indexOf(this.focusedItem)); |
@@ -5638,11 +4105,6 @@ Polymer({ |
} |
}, |
- /** |
- * Focuses the next item (relative to the currently focused item) in the |
- * menu, disabled items will be skipped. |
- * Loop until length + 1 to handle case of single item in menu. |
- */ |
_focusNext: function() { |
var length = this.items.length; |
var curFocusIndex = Number(this.indexOf(this.focusedItem)); |
@@ -5655,14 +4117,6 @@ Polymer({ |
} |
}, |
- /** |
- * Mutates items in the menu based on provided selection details, so that |
- * all items correctly reflect selection state. |
- * |
- * @param {Element} item An item in the menu. |
- * @param {boolean} isSelected True if the item should be shown in a |
- * selected state, otherwise false. |
- */ |
_applySelection: function(item, isSelected) { |
if (isSelected) { |
item.setAttribute('aria-selected', 'true'); |
@@ -5672,14 +4126,6 @@ Polymer({ |
Polymer.IronSelectableBehavior._applySelection.apply(this, arguments); |
}, |
- /** |
- * Discretely updates tabindex values among menu items as the focused item |
- * changes. |
- * |
- * @param {Element} focusedItem The element that is currently focused. |
- * @param {?Element} old The last element that was considered focused, if |
- * applicable. |
- */ |
_focusedItemChanged: function(focusedItem, old) { |
old && old.setAttribute('tabindex', '-1'); |
if (focusedItem) { |
@@ -5688,24 +4134,12 @@ Polymer({ |
} |
}, |
- /** |
- * A handler that responds to mutation changes related to the list of items |
- * in the menu. |
- * |
- * @param {CustomEvent} event An event containing mutation records as its |
- * detail. |
- */ |
_onIronItemsChanged: function(event) { |
if (event.detail.addedNodes.length) { |
this._resetTabindices(); |
} |
}, |
- /** |
- * Handler that is called when a shift+tab keypress is detected by the menu. |
- * |
- * @param {CustomEvent} event A key combination event. |
- */ |
_onShiftTabDown: function(event) { |
var oldTabIndex = this.getAttribute('tabindex'); |
@@ -5718,33 +4152,21 @@ Polymer({ |
this.async(function() { |
this.setAttribute('tabindex', oldTabIndex); |
Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; |
- // NOTE(cdata): polymer/polymer#1305 |
}, 1); |
}, |
- /** |
- * Handler that is called when the menu receives focus. |
- * |
- * @param {FocusEvent} event A focus event. |
- */ |
_onFocus: function(event) { |
if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { |
- // do not focus the menu itself |
return; |
} |
- // Do not focus the selected tab if the deepest target is part of the |
- // menu element's local DOM and is focusable. |
var rootTarget = /** @type {?HTMLElement} */( |
Polymer.dom(event).rootTarget); |
if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !this.isLightDescendant(rootTarget)) { |
return; |
} |
- // clear the cached focus item |
this._defaultFocusAsync = this.async(function() { |
- // focus the selected item when the menu receives focus, or the first item |
- // if no item is selected |
var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem; |
this._setFocusedItem(null); |
@@ -5752,57 +4174,32 @@ Polymer({ |
if (selectedItem) { |
this._setFocusedItem(selectedItem); |
} else if (this.items[0]) { |
- // We find the first none-disabled item (if one exists) |
this._focusNext(); |
} |
}); |
}, |
- /** |
- * Handler that is called when the up key is pressed. |
- * |
- * @param {CustomEvent} event A key combination event. |
- */ |
_onUpKey: function(event) { |
- // up and down arrows moves the focus |
this._focusPrevious(); |
event.detail.keyboardEvent.preventDefault(); |
}, |
- /** |
- * Handler that is called when the down key is pressed. |
- * |
- * @param {CustomEvent} event A key combination event. |
- */ |
_onDownKey: function(event) { |
this._focusNext(); |
event.detail.keyboardEvent.preventDefault(); |
}, |
- /** |
- * Handler that is called when the esc key is pressed. |
- * |
- * @param {CustomEvent} event A key combination event. |
- */ |
_onEscKey: function(event) { |
- // esc blurs the control |
this.focusedItem.blur(); |
}, |
- /** |
- * Handler that is called when a keydown event is detected. |
- * |
- * @param {KeyboardEvent} event A keyboard event. |
- */ |
_onKeydown: function(event) { |
if (!this.keyboardEventMatchesKeys(event, 'up down esc')) { |
- // all other keys focus the menu item starting with that character |
this._focusWithKeyboardEvent(event); |
} |
event.stopPropagation(); |
}, |
- // override _activateHandler |
_activateHandler: function(event) { |
Polymer.IronSelectableBehavior._activateHandler.call(this, event); |
event.stopPropagation(); |
@@ -5817,11 +4214,6 @@ Polymer({ |
Polymer.IronA11yKeysBehavior, |
Polymer.IronMenuBehaviorImpl |
]; |
-/** |
- * `Polymer.IronMenubarBehavior` implements accessible menubar behavior. |
- * |
- * @polymerBehavior Polymer.IronMenubarBehavior |
- */ |
Polymer.IronMenubarBehaviorImpl = { |
hostAttributes: { |
@@ -5870,7 +4262,6 @@ Polymer({ |
return; |
} |
- // all other keys focus the menu item starting with that character |
this._focusWithKeyboardEvent(event); |
} |
@@ -5881,57 +4272,16 @@ Polymer({ |
Polymer.IronMenuBehavior, |
Polymer.IronMenubarBehaviorImpl |
]; |
-/** |
- * The `iron-iconset-svg` element allows users to define their own icon sets |
- * that contain svg icons. The svg icon elements should be children of the |
- * `iron-iconset-svg` element. Multiple icons should be given distinct id's. |
- * |
- * Using svg elements to create icons has a few advantages over traditional |
- * bitmap graphics like jpg or png. Icons that use svg are vector based so |
- * they are resolution independent and should look good on any device. They |
- * are stylable via css. Icons can be themed, colorized, and even animated. |
- * |
- * Example: |
- * |
- * <iron-iconset-svg name="my-svg-icons" size="24"> |
- * <svg> |
- * <defs> |
- * <g id="shape"> |
- * <rect x="12" y="0" width="12" height="24" /> |
- * <circle cx="12" cy="12" r="12" /> |
- * </g> |
- * </defs> |
- * </svg> |
- * </iron-iconset-svg> |
- * |
- * This will automatically register the icon set "my-svg-icons" to the iconset |
- * database. To use these icons from within another element, make a |
- * `iron-iconset` element and call the `byId` method |
- * to retrieve a given iconset. To apply a particular icon inside an |
- * element use the `applyIcon` method. For example: |
- * |
- * iconset.applyIcon(iconNode, 'car'); |
- * |
- * @element iron-iconset-svg |
- * @demo demo/index.html |
- * @implements {Polymer.Iconset} |
- */ |
Polymer({ |
is: 'iron-iconset-svg', |
properties: { |
- /** |
- * The name of the iconset. |
- */ |
name: { |
type: String, |
observer: '_nameChanged' |
}, |
- /** |
- * The size of an individual icon. Note that icons must be square. |
- */ |
size: { |
type: Number, |
value: 24 |
@@ -5943,11 +4293,6 @@ Polymer({ |
this.style.display = 'none'; |
}, |
- /** |
- * Construct an array of all icon names in this iconset. |
- * |
- * @return {!Array} Array of icon names. |
- */ |
getIconNames: function() { |
this._icons = this._createIconMap(); |
return Object.keys(this._icons).map(function(n) { |
@@ -5955,23 +4300,9 @@ Polymer({ |
}, this); |
}, |
- /** |
- * Applies an icon to the given element. |
- * |
- * An svg icon is prepended to the element's shadowRoot if it exists, |
- * otherwise to the element itself. |
- * |
- * @method applyIcon |
- * @param {Element} element Element to which the icon is applied. |
- * @param {string} iconName Name of the icon to apply. |
- * @return {?Element} The svg element which renders the icon. |
- */ |
applyIcon: function(element, iconName) { |
- // insert svg element into shadow root, if it exists |
element = element.root || element; |
- // Remove old svg element |
this.removeIcon(element); |
- // install new svg element |
var svg = this._cloneIcon(iconName); |
if (svg) { |
var pde = Polymer.dom(element); |
@@ -5981,25 +4312,13 @@ Polymer({ |
return null; |
}, |
- /** |
- * Remove an icon from the given element by undoing the changes effected |
- * by `applyIcon`. |
- * |
- * @param {Element} element The element from which the icon is removed. |
- */ |
removeIcon: function(element) { |
- // Remove old svg element |
if (element._svgIcon) { |
Polymer.dom(element).removeChild(element._svgIcon); |
element._svgIcon = null; |
} |
}, |
- /** |
- * |
- * When name is changed, register iconset metadata |
- * |
- */ |
_nameChanged: function() { |
new Polymer.IronMeta({type: 'iconset', key: this.name, value: this}); |
this.async(function() { |
@@ -6007,15 +4326,7 @@ Polymer({ |
}); |
}, |
- /** |
- * Create a map of child SVG elements by id. |
- * |
- * @return {!Object} Map of id's to SVG elements. |
- */ |
_createIconMap: function() { |
- // Objects chained to Object.prototype (`{}`) have members. Specifically, |
- // on FF there is a `watch` method that confuses the icon map, so we |
- // need to use a null-based object here. |
var icons = Object.create(null); |
Polymer.dom(this).querySelectorAll('[id]') |
.forEach(function(icon) { |
@@ -6024,25 +4335,11 @@ Polymer({ |
return icons; |
}, |
- /** |
- * Produce installable clone of the SVG element matching `id` in this |
- * iconset, or `undefined` if there is no matching element. |
- * |
- * @return {Element} Returns an installable clone of the SVG element |
- * matching `id`. |
- */ |
_cloneIcon: function(id) { |
- // create the icon map on-demand, since the iconset itself has no discrete |
- // signal to know when it's children are fully parsed |
this._icons = this._icons || this._createIconMap(); |
return this._prepareSvgClone(this._icons[id], this.size); |
}, |
- /** |
- * @param {Element} sourceSvg |
- * @param {number} size |
- * @return {Element} |
- */ |
_prepareSvgClone: function(sourceSvg, size) { |
if (sourceSvg) { |
var content = sourceSvg.cloneNode(true), |
@@ -6050,8 +4347,6 @@ Polymer({ |
viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size; |
svg.setAttribute('viewBox', viewBox); |
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); |
- // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136 |
- // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root |
svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; |
svg.appendChild(content).removeAttribute('id'); |
return svg; |
@@ -6069,69 +4364,42 @@ Polymer({ |
], |
properties: { |
- /** |
- * If true, ink ripple effect is disabled. When this property is changed, |
- * all descendant `<paper-tab>` elements have their `noink` property |
- * changed to the new value as well. |
- */ |
noink: { |
type: Boolean, |
value: false, |
observer: '_noinkChanged' |
}, |
- /** |
- * If true, the bottom bar to indicate the selected tab will not be shown. |
- */ |
noBar: { |
type: Boolean, |
value: false |
}, |
- /** |
- * If true, the slide effect for the bottom bar is disabled. |
- */ |
noSlide: { |
type: Boolean, |
value: false |
}, |
- /** |
- * If true, tabs are scrollable and the tab width is based on the label width. |
- */ |
scrollable: { |
type: Boolean, |
value: false |
}, |
- /** |
- * If true, tabs expand to fit their container. This currently only applies when |
- * scrollable is true. |
- */ |
fitContainer: { |
type: Boolean, |
value: false |
}, |
- /** |
- * If true, dragging on the tabs to scroll is disabled. |
- */ |
disableDrag: { |
type: Boolean, |
value: false |
}, |
- /** |
- * If true, scroll buttons (left/right arrow) will be hidden for scrollable tabs. |
- */ |
hideScrollButtons: { |
type: Boolean, |
value: false |
}, |
- /** |
- * If true, the tabs are aligned to bottom (the selection bar appears at the top). |
- */ |
alignBottom: { |
type: Boolean, |
value: false |
@@ -6142,20 +4410,11 @@ Polymer({ |
value: 'paper-tab' |
}, |
- /** |
- * If true, tabs are automatically selected when focused using the |
- * keyboard. |
- */ |
autoselect: { |
type: Boolean, |
value: false |
}, |
- /** |
- * The delay (in milliseconds) between when the user stops interacting |
- * with the tabs through the keyboard and when the focused item is |
- * automatically selected (if `autoselect` is true). |
- */ |
autoselectDelay: { |
type: Number, |
value: 0 |
@@ -6256,7 +4515,6 @@ Polymer({ |
return ''; |
}, |
- // TODO(cdata): Add `track` response back in when gesture lands. |
_onTabSizingChanged: function() { |
this.debounce('_onTabSizingChanged', function() { |
@@ -6275,30 +4533,21 @@ Polymer({ |
this.debounce('tab-changed', function() { |
this._tabChanged(null, this._previousTab); |
this._previousTab = null; |
- // See polymer/polymer#1305 |
}, 1); |
}, |
_activateHandler: function() { |
- // Cancel item activations scheduled by keyboard events when any other |
- // action causes an item to be activated (e.g. clicks). |
this._cancelPendingActivation(); |
Polymer.IronMenuBehaviorImpl._activateHandler.apply(this, arguments); |
}, |
- /** |
- * Activates an item after a delay (in milliseconds). |
- */ |
_scheduleActivation: function(item, delay) { |
this._pendingActivationItem = item; |
this._pendingActivationTimeout = this.async( |
this._bindDelayedActivationHandler, delay); |
}, |
- /** |
- * Activates the last item given to `_scheduleActivation`. |
- */ |
_delayedActivationHandler: function() { |
var item = this._pendingActivationItem; |
this._pendingActivationItem = undefined; |
@@ -6309,10 +4558,6 @@ Polymer({ |
}); |
}, |
- /** |
- * Cancels a previously scheduled item activation made with |
- * `_scheduleActivation`. |
- */ |
_cancelPendingActivation: function() { |
if (this._pendingActivationTimeout !== undefined) { |
this.cancelAsync(this._pendingActivationTimeout); |
@@ -6328,8 +4573,6 @@ Polymer({ |
}, |
_onBlurCapture: function(event) { |
- // Cancel a scheduled item activation (if any) when that item is |
- // blurred. |
if (event.target === this._pendingActivationItem) { |
this._cancelPendingActivation(); |
} |
@@ -6353,8 +4596,6 @@ Polymer({ |
}, |
_down: function(e) { |
- // go one beat async to defeat IronMenuBehavior |
- // autorefocus-on-no-selection timeout |
this.async(function() { |
if (this._defaultFocusAsync) { |
this.cancelAsync(this._defaultFocusAsync); |
@@ -6397,7 +4638,6 @@ Polymer({ |
_tabChanged: function(tab, old) { |
if (!tab) { |
- // Remove the bar without animation. |
this.$.selectionBar.classList.remove('expand'); |
this.$.selectionBar.classList.remove('contract'); |
this._positionBar(0, 0); |
@@ -6415,7 +4655,6 @@ Polymer({ |
}; |
if (this.noSlide || old == null) { |
- // Position the bar without animation. |
this.$.selectionBar.classList.remove('expand'); |
this.$.selectionBar.classList.remove('contract'); |
this._positionBar(this._pos.width, this._pos.left); |
@@ -6427,7 +4666,6 @@ Polymer({ |
var index = this.items.indexOf(tab); |
var m = 5; |
- // bar animation: expand |
this.$.selectionBar.classList.add('expand'); |
var moveRight = oldIndex < index; |
@@ -6478,12 +4716,10 @@ Polymer({ |
_onBarTransitionEnd: function(e) { |
var cl = this.$.selectionBar.classList; |
- // bar animation: expand -> contract |
if (cl.contains('expand')) { |
cl.remove('expand'); |
cl.add('contract'); |
this._positionBar(this._pos.width, this._pos.left); |
- // bar animation done |
} else if (cl.contains('contract')) { |
cl.remove('contract'); |
} |
@@ -6497,11 +4733,6 @@ Polymer({ |
properties: { |
- /** |
- * The value of mode is used to set the `aria-live` attribute |
- * for the element that will be announced. Valid values are: `off`, |
- * `polite` and `assertive`. |
- */ |
mode: { |
type: String, |
value: 'polite' |
@@ -6521,11 +4752,6 @@ Polymer({ |
document.body.addEventListener('iron-announce', this._onIronAnnounce.bind(this)); |
}, |
- /** |
- * Cause a text string to be announced by screen readers. |
- * |
- * @param {string} text The text that should be announced. |
- */ |
announce: function(text) { |
this._text = ''; |
this.async(function() { |
@@ -6550,49 +4776,16 @@ Polymer({ |
document.body.appendChild(Polymer.IronA11yAnnouncer.instance); |
}; |
})(); |
-/** |
- * Singleton IronMeta instance. |
- */ |
Polymer.IronValidatableBehaviorMeta = null; |
- /** |
- * `Use Polymer.IronValidatableBehavior` to implement an element that validates user input. |
- * Use the related `Polymer.IronValidatorBehavior` to add custom validation logic to an iron-input. |
- * |
- * By default, an `<iron-form>` element validates its fields when the user presses the submit button. |
- * To validate a form imperatively, call the form's `validate()` method, which in turn will |
- * call `validate()` on all its children. By using `Polymer.IronValidatableBehavior`, your |
- * custom element will get a public `validate()`, which |
- * will return the validity of the element, and a corresponding `invalid` attribute, |
- * which can be used for styling. |
- * |
- * To implement the custom validation logic of your element, you must override |
- * the protected `_getValidity()` method of this behaviour, rather than `validate()`. |
- * See [this](https://github.com/PolymerElements/iron-form/blob/master/demo/simple-element.html) |
- * for an example. |
- * |
- * ### Accessibility |
- * |
- * Changing the `invalid` property, either manually or by calling `validate()` will update the |
- * `aria-invalid` attribute. |
- * |
- * @demo demo/index.html |
- * @polymerBehavior |
- */ |
Polymer.IronValidatableBehavior = { |
properties: { |
- /** |
- * Name of the validator to use. |
- */ |
validator: { |
type: String |
}, |
- /** |
- * True if the last call to `validate` is invalid. |
- */ |
invalid: { |
notify: true, |
reflectToAttribute: true, |
@@ -6600,19 +4793,10 @@ Polymer({ |
value: false |
}, |
- /** |
- * This property is deprecated and should not be used. Use the global |
- * validator meta singleton, `Polymer.IronValidatableBehaviorMeta` instead. |
- */ |
_validatorMeta: { |
type: Object |
}, |
- /** |
- * Namespace for this validator. This property is deprecated and should |
- * not be used. For all intents and purposes, please consider it a |
- * read-only, config-time property. |
- */ |
validatorType: { |
type: String, |
value: 'validator' |
@@ -6640,36 +4824,15 @@ Polymer({ |
} |
}, |
- /** |
- * @return {boolean} True if the validator `validator` exists. |
- */ |
hasValidator: function() { |
return this._validator != null; |
}, |
- /** |
- * Returns true if the `value` is valid, and updates `invalid`. If you want |
- * your element to have custom validation logic, do not override this method; |
- * override `_getValidity(value)` instead. |
- |
- * @param {Object} value The value to be validated. By default, it is passed |
- * to the validator's `validate()` function, if a validator is set. |
- * @return {boolean} True if `value` is valid. |
- */ |
validate: function(value) { |
this.invalid = !this._getValidity(value); |
return !this.invalid; |
}, |
- /** |
- * Returns true if `value` is valid. By default, it is passed |
- * to the validator's `validate()` function, if a validator is set. You |
- * should override this method if you want to implement custom validity |
- * logic for your element. |
- * |
- * @param {Object} value The value to be validated. |
- * @return {boolean} True if `value` is valid. |
- */ |
_getValidity: function(value) { |
if (this.hasValidator()) { |
@@ -6683,39 +4846,6 @@ Polymer({ |
Polymer.IronValidatableBehaviorMeta.byKey(this.validator); |
} |
}; |
-/* |
-`<iron-input>` adds two-way binding and custom validators using `Polymer.IronValidatorBehavior` |
-to `<input>`. |
- |
-### Two-way binding |
- |
-By default you can only get notified of changes to an `input`'s `value` due to user input: |
- |
- <input value="{{myValue::input}}"> |
- |
-`iron-input` adds the `bind-value` property that mirrors the `value` property, and can be used |
-for two-way data binding. `bind-value` will notify if it is changed either by user input or by script. |
- |
- <input is="iron-input" bind-value="{{myValue}}"> |
- |
-### Custom validators |
- |
-You can use custom validators that implement `Polymer.IronValidatorBehavior` with `<iron-input>`. |
- |
- <input is="iron-input" validator="my-custom-validator"> |
- |
-### Stopping invalid input |
- |
-It may be desirable to only allow users to enter certain characters. You can use the |
-`prevent-invalid-input` and `allowed-pattern` attributes together to accomplish this. This feature |
-is separate from validation, and `allowed-pattern` does not affect how the input is validated. |
- |
- \x3c!-- only allow characters that match [0-9] --\x3e |
- <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]"> |
- |
-@hero hero.svg |
-@demo demo/index.html |
-*/ |
Polymer({ |
@@ -6729,32 +4859,15 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
properties: { |
- /** |
- * Use this property instead of `value` for two-way data binding. |
- */ |
bindValue: { |
observer: '_bindValueChanged', |
type: String |
}, |
- /** |
- * Set to true to prevent the user from entering invalid input. If `allowedPattern` is set, |
- * any character typed by the user will be matched against that pattern, and rejected if it's not a match. |
- * Pasted input will have each character checked individually; if any character |
- * doesn't match `allowedPattern`, the entire pasted string will be rejected. |
- * If `allowedPattern` is not set, it will use the `type` attribute (only supported for `type=number`). |
- */ |
preventInvalidInput: { |
type: Boolean |
}, |
- /** |
- * Regular expression that list the characters allowed as input. |
- * This pattern represents the allowed characters for the field; as the user inputs text, |
- * each individual character will be checked against the pattern (rather than checking |
- * the entire value as a whole). The recommended format should be a list of allowed characters; |
- * for example, `[a-zA-Z0-9.+-!;:]` |
- */ |
allowedPattern: { |
type: String, |
observer: "_allowedPatternChanged" |
@@ -6779,7 +4892,6 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
/** @suppress {checkTypes} */ |
registered: function() { |
- // Feature detect whether we need to patch dispatchEvent (i.e. on FF and IE). |
if (!this._canDispatchEventOnDisabled()) { |
this._origDispatchEvent = this.dispatchEvent; |
this.dispatchEvent = this._dispatchEventFirefoxIE; |
@@ -6807,11 +4919,6 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
}, |
_dispatchEventFirefoxIE: function() { |
- // Due to Firefox bug, events fired on disabled form controls can throw |
- // errors; furthermore, neither IE nor Firefox will actually dispatch |
- // events from disabled form controls; as such, we toggle disable around |
- // the dispatch to allow notifying properties to notify |
- // See issue #47 for details |
var disabled = this.disabled; |
this.disabled = false; |
this._origDispatchEvent.apply(this, arguments); |
@@ -6836,25 +4943,18 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
this.bindValue = this.value; |
}, |
- /** |
- * @suppress {checkTypes} |
- */ |
_bindValueChanged: function() { |
if (this.value !== this.bindValue) { |
this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue === false) ? '' : this.bindValue; |
} |
- // manually notify because we don't want to notify until after setting value |
this.fire('bind-value-changed', {value: this.bindValue}); |
}, |
_allowedPatternChanged: function() { |
- // Force to prevent invalid input when an `allowed-pattern` is set |
this.preventInvalidInput = this.allowedPattern ? true : false; |
}, |
_onInput: function() { |
- // Need to validate each of the characters pasted if they haven't |
- // been validated inside `_onKeypress` already. |
if (this.preventInvalidInput && !this._patternAlreadyChecked) { |
var valid = this._checkPatternValidity(); |
if (!valid) { |
@@ -6869,24 +4969,13 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
}, |
_isPrintable: function(event) { |
- // What a control/printable character is varies wildly based on the browser. |
- // - most control characters (arrows, backspace) do not send a `keypress` event |
- // in Chrome, but the *do* on Firefox |
- // - in Firefox, when they do send a `keypress` event, control chars have |
- // a charCode = 0, keyCode = xx (for ex. 40 for down arrow) |
- // - printable characters always send a keypress event. |
- // - in Firefox, printable chars always have a keyCode = 0. In Chrome, the keyCode |
- // always matches the charCode. |
- // None of this makes any sense. |
- |
- // For these keys, ASCII code == browser keycode. |
+ |
var anyNonPrintable = |
(event.keyCode == 8) || // backspace |
(event.keyCode == 9) || // tab |
(event.keyCode == 13) || // enter |
(event.keyCode == 27); // escape |
- // For these keys, make sure it's a browser keycode and not an ASCII code. |
var mozNonPrintable = |
(event.keyCode == 19) || // pause |
(event.keyCode == 20) || // caps lock |
@@ -6909,11 +4998,9 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
return; |
} |
- // Handle special keys and backspace |
if (event.metaKey || event.ctrlKey || event.altKey) |
return; |
- // Check the pattern either here or in `_onInput`, but not in both. |
this._patternAlreadyChecked = true; |
var thisChar = String.fromCharCode(event.charCode); |
@@ -6936,20 +5023,10 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
return true; |
}, |
- /** |
- * Returns true if `value` is valid. The validator provided in `validator` will be used first, |
- * then any constraints. |
- * @return {boolean} True if the value is valid. |
- */ |
validate: function() { |
- // First, check what the browser thinks. Some inputs (like type=number) |
- // behave weirdly and will set the value to "" if something invalid is |
- // entered, but will set the validity correctly. |
var valid = this.checkValidity(); |
- // Only do extra checking if the browser thought this was valid. |
if (valid) { |
- // Empty, required input is invalid |
if (this.required && this.value === '') { |
valid = false; |
} else if (this.hasValidator()) { |
@@ -6967,60 +5044,36 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
} |
}); |
- /* |
- The `iron-input-validate` event is fired whenever `validate()` is called. |
- @event iron-input-validate |
- */ |
Polymer({ |
is: 'paper-input-container', |
properties: { |
- /** |
- * Set to true to disable the floating label. The label disappears when the input value is |
- * not null. |
- */ |
noLabelFloat: { |
type: Boolean, |
value: false |
}, |
- /** |
- * Set to true to always float the floating label. |
- */ |
alwaysFloatLabel: { |
type: Boolean, |
value: false |
}, |
- /** |
- * The attribute to listen for value changes on. |
- */ |
attrForValue: { |
type: String, |
value: 'bind-value' |
}, |
- /** |
- * Set to true to auto-validate the input value when it changes. |
- */ |
autoValidate: { |
type: Boolean, |
value: false |
}, |
- /** |
- * True if the input is invalid. This property is set automatically when the input value |
- * changes if auto-validating, or when the `iron-input-validate` event is heard from a child. |
- */ |
invalid: { |
observer: '_invalidChanged', |
type: Boolean, |
value: false |
}, |
- /** |
- * True if the input has focus. |
- */ |
focused: { |
readOnly: true, |
type: Boolean, |
@@ -7030,9 +5083,6 @@ Polymer({ |
_addons: { |
type: Array |
- // do not set a default value here intentionally - it will be initialized lazily when a |
- // distributed child is attached, which may occur before configuration for this element |
- // in polyfill. |
}, |
_inputHasContent: { |
@@ -7110,7 +5160,6 @@ Polymer({ |
this.addEventListener('input', this._onInput); |
} |
- // Only validate when attached if the input already has a value. |
if (this._inputElementValue != '') { |
this._handleValueAndAutoValidate(this._inputElement); |
} else { |
@@ -7151,7 +5200,6 @@ Polymer({ |
_handleValue: function(inputElement) { |
var value = this._inputElementValue; |
- // type="number" hack needed because this.value is empty until it's valid |
if (value || value === 0 || (inputElement.type === 'number' && !inputElement.checkValidity())) { |
this._inputHasContent = true; |
} else { |
@@ -7176,7 +5224,6 @@ Polymer({ |
this.invalid = !valid; |
} |
- // Call this last to notify the add-ons. |
this._handleValue(inputElement); |
}, |
@@ -7190,10 +5237,6 @@ Polymer({ |
} |
}, |
- /** |
- * Call this to update the state of add-ons. |
- * @param {Object} state Add-on state. |
- */ |
updateAddons: function(state) { |
for (var addon, index = 0; addon = this._addons[index]; index++) { |
addon.update(state); |
@@ -7207,8 +5250,6 @@ Polymer({ |
if (alwaysFloatLabel || _inputHasContent) { |
cls += ' label-is-floating'; |
- // If the label is floating, ignore any offsets that may have been |
- // applied from a prefix element. |
this.$.labelAndInputContainer.style.position = 'static'; |
if (invalid) { |
@@ -7217,7 +5258,6 @@ Polymer({ |
cls += " label-is-highlighted"; |
} |
} else { |
- // When the label is not floating, it should overlap the input element. |
if (label) { |
this.$.labelAndInputContainer.style.position = 'relative'; |
} |
@@ -7259,9 +5299,6 @@ Polymer({ |
}, |
properties: { |
- /** |
- * Displays the spinner. |
- */ |
active: { |
type: Boolean, |
value: false, |
@@ -7269,11 +5306,6 @@ Polymer({ |
observer: '__activeChanged' |
}, |
- /** |
- * Alternative text content for accessibility support. |
- * If alt is present, it will add an aria-label whose content matches alt when active. |
- * If alt is not present, it will default to 'loading' as the alt value. |
- */ |
alt: { |
type: String, |
value: 'loading', |
@@ -7299,7 +5331,6 @@ Polymer({ |
}, |
__altChanged: function(alt) { |
- // user-provided `aria-label` takes precedence over prototype default |
if (alt === this.getPropertyInfo('alt').value) { |
this.alt = this.getAttribute('aria-label') || alt; |
} else { |
@@ -7333,11 +5364,6 @@ Polymer({ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * Implements an incremental search field which can be shown and hidden. |
- * Canonical implementation is <cr-search-field>. |
- * @polymerBehavior |
- */ |
var CrSearchFieldBehavior = { |
properties: { |
label: { |
@@ -7365,27 +5391,13 @@ var CrSearchFieldBehavior = { |
}, |
}, |
- /** |
- * @abstract |
- * @return {!HTMLInputElement} The input field element the behavior should |
- * use. |
- */ |
getSearchInput: function() {}, |
- /** |
- * @return {string} The value of the search field. |
- */ |
getValue: function() { |
return this.getSearchInput().value; |
}, |
- /** |
- * Sets the value of the search field. |
- * @param {string} value |
- */ |
setValue: function(value) { |
- // Use bindValue when setting the input value so that changes propagate |
- // correctly. |
this.getSearchInput().bindValue = value; |
this.onValueChanged_(value); |
}, |
@@ -7404,12 +5416,6 @@ var CrSearchFieldBehavior = { |
this.onValueChanged_(this.getValue()); |
}, |
- /** |
- * Updates the internal state of the search field based on a change that has |
- * already happened. |
- * @param {string} newValue |
- * @private |
- */ |
onValueChanged_: function(newValue) { |
if (newValue == this.lastValue_) |
return; |
@@ -7438,7 +5444,6 @@ var CrSearchFieldBehavior = { |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-// TODO(tsergeant): Add tests for cr-toolbar-search-field. |
Polymer({ |
is: 'cr-toolbar-search-field', |
@@ -7450,14 +5455,10 @@ Polymer({ |
reflectToAttribute: true, |
}, |
- // Prompt text to display in the search field. |
label: String, |
- // Tooltip to display on the clear search button. |
clearLabel: String, |
- // When true, show a loading spinner to indicate that the backend is |
- // processing the search. Will only show if the search field is open. |
spinnerActive: { |
type: Boolean, |
reflectToAttribute: true |
@@ -7482,21 +5483,10 @@ Polymer({ |
return this.$.searchTerm.focused; |
}, |
- /** |
- * @param {boolean} narrow |
- * @return {number} |
- * @private |
- */ |
computeIconTabIndex_: function(narrow) { |
return narrow ? 0 : -1; |
}, |
- /** |
- * @param {boolean} spinnerActive |
- * @param {boolean} showingSearch |
- * @return {boolean} |
- * @private |
- */ |
isSpinnerShown_: function(spinnerActive, showingSearch) { |
return spinnerActive && showingSearch; |
}, |
@@ -7507,12 +5497,6 @@ Polymer({ |
this.showingSearch = false; |
}, |
- /** |
- * Update the state of the search field whenever the underlying input value |
- * changes. Unlike onsearch or onkeypress, this is reliably called immediately |
- * after any change, whether the result of user input or JS modification. |
- * @private |
- */ |
onBindValueChanged_: function() { |
var newValue = this.$.searchInput.bindValue; |
this.hasSearchText_ = newValue != ''; |
@@ -7520,19 +5504,11 @@ Polymer({ |
this.showingSearch = true; |
}, |
- /** |
- * @param {Event} e |
- * @private |
- */ |
showSearch_: function(e) { |
if (e.target != this.$.clearSearch) |
this.showingSearch = true; |
}, |
- /** |
- * @param {Event} e |
- * @private |
- */ |
hideSearch_: function(e) { |
this.showingSearch = false; |
e.stopPropagation(); |
@@ -7546,20 +5522,14 @@ Polymer({ |
is: 'cr-toolbar', |
properties: { |
- // Name to display in the toolbar, in titlecase. |
pageName: String, |
- // Prompt text to display in the search field. |
searchPrompt: String, |
- // Tooltip to display on the clear search button. |
clearLabel: String, |
- // Value is proxied through to cr-toolbar-search-field. When true, |
- // the search field will show a processing spinner. |
spinnerActive: Boolean, |
- // Controls whether the menu button is shown at the start of the menu. |
showMenu: { |
type: Boolean, |
reflectToAttribute: true, |
@@ -7596,32 +5566,23 @@ Polymer({ |
Polymer({ |
is: 'history-toolbar', |
properties: { |
- // Number of history items currently selected. |
- // TODO(calamity): bind this to |
- // listContainer.selectedItem.selectedPaths.length. |
count: { |
type: Number, |
value: 0, |
observer: 'changeToolbarView_' |
}, |
- // True if 1 or more history items are selected. When this value changes |
- // the background colour changes. |
itemsSelected_: { |
type: Boolean, |
value: false, |
reflectToAttribute: true |
}, |
- // The most recent term entered in the search field. Updated incrementally |
- // as the user types. |
searchTerm: { |
type: String, |
notify: true, |
}, |
- // True if the backend is processing and a spinner should be shown in the |
- // toolbar. |
spinnerActive: { |
type: Boolean, |
value: false |
@@ -7633,13 +5594,11 @@ Polymer({ |
reflectToAttribute: true, |
}, |
- // Whether domain-grouped history is enabled. |
isGroupedMode: { |
type: Boolean, |
reflectToAttribute: true, |
}, |
- // The period to search over. Matches BrowsingHistoryHandler::Range. |
groupedRange: { |
type: Number, |
value: 0, |
@@ -7647,27 +5606,15 @@ Polymer({ |
notify: true |
}, |
- // The start time of the query range. |
queryStartTime: String, |
- // The end time of the query range. |
queryEndTime: String, |
}, |
- /** |
- * Changes the toolbar background color depending on whether any history items |
- * are currently selected. |
- * @private |
- */ |
changeToolbarView_: function() { |
this.itemsSelected_ = this.count > 0; |
}, |
- /** |
- * When changing the search term externally, update the search field to |
- * reflect the new search term. |
- * @param {string} search |
- */ |
setSearchTerm: function(search) { |
if (this.searchTerm == search) |
return; |
@@ -7679,10 +5626,6 @@ Polymer({ |
searchField.setValue(search); |
}, |
- /** |
- * @param {!CustomEvent} event |
- * @private |
- */ |
onSearchChanged_: function(event) { |
this.searchTerm = /** @type {string} */ (event.detail); |
}, |
@@ -7705,10 +5648,6 @@ Polymer({ |
.showAndFocus(); |
}, |
- /** |
- * If the user is a supervised user the delete button is not shown. |
- * @private |
- */ |
deletingAllowed_: function() { |
return loadTimeData.getBoolean('allowDeletingHistory'); |
}, |
@@ -7718,7 +5657,6 @@ Polymer({ |
}, |
getHistoryInterval_: function(queryStartTime, queryEndTime) { |
- // TODO(calamity): Fix the format of these dates. |
return loadTimeData.getStringF( |
'historyInterval', queryStartTime, queryEndTime); |
}, |
@@ -7732,15 +5670,6 @@ Polymer({ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * @fileoverview 'cr-dialog' is a component for showing a modal dialog. If the |
- * dialog is closed via close(), a 'close' event is fired. If the dialog is |
- * canceled via cancel(), a 'cancel' event is fired followed by a 'close' event. |
- * Additionally clients can inspect the dialog's |returnValue| property inside |
- * the 'close' event listener to determine whether it was canceled or just |
- * closed, where a truthy value means success, and a falsy value means it was |
- * canceled. |
- */ |
Polymer({ |
is: 'cr-dialog', |
extends: 'dialog', |
@@ -7750,10 +5679,6 @@ Polymer({ |
HTMLDialogElement.prototype.close.call(this, ''); |
}, |
- /** |
- * @param {string=} opt_returnValue |
- * @override |
- */ |
close: function(opt_returnValue) { |
HTMLDialogElement.prototype.close.call(this, 'success'); |
}, |
@@ -7763,52 +5688,11 @@ Polymer({ |
return this.$.close; |
}, |
}); |
-/** |
-`Polymer.IronFitBehavior` fits an element in another element using `max-height` and `max-width`, and |
-optionally centers it in the window or another element. |
- |
-The element will only be sized and/or positioned if it has not already been sized and/or positioned |
-by CSS. |
- |
-CSS properties | Action |
------------------------------|------------------------------------------- |
-`position` set | Element is not centered horizontally or vertically |
-`top` or `bottom` set | Element is not vertically centered |
-`left` or `right` set | Element is not horizontally centered |
-`max-height` set | Element respects `max-height` |
-`max-width` set | Element respects `max-width` |
- |
-`Polymer.IronFitBehavior` can position an element into another element using |
-`verticalAlign` and `horizontalAlign`. This will override the element's css position. |
- |
- <div class="container"> |
- <iron-fit-impl vertical-align="top" horizontal-align="auto"> |
- Positioned into the container |
- </iron-fit-impl> |
- </div> |
- |
-Use `noOverlap` to position the element around another element without overlapping it. |
- |
- <div class="container"> |
- <iron-fit-impl no-overlap vertical-align="auto" horizontal-align="auto"> |
- Positioned around the container |
- </iron-fit-impl> |
- </div> |
- |
-@demo demo/index.html |
-@polymerBehavior |
-*/ |
Polymer.IronFitBehavior = { |
properties: { |
- /** |
- * The element that will receive a `max-height`/`width`. By default it is the same as `this`, |
- * but it can be set to a child element. This is useful, for example, for implementing a |
- * scrolling region inside the element. |
- * @type {!Element} |
- */ |
sizingTarget: { |
type: Object, |
value: function() { |
@@ -7816,77 +5700,43 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * The element to fit `this` into. |
- */ |
fitInto: { |
type: Object, |
value: window |
}, |
- /** |
- * Will position the element around the positionTarget without overlapping it. |
- */ |
noOverlap: { |
type: Boolean |
}, |
- /** |
- * The element that should be used to position the element. If not set, it will |
- * default to the parent node. |
- * @type {!Element} |
- */ |
positionTarget: { |
type: Element |
}, |
- /** |
- * The orientation against which to align the element horizontally |
- * relative to the `positionTarget`. Possible values are "left", "right", "auto". |
- */ |
horizontalAlign: { |
type: String |
}, |
- /** |
- * The orientation against which to align the element vertically |
- * relative to the `positionTarget`. Possible values are "top", "bottom", "auto". |
- */ |
verticalAlign: { |
type: String |
}, |
- /** |
- * If true, it will use `horizontalAlign` and `verticalAlign` values as preferred alignment |
- * and if there's not enough space, it will pick the values which minimize the cropping. |
- */ |
dynamicAlign: { |
type: Boolean |
}, |
- /** |
- * The same as setting margin-left and margin-right css properties. |
- * @deprecated |
- */ |
horizontalOffset: { |
type: Number, |
value: 0, |
notify: true |
}, |
- /** |
- * The same as setting margin-top and margin-bottom css properties. |
- * @deprecated |
- */ |
verticalOffset: { |
type: Number, |
value: 0, |
notify: true |
}, |
- /** |
- * Set to true to auto-fit on attach. |
- */ |
autoFitOnAttach: { |
type: Boolean, |
value: false |
@@ -7938,10 +5788,6 @@ Use `noOverlap` to position the element around another element without overlappi |
return fitTop; |
}, |
- /** |
- * The element that should be used to position the element, |
- * if no position target is configured. |
- */ |
get _defaultPositionTarget() { |
var parent = Polymer.dom(this).parentNode; |
@@ -7952,12 +5798,8 @@ Use `noOverlap` to position the element around another element without overlappi |
return parent; |
}, |
- /** |
- * The horizontal align value, accounting for the RTL/LTR text direction. |
- */ |
get _localeHorizontalAlign() { |
if (this._isRTL) { |
- // In RTL, "left" becomes "right". |
if (this.horizontalAlign === 'right') { |
return 'left'; |
} |
@@ -7969,7 +5811,6 @@ Use `noOverlap` to position the element around another element without overlappi |
}, |
attached: function() { |
- // Memoize this to avoid expensive calculations & relayouts. |
this._isRTL = window.getComputedStyle(this).direction == 'rtl'; |
this.positionTarget = this.positionTarget || this._defaultPositionTarget; |
if (this.autoFitOnAttach) { |
@@ -7983,19 +5824,12 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * Positions and fits the element into the `fitInto` element. |
- */ |
fit: function() { |
this.position(); |
this.constrain(); |
this.center(); |
}, |
- /** |
- * Memoize information needed to position and size the target element. |
- * @suppress {deprecated} |
- */ |
_discoverInfo: function() { |
if (this._fitInfo) { |
return; |
@@ -8034,7 +5868,6 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}; |
- // Support these properties until they are removed. |
if (this.verticalOffset) { |
this._fitInfo.margin.top = this._fitInfo.margin.bottom = this.verticalOffset; |
this._fitInfo.inlineStyle.marginTop = this.style.marginTop || ''; |
@@ -8049,10 +5882,6 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * Resets the target element's position and size constraints, and clear |
- * the memoized data. |
- */ |
resetFit: function() { |
var info = this._fitInfo || {}; |
for (var property in info.sizerInlineStyle) { |
@@ -8065,12 +5894,6 @@ Use `noOverlap` to position the element around another element without overlappi |
this._fitInfo = null; |
}, |
- /** |
- * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after |
- * the element or the `fitInto` element has been resized, or if any of the |
- * positioning properties (e.g. `horizontalAlign, verticalAlign`) is updated. |
- * It preserves the scroll position of the sizingTarget. |
- */ |
refit: function() { |
var scrollLeft = this.sizingTarget.scrollLeft; |
var scrollTop = this.sizingTarget.scrollTop; |
@@ -8080,20 +5903,14 @@ Use `noOverlap` to position the element around another element without overlappi |
this.sizingTarget.scrollTop = scrollTop; |
}, |
- /** |
- * Positions the element according to `horizontalAlign, verticalAlign`. |
- */ |
position: function() { |
if (!this.horizontalAlign && !this.verticalAlign) { |
- // needs to be centered, and it is done after constrain. |
return; |
} |
this._discoverInfo(); |
this.style.position = 'fixed'; |
- // Need border-box for margin/padding. |
this.sizingTarget.style.boxSizing = 'border-box'; |
- // Set to 0, 0 in order to discover any offset caused by parent stacking contexts. |
this.style.left = '0px'; |
this.style.top = '0px'; |
@@ -8103,7 +5920,6 @@ Use `noOverlap` to position the element around another element without overlappi |
var margin = this._fitInfo.margin; |
- // Consider the margin as part of the size for position calculations. |
var size = { |
width: rect.width + margin.left + margin.right, |
height: rect.height + margin.top + margin.bottom |
@@ -8114,7 +5930,6 @@ Use `noOverlap` to position the element around another element without overlappi |
var left = position.left + margin.left; |
var top = position.top + margin.top; |
- // Use original size (without margin). |
var right = Math.min(fitRect.right - margin.right, left + rect.width); |
var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height); |
@@ -8136,15 +5951,10 @@ Use `noOverlap` to position the element around another element without overlappi |
this.sizingTarget.style.maxWidth = (right - left) + 'px'; |
this.sizingTarget.style.maxHeight = (bottom - top) + 'px'; |
- // Remove the offset caused by any stacking context. |
this.style.left = (left - rect.left) + 'px'; |
this.style.top = (top - rect.top) + 'px'; |
}, |
- /** |
- * Constrains the size of the element to `fitInto` by setting `max-height` |
- * and/or `max-width`. |
- */ |
constrain: function() { |
if (this.horizontalAlign || this.verticalAlign) { |
return; |
@@ -8152,7 +5962,6 @@ Use `noOverlap` to position the element around another element without overlappi |
this._discoverInfo(); |
var info = this._fitInfo; |
- // position at (0px, 0px) if not already positioned, so we can measure the natural size. |
if (!info.positionedBy.vertically) { |
this.style.position = 'fixed'; |
this.style.top = '0px'; |
@@ -8162,9 +5971,7 @@ Use `noOverlap` to position the element around another element without overlappi |
this.style.left = '0px'; |
} |
- // need border-box for margin/padding |
this.sizingTarget.style.boxSizing = 'border-box'; |
- // constrain the width and height if not already set |
var rect = this.getBoundingClientRect(); |
if (!info.sizedBy.height) { |
this.__sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height'); |
@@ -8174,17 +5981,10 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * @protected |
- * @deprecated |
- */ |
_sizeDimension: function(rect, positionedBy, start, end, extent) { |
this.__sizeDimension(rect, positionedBy, start, end, extent); |
}, |
- /** |
- * @private |
- */ |
__sizeDimension: function(rect, positionedBy, start, end, extent) { |
var info = this._fitInfo; |
var fitRect = this.__getNormalizedRect(this.fitInto); |
@@ -8197,10 +5997,6 @@ Use `noOverlap` to position the element around another element without overlappi |
this.sizingTarget.style['max' + extent] = (max - margin - offset - sizingOffset) + 'px'; |
}, |
- /** |
- * Centers horizontally and vertically if not already positioned. This also sets |
- * `position:fixed`. |
- */ |
center: function() { |
if (this.horizontalAlign || this.verticalAlign) { |
return; |
@@ -8209,21 +6005,15 @@ Use `noOverlap` to position the element around another element without overlappi |
var positionedBy = this._fitInfo.positionedBy; |
if (positionedBy.vertically && positionedBy.horizontally) { |
- // Already positioned. |
return; |
} |
- // Need position:fixed to center |
this.style.position = 'fixed'; |
- // Take into account the offset caused by parents that create stacking |
- // contexts (e.g. with transform: translate3d). Translate to 0,0 and |
- // measure the bounding rect. |
if (!positionedBy.vertically) { |
this.style.top = '0px'; |
} |
if (!positionedBy.horizontally) { |
this.style.left = '0px'; |
} |
- // It will take in consideration margins and transforms |
var rect = this.getBoundingClientRect(); |
var fitRect = this.__getNormalizedRect(this.fitInto); |
if (!positionedBy.vertically) { |
@@ -8258,8 +6048,6 @@ Use `noOverlap` to position the element around another element without overlappi |
__getPosition: function(hAlign, vAlign, size, positionRect, fitRect) { |
- // All the possible configurations. |
- // Ordered as top-left, top-right, bottom-left, bottom-right. |
var positions = [{ |
verticalAlign: 'top', |
horizontalAlign: 'left', |
@@ -8283,7 +6071,6 @@ Use `noOverlap` to position the element around another element without overlappi |
}]; |
if (this.noOverlap) { |
- // Duplicate. |
for (var i = 0, l = positions.length; i < l; i++) { |
var copy = {}; |
for (var key in positions[i]) { |
@@ -8291,15 +6078,12 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
positions.push(copy); |
} |
- // Horizontal overlap only. |
positions[0].top = positions[1].top += positionRect.height; |
positions[2].top = positions[3].top -= positionRect.height; |
- // Vertical overlap only. |
positions[4].left = positions[6].left += positionRect.width; |
positions[5].left = positions[7].left -= positionRect.width; |
} |
- // Consider auto as null for coding convenience. |
vAlign = vAlign === 'auto' ? null : vAlign; |
hAlign = hAlign === 'auto' ? null : hAlign; |
@@ -8307,23 +6091,15 @@ Use `noOverlap` to position the element around another element without overlappi |
for (var i = 0; i < positions.length; i++) { |
var pos = positions[i]; |
- // If both vAlign and hAlign are defined, return exact match. |
- // For dynamicAlign and noOverlap we'll have more than one candidate, so |
- // we'll have to check the croppedArea to make the best choice. |
if (!this.dynamicAlign && !this.noOverlap && |
pos.verticalAlign === vAlign && pos.horizontalAlign === hAlign) { |
position = pos; |
break; |
} |
- // Align is ok if alignment preferences are respected. If no preferences, |
- // it is considered ok. |
var alignOk = (!vAlign || pos.verticalAlign === vAlign) && |
(!hAlign || pos.horizontalAlign === hAlign); |
- // Filter out elements that don't match the alignment (if defined). |
- // With dynamicAlign, we need to consider all the positions to find the |
- // one that minimizes the cropped area. |
if (!this.dynamicAlign && !alignOk) { |
continue; |
} |
@@ -8331,13 +6107,9 @@ Use `noOverlap` to position the element around another element without overlappi |
position = position || pos; |
pos.croppedArea = this.__getCroppedArea(pos, size, fitRect); |
var diff = pos.croppedArea - position.croppedArea; |
- // Check which crops less. If it crops equally, check if align is ok. |
if (diff < 0 || (diff === 0 && alignOk)) { |
position = pos; |
} |
- // If not cropped and respects the align requirements, keep it. |
- // This allows to prefer positions overlapping horizontally over the |
- // ones overlapping vertically. |
if (position.croppedArea === 0 && alignOk) { |
break; |
} |
@@ -8356,9 +6128,6 @@ Use `noOverlap` to position the element around another element without overlappi |
properties: { |
- /** |
- * Returns true if the backdrop is opened. |
- */ |
opened: { |
reflectToAttribute: true, |
type: Boolean, |
@@ -8373,7 +6142,6 @@ Use `noOverlap` to position the element around another element without overlappi |
}, |
created: function() { |
- // Used to cancel previous requestAnimationFrame calls when opened changes. |
this.__openedRaf = null; |
}, |
@@ -8381,32 +6149,20 @@ Use `noOverlap` to position the element around another element without overlappi |
this.opened && this._openedChanged(this.opened); |
}, |
- /** |
- * Appends the backdrop to document body if needed. |
- */ |
prepare: function() { |
if (this.opened && !this.parentNode) { |
Polymer.dom(document.body).appendChild(this); |
} |
}, |
- /** |
- * Shows the backdrop. |
- */ |
open: function() { |
this.opened = true; |
}, |
- /** |
- * Hides the backdrop. |
- */ |
close: function() { |
this.opened = false; |
}, |
- /** |
- * Removes the backdrop from document body if needed. |
- */ |
complete: function() { |
if (!this.opened && this.parentNode === document.body) { |
Polymer.dom(this.parentNode).removeChild(this); |
@@ -8419,17 +6175,10 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * @param {boolean} opened |
- * @private |
- */ |
_openedChanged: function(opened) { |
if (opened) { |
- // Auto-attach. |
this.prepare(); |
} else { |
- // Animation might be disabled via the mixin or opacity custom property. |
- // If it is disabled in other ways, it's up to the user to call complete. |
var cs = window.getComputedStyle(this); |
if (cs.transitionDuration === '0s' || cs.opacity == 0) { |
this.complete(); |
@@ -8440,12 +6189,10 @@ Use `noOverlap` to position the element around another element without overlappi |
return; |
} |
- // Always cancel previous requestAnimationFrame. |
if (this.__openedRaf) { |
window.cancelAnimationFrame(this.__openedRaf); |
this.__openedRaf = null; |
} |
- // Force relayout to ensure proper transitions. |
this.scrollTop = this.scrollTop; |
this.__openedRaf = window.requestAnimationFrame(function() { |
this.__openedRaf = null; |
@@ -8455,32 +6202,13 @@ Use `noOverlap` to position the element around another element without overlappi |
}); |
})(); |
-/** |
- * @struct |
- * @constructor |
- * @private |
- */ |
Polymer.IronOverlayManagerClass = function() { |
- /** |
- * Used to keep track of the opened overlays. |
- * @private {Array<Element>} |
- */ |
this._overlays = []; |
- /** |
- * iframes have a default z-index of 100, |
- * so this default should be at least that. |
- * @private {number} |
- */ |
this._minimumZ = 101; |
- /** |
- * Memoized backdrop element. |
- * @private {Element|null} |
- */ |
this._backdropElement = null; |
- // Enable document-wide tap recognizer. |
Polymer.Gestures.add(document, 'tap', this._onCaptureClick.bind(this)); |
document.addEventListener('focus', this._onCaptureFocus.bind(this), true); |
@@ -8491,10 +6219,6 @@ Use `noOverlap` to position the element around another element without overlappi |
constructor: Polymer.IronOverlayManagerClass, |
- /** |
- * The shared backdrop element. |
- * @type {!Element} backdropElement |
- */ |
get backdropElement() { |
if (!this._backdropElement) { |
this._backdropElement = document.createElement('iron-overlay-backdrop'); |
@@ -8502,14 +6226,7 @@ Use `noOverlap` to position the element around another element without overlappi |
return this._backdropElement; |
}, |
- /** |
- * The deepest active element. |
- * @type {!Element} activeElement the active element |
- */ |
get deepActiveElement() { |
- // document.activeElement can be null |
- // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement |
- // In case of null, default it to document.body. |
var active = document.activeElement || document.body; |
while (active.root && Polymer.dom(active.root).activeElement) { |
active = Polymer.dom(active.root).activeElement; |
@@ -8517,11 +6234,6 @@ Use `noOverlap` to position the element around another element without overlappi |
return active; |
}, |
- /** |
- * Brings the overlay at the specified index to the front. |
- * @param {number} i |
- * @private |
- */ |
_bringOverlayAtIndexToFront: function(i) { |
var overlay = this._overlays[i]; |
if (!overlay) { |
@@ -8529,21 +6241,17 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
var lastI = this._overlays.length - 1; |
var currentOverlay = this._overlays[lastI]; |
- // Ensure always-on-top overlay stays on top. |
if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) { |
lastI--; |
} |
- // If already the top element, return. |
if (i >= lastI) { |
return; |
} |
- // Update z-index to be on top. |
var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); |
if (this._getZ(overlay) <= minimumZ) { |
this._applyOverlayZ(overlay, minimumZ); |
} |
- // Shift other overlays behind the new on top. |
while (i < lastI) { |
this._overlays[i] = this._overlays[i + 1]; |
i++; |
@@ -8551,11 +6259,6 @@ Use `noOverlap` to position the element around another element without overlappi |
this._overlays[lastI] = overlay; |
}, |
- /** |
- * Adds the overlay and updates its z-index if it's opened, or removes it if it's closed. |
- * Also updates the backdrop z-index. |
- * @param {!Element} overlay |
- */ |
addOrRemoveOverlay: function(overlay) { |
if (overlay.opened) { |
this.addOverlay(overlay); |
@@ -8564,11 +6267,6 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * Tracks overlays for z-index and focus management. |
- * Ensures the last added overlay with always-on-top remains on top. |
- * @param {!Element} overlay |
- */ |
addOverlay: function(overlay) { |
var i = this._overlays.indexOf(overlay); |
if (i >= 0) { |
@@ -8581,17 +6279,13 @@ Use `noOverlap` to position the element around another element without overlappi |
var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ); |
var newZ = this._getZ(overlay); |
- // Ensure always-on-top overlay stays on top. |
if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) { |
- // This bumps the z-index of +2. |
this._applyOverlayZ(currentOverlay, minimumZ); |
insertionIndex--; |
- // Update minimumZ to match previous overlay's z-index. |
var previousOverlay = this._overlays[insertionIndex - 1]; |
minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ); |
} |
- // Update z-index and insert overlay. |
if (newZ <= minimumZ) { |
this._applyOverlayZ(overlay, minimumZ); |
} |
@@ -8600,9 +6294,6 @@ Use `noOverlap` to position the element around another element without overlappi |
this.trackBackdrop(); |
}, |
- /** |
- * @param {!Element} overlay |
- */ |
removeOverlay: function(overlay) { |
var i = this._overlays.indexOf(overlay); |
if (i === -1) { |
@@ -8613,28 +6304,15 @@ Use `noOverlap` to position the element around another element without overlappi |
this.trackBackdrop(); |
}, |
- /** |
- * Returns the current overlay. |
- * @return {Element|undefined} |
- */ |
currentOverlay: function() { |
var i = this._overlays.length - 1; |
return this._overlays[i]; |
}, |
- /** |
- * Returns the current overlay z-index. |
- * @return {number} |
- */ |
currentOverlayZ: function() { |
return this._getZ(this.currentOverlay()); |
}, |
- /** |
- * Ensures that the minimum z-index of new overlays is at least `minimumZ`. |
- * This does not effect the z-index of any existing overlays. |
- * @param {number} minimumZ |
- */ |
ensureMinimumZ: function(minimumZ) { |
this._minimumZ = Math.max(this._minimumZ, minimumZ); |
}, |
@@ -8646,12 +6324,8 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * Updates the backdrop z-index. |
- */ |
trackBackdrop: function() { |
var overlay = this._overlayWithBackdrop(); |
- // Avoid creating the backdrop if there is no overlay with backdrop. |
if (!overlay && !this._backdropElement) { |
return; |
} |
@@ -8659,9 +6333,6 @@ Use `noOverlap` to position the element around another element without overlappi |
this.backdropElement.opened = !!overlay; |
}, |
- /** |
- * @return {Array<Element>} |
- */ |
getBackdrops: function() { |
var backdrops = []; |
for (var i = 0; i < this._overlays.length; i++) { |
@@ -8672,19 +6343,10 @@ Use `noOverlap` to position the element around another element without overlappi |
return backdrops; |
}, |
- /** |
- * Returns the z-index for the backdrop. |
- * @return {number} |
- */ |
backdropZ: function() { |
return this._getZ(this._overlayWithBackdrop()) - 1; |
}, |
- /** |
- * Returns the first opened overlay that has a backdrop. |
- * @return {Element|undefined} |
- * @private |
- */ |
_overlayWithBackdrop: function() { |
for (var i = 0; i < this._overlays.length; i++) { |
if (this._overlays[i].withBackdrop) { |
@@ -8693,17 +6355,10 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * Calculates the minimum z-index for the overlay. |
- * @param {Element=} overlay |
- * @private |
- */ |
_getZ: function(overlay) { |
var z = this._minimumZ; |
if (overlay) { |
var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay).zIndex); |
- // Check if is a number |
- // Number.isNaN not supported in IE 10+ |
if (z1 === z1) { |
z = z1; |
} |
@@ -8711,31 +6366,14 @@ Use `noOverlap` to position the element around another element without overlappi |
return z; |
}, |
- /** |
- * @param {!Element} element |
- * @param {number|string} z |
- * @private |
- */ |
_setZ: function(element, z) { |
element.style.zIndex = z; |
}, |
- /** |
- * @param {!Element} overlay |
- * @param {number} aboveZ |
- * @private |
- */ |
_applyOverlayZ: function(overlay, aboveZ) { |
this._setZ(overlay, aboveZ + 2); |
}, |
- /** |
- * Returns the deepest overlay in the path. |
- * @param {Array<Element>=} path |
- * @return {Element|undefined} |
- * @suppress {missingProperties} |
- * @private |
- */ |
_overlayInPath: function(path) { |
path = path || []; |
for (var i = 0; i < path.length; i++) { |
@@ -8745,24 +6383,13 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * Ensures the click event is delegated to the right overlay. |
- * @param {!Event} event |
- * @private |
- */ |
_onCaptureClick: function(event) { |
var overlay = /** @type {?} */ (this.currentOverlay()); |
- // Check if clicked outside of top overlay. |
if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) { |
overlay._onCaptureClick(event); |
} |
}, |
- /** |
- * Ensures the focus event is delegated to the right overlay. |
- * @param {!Event} event |
- * @private |
- */ |
_onCaptureFocus: function(event) { |
var overlay = /** @type {?} */ (this.currentOverlay()); |
if (overlay) { |
@@ -8770,11 +6397,6 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * Ensures TAB and ESC keyboard events are delegated to the right overlay. |
- * @param {!Event} event |
- * @private |
- */ |
_onCaptureKeyDown: function(event) { |
var overlay = /** @type {?} */ (this.currentOverlay()); |
if (overlay) { |
@@ -8786,14 +6408,6 @@ Use `noOverlap` to position the element around another element without overlappi |
} |
}, |
- /** |
- * Returns if the overlay1 should be behind overlay2. |
- * @param {!Element} overlay1 |
- * @param {!Element} overlay2 |
- * @return {boolean} |
- * @suppress {missingProperties} |
- * @private |
- */ |
_shouldBeBehindOverlay: function(overlay1, overlay2) { |
return !overlay1.alwaysOnTop && overlay2.alwaysOnTop; |
} |
@@ -8803,56 +6417,11 @@ Use `noOverlap` to position the element around another element without overlappi |
(function() { |
'use strict'; |
-/** |
-Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or shown, and displays |
-on top of other content. It includes an optional backdrop, and can be used to implement a variety |
-of UI controls including dialogs and drop downs. Multiple overlays may be displayed at once. |
- |
-See the [demo source code](https://github.com/PolymerElements/iron-overlay-behavior/blob/master/demo/simple-overlay.html) |
-for an example. |
- |
-### Closing and canceling |
- |
-An overlay may be hidden by closing or canceling. The difference between close and cancel is user |
-intent. Closing generally implies that the user acknowledged the content on the overlay. By default, |
-it will cancel whenever the user taps outside it or presses the escape key. This behavior is |
-configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties. |
-`close()` should be called explicitly by the implementer when the user interacts with a control |
-in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled' |
-event. Call `preventDefault` on this event to prevent the overlay from closing. |
- |
-### Positioning |
- |
-By default the element is sized and positioned to fit and centered inside the window. You can |
-position and size it manually using CSS. See `Polymer.IronFitBehavior`. |
- |
-### Backdrop |
- |
-Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is |
-appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling |
-options. |
- |
-In addition, `with-backdrop` will wrap the focus within the content in the light DOM. |
-Override the [`_focusableNodes` getter](#Polymer.IronOverlayBehavior:property-_focusableNodes) |
-to achieve a different behavior. |
- |
-### Limitations |
- |
-The element is styled to appear on top of other content by setting its `z-index` property. You |
-must ensure no element has a stacking context with a higher `z-index` than its parent stacking |
-context. You should place this element as a child of `<body>` whenever possible. |
- |
-@demo demo/index.html |
-@polymerBehavior Polymer.IronOverlayBehavior |
-*/ |
Polymer.IronOverlayBehaviorImpl = { |
properties: { |
- /** |
- * True if the overlay is currently displayed. |
- */ |
opened: { |
observer: '_openedChanged', |
type: Boolean, |
@@ -8860,9 +6429,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
notify: true |
}, |
- /** |
- * True if the overlay was canceled when it was last closed. |
- */ |
canceled: { |
observer: '_canceledChanged', |
readOnly: true, |
@@ -8870,80 +6436,44 @@ context. You should place this element as a child of `<body>` whenever possible. |
value: false |
}, |
- /** |
- * Set to true to display a backdrop behind the overlay. It traps the focus |
- * within the light DOM of the overlay. |
- */ |
withBackdrop: { |
observer: '_withBackdropChanged', |
type: Boolean |
}, |
- /** |
- * Set to true to disable auto-focusing the overlay or child nodes with |
- * the `autofocus` attribute` when the overlay is opened. |
- */ |
noAutoFocus: { |
type: Boolean, |
value: false |
}, |
- /** |
- * Set to true to disable canceling the overlay with the ESC key. |
- */ |
noCancelOnEscKey: { |
type: Boolean, |
value: false |
}, |
- /** |
- * Set to true to disable canceling the overlay by clicking outside it. |
- */ |
noCancelOnOutsideClick: { |
type: Boolean, |
value: false |
}, |
- /** |
- * Contains the reason(s) this overlay was last closed (see `iron-overlay-closed`). |
- * `IronOverlayBehavior` provides the `canceled` reason; implementers of the |
- * behavior can provide other reasons in addition to `canceled`. |
- */ |
closingReason: { |
- // was a getter before, but needs to be a property so other |
- // behaviors can override this. |
type: Object |
}, |
- /** |
- * Set to true to enable restoring of focus when overlay is closed. |
- */ |
restoreFocusOnClose: { |
type: Boolean, |
value: false |
}, |
- /** |
- * Set to true to keep overlay always on top. |
- */ |
alwaysOnTop: { |
type: Boolean |
}, |
- /** |
- * Shortcut to access to the overlay manager. |
- * @private |
- * @type {Polymer.IronOverlayManagerClass} |
- */ |
_manager: { |
type: Object, |
value: Polymer.IronOverlayManager |
}, |
- /** |
- * The node being focused. |
- * @type {?Node} |
- */ |
_focusedChild: { |
type: Object |
} |
@@ -8954,34 +6484,15 @@ context. You should place this element as a child of `<body>` whenever possible. |
'iron-resize': '_onIronResize' |
}, |
- /** |
- * The backdrop element. |
- * @type {Element} |
- */ |
get backdropElement() { |
return this._manager.backdropElement; |
}, |
- /** |
- * Returns the node to give focus to. |
- * @type {Node} |
- */ |
get _focusNode() { |
return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this; |
}, |
- /** |
- * Array of nodes that can receive focus (overlay included), ordered by `tabindex`. |
- * This is used to retrieve which is the first and last focusable nodes in order |
- * to wrap the focus for overlays `with-backdrop`. |
- * |
- * If you know what is your content (specifically the first and last focusable children), |
- * you can override this method to return only `[firstFocusable, lastFocusable];` |
- * @type {Array<Node>} |
- * @protected |
- */ |
get _focusableNodes() { |
- // Elements that can be focused even if they have [disabled] attribute. |
var FOCUSABLE_WITH_DISABLED = [ |
'a[href]', |
'area[href]', |
@@ -8990,7 +6501,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
'[contentEditable=true]' |
]; |
- // Elements that cannot be focused if they have [disabled] attribute. |
var FOCUSABLE_WITHOUT_DISABLED = [ |
'input', |
'select', |
@@ -8998,7 +6508,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
'button' |
]; |
- // Discard elements with tabindex=-1 (makes them not focusable). |
var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') + |
':not([tabindex="-1"]),' + |
FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),') + |
@@ -9006,11 +6515,8 @@ context. You should place this element as a child of `<body>` whenever possible. |
var focusables = Polymer.dom(this).querySelectorAll(selector); |
if (this.tabIndex >= 0) { |
- // Insert at the beginning because we might have all elements with tabIndex = 0, |
- // and the overlay should be the first of the list. |
focusables.splice(0, 0, this); |
} |
- // Sort by tabindex. |
return focusables.sort(function (a, b) { |
if (a.tabIndex === b.tabIndex) { |
return 0; |
@@ -9023,22 +6529,15 @@ context. You should place this element as a child of `<body>` whenever possible. |
}, |
ready: function() { |
- // Used to skip calls to notifyResize and refit while the overlay is animating. |
this.__isAnimating = false; |
- // with-backdrop needs tabindex to be set in order to trap the focus. |
- // If it is not set, IronOverlayBehavior will set it, and remove it if with-backdrop = false. |
this.__shouldRemoveTabIndex = false; |
- // Used for wrapping the focus on TAB / Shift+TAB. |
this.__firstFocusableNode = this.__lastFocusableNode = null; |
- // Used by __onNextAnimationFrame to cancel any previous callback. |
this.__raf = null; |
- // Focused node before overlay gets opened. Can be restored on close. |
this.__restoreFocusNode = null; |
this._ensureSetup(); |
}, |
attached: function() { |
- // Call _openedChanged here so that position can be computed correctly. |
if (this.opened) { |
this._openedChanged(this.opened); |
} |
@@ -9055,34 +6554,21 @@ context. You should place this element as a child of `<body>` whenever possible. |
this._manager.removeOverlay(this); |
}, |
- /** |
- * Toggle the opened state of the overlay. |
- */ |
toggle: function() { |
this._setCanceled(false); |
this.opened = !this.opened; |
}, |
- /** |
- * Open the overlay. |
- */ |
open: function() { |
this._setCanceled(false); |
this.opened = true; |
}, |
- /** |
- * Close the overlay. |
- */ |
close: function() { |
this._setCanceled(false); |
this.opened = false; |
}, |
- /** |
- * Cancels the overlay. |
- * @param {Event=} event The original event |
- */ |
cancel: function(event) { |
var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: true}); |
if (cancelEvent.defaultPrevented) { |
@@ -9102,11 +6588,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
this.style.display = 'none'; |
}, |
- /** |
- * Called when `opened` changes. |
- * @param {boolean=} opened |
- * @protected |
- */ |
_openedChanged: function(opened) { |
if (opened) { |
this.removeAttribute('aria-hidden'); |
@@ -9114,15 +6595,12 @@ context. You should place this element as a child of `<body>` whenever possible. |
this.setAttribute('aria-hidden', 'true'); |
} |
- // Defer any animation-related code on attached |
- // (_openedChanged gets called again on attached). |
if (!this.isAttached) { |
return; |
} |
this.__isAnimating = true; |
- // Use requestAnimationFrame for non-blocking rendering. |
this.__onNextAnimationFrame(this.__openedChanged); |
}, |
@@ -9132,7 +6610,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
}, |
_withBackdropChanged: function() { |
- // If tabindex is already set, no need to override it. |
if (this.withBackdrop && !this.hasAttribute('tabindex')) { |
this.setAttribute('tabindex', '-1'); |
this.__shouldRemoveTabIndex = true; |
@@ -9145,53 +6622,31 @@ context. You should place this element as a child of `<body>` whenever possible. |
} |
}, |
- /** |
- * tasks which must occur before opening; e.g. making the element visible. |
- * @protected |
- */ |
_prepareRenderOpened: function() { |
- // Store focused node. |
this.__restoreFocusNode = this._manager.deepActiveElement; |
- // Needed to calculate the size of the overlay so that transitions on its size |
- // will have the correct starting points. |
this._preparePositioning(); |
this.refit(); |
this._finishPositioning(); |
- // Safari will apply the focus to the autofocus element when displayed |
- // for the first time, so we make sure to return the focus where it was. |
if (this.noAutoFocus && document.activeElement === this._focusNode) { |
this._focusNode.blur(); |
this.__restoreFocusNode.focus(); |
} |
}, |
- /** |
- * Tasks which cause the overlay to actually open; typically play an animation. |
- * @protected |
- */ |
_renderOpened: function() { |
this._finishRenderOpened(); |
}, |
- /** |
- * Tasks which cause the overlay to actually close; typically play an animation. |
- * @protected |
- */ |
_renderClosed: function() { |
this._finishRenderClosed(); |
}, |
- /** |
- * Tasks to be performed at the end of open action. Will fire `iron-overlay-opened`. |
- * @protected |
- */ |
_finishRenderOpened: function() { |
this.notifyResize(); |
this.__isAnimating = false; |
- // Store it so we don't query too much. |
var focusableNodes = this._focusableNodes; |
this.__firstFocusableNode = focusableNodes[0]; |
this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; |
@@ -9199,14 +6654,8 @@ context. You should place this element as a child of `<body>` whenever possible. |
this.fire('iron-overlay-opened'); |
}, |
- /** |
- * Tasks to be performed at the end of close action. Will fire `iron-overlay-closed`. |
- * @protected |
- */ |
_finishRenderClosed: function() { |
- // Hide the overlay. |
this.style.display = 'none'; |
- // Reset z-index only at the end of the animation. |
this.style.zIndex = ''; |
this.notifyResize(); |
this.__isAnimating = false; |
@@ -9220,24 +6669,14 @@ context. You should place this element as a child of `<body>` whenever possible. |
}, |
_finishPositioning: function() { |
- // First, make it invisible & reactivate animations. |
this.style.display = 'none'; |
- // Force reflow before re-enabling animations so that they don't start. |
- // Set scrollTop to itself so that Closure Compiler doesn't remove this. |
this.scrollTop = this.scrollTop; |
this.style.transition = this.style.webkitTransition = ''; |
this.style.transform = this.style.webkitTransform = ''; |
- // Now that animations are enabled, make it visible again |
this.style.display = ''; |
- // Force reflow, so that following animations are properly started. |
- // Set scrollTop to itself so that Closure Compiler doesn't remove this. |
this.scrollTop = this.scrollTop; |
}, |
- /** |
- * Applies focus according to the opened state. |
- * @protected |
- */ |
_applyFocus: function() { |
if (this.opened) { |
if (!this.noAutoFocus) { |
@@ -9247,14 +6686,10 @@ context. You should place this element as a child of `<body>` whenever possible. |
else { |
this._focusNode.blur(); |
this._focusedChild = null; |
- // Restore focus. |
if (this.restoreFocusOnClose && this.__restoreFocusNode) { |
this.__restoreFocusNode.focus(); |
} |
this.__restoreFocusNode = null; |
- // If many overlays get closed at the same time, one of them would still |
- // be the currentOverlay even if already closed, and would call _applyFocus |
- // infinitely, so we check for this not to be the current overlay. |
var currentOverlay = this._manager.currentOverlay(); |
if (currentOverlay && this !== currentOverlay) { |
currentOverlay._applyFocus(); |
@@ -9262,22 +6697,12 @@ context. You should place this element as a child of `<body>` whenever possible. |
} |
}, |
- /** |
- * Cancels (closes) the overlay. Call when click happens outside the overlay. |
- * @param {!Event} event |
- * @protected |
- */ |
_onCaptureClick: function(event) { |
if (!this.noCancelOnOutsideClick) { |
this.cancel(event); |
} |
}, |
- /** |
- * Keeps track of the focused child. If withBackdrop, traps focus within overlay. |
- * @param {!Event} event |
- * @protected |
- */ |
_onCaptureFocus: function (event) { |
if (!this.withBackdrop) { |
return; |
@@ -9291,103 +6716,54 @@ context. You should place this element as a child of `<body>` whenever possible. |
} |
}, |
- /** |
- * Handles the ESC key event and cancels (closes) the overlay. |
- * @param {!Event} event |
- * @protected |
- */ |
_onCaptureEsc: function(event) { |
if (!this.noCancelOnEscKey) { |
this.cancel(event); |
} |
}, |
- /** |
- * Handles TAB key events to track focus changes. |
- * Will wrap focus for overlays withBackdrop. |
- * @param {!Event} event |
- * @protected |
- */ |
_onCaptureTab: function(event) { |
if (!this.withBackdrop) { |
return; |
} |
- // TAB wraps from last to first focusable. |
- // Shift + TAB wraps from first to last focusable. |
var shift = event.shiftKey; |
var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode; |
var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode; |
var shouldWrap = false; |
if (nodeToCheck === nodeToSet) { |
- // If nodeToCheck is the same as nodeToSet, it means we have an overlay |
- // with 0 or 1 focusables; in either case we still need to trap the |
- // focus within the overlay. |
shouldWrap = true; |
} else { |
- // In dom=shadow, the manager will receive focus changes on the main |
- // root but not the ones within other shadow roots, so we can't rely on |
- // _focusedChild, but we should check the deepest active element. |
var focusedNode = this._manager.deepActiveElement; |
- // If the active element is not the nodeToCheck but the overlay itself, |
- // it means the focus is about to go outside the overlay, hence we |
- // should prevent that (e.g. user opens the overlay and hit Shift+TAB). |
shouldWrap = (focusedNode === nodeToCheck || focusedNode === this); |
} |
if (shouldWrap) { |
- // When the overlay contains the last focusable element of the document |
- // and it's already focused, pressing TAB would move the focus outside |
- // the document (e.g. to the browser search bar). Similarly, when the |
- // overlay contains the first focusable element of the document and it's |
- // already focused, pressing Shift+TAB would move the focus outside the |
- // document (e.g. to the browser search bar). |
- // In both cases, we would not receive a focus event, but only a blur. |
- // In order to achieve focus wrapping, we prevent this TAB event and |
- // force the focus. This will also prevent the focus to temporarily move |
- // outside the overlay, which might cause scrolling. |
event.preventDefault(); |
this._focusedChild = nodeToSet; |
this._applyFocus(); |
} |
}, |
- /** |
- * Refits if the overlay is opened and not animating. |
- * @protected |
- */ |
_onIronResize: function() { |
if (this.opened && !this.__isAnimating) { |
this.__onNextAnimationFrame(this.refit); |
} |
}, |
- /** |
- * Will call notifyResize if overlay is opened. |
- * Can be overridden in order to avoid multiple observers on the same node. |
- * @protected |
- */ |
_onNodesChange: function() { |
if (this.opened && !this.__isAnimating) { |
this.notifyResize(); |
} |
}, |
- /** |
- * Tasks executed when opened changes: prepare for the opening, move the |
- * focus, update the manager, render opened/closed. |
- * @private |
- */ |
__openedChanged: function() { |
if (this.opened) { |
- // Make overlay visible, then add it to the manager. |
this._prepareRenderOpened(); |
this._manager.addOverlay(this); |
- // Move the focus to the child node with [autofocus]. |
this._applyFocus(); |
this._renderOpened(); |
} else { |
- // Remove overlay, then restore the focus before actually closing. |
this._manager.removeOverlay(this); |
this._applyFocus(); |
@@ -9395,14 +6771,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
} |
}, |
- /** |
- * Executes a callback on the next animation frame, overriding any previous |
- * callback awaiting for the next animation frame. e.g. |
- * `__onNextAnimationFrame(callback1) && __onNextAnimationFrame(callback2)`; |
- * `callback1` will never be invoked. |
- * @param {!Function} callback Its `this` parameter is the overlay itself. |
- * @private |
- */ |
__onNextAnimationFrame: function(callback) { |
if (this.__raf) { |
window.cancelAnimationFrame(this.__raf); |
@@ -9419,56 +6787,23 @@ context. You should place this element as a child of `<body>` whenever possible. |
/** @polymerBehavior */ |
Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl]; |
- /** |
- * Fired after the overlay opens. |
- * @event iron-overlay-opened |
- */ |
- |
- /** |
- * Fired when the overlay is canceled, but before it is closed. |
- * @event iron-overlay-canceled |
- * @param {Event} event The closing of the overlay can be prevented |
- * by calling `event.preventDefault()`. The `event.detail` is the original event that |
- * originated the canceling (e.g. ESC keyboard event or click event outside the overlay). |
- */ |
- |
- /** |
- * Fired after the overlay closes. |
- * @event iron-overlay-closed |
- * @param {Event} event The `event.detail` is the `closingReason` property |
- * (contains `canceled`, whether the overlay was canceled). |
- */ |
+ |
+ |
})(); |
-/** |
- * `Polymer.NeonAnimatableBehavior` is implemented by elements containing animations for use with |
- * elements implementing `Polymer.NeonAnimationRunnerBehavior`. |
- * @polymerBehavior |
- */ |
Polymer.NeonAnimatableBehavior = { |
properties: { |
- /** |
- * Animation configuration. See README for more info. |
- */ |
animationConfig: { |
type: Object |
}, |
- /** |
- * Convenience property for setting an 'entry' animation. Do not set `animationConfig.entry` |
- * manually if using this. The animated node is set to `this` if using this property. |
- */ |
entryAnimation: { |
observer: '_entryAnimationChanged', |
type: String |
}, |
- /** |
- * Convenience property for setting an 'exit' animation. Do not set `animationConfig.exit` |
- * manually if using this. The animated node is set to `this` if using this property. |
- */ |
exitAnimation: { |
observer: '_exitAnimationChanged', |
type: String |
@@ -9493,7 +6828,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
}, |
_copyProperties: function(config1, config2) { |
- // shallowly copy properties from config2 to config1 |
for (var property in config2) { |
config1[property] = config2[property]; |
} |
@@ -9517,7 +6851,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
return; |
} |
- // type is optional |
var thisConfig; |
if (type) { |
thisConfig = this.animationConfig[type]; |
@@ -9529,7 +6862,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
thisConfig = [thisConfig]; |
} |
- // iterate animations and recurse to process configurations from child nodes |
if (thisConfig) { |
for (var config, index = 0; config = thisConfig[index]; index++) { |
if (config.animatable) { |
@@ -9538,14 +6870,12 @@ context. You should place this element as a child of `<body>` whenever possible. |
if (config.id) { |
var cachedConfig = map[config.id]; |
if (cachedConfig) { |
- // merge configurations with the same id, making a clone lazily |
if (!cachedConfig.isClone) { |
map[config.id] = this._cloneConfig(cachedConfig) |
cachedConfig = map[config.id]; |
} |
this._copyProperties(cachedConfig, config); |
} else { |
- // put any configs with an id into a map |
map[config.id] = config; |
} |
} else { |
@@ -9556,17 +6886,10 @@ context. You should place this element as a child of `<body>` whenever possible. |
} |
}, |
- /** |
- * An element implementing `Polymer.NeonAnimationRunnerBehavior` calls this method to configure |
- * an animation with an optional type. Elements implementing `Polymer.NeonAnimatableBehavior` |
- * should define the property `animationConfig`, which is either a configuration object |
- * or a map of animation type to array of configuration objects. |
- */ |
getAnimationConfig: function(type) { |
var map = {}; |
var allConfigs = []; |
this._getAnimationConfigRecursive(type, map, allConfigs); |
- // append the configurations saved in the map to the array |
for (var key in map) { |
allConfigs.push(map[key]); |
} |
@@ -9574,11 +6897,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
} |
}; |
-/** |
- * `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations. |
- * |
- * @polymerBehavior Polymer.NeonAnimationRunnerBehavior |
- */ |
Polymer.NeonAnimationRunnerBehaviorImpl = { |
_configureAnimations: function(configs) { |
@@ -9586,13 +6904,10 @@ context. You should place this element as a child of `<body>` whenever possible. |
if (configs.length > 0) { |
for (var config, index = 0; config = configs[index]; index++) { |
var neonAnimation = document.createElement(config.name); |
- // is this element actually a neon animation? |
if (neonAnimation.isNeonAnimation) { |
var result = null; |
- // configuration or play could fail if polyfills aren't loaded |
try { |
result = neonAnimation.configure(config); |
- // Check if we have an Effect rather than an Animation |
if (typeof result.cancel != 'function') { |
result = document.timeline.play(result); |
} |
@@ -9635,11 +6950,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
} |
}, |
- /** |
- * Plays an animation with an optional `type`. |
- * @param {string=} type |
- * @param {!Object=} cookie |
- */ |
playAnimation: function(type, cookie) { |
var configs = this.getAnimationConfig(type); |
if (!configs) { |
@@ -9671,9 +6981,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
} |
}, |
- /** |
- * Cancels the currently running animations. |
- */ |
cancelAnimation: function() { |
for (var k in this._animations) { |
this._animations[k].cancel(); |
@@ -9687,17 +6994,10 @@ context. You should place this element as a child of `<body>` whenever possible. |
Polymer.NeonAnimatableBehavior, |
Polymer.NeonAnimationRunnerBehaviorImpl |
]; |
-/** |
- * Use `Polymer.NeonAnimationBehavior` to implement an animation. |
- * @polymerBehavior |
- */ |
Polymer.NeonAnimationBehavior = { |
properties: { |
- /** |
- * Defines the animation timing. |
- */ |
animationTiming: { |
type: Object, |
value: function() { |
@@ -9711,21 +7011,9 @@ context. You should place this element as a child of `<body>` whenever possible. |
}, |
- /** |
- * Can be used to determine that elements implement this behavior. |
- */ |
isNeonAnimation: true, |
- /** |
- * Do any animation configuration here. |
- */ |
- // configure: function(config) { |
- // }, |
- /** |
- * Returns the animation timing by mixing in properties from `config` to the defaults defined |
- * by the animation. |
- */ |
timingFromConfig: function(config) { |
if (config.timing) { |
for (var property in config.timing) { |
@@ -9735,9 +7023,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
return this.animationTiming; |
}, |
- /** |
- * Sets `transform` and `transformOrigin` properties along with the prefixed versions. |
- */ |
setPrefixedProperty: function(node, property, value) { |
var map = { |
'transform': ['webkitTransform'], |
@@ -9750,9 +7035,6 @@ context. You should place this element as a child of `<body>` whenever possible. |
node.style[property] = value; |
}, |
- /** |
- * Called when the animation finishes. |
- */ |
complete: function() {} |
}; |
@@ -9781,38 +7063,20 @@ Polymer({ |
}); |
(function() { |
'use strict'; |
- // Used to calculate the scroll direction during touch events. |
var LAST_TOUCH_POSITION = { |
pageX: 0, |
pageY: 0 |
}; |
- // Used to avoid computing event.path and filter scrollable nodes (better perf). |
var ROOT_TARGET = null; |
var SCROLLABLE_NODES = []; |
- /** |
- * The IronDropdownScrollManager is intended to provide a central source |
- * of authority and control over which elements in a document are currently |
- * allowed to scroll. |
- */ |
Polymer.IronDropdownScrollManager = { |
- /** |
- * The current element that defines the DOM boundaries of the |
- * scroll lock. This is always the most recently locking element. |
- */ |
get currentLockingElement() { |
return this._lockingElements[this._lockingElements.length - 1]; |
}, |
- /** |
- * Returns true if the provided element is "scroll locked", which is to |
- * say that it cannot be scrolled via pointer or keyboard interactions. |
- * |
- * @param {HTMLElement} element An HTML element instance which may or may |
- * not be scroll locked. |
- */ |
elementIsScrollLocked: function(element) { |
var currentLockingElement = this.currentLockingElement; |
@@ -9842,18 +7106,7 @@ Polymer({ |
return scrollLocked; |
}, |
- /** |
- * Push an element onto the current scroll lock stack. The most recently |
- * pushed element and its children will be considered scrollable. All |
- * other elements will not be scrollable. |
- * |
- * Scroll locking is implemented as a stack so that cases such as |
- * dropdowns within dropdowns are handled well. |
- * |
- * @param {HTMLElement} element The element that should lock scroll. |
- */ |
pushScrollLock: function(element) { |
- // Prevent pushing the same element twice |
if (this._lockingElements.indexOf(element) >= 0) { |
return; |
} |
@@ -9868,15 +7121,6 @@ Polymer({ |
this._unlockedElementCache = []; |
}, |
- /** |
- * Remove an element from the scroll lock stack. The element being |
- * removed does not need to be the most recently pushed element. However, |
- * the scroll lock constraints only change when the most recently pushed |
- * element is removed. |
- * |
- * @param {HTMLElement} element The element to remove from the scroll |
- * lock stack. |
- */ |
removeScrollLock: function(element) { |
var index = this._lockingElements.indexOf(element); |
@@ -9909,12 +7153,6 @@ Polymer({ |
}, |
_composedTreeContains: function(element, child) { |
- // NOTE(cdata): This method iterates over content elements and their |
- // corresponding distributed nodes to implement a contains-like method |
- // that pierces through the composed tree of the ShadowDOM. Results of |
- // this operation are cached (elsewhere) on a per-scroll-lock basis, to |
- // guard against potentially expensive lookups happening repeatedly as |
- // a user scrolls / touchmoves. |
var contentElements; |
var distributedNodes; |
var contentIndex; |
@@ -9942,12 +7180,9 @@ Polymer({ |
}, |
_scrollInteractionHandler: function(event) { |
- // Avoid canceling an event with cancelable=false, e.g. scrolling is in |
- // progress and cannot be interrupted. |
if (event.cancelable && this._shouldPreventScrolling(event)) { |
event.preventDefault(); |
} |
- // If event has targetTouches (touch event), update last touch position. |
if (event.targetTouches) { |
var touch = event.targetTouches[0]; |
LAST_TOUCH_POSITION.pageX = touch.pageX; |
@@ -9958,15 +7193,10 @@ Polymer({ |
_lockScrollInteractions: function() { |
this._boundScrollHandler = this._boundScrollHandler || |
this._scrollInteractionHandler.bind(this); |
- // Modern `wheel` event for mouse wheel scrolling: |
document.addEventListener('wheel', this._boundScrollHandler, true); |
- // Older, non-standard `mousewheel` event for some FF: |
document.addEventListener('mousewheel', this._boundScrollHandler, true); |
- // IE: |
document.addEventListener('DOMMouseScroll', this._boundScrollHandler, true); |
- // Save the SCROLLABLE_NODES on touchstart, to be used on touchmove. |
document.addEventListener('touchstart', this._boundScrollHandler, true); |
- // Mobile devices can scroll on touch move: |
document.addEventListener('touchmove', this._boundScrollHandler, true); |
}, |
@@ -9978,57 +7208,32 @@ Polymer({ |
document.removeEventListener('touchmove', this._boundScrollHandler, true); |
}, |
- /** |
- * Returns true if the event causes scroll outside the current locking |
- * element, e.g. pointer/keyboard interactions, or scroll "leaking" |
- * outside the locking element when it is already at its scroll boundaries. |
- * @param {!Event} event |
- * @return {boolean} |
- * @private |
- */ |
_shouldPreventScrolling: function(event) { |
- // Update if root target changed. For touch events, ensure we don't |
- // update during touchmove. |
var target = Polymer.dom(event).rootTarget; |
if (event.type !== 'touchmove' && ROOT_TARGET !== target) { |
ROOT_TARGET = target; |
SCROLLABLE_NODES = this._getScrollableNodes(Polymer.dom(event).path); |
} |
- // Prevent event if no scrollable nodes. |
if (!SCROLLABLE_NODES.length) { |
return true; |
} |
- // Don't prevent touchstart event inside the locking element when it has |
- // scrollable nodes. |
if (event.type === 'touchstart') { |
return false; |
} |
- // Get deltaX/Y. |
var info = this._getScrollInfo(event); |
- // Prevent if there is no child that can scroll. |
return !this._getScrollingNode(SCROLLABLE_NODES, info.deltaX, info.deltaY); |
}, |
- /** |
- * Returns an array of scrollable nodes up to the current locking element, |
- * which is included too if scrollable. |
- * @param {!Array<Node>} nodes |
- * @return {Array<Node>} scrollables |
- * @private |
- */ |
_getScrollableNodes: function(nodes) { |
var scrollables = []; |
var lockingIndex = nodes.indexOf(this.currentLockingElement); |
- // Loop from root target to locking element (included). |
for (var i = 0; i <= lockingIndex; i++) { |
var node = nodes[i]; |
- // Skip document fragments. |
if (node.nodeType === 11) { |
continue; |
} |
- // Check inline style before checking computed style. |
var style = node.style; |
if (style.overflow !== 'scroll' && style.overflow !== 'auto') { |
style = window.getComputedStyle(node); |
@@ -10040,32 +7245,18 @@ Polymer({ |
return scrollables; |
}, |
- /** |
- * Returns the node that is scrolling. If there is no scrolling, |
- * returns undefined. |
- * @param {!Array<Node>} nodes |
- * @param {number} deltaX Scroll delta on the x-axis |
- * @param {number} deltaY Scroll delta on the y-axis |
- * @return {Node|undefined} |
- * @private |
- */ |
_getScrollingNode: function(nodes, deltaX, deltaY) { |
- // No scroll. |
if (!deltaX && !deltaY) { |
return; |
} |
- // Check only one axis according to where there is more scroll. |
- // Prefer vertical to horizontal. |
var verticalScroll = Math.abs(deltaY) >= Math.abs(deltaX); |
for (var i = 0; i < nodes.length; i++) { |
var node = nodes[i]; |
var canScroll = false; |
if (verticalScroll) { |
- // delta < 0 is scroll up, delta > 0 is scroll down. |
canScroll = deltaY < 0 ? node.scrollTop > 0 : |
node.scrollTop < node.scrollHeight - node.clientHeight; |
} else { |
- // delta < 0 is scroll left, delta > 0 is scroll right. |
canScroll = deltaX < 0 ? node.scrollLeft > 0 : |
node.scrollLeft < node.scrollWidth - node.clientWidth; |
} |
@@ -10075,42 +7266,24 @@ Polymer({ |
} |
}, |
- /** |
- * Returns scroll `deltaX` and `deltaY`. |
- * @param {!Event} event The scroll event |
- * @return {{ |
- * deltaX: number The x-axis scroll delta (positive: scroll right, |
- * negative: scroll left, 0: no scroll), |
- * deltaY: number The y-axis scroll delta (positive: scroll down, |
- * negative: scroll up, 0: no scroll) |
- * }} info |
- * @private |
- */ |
_getScrollInfo: function(event) { |
var info = { |
deltaX: event.deltaX, |
deltaY: event.deltaY |
}; |
- // Already available. |
if ('deltaX' in event) { |
- // do nothing, values are already good. |
} |
- // Safari has scroll info in `wheelDeltaX/Y`. |
else if ('wheelDeltaX' in event) { |
info.deltaX = -event.wheelDeltaX; |
info.deltaY = -event.wheelDeltaY; |
} |
- // Firefox has scroll info in `detail` and `axis`. |
else if ('axis' in event) { |
info.deltaX = event.axis === 1 ? event.detail : 0; |
info.deltaY = event.axis === 2 ? event.detail : 0; |
} |
- // On mobile devices, calculate scroll direction. |
else if (event.targetTouches) { |
var touch = event.targetTouches[0]; |
- // Touch moves from right to left => scrolling goes right. |
info.deltaX = LAST_TOUCH_POSITION.pageX - touch.pageX; |
- // Touch moves from down to up => scrolling goes down. |
info.deltaY = LAST_TOUCH_POSITION.pageY - touch.pageY; |
} |
return info; |
@@ -10131,77 +7304,40 @@ Polymer({ |
], |
properties: { |
- /** |
- * The orientation against which to align the dropdown content |
- * horizontally relative to the dropdown trigger. |
- * Overridden from `Polymer.IronFitBehavior`. |
- */ |
horizontalAlign: { |
type: String, |
value: 'left', |
reflectToAttribute: true |
}, |
- /** |
- * The orientation against which to align the dropdown content |
- * vertically relative to the dropdown trigger. |
- * Overridden from `Polymer.IronFitBehavior`. |
- */ |
verticalAlign: { |
type: String, |
value: 'top', |
reflectToAttribute: true |
}, |
- /** |
- * An animation config. If provided, this will be used to animate the |
- * opening of the dropdown. |
- */ |
openAnimationConfig: { |
type: Object |
}, |
- /** |
- * An animation config. If provided, this will be used to animate the |
- * closing of the dropdown. |
- */ |
closeAnimationConfig: { |
type: Object |
}, |
- /** |
- * If provided, this will be the element that will be focused when |
- * the dropdown opens. |
- */ |
focusTarget: { |
type: Object |
}, |
- /** |
- * Set to true to disable animations when opening and closing the |
- * dropdown. |
- */ |
noAnimations: { |
type: Boolean, |
value: false |
}, |
- /** |
- * By default, the dropdown will constrain scrolling on the page |
- * to itself when opened. |
- * Set to true in order to prevent scroll from being constrained |
- * to the dropdown when it opens. |
- */ |
allowOutsideScroll: { |
type: Boolean, |
value: false |
}, |
- /** |
- * Callback for scroll events. |
- * @type {Function} |
- * @private |
- */ |
_boundOnCaptureScroll: { |
type: Function, |
value: function() { |
@@ -10218,26 +7354,17 @@ Polymer({ |
'_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)' |
], |
- /** |
- * The element that is contained by the dropdown, if any. |
- */ |
get containedElement() { |
return Polymer.dom(this.$.content).getDistributedNodes()[0]; |
}, |
- /** |
- * The element that should be focused when the dropdown opens. |
- * @deprecated |
- */ |
get _focusTarget() { |
return this.focusTarget || this.containedElement; |
}, |
ready: function() { |
- // Memoized scrolling position, used to block scrolling outside. |
this._scrollTop = 0; |
this._scrollLeft = 0; |
- // Used to perform a non-blocking refit on scroll. |
this._refitOnScrollRAF = null; |
}, |
@@ -10246,10 +7373,6 @@ Polymer({ |
Polymer.IronDropdownScrollManager.removeScrollLock(this); |
}, |
- /** |
- * Called when the value of `opened` changes. |
- * Overridden from `IronOverlayBehavior` |
- */ |
_openedChanged: function() { |
if (this.opened && this.disabled) { |
this.cancel(); |
@@ -10269,9 +7392,6 @@ Polymer({ |
} |
}, |
- /** |
- * Overridden from `IronOverlayBehavior`. |
- */ |
_renderOpened: function() { |
if (!this.noAnimations && this.animationConfig.open) { |
this.$.contentWrapper.classList.add('animating'); |
@@ -10281,9 +7401,6 @@ Polymer({ |
} |
}, |
- /** |
- * Overridden from `IronOverlayBehavior`. |
- */ |
_renderClosed: function() { |
if (!this.noAnimations && this.animationConfig.close) { |
@@ -10294,12 +7411,6 @@ Polymer({ |
} |
}, |
- /** |
- * Called when animation finishes on the dropdown (when opening or |
- * closing). Responsible for "completing" the process of opening or |
- * closing the dropdown by positioning it or setting its display to |
- * none. |
- */ |
_onNeonAnimationFinish: function() { |
this.$.contentWrapper.classList.remove('animating'); |
if (this.opened) { |
@@ -10318,31 +7429,21 @@ Polymer({ |
} |
}, |
- /** |
- * Memoizes the scroll position of the outside scrolling element. |
- * @private |
- */ |
_saveScrollPosition: function() { |
if (document.scrollingElement) { |
this._scrollTop = document.scrollingElement.scrollTop; |
this._scrollLeft = document.scrollingElement.scrollLeft; |
} else { |
- // Since we don't know if is the body or html, get max. |
this._scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop); |
this._scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); |
} |
}, |
- /** |
- * Resets the scroll position of the outside scrolling element. |
- * @private |
- */ |
_restoreScrollPosition: function() { |
if (document.scrollingElement) { |
document.scrollingElement.scrollTop = this._scrollTop; |
document.scrollingElement.scrollLeft = this._scrollLeft; |
} else { |
- // Since we don't know if is the body or html, set both. |
document.documentElement.scrollTop = this._scrollTop; |
document.documentElement.scrollLeft = this._scrollLeft; |
document.body.scrollTop = this._scrollTop; |
@@ -10350,10 +7451,6 @@ Polymer({ |
} |
}, |
- /** |
- * Constructs the final animation config from different properties used |
- * to configure specific parts of the opening and closing animations. |
- */ |
_updateAnimationConfig: function() { |
var animations = (this.openAnimationConfig || []).concat(this.closeAnimationConfig || []); |
for (var i = 0; i < animations.length; i++) { |
@@ -10365,20 +7462,12 @@ Polymer({ |
}; |
}, |
- /** |
- * Updates the overlay position based on configured horizontal |
- * and vertical alignment. |
- */ |
_updateOverlayPosition: function() { |
if (this.isAttached) { |
- // This triggers iron-resize, and iron-overlay-behavior will call refit if needed. |
this.notifyResize(); |
} |
}, |
- /** |
- * Apply focus to focusTarget or containedElement |
- */ |
_applyFocus: function () { |
var focusTarget = this.focusTarget || this.containedElement; |
if (focusTarget && this.opened && !this.noAutoFocus) { |
@@ -10537,11 +7626,6 @@ Polymer({ |
notify: true, |
}, |
- /** |
- * The contextual item that this menu was clicked for. |
- * e.g. the data used to render an item in an <iron-list> or <dom-repeat> |
- * @type {?Object} |
- */ |
itemData: { |
type: Object, |
value: null, |
@@ -10603,22 +7687,10 @@ Polymer({ |
'dropdown.iron-overlay-canceled': 'onOverlayCanceled_', |
}, |
- /** |
- * The last anchor that was used to open a menu. It's necessary for toggling. |
- * @private {?Element} |
- */ |
lastAnchor_: null, |
- /** |
- * The first focusable child in the menu's light DOM. |
- * @private {?Element} |
- */ |
firstFocus_: null, |
- /** |
- * The last focusable child in the menu's light DOM. |
- * @private {?Element} |
- */ |
lastFocus_: null, |
/** @override */ |
@@ -10629,18 +7701,11 @@ Polymer({ |
/** Closes the menu. */ |
closeMenu: function() { |
if (this.root.activeElement == null) { |
- // Something else has taken focus away from the menu. Do not attempt to |
- // restore focus to the button which opened the menu. |
this.$.dropdown.restoreFocusOnClose = false; |
} |
this.menuOpen = false; |
}, |
- /** |
- * Opens the menu at the anchor location. |
- * @param {!Element} anchor The location to display the menu. |
- * @param {!Object} itemData The contextual item's data. |
- */ |
openMenu: function(anchor, itemData) { |
if (this.lastAnchor_ == anchor && this.menuOpen) |
return; |
@@ -10660,16 +7725,10 @@ Polymer({ |
this.lastFocus_ = focusableChildren[focusableChildren.length - 1]; |
} |
- // Move the menu to the anchor. |
this.$.dropdown.positionTarget = anchor; |
this.menuOpen = true; |
}, |
- /** |
- * Toggles the menu for the anchor that is passed in. |
- * @param {!Element} anchor The location to display the menu. |
- * @param {!Object} itemData The contextual item's data. |
- */ |
toggleMenu: function(anchor, itemData) { |
if (anchor == this.lastAnchor_ && this.menuOpen) |
this.closeMenu(); |
@@ -10677,13 +7736,6 @@ Polymer({ |
this.openMenu(anchor, itemData); |
}, |
- /** |
- * Trap focus inside the menu. As a very basic heuristic, will wrap focus from |
- * the first element with a nonzero tabindex to the last such element. |
- * TODO(tsergeant): Use iron-focus-wrap-behavior once it is available |
- * (https://github.com/PolymerElements/iron-overlay-behavior/issues/179). |
- * @param {CustomEvent} e |
- */ |
onTabPressed_: function(e) { |
if (!this.firstFocus_ || !this.lastFocus_) |
return; |
@@ -10702,11 +7754,6 @@ Polymer({ |
toFocus.focus(); |
}, |
- /** |
- * Ensure the menu is reset properly when it is closed by the dropdown (eg, |
- * clicking outside). |
- * @private |
- */ |
menuOpenChanged_: function() { |
if (!this.menuOpen) { |
this.itemData = null; |
@@ -10714,12 +7761,6 @@ Polymer({ |
} |
}, |
- /** |
- * Prevent focus restoring when tapping outside the menu. This stops the |
- * focus moving around unexpectedly when closing the menu with the mouse. |
- * @param {CustomEvent} e |
- * @private |
- */ |
onOverlayCanceled_: function(e) { |
if (e.detail.type == 'tap') |
this.$.dropdown.restoreFocusOnClose = false; |
@@ -10756,22 +7797,12 @@ Polymer({ |
properties: { |
- /** |
- * If true, the orientation is horizontal; otherwise is vertical. |
- * |
- * @attribute horizontal |
- */ |
horizontal: { |
type: Boolean, |
value: false, |
observer: '_horizontalChanged' |
}, |
- /** |
- * Set opened to true to show the collapse element and to false to hide it. |
- * |
- * @attribute opened |
- */ |
opened: { |
type: Boolean, |
value: false, |
@@ -10779,11 +7810,6 @@ Polymer({ |
observer: '_openedChanged' |
}, |
- /** |
- * Set noAnimation to true to disable animations |
- * |
- * @attribute noAnimation |
- */ |
noAnimation: { |
type: Boolean |
}, |
@@ -10794,18 +7820,10 @@ Polymer({ |
return this.horizontal ? 'width' : 'height'; |
}, |
- /** |
- * `maxWidth` or `maxHeight`. |
- * @private |
- */ |
get _dimensionMax() { |
return this.horizontal ? 'maxWidth' : 'maxHeight'; |
}, |
- /** |
- * `max-width` or `max-height`. |
- * @private |
- */ |
get _dimensionMaxCss() { |
return this.horizontal ? 'max-width' : 'max-height'; |
}, |
@@ -10821,15 +7839,9 @@ Polymer({ |
}, |
attached: function() { |
- // It will take care of setting correct classes and styles. |
this._transitionEnd(); |
}, |
- /** |
- * Toggle the opened state. |
- * |
- * @method toggle |
- */ |
toggle: function() { |
this.opened = !this.opened; |
}, |
@@ -10842,38 +7854,23 @@ Polymer({ |
this.opened = false; |
}, |
- /** |
- * Updates the size of the element. |
- * @param {string} size The new value for `maxWidth`/`maxHeight` as css property value, usually `auto` or `0px`. |
- * @param {boolean=} animated if `true` updates the size with an animation, otherwise without. |
- */ |
updateSize: function(size, animated) { |
- // No change! |
var curSize = this.style[this._dimensionMax]; |
if (curSize === size || (size === 'auto' && !curSize)) { |
return; |
} |
this._updateTransition(false); |
- // If we can animate, must do some prep work. |
if (animated && !this.noAnimation && this._isDisplayed) { |
- // Animation will start at the current size. |
var startSize = this._calcSize(); |
- // For `auto` we must calculate what is the final size for the animation. |
- // After the transition is done, _transitionEnd will set the size back to `auto`. |
if (size === 'auto') { |
this.style[this._dimensionMax] = ''; |
size = this._calcSize(); |
} |
- // Go to startSize without animation. |
this.style[this._dimensionMax] = startSize; |
- // Force layout to ensure transition will go. Set scrollTop to itself |
- // so that compilers won't remove it. |
this.scrollTop = this.scrollTop; |
- // Enable animation. |
this._updateTransition(true); |
} |
- // Set the final size. |
if (size === 'auto') { |
this.style[this._dimensionMax] = ''; |
} else { |
@@ -10881,13 +7878,6 @@ Polymer({ |
} |
}, |
- /** |
- * enableTransition() is deprecated, but left over so it doesn't break existing code. |
- * Please use `noAnimation` property instead. |
- * |
- * @method enableTransition |
- * @deprecated since version 1.0.4 |
- */ |
enableTransition: function(enabled) { |
Polymer.Base._warn('`enableTransition()` is deprecated, use `noAnimation` instead.'); |
this.noAnimation = !enabled; |
@@ -10912,7 +7902,6 @@ Polymer({ |
this.toggleClass('iron-collapse-opened', false); |
this.updateSize(this.opened ? 'auto' : '0px', true); |
- // Focus the current collapse. |
if (this.opened) { |
this.focus(); |
} |
@@ -10931,11 +7920,6 @@ Polymer({ |
this.notifyResize(); |
}, |
- /** |
- * Simplistic heuristic to detect if element has a parent with display: none |
- * |
- * @private |
- */ |
get _isDisplayed() { |
var rect = this.getBoundingClientRect(); |
for (var prop in rect) { |
@@ -10949,67 +7933,31 @@ Polymer({ |
} |
}); |
-/** |
- Polymer.IronFormElementBehavior enables a custom element to be included |
- in an `iron-form`. |
- |
- @demo demo/index.html |
- @polymerBehavior |
- */ |
Polymer.IronFormElementBehavior = { |
properties: { |
- /** |
- * Fired when the element is added to an `iron-form`. |
- * |
- * @event iron-form-element-register |
- */ |
- |
- /** |
- * Fired when the element is removed from an `iron-form`. |
- * |
- * @event iron-form-element-unregister |
- */ |
- |
- /** |
- * The name of this element. |
- */ |
+ |
+ |
name: { |
type: String |
}, |
- /** |
- * The value for this element. |
- */ |
value: { |
notify: true, |
type: String |
}, |
- /** |
- * Set to true to mark the input as required. If used in a form, a |
- * custom element that uses this behavior should also use |
- * Polymer.IronValidatableBehavior and define a custom validation method. |
- * Otherwise, a `required` element will always be considered valid. |
- * It's also strongly recommended to provide a visual style for the element |
- * when its value is invalid. |
- */ |
required: { |
type: Boolean, |
value: false |
}, |
- /** |
- * The form that the element is registered to. |
- */ |
_parentForm: { |
type: Object |
} |
}, |
attached: function() { |
- // Note: the iron-form that this element belongs to will set this |
- // element's _parentForm property when handling this event. |
this.fire('iron-form-element-register'); |
}, |
@@ -11020,27 +7968,10 @@ Polymer({ |
} |
}; |
-/** |
- * Use `Polymer.IronCheckedElementBehavior` to implement a custom element |
- * that has a `checked` property, which can be used for validation if the |
- * element is also `required`. Element instances implementing this behavior |
- * will also be registered for use in an `iron-form` element. |
- * |
- * @demo demo/index.html |
- * @polymerBehavior Polymer.IronCheckedElementBehavior |
- */ |
Polymer.IronCheckedElementBehaviorImpl = { |
properties: { |
- /** |
- * Fired when the checked state changes. |
- * |
- * @event iron-change |
- */ |
- |
- /** |
- * Gets or sets the state, `true` is checked and `false` is unchecked. |
- */ |
+ |
checked: { |
type: Boolean, |
value: false, |
@@ -11049,10 +7980,6 @@ Polymer({ |
observer: '_checkedChanged' |
}, |
- /** |
- * If true, the button toggles the active state with each tap or press |
- * of the spacebar. |
- */ |
toggles: { |
type: Boolean, |
value: true, |
@@ -11072,24 +7999,13 @@ Polymer({ |
], |
created: function() { |
- // Used by `iron-form` to handle the case that an element with this behavior |
- // doesn't have a role of 'checkbox' or 'radio', but should still only be |
- // included when the form is serialized if `this.checked === true`. |
this._hasIronCheckedElementBehavior = true; |
}, |
- /** |
- * Returns false if the element is required and not checked, and true otherwise. |
- * @param {*=} _value Ignored. |
- * @return {boolean} true if `required` is false or if `checked` is true. |
- */ |
_getValidity: function(_value) { |
return this.disabled || !this.required || this.checked; |
}, |
- /** |
- * Update the aria-required label when `required` is changed. |
- */ |
_requiredChanged: function() { |
if (this.required) { |
this.setAttribute('aria-required', 'true'); |
@@ -11098,17 +8014,11 @@ Polymer({ |
} |
}, |
- /** |
- * Fire `iron-changed` when the checked state changes. |
- */ |
_checkedChanged: function() { |
this.active = this.checked; |
this.fire('iron-change'); |
}, |
- /** |
- * Reset value to 'on' if it is set to `undefined`. |
- */ |
_valueChanged: function() { |
if (this.value === undefined || this.value === null) { |
this.value = 'on'; |
@@ -11122,16 +8032,7 @@ Polymer({ |
Polymer.IronValidatableBehavior, |
Polymer.IronCheckedElementBehaviorImpl |
]; |
-/** |
- * Use `Polymer.PaperCheckedElementBehavior` to implement a custom element |
- * that has a `checked` property similar to `Polymer.IronCheckedElementBehavior` |
- * and is compatible with having a ripple effect. |
- * @polymerBehavior Polymer.PaperCheckedElementBehavior |
- */ |
Polymer.PaperCheckedElementBehaviorImpl = { |
- /** |
- * Synchronizes the element's checked state with its ripple effect. |
- */ |
_checkedChanged: function() { |
Polymer.IronCheckedElementBehaviorImpl._checkedChanged.call(this); |
if (this.hasRipple()) { |
@@ -11143,9 +8044,6 @@ Polymer({ |
} |
}, |
- /** |
- * Synchronizes the element's `active` and `checked` state. |
- */ |
_buttonStateChanged: function() { |
Polymer.PaperRippleBehavior._buttonStateChanged.call(this); |
if (this.disabled) { |
@@ -11177,17 +8075,7 @@ Polymer({ |
}, |
properties: { |
- /** |
- * Fired when the checked state changes due to user interaction. |
- * |
- * @event change |
- */ |
- |
- /** |
- * Fired when the checked state changes. |
- * |
- * @event iron-change |
- */ |
+ |
ariaActiveAttribute: { |
type: String, |
value: 'aria-checked' |
@@ -11209,7 +8097,6 @@ Polymer({ |
return checked ? '' : 'hidden'; |
}, |
- // create ripple inside the checkboxContainer |
_createRipple: function() { |
this._rippleContainer = this.$.checkboxContainer; |
return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this); |
@@ -11239,9 +8126,6 @@ Polymer({ |
this.getRipple().upAction(); |
}, |
- /** |
- * @param {...*} var_args |
- */ |
ensureRipple: function(var_args) { |
var lastRipple = this._ripple; |
Polymer.PaperRippleBehavior.ensureRipple.apply(this, arguments); |
@@ -11256,37 +8140,17 @@ Polymer({ |
// found in the LICENSE file. |
cr.define('cr.icon', function() { |
- /** |
- * @return {!Array<number>} The scale factors supported by this platform for |
- * webui resources. |
- */ |
function getSupportedScaleFactors() { |
var supportedScaleFactors = []; |
if (cr.isMac || cr.isChromeOS || cr.isWindows || cr.isLinux) { |
- // All desktop platforms support zooming which also updates the |
- // renderer's device scale factors (a.k.a devicePixelRatio), and |
- // these platforms has high DPI assets for 2.0x. Use 1x and 2x in |
- // image-set on these platforms so that the renderer can pick the |
- // closest image for the current device scale factor. |
supportedScaleFactors.push(1); |
supportedScaleFactors.push(2); |
} else { |
- // For other platforms that use fixed device scale factor, use |
- // the window's device pixel ratio. |
- // TODO(oshima): Investigate if Android/iOS need to use image-set. |
supportedScaleFactors.push(window.devicePixelRatio); |
} |
return supportedScaleFactors; |
} |
- /** |
- * Returns the URL of the image, or an image set of URLs for the profile |
- * avatar. Default avatars have resources available for multiple scalefactors, |
- * whereas the GAIA profile image only comes in one size. |
- * |
- * @param {string} path The path of the image. |
- * @return {string} The url, or an image set of URLs of the avatar image. |
- */ |
function getProfileAvatarIcon(path) { |
var chromeThemePath = 'chrome://theme'; |
var isDefaultAvatar = |
@@ -11294,15 +8158,6 @@ cr.define('cr.icon', function() { |
return isDefaultAvatar ? imageset(path + '@scalefactorx'): url(path); |
} |
- /** |
- * Generates a CSS -webkit-image-set for a chrome:// url. |
- * An entry in the image set is added for each of getSupportedScaleFactors(). |
- * The scale-factor-specific url is generated by replacing the first instance |
- * of 'scalefactor' in |path| with the numeric scale factor. |
- * @param {string} path The URL to generate an image set for. |
- * 'scalefactor' should be a substring of |path|. |
- * @return {string} The CSS -webkit-image-set. |
- */ |
function imageset(path) { |
var supportedScaleFactors = getSupportedScaleFactors(); |
@@ -11324,29 +8179,14 @@ cr.define('cr.icon', function() { |
return '-webkit-image-set(' + s + ')'; |
} |
- /** |
- * A regular expression for identifying favicon URLs. |
- * @const {!RegExp} |
- */ |
var FAVICON_URL_REGEX = /\.ico$/i; |
- /** |
- * Creates a CSS -webkit-image-set for a favicon request. |
- * @param {string} url Either the URL of the original page or of the favicon |
- * itself. |
- * @param {number=} opt_size Optional preferred size of the favicon. |
- * @param {string=} opt_type Optional type of favicon to request. Valid values |
- * are 'favicon' and 'touch-icon'. Default is 'favicon'. |
- * @return {string} -webkit-image-set for the favicon. |
- */ |
function getFaviconImageSet(url, opt_size, opt_type) { |
var size = opt_size || 16; |
var type = opt_type || 'favicon'; |
return imageset( |
'chrome://' + type + '/size/' + size + '@scalefactorx/' + |
- // Note: Literal 'iconurl' must match |kIconURLParameter| in |
- // components/favicon_base/favicon_url_parser.cc. |
(FAVICON_URL_REGEX.test(url) ? 'iconurl/' : '') + url); |
} |
@@ -11360,10 +8200,6 @@ cr.define('cr.icon', function() { |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * @fileoverview Defines a singleton object, md_history.BrowserService, which |
- * provides access to chrome.send APIs. |
- */ |
cr.define('md_history', function() { |
/** @constructor */ |
@@ -11375,13 +8211,8 @@ cr.define('md_history', function() { |
} |
BrowserService.prototype = { |
- /** |
- * @param {!Array<!HistoryEntry>} items |
- * @return {Promise<!Array<!HistoryEntry>>} |
- */ |
deleteItems: function(items) { |
if (this.pendingDeleteItems_ != null) { |
- // There's already a deletion in progress, reject immediately. |
return new Promise(function(resolve, reject) { reject(items); }); |
} |
@@ -11400,26 +8231,14 @@ cr.define('md_history', function() { |
return this.pendingDeletePromise_.promise; |
}, |
- /** |
- * @param {!string} url |
- */ |
removeBookmark: function(url) { |
chrome.send('removeBookmark', [url]); |
}, |
- /** |
- * @param {string} sessionTag |
- */ |
openForeignSessionAllTabs: function(sessionTag) { |
chrome.send('openForeignSession', [sessionTag]); |
}, |
- /** |
- * @param {string} sessionTag |
- * @param {number} windowId |
- * @param {number} tabId |
- * @param {MouseEvent} e |
- */ |
openForeignSessionTab: function(sessionTag, windowId, tabId, e) { |
chrome.send('openForeignSession', [ |
sessionTag, String(windowId), String(tabId), e.button || 0, e.altKey, |
@@ -11427,9 +8246,6 @@ cr.define('md_history', function() { |
]); |
}, |
- /** |
- * @param {string} sessionTag |
- */ |
deleteForeignSession: function(sessionTag) { |
chrome.send('deleteForeignSession', [sessionTag]); |
}, |
@@ -11438,18 +8254,10 @@ cr.define('md_history', function() { |
chrome.send('clearBrowsingData'); |
}, |
- /** |
- * Record an action in UMA. |
- * @param {string} actionDesc The name of the action to be logged. |
- */ |
recordAction: function(actionDesc) { |
chrome.send('metricsHandler:recordAction', [actionDesc]); |
}, |
- /** |
- * @param {boolean} successful |
- * @private |
- */ |
resolveDelete_: function(successful) { |
if (this.pendingDeleteItems_ == null || |
this.pendingDeletePromise_ == null) { |
@@ -11471,16 +8279,10 @@ cr.define('md_history', function() { |
return {BrowserService: BrowserService}; |
}); |
-/** |
- * Called by the history backend when deletion was succesful. |
- */ |
function deleteComplete() { |
md_history.BrowserService.getInstance().resolveDelete_(true); |
} |
-/** |
- * Called by the history backend when the deletion failed. |
- */ |
function deleteFailed() { |
md_history.BrowserService.getInstance().resolveDelete_(false); |
}; |
@@ -11492,20 +8294,13 @@ Polymer({ |
is: 'history-searched-label', |
properties: { |
- // The text to show in this label. |
title: String, |
- // The search term to bold within the title. |
searchTerm: String, |
}, |
observers: ['setSearchedTextToBold_(title, searchTerm)'], |
- /** |
- * Updates the page title. If a search term is specified, highlights any |
- * occurrences of the search term in bold. |
- * @private |
- */ |
setSearchedTextToBold_: function() { |
var i = 0; |
var titleElem = this.$.container; |
@@ -11524,7 +8319,6 @@ Polymer({ |
titleElem.appendChild(document.createTextNode( |
titleText.slice(i, match.index))); |
i = re.lastIndex; |
- // Mark the highlighted text in bold. |
var b = document.createElement('b'); |
b.textContent = titleText.substring(match.index, i); |
titleElem.appendChild(b); |
@@ -11543,11 +8337,8 @@ cr.define('md_history', function() { |
is: 'history-item', |
properties: { |
- // Underlying HistoryEntry data for this item. Contains read-only fields |
- // from the history backend, as well as fields computed by history-list. |
item: {type: Object, observer: 'showIcon_'}, |
- // Search term used to obtain this history-item. |
searchTerm: {type: String}, |
selected: {type: Boolean, notify: true}, |
@@ -11558,26 +8349,16 @@ cr.define('md_history', function() { |
isCardEnd: {type: Boolean, reflectToAttribute: true}, |
- // True if the item is being displayed embedded in another element and |
- // should not manage its own borders or size. |
embedded: {type: Boolean, reflectToAttribute: true}, |
hasTimeGap: {type: Boolean}, |
numberOfItems: {type: Number}, |
- // The path of this history item inside its parent. |
path: String, |
}, |
- /** |
- * When a history-item is selected the toolbar is notified and increases |
- * or decreases its count of selected items accordingly. |
- * @param {MouseEvent} e |
- * @private |
- */ |
onCheckboxSelected_: function(e) { |
- // TODO(calamity): Fire this event whenever |selected| changes. |
this.fire('history-checkbox-select', { |
element: this, |
shiftKey: e.shiftKey, |
@@ -11585,20 +8366,11 @@ cr.define('md_history', function() { |
e.preventDefault(); |
}, |
- /** |
- * @param {MouseEvent} e |
- * @private |
- */ |
onCheckboxMousedown_: function(e) { |
- // Prevent shift clicking a checkbox from selecting text. |
if (e.shiftKey) |
e.preventDefault(); |
}, |
- /** |
- * Remove bookmark of current item when bookmark-star is clicked. |
- * @private |
- */ |
onRemoveBookmarkTap_: function() { |
if (!this.item.starred) |
return; |
@@ -11611,10 +8383,6 @@ cr.define('md_history', function() { |
this.fire('remove-bookmark-stars', this.item.url); |
}, |
- /** |
- * Fires a custom event when the menu button is clicked. Sends the details |
- * of the history item and where the menu should appear. |
- */ |
onMenuButtonTap_: function(e) { |
this.fire('toggle-menu', { |
target: Polymer.dom(e).localTarget, |
@@ -11622,14 +8390,9 @@ cr.define('md_history', function() { |
path: this.path, |
}); |
- // Stops the 'tap' event from closing the menu when it opens. |
e.stopPropagation(); |
}, |
- /** |
- * Set the favicon image, based on the URL of the history item. |
- * @private |
- */ |
showIcon_: function() { |
this.$.icon.style.backgroundImage = |
cr.icon.getFaviconImageSet(this.item.url); |
@@ -11639,12 +8402,6 @@ cr.define('md_history', function() { |
return !loadTimeData.getBoolean('allowDeletingHistory'); |
}, |
- /** |
- * Generates the title for this history card. |
- * @param {number} numberOfItems The number of items in the card. |
- * @param {string} search The search term associated with these results. |
- * @private |
- */ |
cardTitle_: function(numberOfItems, historyDate, search) { |
if (!search) |
return this.item.dateRelativeDay; |
@@ -11654,12 +8411,6 @@ cr.define('md_history', function() { |
loadTimeData.getString(resultId), search); |
}, |
- /** |
- * Crop long item titles to reduce their effect on layout performance. See |
- * crbug.com/621347. |
- * @param {string} title |
- * @return {string} |
- */ |
cropItemTitle_: function(title) { |
return (title.length > TITLE_MAX_LENGTH) ? |
title.substr(0, TITLE_MAX_LENGTH) : |
@@ -11667,15 +8418,6 @@ cr.define('md_history', function() { |
} |
}); |
- /** |
- * Check whether the time difference between the given history item and the |
- * next one is large enough for a spacer to be required. |
- * @param {Array<HistoryEntry>} visits |
- * @param {number} currentIndex |
- * @param {string} searchedTerm |
- * @return {boolean} Whether or not time gap separator is required. |
- * @private |
- */ |
HistoryItem.needsTimeGap = function(visits, currentIndex, searchedTerm) { |
if (currentIndex >= visits.length - 1 || visits.length == 0) |
return false; |
@@ -11696,10 +8438,6 @@ cr.define('md_history', function() { |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * @constructor |
- * @param {string} currentPath |
- */ |
var SelectionTreeNode = function(currentPath) { |
/** @type {string} */ |
this.currentPath = currentPath; |
@@ -11711,10 +8449,6 @@ var SelectionTreeNode = function(currentPath) { |
this.children = []; |
}; |
-/** |
- * @param {number} index |
- * @param {string} path |
- */ |
SelectionTreeNode.prototype.addChild = function(index, path) { |
this.indexes.push(index); |
this.children[index] = new SelectionTreeNode(path); |
@@ -11723,10 +8457,6 @@ SelectionTreeNode.prototype.addChild = function(index, path) { |
/** @polymerBehavior */ |
var HistoryListBehavior = { |
properties: { |
- /** |
- * Polymer paths to the history items contained in this list. |
- * @type {!Set<string>} selectedPaths |
- */ |
selectedPaths: { |
type: Object, |
value: /** @return {!Set<string>} */ function() { return new Set(); } |
@@ -11739,19 +8469,8 @@ var HistoryListBehavior = { |
'history-checkbox-select': 'itemSelected_', |
}, |
- /** |
- * @param {number} historyDataLength |
- * @return {boolean} |
- * @private |
- */ |
hasResults: function(historyDataLength) { return historyDataLength > 0; }, |
- /** |
- * @param {string} searchedTerm |
- * @param {boolean} isLoading |
- * @return {string} |
- * @private |
- */ |
noResultsMessage: function(searchedTerm, isLoading) { |
if (isLoading) |
return ''; |
@@ -11760,9 +8479,6 @@ var HistoryListBehavior = { |
return loadTimeData.getString(messageId); |
}, |
- /** |
- * Deselect each item in |selectedPaths|. |
- */ |
unselectAllItems: function() { |
this.selectedPaths.forEach(function(path) { |
this.set(path + '.selected', false); |
@@ -11771,12 +8487,6 @@ var HistoryListBehavior = { |
this.selectedPaths.clear(); |
}, |
- /** |
- * Performs a request to the backend to delete all selected items. If |
- * successful, removes them from the view. Does not prompt the user before |
- * deleting -- see <history-list-container> for a version of this method which |
- * does prompt. |
- */ |
deleteSelected: function() { |
var toBeRemoved = |
Array.from(this.selectedPaths.values()).map(function(path) { |
@@ -11791,19 +8501,6 @@ var HistoryListBehavior = { |
}.bind(this)); |
}, |
- /** |
- * Removes the history items in |paths|. Assumes paths are of a.0.b.0... |
- * structure. |
- * |
- * We want to use notifySplices to update the arrays for performance reasons |
- * which requires manually batching and sending the notifySplices for each |
- * level. To do this, we build a tree where each node is an array and then |
- * depth traverse it to remove items. Each time a node has all children |
- * deleted, we can also remove the node. |
- * |
- * @param {Array<string>} paths |
- * @private |
- */ |
removeItemsByPath: function(paths) { |
if (paths.length == 0) |
return; |
@@ -11811,19 +8508,9 @@ var HistoryListBehavior = { |
this.removeItemsBeneathNode_(this.buildRemovalTree_(paths)); |
}, |
- /** |
- * Creates the tree to traverse in order to remove |paths| from this list. |
- * Assumes paths are of a.0.b.0... |
- * structure. |
- * |
- * @param {Array<string>} paths |
- * @return {SelectionTreeNode} |
- * @private |
- */ |
buildRemovalTree_: function(paths) { |
var rootNode = new SelectionTreeNode(paths[0].split('.')[0]); |
- // Build a tree to each history item specified in |paths|. |
paths.forEach(function(path) { |
var components = path.split('.'); |
var node = rootNode; |
@@ -11844,13 +8531,6 @@ var HistoryListBehavior = { |
return rootNode; |
}, |
- /** |
- * Removes the history items underneath |node| and deletes container arrays as |
- * they become empty. |
- * @param {SelectionTreeNode} node |
- * @return {boolean} Whether this node's array should be deleted. |
- * @private |
- */ |
removeItemsBeneathNode_: function(node) { |
var array = this.get(node.currentPath); |
var splices = []; |
@@ -11872,23 +8552,15 @@ var HistoryListBehavior = { |
if (array.length == 0) |
return true; |
- // notifySplices gives better performance than individually splicing as it |
- // batches all of the updates together. |
this.notifySplices(node.currentPath, splices); |
return false; |
}, |
- /** |
- * @param {Event} e |
- * @private |
- */ |
itemSelected_: function(e) { |
var item = e.detail.element; |
var paths = []; |
var itemPath = item.path; |
- // Handle shift selection. Change the selection state of all items between |
- // |path| and |lastSelected| to the selection state of |item|. |
if (e.detail.shiftKey && this.lastSelectedPath) { |
var itemPathComponents = itemPath.split('.'); |
var itemIndex = Number(itemPathComponents.pop()); |
@@ -11925,18 +8597,8 @@ var HistoryListBehavior = { |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * @typedef {{domain: string, |
- * visits: !Array<HistoryEntry>, |
- * rendered: boolean, |
- * expanded: boolean}} |
- */ |
var HistoryDomain; |
-/** |
- * @typedef {{title: string, |
- * domains: !Array<HistoryDomain>}} |
- */ |
var HistoryGroup; |
Polymer({ |
@@ -11945,14 +8607,10 @@ Polymer({ |
behaviors: [HistoryListBehavior], |
properties: { |
- // An array of history entries in reverse chronological order. |
historyData: { |
type: Array, |
}, |
- /** |
- * @type {Array<HistoryGroup>} |
- */ |
groupedHistoryData_: { |
type: Array, |
}, |
@@ -11974,16 +8632,10 @@ Polymer({ |
'updateGroupedHistoryData_(range, historyData)' |
], |
- /** |
- * Make a list of domains from visits. |
- * @param {!Array<!HistoryEntry>} visits |
- * @return {!Array<!HistoryDomain>} |
- */ |
createHistoryDomains_: function(visits) { |
var domainIndexes = {}; |
var domains = []; |
- // Group the visits into a dictionary and generate a list of domains. |
for (var i = 0, visit; visit = visits[i]; i++) { |
var domain = visit.domain; |
if (domainIndexes[domain] == undefined) { |
@@ -12012,7 +8664,6 @@ Polymer({ |
} |
if (this.range == HistoryRange.WEEK) { |
- // Group each day into a list of results. |
var days = []; |
var currentDayVisits = [this.historyData[0]]; |
@@ -12043,7 +8694,6 @@ Polymer({ |
this.groupedHistoryData_ = days; |
} else if (this.range == HistoryRange.MONTH) { |
- // Group each all visits into a single list. |
this.groupedHistoryData_ = [{ |
title: this.queryStartTime + ' – ' + this.queryEndTime, |
domains: this.createHistoryDomains_(this.historyData) |
@@ -12051,26 +8701,13 @@ Polymer({ |
} |
}, |
- /** |
- * @param {{model:Object, currentTarget:IronCollapseElement}} e |
- */ |
toggleDomainExpanded_: function(e) { |
var collapse = e.currentTarget.parentNode.querySelector('iron-collapse'); |
e.model.set('domain.rendered', true); |
- // Give the history-items time to render. |
setTimeout(function() { collapse.toggle() }, 0); |
}, |
- /** |
- * Check whether the time difference between the given history item and the |
- * next one is large enough for a spacer to be required. |
- * @param {number} groupIndex |
- * @param {number} domainIndex |
- * @param {number} itemIndex |
- * @return {boolean} Whether or not time gap separator is required. |
- * @private |
- */ |
needsTimeGap_: function(groupIndex, domainIndex, itemIndex) { |
var visits = |
this.groupedHistoryData_[groupIndex].domains[domainIndex].visits; |
@@ -12079,13 +8716,6 @@ Polymer({ |
visits, itemIndex, this.searchedTerm); |
}, |
- /** |
- * @param {number} groupIndex |
- * @param {number} domainIndex |
- * @param {number} itemIndex |
- * @return {string} |
- * @private |
- */ |
pathForItem_: function(groupIndex, domainIndex, itemIndex) { |
return [ |
'groupedHistoryData_', groupIndex, 'domains', domainIndex, 'visits', |
@@ -12093,74 +8723,19 @@ Polymer({ |
].join('.'); |
}, |
- /** |
- * @param {HistoryDomain} domain |
- * @return {string} |
- * @private |
- */ |
getWebsiteIconStyle_: function(domain) { |
return 'background-image: ' + |
cr.icon.getFaviconImageSet(domain.visits[0].url); |
}, |
- /** |
- * @param {boolean} expanded |
- * @return {string} |
- * @private |
- */ |
getDropdownIcon_: function(expanded) { |
return expanded ? 'cr:expand-less' : 'cr:expand-more'; |
}, |
}); |
-/** |
- * `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: auto;"> |
- * <x-element scroll-target="scrollable-element"> |
- * \x3c!-- Content--\x3e |
- * </x-element> |
- * </div> |
- *``` |
- * In this case, the `scrollTarget` will point to the outer div element. |
- * |
- * ### Document scrolling |
- * |
- * For document scrolling, you can use the reserved word `document`: |
- * |
- *```html |
- * <x-element scroll-target="document"> |
- * \x3c!-- Content --\x3e |
- * </x-element> |
- *``` |
- * |
- * ### Elements reference |
- * |
- *```js |
- * appHeader.scrollTarget = document.querySelector('#scrollable-element'); |
- *``` |
- * |
- * @type {HTMLElement} |
- */ |
scrollTarget: { |
type: HTMLElement, |
value: function() { |
@@ -12185,7 +8760,6 @@ Polymer({ |
if (!isAttached) { |
return; |
} |
- // Support element id references |
if (scrollTarget === 'document') { |
this.scrollTarget = this._doc; |
@@ -12205,37 +8779,16 @@ Polymer({ |
} |
}, |
- /** |
- * Runs on every scroll event. Consumer of this behavior may 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; |
@@ -12243,11 +8796,6 @@ Polymer({ |
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; |
@@ -12255,11 +8803,6 @@ Polymer({ |
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); |
@@ -12268,11 +8811,6 @@ Polymer({ |
} |
}, |
- /** |
- * 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); |
@@ -12281,13 +8819,6 @@ Polymer({ |
} |
}, |
- /** |
- * Scrolls the content to a particular place. |
- * |
- * @method scroll |
- * @param {number} left The left position |
- * @param {number} top The top position |
- */ |
scroll: function(left, top) { |
if (this.scrollTarget === this._doc) { |
window.scrollTo(left, top); |
@@ -12297,11 +8828,6 @@ Polymer({ |
} |
}, |
- /** |
- * Gets the width of the scroll target. |
- * |
- * @type {number} |
- */ |
get _scrollTargetWidth() { |
if (this._isValidScrollTarget()) { |
return this.scrollTarget === this._doc ? window.innerWidth : this.scrollTarget.offsetWidth; |
@@ -12309,11 +8835,6 @@ Polymer({ |
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; |
@@ -12321,11 +8842,6 @@ Polymer({ |
return 0; |
}, |
- /** |
- * Returns true if the scroll target is a valid HTMLElement. |
- * |
- * @return {boolean} |
- */ |
_isValidScrollTarget: function() { |
return this.scrollTarget instanceof HTMLElement; |
} |
@@ -12345,101 +8861,51 @@ Polymer({ |
properties: { |
- /** |
- * An array containing items determining how many instances of the template |
- * to stamp and that that each template instance should bind to. |
- */ |
items: { |
type: Array |
}, |
- /** |
- * The max count of physical items the pool can extend to. |
- */ |
maxPhysicalCount: { |
type: Number, |
value: 500 |
}, |
- /** |
- * The name of the variable to add to the binding scope for the array |
- * element associated with a given template instance. |
- */ |
as: { |
type: String, |
value: 'item' |
}, |
- /** |
- * The name of the variable to add to the binding scope with the index |
- * for the row. |
- */ |
indexAs: { |
type: String, |
value: 'index' |
}, |
- /** |
- * The name of the variable to add to the binding scope to indicate |
- * if the row is selected. |
- */ |
selectedAs: { |
type: String, |
value: 'selected' |
}, |
- /** |
- * When true, the list is rendered as a grid. Grid items must have |
- * fixed width and height set via CSS. e.g. |
- * |
- * ```html |
- * <iron-list grid> |
- * <template> |
- * <div style="width: 100px; height: 100px;"> 100x100 </div> |
- * </template> |
- * </iron-list> |
- * ``` |
- */ |
grid: { |
type: Boolean, |
value: false, |
reflectToAttribute: true |
}, |
- /** |
- * When true, tapping a row will select the item, placing its data model |
- * in the set of selected items retrievable via the selection property. |
- * |
- * Note that tapping focusable elements within the list item will not |
- * result in selection, since they are presumed to have their * own action. |
- */ |
selectionEnabled: { |
type: Boolean, |
value: false |
}, |
- /** |
- * When `multiSelection` is false, this is the currently selected item, or `null` |
- * if no item is selected. |
- */ |
selectedItem: { |
type: Object, |
notify: true |
}, |
- /** |
- * When `multiSelection` is true, this is an array that contains the selected items. |
- */ |
selectedItems: { |
type: Object, |
notify: true |
}, |
- /** |
- * When `true`, multiple items may be selected at once (in this case, |
- * `selected` is an array of currently selected items). When `false`, |
- * only one item may be selected at a time. |
- */ |
multiSelection: { |
type: Boolean, |
value: false |
@@ -12466,209 +8932,89 @@ Polymer({ |
'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. |
- */ |
_ratio: 0.5, |
- /** |
- * The padding-top value for the list. |
- */ |
_scrollerPaddingTop: 0, |
- /** |
- * This value is the same as `scrollTop`. |
- */ |
_scrollPosition: 0, |
- /** |
- * The sum of the heights of all the tiles in the DOM. |
- */ |
_physicalSize: 0, |
- /** |
- * The average `offsetHeight` of the tiles observed till now. |
- */ |
_physicalAverage: 0, |
- /** |
- * The number of tiles which `offsetHeight` > 0 observed until now. |
- */ |
_physicalAverageCount: 0, |
- /** |
- * The Y position of the item rendered in the `_physicalStart` |
- * tile relative to the scrolling list. |
- */ |
_physicalTop: 0, |
- /** |
- * The number of items in the list. |
- */ |
_virtualCount: 0, |
- /** |
- * A map between an item key and its physical item index |
- */ |
_physicalIndexForKey: null, |
- /** |
- * The estimated scroll height based on `_physicalAverage` |
- */ |
_estScrollHeight: 0, |
- /** |
- * The scroll height of the dom node |
- */ |
_scrollHeight: 0, |
- /** |
- * The height of the list. This is referred as the viewport in the context of list. |
- */ |
_viewportHeight: 0, |
- /** |
- * The width of the list. This is referred as the viewport in the context of list. |
- */ |
_viewportWidth: 0, |
- /** |
- * An array of DOM nodes that are currently in the tree |
- * @type {?Array<!TemplatizerNode>} |
- */ |
_physicalItems: null, |
- /** |
- * An array of heights for each item in `_physicalItems` |
- * @type {?Array<number>} |
- */ |
_physicalSizes: null, |
- /** |
- * 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} |
- */ |
_collection: null, |
- /** |
- * True if the current item list was rendered for the first time |
- * after attached. |
- */ |
_itemsRendered: false, |
- /** |
- * The page that is currently rendered. |
- */ |
_lastPage: null, |
- /** |
- * The max number of pages to render. One page is equivalent to the height of the list. |
- */ |
_maxPages: 3, |
- /** |
- * The currently focused physical item. |
- */ |
_focusedItem: null, |
- /** |
- * The index of the `_focusedItem`. |
- */ |
_focusedIndex: -1, |
- /** |
- * 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 maximum items per row |
- */ |
_itemsPerRow: 1, |
- /** |
- * The width of each grid item |
- */ |
_itemWidth: 0, |
- /** |
- * The height of the row in grid layout. |
- */ |
_rowHeight: 0, |
- /** |
- * The bottom of the physical content. |
- */ |
get _physicalBottom() { |
return this._physicalTop + this._physicalSize; |
}, |
- /** |
- * The bottom of the scroll. |
- */ |
get _scrollBottom() { |
return this._scrollPosition + this._viewportHeight; |
}, |
- /** |
- * The n-th item rendered in the last physical item. |
- */ |
get _virtualEnd() { |
return this._virtualStart + this._physicalCount - 1; |
}, |
- /** |
- * The height of the physical content that isn't on the screen. |
- */ |
get _hiddenContentSize() { |
var size = this.grid ? this._physicalRows * this._rowHeight : this._physicalSize; |
return size - this._viewportHeight; |
}, |
- /** |
- * The maximum scroll top value. |
- */ |
get _maxScrollTop() { |
return this._estScrollHeight - this._viewportHeight + this._scrollerPaddingTop; |
}, |
- /** |
- * The lowest n-th value for an item such that it can be rendered in `_physicalStart`. |
- */ |
_minVirtualStart: 0, |
- /** |
- * The largest n-th value for an item such that it can be rendered in `_physicalStart`. |
- */ |
get _maxVirtualStart() { |
return Math.max(0, this._virtualCount - this._physicalCount); |
}, |
- /** |
- * The n-th item rendered in the `_physicalStart` tile. |
- */ |
_virtualStartVal: 0, |
set _virtualStart(val) { |
@@ -12679,9 +9025,6 @@ Polymer({ |
return this._virtualStartVal || 0; |
}, |
- /** |
- * The k-th tile that is at the top of the scrolling list. |
- */ |
_physicalStartVal: 0, |
set _physicalStart(val) { |
@@ -12696,9 +9039,6 @@ Polymer({ |
return this._physicalStartVal || 0; |
}, |
- /** |
- * The number of tiles in the DOM. |
- */ |
_physicalCountVal: 0, |
set _physicalCount(val) { |
@@ -12710,18 +9050,8 @@ Polymer({ |
return this._physicalCountVal; |
}, |
- /** |
- * The k-th tile that is at the bottom of the scrolling list. |
- */ |
_physicalEnd: 0, |
- /** |
- * An optimal physical size such that we will have enough physical items |
- * to fill up the viewport and recycle when the user scrolls. |
- * |
- * This default value assumes that we will at least have the equivalent |
- * to a viewport of physical items above and below the user's viewport. |
- */ |
get _optPhysicalSize() { |
if (this.grid) { |
return this._estRowsInView * this._rowHeight * this._maxPages; |
@@ -12733,18 +9063,10 @@ Polymer({ |
return this._estRowsInView * this._itemsPerRow * this._maxPages; |
}, |
- /** |
- * True if the current list is visible. |
- */ |
get _isVisible() { |
return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.scrollTarget.offsetHeight); |
}, |
- /** |
- * Gets the index of the first visible item in the viewport. |
- * |
- * @type {number} |
- */ |
get firstVisibleIndex() { |
if (this._firstVisibleIndexVal === null) { |
var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddingTop); |
@@ -12756,7 +9078,6 @@ Polymer({ |
if (physicalOffset > this._scrollPosition) { |
return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx; |
} |
- // Handle a partially rendered final row in grid mode |
if (this.grid && this._virtualCount - 1 === vidx) { |
return vidx - (vidx % this._itemsPerRow); |
} |
@@ -12765,11 +9086,6 @@ Polymer({ |
return this._firstVisibleIndexVal; |
}, |
- /** |
- * Gets the index of the last visible item in the viewport. |
- * |
- * @type {number} |
- */ |
get lastVisibleIndex() { |
if (this._lastVisibleIndexVal === null) { |
if (this.grid) { |
@@ -12781,7 +9097,6 @@ Polymer({ |
if (physicalOffset < this._scrollBottom) { |
this._lastVisibleIndexVal = vidx; |
} else { |
- // Break _iterateItems |
return true; |
} |
physicalOffset += this._getPhysicalSizeIncrement(pidx); |
@@ -12813,8 +9128,6 @@ Polymer({ |
attached: function() { |
this.updateViewportBoundaries(); |
this._render(); |
- // `iron-resize` is fired when the list is attached if the event is added |
- // before attached causing unnecessary work. |
this.listen(this, 'iron-resize', '_resizeHandler'); |
}, |
@@ -12823,20 +9136,11 @@ Polymer({ |
this.unlisten(this, 'iron-resize', '_resizeHandler'); |
}, |
- /** |
- * 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' : ''; |
}, |
- /** |
- * Invoke this method if you dynamically update the viewport's |
- * size or CSS padding. |
- * |
- * @method updateViewportBoundaries |
- */ |
updateViewportBoundaries: function() { |
this._scrollerPaddingTop = this.scrollTarget === this ? 0 : |
parseInt(window.getComputedStyle(this)['padding-top'], 10); |
@@ -12847,12 +9151,7 @@ Polymer({ |
} |
}, |
- /** |
- * Update the models, the position of the |
- * items in the viewport and recycle tiles as needed. |
- */ |
_scrollHandler: function() { |
- // clamp the `scrollTop` value |
var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop)); |
var delta = scrollTop - this._scrollPosition; |
var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBottom; |
@@ -12862,22 +9161,18 @@ Polymer({ |
var currentRatio = ratio; |
var movingUp = []; |
- // track the last `scrollTop` |
this._scrollPosition = scrollTop; |
- // clear cached visible indexes |
this._firstVisibleIndexVal = null; |
this._lastVisibleIndexVal = null; |
scrollBottom = this._scrollBottom; |
physicalBottom = this._physicalBottom; |
- // random access |
if (Math.abs(delta) > this._physicalSize) { |
this._physicalTop += delta; |
recycledTiles = Math.round(delta / this._physicalAverage); |
} |
- // scroll up |
else if (delta < 0) { |
var topSpace = scrollTop - this._physicalTop; |
var virtualStart = this._virtualStart; |
@@ -12887,15 +9182,10 @@ Polymer({ |
kth = this._physicalEnd; |
currentRatio = topSpace / hiddenContentSize; |
- // move tiles from bottom to top |
while ( |
- // approximate `currentRatio` to `ratio` |
currentRatio < ratio && |
- // recycle less physical items than the total |
recycledTiles < this._physicalCount && |
- // ensure that these recycled tiles are needed |
virtualStart - recycledTiles > 0 && |
- // ensure that the tile is not visible |
physicalBottom - this._getPhysicalSizeIncrement(kth) > scrollBottom |
) { |
@@ -12910,7 +9200,6 @@ Polymer({ |
movingUp = recycledTileSet; |
recycledTiles = -recycledTiles; |
} |
- // scroll down |
else if (delta > 0) { |
var bottomSpace = physicalBottom - scrollBottom; |
var virtualEnd = this._virtualEnd; |
@@ -12921,15 +9210,10 @@ Polymer({ |
kth = this._physicalStart; |
currentRatio = bottomSpace / hiddenContentSize; |
- // move tiles from top to bottom |
while ( |
- // approximate `currentRatio` to `ratio` |
currentRatio < ratio && |
- // recycle less physical items than the total |
recycledTiles < this._physicalCount && |
- // ensure that these recycled tiles are needed |
virtualEnd + recycledTiles < lastVirtualItemIndex && |
- // ensure that the tile is not visible |
this._physicalTop + this._getPhysicalSizeIncrement(kth) < scrollTop |
) { |
@@ -12944,7 +9228,6 @@ Polymer({ |
} |
if (recycledTiles === 0) { |
- // Try to increase the pool if the list's client height isn't filled up with physical items |
if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { |
this._increasePoolIfNeeded(); |
} |
@@ -12955,36 +9238,21 @@ Polymer({ |
} |
}, |
- /** |
- * Update the list of items, starting from the `_virtualStart` item. |
- * @param {!Array<number>=} itemSet |
- * @param {!Array<number>=} movingUp |
- */ |
_update: function(itemSet, movingUp) { |
- // manage focus |
this._manageFocus(); |
- // update models |
this._assignModels(itemSet); |
- // measure heights |
this._updateMetrics(itemSet); |
- // adjust offset after measuring |
if (movingUp) { |
while (movingUp.length) { |
var idx = movingUp.pop(); |
this._physicalTop -= this._getPhysicalSizeIncrement(idx); |
} |
} |
- // update the position of the items |
this._positionItems(); |
- // set the scroller size |
this._updateScrollerSize(); |
- // increase the pool of physical items |
this._increasePoolIfNeeded(); |
}, |
- /** |
- * Creates a pool of DOM elements and attaches them to the local dom. |
- */ |
_createPool: function(size) { |
var physicalItems = new Array(size); |
@@ -12992,42 +9260,27 @@ Polymer({ |
for (var i = 0; i < size; i++) { |
var inst = this.stamp(null); |
- // First element child is item; Safari doesn't support children[0] |
- // on a doc fragment |
physicalItems[i] = inst.root.querySelector('*'); |
Polymer.dom(this).appendChild(inst.root); |
} |
return physicalItems; |
}, |
- /** |
- * Increases the pool of physical items only if needed. |
- * |
- * @return {boolean} True if the pool was increased. |
- */ |
_increasePoolIfNeeded: function() { |
- // Base case 1: the list has no height. |
if (this._viewportHeight === 0) { |
return false; |
} |
- // Base case 2: If the physical size is optimal and the list's client height is full |
- // with physical items, don't increase the pool. |
var isClientHeightFull = this._physicalBottom >= this._scrollBottom && this._physicalTop <= this._scrollPosition; |
if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) { |
return false; |
} |
- // this value should range between [0 <= `currentPage` <= `_maxPages`] |
var currentPage = Math.floor(this._physicalSize / this._viewportHeight); |
if (currentPage === 0) { |
- // fill the first page |
this._debounceTemplate(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5))); |
} else if (this._lastPage !== currentPage && isClientHeightFull) { |
- // 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, this._itemsPerRow), 16)); |
} else { |
- // fill the rest of the pages |
this._debounceTemplate(this._increasePool.bind(this, this._itemsPerRow)); |
} |
@@ -13036,9 +9289,6 @@ Polymer({ |
return true; |
}, |
- /** |
- * Increases the pool size. |
- */ |
_increasePool: function(missingItems) { |
var nextPhysicalCount = Math.min( |
this._physicalCount + missingItems, |
@@ -13057,9 +9307,6 @@ Polymer({ |
this._physicalCount = prevPhysicalCount + delta; |
- // update the physical start if we need to preserve the model of the focused item. |
- // In this situation, the focused item is currently rendered and its model would |
- // have changed after increasing the pool if the physical start remained unchanged. |
if (this._physicalStart > this._physicalEnd && |
this._isIndexRendered(this._focusedIndex) && |
this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd) { |
@@ -13068,10 +9315,6 @@ Polymer({ |
this._update(); |
}, |
- /** |
- * Render a new list of items. This method does exactly the same as `update`, |
- * but it also ensures that only one `update` cycle is created. |
- */ |
_render: function() { |
var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; |
@@ -13082,12 +9325,8 @@ Polymer({ |
} |
}, |
- /** |
- * Templetizes the user template. |
- */ |
_ensureTemplatized: function() { |
if (!this.ctor) { |
- // Template instance props that should be excluded from forwarding |
var props = {}; |
props.__key__ = true; |
props[this.as] = true; |
@@ -13106,18 +9345,10 @@ Polymer({ |
} |
}, |
- /** |
- * Implements extension point from Templatizer mixin. |
- */ |
_getStampedChildren: function() { |
return this._physicalItems; |
}, |
- /** |
- * Implements extension point from Templatizer |
- * Called as a side effect of a template instance path change, responsible |
- * for notifying items.<key-for-instance>.<path> change up to host. |
- */ |
_forwardInstancePath: function(inst, path, value) { |
if (path.indexOf(this.as + '.') === 0) { |
this.notifyPath('items.' + inst.__key__ + '.' + |
@@ -13125,11 +9356,6 @@ Polymer({ |
} |
}, |
- /** |
- * Implements extension point from Templatizer mixin |
- * Called as side-effect of a host property change, responsible for |
- * notifying parent path change on each row. |
- */ |
_forwardParentProp: function(prop, value) { |
if (this._physicalItems) { |
this._physicalItems.forEach(function(item) { |
@@ -13138,11 +9364,6 @@ Polymer({ |
} |
}, |
- /** |
- * Implements extension point from Templatizer |
- * Called as side-effect of a host path change, responsible for |
- * notifying parent.<path> path change on each row. |
- */ |
_forwardParentPath: function(path, value) { |
if (this._physicalItems) { |
this._physicalItems.forEach(function(item) { |
@@ -13151,10 +9372,6 @@ Polymer({ |
} |
}, |
- /** |
- * Called as a side effect of a host items.<key>.<path> path change, |
- * responsible for notifying item.<path> changes. |
- */ |
_forwardItemPath: function(path, value) { |
if (!this._physicalIndexForKey) { |
return; |
@@ -13173,7 +9390,6 @@ Polymer({ |
path = this.as + '.' + path.substring(dot+1); |
el._templateInstance.notifyPath(path, value, true); |
} else { |
- // Update selection if needed |
var currentItem = el._templateInstance[this.as]; |
if (Array.isArray(this.selectedItems)) { |
for (var i = 0; i < this.selectedItems.length; i++) { |
@@ -13189,13 +9405,8 @@ Polymer({ |
} |
}, |
- /** |
- * Called when the items have changed. That is, ressignments |
- * to `items`, splices or updates to a single item. |
- */ |
_itemsChanged: function(change) { |
if (change.path === 'items') { |
- // reset items |
this._virtualStart = 0; |
this._physicalTop = 0; |
this._virtualCount = this.items ? this.items.length : 0; |
@@ -13206,7 +9417,6 @@ Polymer({ |
this._resetScrollPosition(0); |
this._removeFocusedItem(); |
- // create the initial physical items |
if (!this._physicalItems) { |
this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, this._virtualCount)); |
this._physicalItems = this._createPool(this._physicalCount); |
@@ -13221,7 +9431,6 @@ Polymer({ |
this._virtualCount = this.items ? this.items.length : 0; |
} else { |
- // update a single item |
this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.value); |
return; |
} |
@@ -13230,14 +9439,9 @@ Polymer({ |
this._debounceTemplate(this._render); |
}, |
- /** |
- * @param {!Array<!PolymerSplice>} splices |
- */ |
_adjustVirtualIndex: function(splices) { |
splices.forEach(function(splice) { |
- // deselect removed items |
splice.removed.forEach(this._removeItem, this); |
- // We only need to care about changes happening above the current position |
if (splice.index < this._virtualStart) { |
var delta = Math.max( |
splice.addedCount - splice.removed.length, |
@@ -13254,19 +9458,11 @@ Polymer({ |
_removeItem: function(item) { |
this.$.selector.deselect(item); |
- // remove the current focused item |
if (this._focusedItem && this._focusedItem._templateInstance[this.as] === item) { |
this._removeFocusedItem(); |
} |
}, |
- /** |
- * Executes a provided function per every physical index in `itemSet` |
- * `itemSet` default value is equivalent to the entire set of physical indexes. |
- * |
- * @param {!function(number, number)} fn |
- * @param {!Array<number>=} itemSet |
- */ |
_iterateItems: function(fn, itemSet) { |
var pidx, vidx, rtn, i; |
@@ -13295,12 +9491,6 @@ Polymer({ |
} |
}, |
- /** |
- * Returns the virtual index for a given physical index |
- * |
- * @param {number} pidx Physical index |
- * @return {number} |
- */ |
_computeVidx: function(pidx) { |
if (pidx >= this._physicalStart) { |
return this._virtualStart + (pidx - this._physicalStart); |
@@ -13308,10 +9498,6 @@ Polymer({ |
return this._virtualStart + (this._physicalCount - this._physicalStart) + pidx; |
}, |
- /** |
- * Assigns the data models to a given set of items. |
- * @param {!Array<number>=} itemSet |
- */ |
_assignModels: function(itemSet) { |
this._iterateItems(function(pidx, vidx) { |
var el = this._physicalItems[pidx]; |
@@ -13333,14 +9519,7 @@ Polymer({ |
}, itemSet); |
}, |
- /** |
- * Updates the height for a given set of items. |
- * |
- * @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; |
@@ -13365,7 +9544,6 @@ Polymer({ |
this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize; |
} |
- // update the average if we measured something |
if (this._physicalAverageCount !== prevAvgCount) { |
this._physicalAverage = Math.round( |
((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / |
@@ -13375,17 +9553,11 @@ Polymer({ |
_updateGridMetrics: function() { |
this._viewportWidth = this.$.items.offsetWidth; |
- // Set item width to the value of the _physicalItems offsetWidth |
this._itemWidth = this._physicalCount > 0 ? this._physicalItems[0].getBoundingClientRect().width : DEFAULT_GRID_SIZE; |
- // Set row height to the value of the _physicalItems offsetHeight |
this._rowHeight = this._physicalCount > 0 ? this._physicalItems[0].offsetHeight : DEFAULT_GRID_SIZE; |
- // If in grid mode compute how many items with exist in each row |
this._itemsPerRow = this._itemWidth ? Math.floor(this._viewportWidth / this._itemWidth) : this._itemsPerRow; |
}, |
- /** |
- * Updates the position of the physical items. |
- */ |
_positionItems: function() { |
this._adjustScrollPosition(); |
@@ -13427,37 +9599,22 @@ Polymer({ |
return this._rowHeight; |
}, |
- /** |
- * Returns, based on the current index, |
- * whether or not the next index will need |
- * to be rendered on a new row. |
- * |
- * @param {number} vidx Virtual index |
- * @return {boolean} |
- */ |
_shouldRenderNextRow: function(vidx) { |
return vidx % this._itemsPerRow === this._itemsPerRow - 1; |
}, |
- /** |
- * Adjusts the scroll position when it was overestimated. |
- */ |
_adjustScrollPosition: function() { |
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._physicalTop !== 0) { |
this._resetScrollPosition(this._scrollTop - deltaHeight); |
} |
} |
}, |
- /** |
- * Sets the position of the scroll. |
- */ |
_resetScrollPosition: function(pos) { |
if (this.scrollTarget) { |
this._scrollTop = pos; |
@@ -13465,11 +9622,6 @@ Polymer({ |
} |
}, |
- /** |
- * Sets the scroll height, that's the height of the content, |
- * |
- * @param {boolean=} forceUpdate If true, updates the height no matter what. |
- */ |
_updateScrollerSize: function(forceUpdate) { |
if (this.grid) { |
this._estScrollHeight = this._virtualRowCount * this._rowHeight; |
@@ -13482,31 +9634,16 @@ Polymer({ |
forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize; |
forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this._estScrollHeight; |
- // amortize height adjustment, so it won't trigger repaints very often |
if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) { |
this.$.items.style.height = this._estScrollHeight + 'px'; |
this._scrollHeight = this._estScrollHeight; |
} |
}, |
- /** |
- * Scroll to a specific item in the virtual list regardless |
- * of the physical items in the DOM tree. |
- * |
- * @method scrollToItem |
- * @param {(Object)} item The item to be scrolled to |
- */ |
scrollToItem: function(item){ |
return this.scrollToIndex(this.items.indexOf(item)); |
}, |
- /** |
- * Scroll to a specific index in the virtual list regardless |
- * of the physical items in the DOM tree. |
- * |
- * @method scrollToIndex |
- * @param {number} idx The index of the item |
- */ |
scrollToIndex: function(idx) { |
if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) { |
return; |
@@ -13515,18 +9652,13 @@ Polymer({ |
Polymer.dom.flush(); |
idx = Math.min(Math.max(idx, 0), this._virtualCount-1); |
- // update the virtual start only when needed |
if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { |
this._virtualStart = this.grid ? (idx - this._itemsPerRow * 2) : (idx - 1); |
} |
- // manage focus |
this._manageFocus(); |
- // assign new models |
this._assignModels(); |
- // measure the new sizes |
this._updateMetrics(); |
- // estimate new physical offset |
var estPhysicalTop = Math.floor(this._virtualStart / this._itemsPerRow) * this._physicalAverage; |
this._physicalTop = estPhysicalTop; |
@@ -13535,45 +9667,28 @@ Polymer({ |
var targetOffsetTop = 0; |
var hiddenContentSize = this._hiddenContentSize; |
- // scroll to the item as much as we can |
while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) { |
targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(currentTopItem); |
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 + this._scrollerPaddingTop + targetOffsetTop); |
- // increase the pool of physical items if needed |
this._increasePoolIfNeeded(); |
- // clear cached visible index |
this._firstVisibleIndexVal = null; |
this._lastVisibleIndexVal = null; |
}, |
- /** |
- * Reset the physical average and the average count. |
- */ |
_resetAverage: function() { |
this._physicalAverage = 0; |
this._physicalAverageCount = 0; |
}, |
- /** |
- * A handler for the `iron-resize` event triggered by `IronResizableBehavior` |
- * when the element is resized. |
- */ |
_resizeHandler: function() { |
- // iOS fires the resize event when the address bar slides up |
if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100) { |
return; |
} |
- // In Desktop Safari 9.0.3, if the scroll bars are always shown, |
- // changing the scroll position from a resize handler would result in |
- // the scroll position being reset. Waiting 1ms fixes the issue. |
Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() { |
this.updateViewportBoundaries(); |
this._render(); |
@@ -13595,11 +9710,6 @@ Polymer({ |
return null; |
}, |
- /** |
- * Gets a valid item instance from its index or the object value. |
- * |
- * @param {(Object|number)} item The item object or its index |
- */ |
_getNormalizedItem: function(item) { |
if (this._collection.getKey(item) === undefined) { |
if (typeof item === 'number') { |
@@ -13614,12 +9724,6 @@ Polymer({ |
return item; |
}, |
- /** |
- * Select the list item at the given index. |
- * |
- * @method selectItem |
- * @param {(Object|number)} item The item object or its index |
- */ |
selectItem: function(item) { |
item = this._getNormalizedItem(item); |
var model = this._getModelFromItem(item); |
@@ -13634,13 +9738,6 @@ Polymer({ |
this.updateSizeForItem(item); |
}, |
- /** |
- * Deselects the given item list if it is already selected. |
- * |
- |
- * @method deselect |
- * @param {(Object|number)} item The item object or its index |
- */ |
deselectItem: function(item) { |
item = this._getNormalizedItem(item); |
var model = this._getModelFromItem(item); |
@@ -13652,13 +9749,6 @@ Polymer({ |
this.updateSizeForItem(item); |
}, |
- /** |
- * Select or deselect a given item depending on whether the item |
- * has already been selected. |
- * |
- * @method toggleSelectionForItem |
- * @param {(Object|number)} item The item object or its index |
- */ |
toggleSelectionForItem: function(item) { |
item = this._getNormalizedItem(item); |
if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item)) { |
@@ -13668,11 +9758,6 @@ Polymer({ |
} |
}, |
- /** |
- * Clears the current selection state of the list. |
- * |
- * @method clearSelection |
- */ |
clearSelection: function() { |
function unselect(item) { |
var model = this._getModelFromItem(item); |
@@ -13690,18 +9775,11 @@ Polymer({ |
/** @type {!ArraySelectorElement} */ (this.$.selector).clearSelection(); |
}, |
- /** |
- * Add an event listener to `tap` if `selectionEnabled` is true, |
- * it will remove the listener otherwise. |
- */ |
_selectionEnabledChanged: function(selectionEnabled) { |
var handler = selectionEnabled ? this.listen : this.unlisten; |
handler.call(this, this, 'tap', '_selectionHandler'); |
}, |
- /** |
- * Select an item from an event object. |
- */ |
_selectionHandler: function(e) { |
var model = this.modelForElement(e.target); |
if (!model) { |
@@ -13711,20 +9789,15 @@ Polymer({ |
var target = Polymer.dom(e).path[0]; |
var activeEl = Polymer.dom(this.domHost ? this.domHost.root : document).activeElement; |
var physicalItem = this._physicalItems[this._getPhysicalIndex(model[this.indexAs])]; |
- // Safari does not focus certain form controls via mouse |
- // https://bugs.webkit.org/show_bug.cgi?id=118043 |
if (target.localName === 'input' || |
target.localName === 'button' || |
target.localName === 'select') { |
return; |
} |
- // Set a temporary tabindex |
modelTabIndex = model.tabIndex; |
model.tabIndex = SECRET_TABINDEX; |
activeElTabIndex = activeEl ? activeEl.tabIndex : -1; |
model.tabIndex = modelTabIndex; |
- // Only select the item if the tap wasn't on a focusable child |
- // or the element bound to `tabIndex` |
if (activeEl && physicalItem.contains(activeEl) && activeElTabIndex !== SECRET_TABINDEX) { |
return; |
} |
@@ -13736,12 +9809,6 @@ Polymer({ |
this.$.selector.multi = multiSelection; |
}, |
- /** |
- * Updates the size of an item. |
- * |
- * @method updateSizeForItem |
- * @param {(Object|number)} item The item object or its index |
- */ |
updateSizeForItem: function(item) { |
item = this._getNormalizedItem(item); |
var key = this._collection.getKey(item); |
@@ -13753,26 +9820,16 @@ Polymer({ |
} |
}, |
- /** |
- * Creates a temporary backfill item in the rendered pool of physical items |
- * to replace the main focused item. The focused item has tabIndex = 0 |
- * and might be currently focused by the user. |
- * |
- * This dynamic replacement helps to preserve the focus state. |
- */ |
_manageFocus: function() { |
var fidx = this._focusedIndex; |
if (fidx >= 0 && fidx < this._virtualCount) { |
- // if it's a valid index, check if that index is rendered |
- // in a physical item. |
if (this._isIndexRendered(fidx)) { |
this._restoreFocusedItem(); |
} else { |
this._createFocusBackfillItem(); |
} |
} else if (this._virtualCount > 0 && this._physicalCount > 0) { |
- // otherwise, assign the initial focused index. |
this._focusedIndex = this._virtualStart; |
this._focusedItem = this._physicalItems[this._physicalStart]; |
} |
@@ -13795,7 +9852,6 @@ Polymer({ |
return; |
} |
this._restoreFocusedItem(); |
- // scroll to index to make sure it's rendered |
if (!this._isIndexRendered(idx)) { |
this.scrollToIndex(idx); |
} |
@@ -13804,19 +9860,14 @@ Polymer({ |
var model = physicalItem._templateInstance; |
var focusable; |
- // set a secret tab index |
model.tabIndex = SECRET_TABINDEX; |
- // check if focusable element is the physical item |
if (physicalItem.tabIndex === SECRET_TABINDEX) { |
focusable = physicalItem; |
} |
- // search for the element which tabindex is bound to the secret tab index |
if (!focusable) { |
focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECRET_TABINDEX + '"]'); |
} |
- // restore the tab index |
model.tabIndex = 0; |
- // focus the focusable element |
this._focusedIndex = idx; |
focusable && focusable.focus(); |
}, |
@@ -13837,20 +9888,15 @@ Polymer({ |
return; |
} |
if (!this._focusBackfillItem) { |
- // create a physical item, so that it backfills the focused item. |
var stampedTemplate = this.stamp(null); |
this._focusBackfillItem = stampedTemplate.root.querySelector('*'); |
Polymer.dom(this).appendChild(stampedTemplate.root); |
} |
- // get the physical index for the focused index |
pidx = this._getPhysicalIndex(fidx); |
if (pidx != null) { |
- // set the offcreen focused physical item |
this._offscreenFocusedItem = this._physicalItems[pidx]; |
- // backfill the focused physical item |
this._physicalItems[pidx] = this._focusBackfillItem; |
- // hide the focused physical |
this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem); |
} |
}, |
@@ -13861,19 +9907,13 @@ Polymer({ |
if (!this._offscreenFocusedItem || this._focusedIndex < 0) { |
return; |
} |
- // assign models to the focused index |
this._assignModels(); |
- // get the new physical index for the focused index |
pidx = this._getPhysicalIndex(fidx); |
if (pidx != null) { |
- // flip the focus backfill |
this._focusBackfillItem = this._physicalItems[pidx]; |
- // restore the focused physical item |
this._physicalItems[pidx] = this._offscreenFocusedItem; |
- // reset the offscreen focused item |
this._offscreenFocusedItem = null; |
- // hide the physical item that backfills |
this.translate3d(0, HIDDEN_Y, 0, this._focusBackfillItem); |
} |
}, |
@@ -13888,15 +9928,12 @@ Polymer({ |
return; |
} |
if (focusedModel === targetModel) { |
- // if the user focused the same item, then bring it into view if it's not visible |
if (!this._isIndexVisible(fidx)) { |
this.scrollToIndex(fidx); |
} |
} else { |
this._restoreFocusedItem(); |
- // restore tabIndex for the currently focused item |
focusedModel.tabIndex = -1; |
- // set the tabIndex for the next focused item |
targetModel.tabIndex = 0; |
fidx = targetModel[this.indexAs]; |
this._focusedIndex = fidx; |
@@ -13913,7 +9950,6 @@ Polymer({ |
}, |
_didMoveDown: function(e) { |
- // disable scroll when pressing the down key |
e.detail.keyboardEvent.preventDefault(); |
this._focusPhysicalItem(this._focusedIndex + 1); |
}, |
@@ -13931,27 +9967,16 @@ Polymer({ |
properties: { |
- /** |
- * Distance from the top (or left, for horizontal) bound of the scroller |
- * where the "upper trigger" will fire. |
- */ |
upperThreshold: { |
type: Number, |
value: 100 |
}, |
- /** |
- * Distance from the bottom (or right, for horizontal) bound of the scroller |
- * where the "lower trigger" will fire. |
- */ |
lowerThreshold: { |
type: Number, |
value: 100 |
}, |
- /** |
- * Read-only value that tracks the triggered state of the upper threshold. |
- */ |
upperTriggered: { |
type: Boolean, |
value: false, |
@@ -13959,9 +9984,6 @@ Polymer({ |
readOnly: true |
}, |
- /** |
- * Read-only value that tracks the triggered state of the lower threshold. |
- */ |
lowerTriggered: { |
type: Boolean, |
value: false, |
@@ -13969,9 +9991,6 @@ Polymer({ |
readOnly: true |
}, |
- /** |
- * True if the orientation of the scroller is horizontal. |
- */ |
horizontal: { |
type: Boolean, |
value: false |
@@ -13996,7 +10015,6 @@ Polymer({ |
}, |
_scrollHandler: function() { |
- // throttle the work on the scroll event |
var THROTTLE_THRESHOLD = 200; |
if (!this.isDebouncerActive('_checkTheshold')) { |
this.debounce('_checkTheshold', function() { |
@@ -14014,12 +10032,6 @@ Polymer({ |
} |
}, |
- /** |
- * Checks the scroll thresholds. |
- * This method is automatically called by iron-scroll-threshold. |
- * |
- * @method checkScrollThesholds |
- */ |
checkScrollThesholds: function() { |
if (!this.scrollTarget || (this.lowerTriggered && this.upperTriggered)) { |
return; |
@@ -14029,39 +10041,22 @@ Polymer({ |
this.scrollTarget.scrollWidth - this._scrollTargetWidth - this._scrollLeft : |
this.scrollTarget.scrollHeight - this._scrollTargetHeight - this._scrollTop; |
- // Detect upper threshold |
if (upperScrollValue <= this.upperThreshold && !this.upperTriggered) { |
this._setUpperTriggered(true); |
this.fire('upper-threshold'); |
} |
- // Detect lower threshold |
if (lowerScrollValue <= this.lowerThreshold && !this.lowerTriggered) { |
this._setLowerTriggered(true); |
this.fire('lower-threshold'); |
} |
}, |
- /** |
- * Clear the upper and lower threshold states. |
- * |
- * @method clearTriggers |
- */ |
clearTriggers: function() { |
this._setUpperTriggered(false); |
this._setLowerTriggered(false); |
} |
- /** |
- * Fires when the lower threshold has been reached. |
- * |
- * @event lower-threshold |
- */ |
- /** |
- * Fires when the upper threshold has been reached. |
- * |
- * @event upper-threshold |
- */ |
}); |
// Copyright 2015 The Chromium Authors. All rights reserved. |
@@ -14074,7 +10069,6 @@ Polymer({ |
behaviors: [HistoryListBehavior], |
properties: { |
- // The search term for the current query. Set when the query returns. |
searchedTerm: { |
type: String, |
value: '', |
@@ -14084,7 +10078,6 @@ Polymer({ |
querying: Boolean, |
- // An array of history entries in reverse chronological order. |
historyData_: Array, |
resultLoadingDisabled_: { |
@@ -14100,19 +10093,11 @@ Polymer({ |
/** @override */ |
attached: function() { |
- // It is possible (eg, when middle clicking the reload button) for all other |
- // resize events to fire before the list is attached and can be measured. |
- // Adding another resize here ensures it will get sized correctly. |
/** @type {IronListElement} */(this.$['infinite-list']).notifyResize(); |
this.$['infinite-list'].scrollTarget = this; |
this.$['scroll-threshold'].scrollTarget = this; |
}, |
- /** |
- * Remove bookmark star for history items with matching URLs. |
- * @param {{detail: !string}} e |
- * @private |
- */ |
removeBookmarkStars_: function(e) { |
var url = e.detail; |
@@ -14125,18 +10110,10 @@ Polymer({ |
} |
}, |
- /** |
- * Disables history result loading when there are no more history results. |
- */ |
disableResultLoading: function() { |
this.resultLoadingDisabled_ = true; |
}, |
- /** |
- * Adds the newly updated history results into historyData_. Adds new fields |
- * for each result. |
- * @param {!Array<!HistoryEntry>} historyResults The new history results. |
- */ |
addNewResults: function(historyResults) { |
var results = historyResults.slice(); |
/** @type {IronScrollThresholdElement} */(this.$['scroll-threshold']) |
@@ -14151,21 +10128,13 @@ Polymer({ |
} |
if (this.historyData_) { |
- // If we have previously received data, push the new items onto the |
- // existing array. |
results.unshift('historyData_'); |
this.push.apply(this, results); |
} else { |
- // The first time we receive data, use set() to ensure the iron-list is |
- // initialized correctly. |
this.set('historyData_', results); |
} |
}, |
- /** |
- * Called when the page is scrolled to near the bottom of the list. |
- * @private |
- */ |
loadMoreData_: function() { |
if (this.resultLoadingDisabled_ || this.querying) |
return; |
@@ -14173,28 +10142,11 @@ Polymer({ |
this.fire('load-more-history'); |
}, |
- /** |
- * Check whether the time difference between the given history item and the |
- * next one is large enough for a spacer to be required. |
- * @param {HistoryEntry} item |
- * @param {number} index The index of |item| in |historyData_|. |
- * @param {number} length The length of |historyData_|. |
- * @return {boolean} Whether or not time gap separator is required. |
- * @private |
- */ |
needsTimeGap_: function(item, index, length) { |
return md_history.HistoryItem.needsTimeGap( |
this.historyData_, index, this.searchedTerm); |
}, |
- /** |
- * True if the given item is the beginning of a new card. |
- * @param {HistoryEntry} item |
- * @param {number} i Index of |item| within |historyData_|. |
- * @param {number} length |
- * @return {boolean} |
- * @private |
- */ |
isCardStart_: function(item, i, length) { |
if (length == 0 || i > length - 1) |
return false; |
@@ -14203,14 +10155,6 @@ Polymer({ |
this.historyData_[i - 1].dateRelativeDay; |
}, |
- /** |
- * True if the given item is the end of a card. |
- * @param {HistoryEntry} item |
- * @param {number} i Index of |item| within |historyData_|. |
- * @param {number} length |
- * @return {boolean} |
- * @private |
- */ |
isCardEnd_: function(item, i, length) { |
if (length == 0 || i > length - 1) |
return false; |
@@ -14219,27 +10163,14 @@ Polymer({ |
this.historyData_[i + 1].dateRelativeDay; |
}, |
- /** |
- * @param {number} index |
- * @return {boolean} |
- * @private |
- */ |
isFirstItem_: function(index) { |
return index == 0; |
}, |
- /** |
- * @private |
- */ |
notifyListScroll_: function() { |
this.fire('history-list-scrolled'); |
}, |
- /** |
- * @param {number} index |
- * @return {string} |
- * @private |
- */ |
pathForItem_: function(index) { |
return 'historyData_.' + index; |
}, |
@@ -14248,19 +10179,6 @@ Polymer({ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * @fileoverview |
- * history-lazy-render is a simple variant of dom-if designed for lazy rendering |
- * of elements that are accessed imperatively. |
- * Usage: |
- * <template is="history-lazy-render" id="menu"> |
- * <heavy-menu></heavy-menu> |
- * </template> |
- * |
- * this.$.menu.get().then(function(menu) { |
- * menu.show(); |
- * }); |
- */ |
Polymer({ |
is: 'history-lazy-render', |
@@ -14276,11 +10194,6 @@ Polymer({ |
/** @private {TemplateInstance} */ |
_instance: null, |
- /** |
- * Stamp the template into the DOM tree asynchronously |
- * @return {Promise<Element>} Promise which resolves when the template has |
- * been stamped. |
- */ |
get: function() { |
if (!this._renderPromise) { |
this._renderPromise = new Promise(function(resolve) { |
@@ -14294,10 +10207,6 @@ Polymer({ |
return this._renderPromise; |
}, |
- /** |
- * @return {?Element} The element contained in the template, if it has |
- * already been stamped. |
- */ |
getIfExists: function() { |
if (this._instance) { |
var children = this._instance._children; |
@@ -14321,19 +10230,11 @@ Polymer({ |
} |
}, |
- /** |
- * @param {string} prop |
- * @param {Object} value |
- */ |
_forwardParentProp: function(prop, value) { |
if (this._instance) |
this._instance.__setProperty(prop, value, true); |
}, |
- /** |
- * @param {string} path |
- * @param {Object} value |
- */ |
_forwardParentPath: function(path, value) { |
if (this._instance) |
this._instance._notifyPath(path, value, true); |
@@ -14347,10 +10248,8 @@ Polymer({ |
is: 'history-list-container', |
properties: { |
- // The path of the currently selected page. |
selectedPage_: String, |
- // Whether domain-grouped history is enabled. |
grouped: Boolean, |
/** @type {!QueryState} */ |
@@ -14370,11 +10269,6 @@ Polymer({ |
'toggle-menu': 'toggleMenu_', |
}, |
- /** |
- * @param {HistoryQuery} info An object containing information about the |
- * query. |
- * @param {!Array<HistoryEntry>} results A list of results. |
- */ |
historyResult: function(info, results) { |
this.initializeResults_(info, results); |
this.closeMenu_(); |
@@ -14390,23 +10284,14 @@ Polymer({ |
list.disableResultLoading(); |
}, |
- /** |
- * Queries the history backend for results based on queryState. |
- * @param {boolean} incremental Whether the new query should continue where |
- * the previous query stopped. |
- */ |
queryHistory: function(incremental) { |
var queryState = this.queryState; |
- // Disable querying until the first set of results have been returned. If |
- // there is a search, query immediately to support search query params from |
- // the URL. |
var noResults = !this.queryResult || this.queryResult.results == null; |
if (queryState.queryingDisabled || |
(!this.queryState.searchTerm && noResults)) { |
return; |
} |
- // Close any open dialog if a new query is initiated. |
var dialog = this.$.dialog.getIfExists(); |
if (!incremental && dialog && dialog.open) |
dialog.close(); |
@@ -14437,10 +10322,6 @@ Polymer({ |
this.getSelectedList_().unselectAllItems(count); |
}, |
- /** |
- * Delete all the currently selected history items. Will prompt the user with |
- * a dialog to confirm that the deletion should be performed. |
- */ |
deleteSelectedWithPrompt: function() { |
if (!loadTimeData.getBoolean('allowDeletingHistory')) |
return; |
@@ -14449,10 +10330,6 @@ Polymer({ |
}); |
}, |
- /** |
- * @param {HistoryRange} range |
- * @private |
- */ |
groupedRangeChanged_: function(range) { |
this.selectedPage_ = this.queryState.range == HistoryRange.ALL_TIME ? |
'infinite-list' : 'grouped-list'; |
@@ -14463,11 +10340,6 @@ Polymer({ |
/** @private */ |
loadMoreHistory_: function() { this.queryHistory(true); }, |
- /** |
- * @param {HistoryQuery} info |
- * @param {!Array<HistoryEntry>} results |
- * @private |
- */ |
initializeResults_: function(info, results) { |
if (results.length == 0) |
return; |
@@ -14475,7 +10347,6 @@ Polymer({ |
var currentDate = results[0].dateRelativeDay; |
for (var i = 0; i < results.length; i++) { |
- // Sets the default values for these fields to prevent undefined types. |
results[i].selected = false; |
results[i].readableTimestamp = |
info.term == '' ? results[i].dateTimeOfDay : results[i].dateShort; |
@@ -14499,23 +10370,12 @@ Polymer({ |
dialog.close(); |
}, |
- /** |
- * Closes the overflow menu. |
- * @private |
- */ |
closeMenu_: function() { |
var menu = this.$.sharedMenu.getIfExists(); |
if (menu) |
menu.closeMenu(); |
}, |
- /** |
- * Opens the overflow menu unless the menu is already open and the same button |
- * is pressed. |
- * @param {{detail: {item: !HistoryEntry, target: !HTMLElement}}} e |
- * @return {Promise<Element>} |
- * @private |
- */ |
toggleMenu_: function(e) { |
var target = e.detail.target; |
return this.$.sharedMenu.get().then(function(menu) { |
@@ -14539,18 +10399,11 @@ Polymer({ |
.deleteItems([itemData.item]) |
.then(function(items) { |
this.getSelectedList_().removeItemsByPath([itemData.path]); |
- // This unselect-all is to reset the toolbar when deleting a selected |
- // item. TODO(tsergeant): Make this automatic based on observing list |
- // modifications. |
this.fire('unselect-all'); |
}.bind(this)); |
menu.closeMenu(); |
}, |
- /** |
- * @return {HTMLElement} |
- * @private |
- */ |
getSelectedList_: function() { |
return this.$.content.selectedItem; |
}, |
@@ -14563,45 +10416,25 @@ Polymer({ |
is: 'history-synced-device-card', |
properties: { |
- // Name of the synced device. |
device: String, |
- // When the device information was last updated. |
lastUpdateTime: String, |
- /** |
- * The list of tabs open for this device. |
- * @type {!Array<!ForeignSessionTab>} |
- */ |
tabs: { |
type: Array, |
value: function() { return []; }, |
observer: 'updateIcons_' |
}, |
- /** |
- * The indexes where a window separator should be shown. The use of a |
- * separate array here is necessary for window separators to appear |
- * correctly in search. See http://crrev.com/2022003002 for more details. |
- * @type {!Array<number>} |
- */ |
separatorIndexes: Array, |
- // Whether the card is open. |
cardOpen_: {type: Boolean, value: true}, |
searchTerm: String, |
- // Internal identifier for the device. |
sessionTag: String, |
}, |
- /** |
- * Open a single synced tab. Listens to 'click' rather than 'tap' |
- * to determine what modifier keys were pressed. |
- * @param {DomRepeatClickEvent} e |
- * @private |
- */ |
openTab_: function(e) { |
var tab = /** @type {ForeignSessionTab} */(e.model.tab); |
md_history.BrowserService.getInstance().openForeignSessionTab( |
@@ -14609,20 +10442,12 @@ Polymer({ |
e.preventDefault(); |
}, |
- /** |
- * Toggles the dropdown display of synced tabs for each device card. |
- */ |
toggleTabCard: function() { |
this.$.collapse.toggle(); |
this.$['dropdown-indicator'].icon = |
this.$.collapse.opened ? 'cr:expand-less' : 'cr:expand-more'; |
}, |
- /** |
- * When the synced tab information is set, the icon associated with the tab |
- * website is also set. |
- * @private |
- */ |
updateIcons_: function() { |
this.async(function() { |
var icons = Polymer.dom(this.root).querySelectorAll('.website-icon'); |
@@ -14639,19 +10464,11 @@ Polymer({ |
return this.separatorIndexes.indexOf(index) != -1; |
}, |
- /** |
- * @param {boolean} cardOpen |
- * @return {string} |
- */ |
getCollapseTitle_: function(cardOpen) { |
return cardOpen ? loadTimeData.getString('collapseSessionButton') : |
loadTimeData.getString('expandSessionButton'); |
}, |
- /** |
- * @param {CustomEvent} e |
- * @private |
- */ |
onMenuButtonTap_: function(e) { |
this.fire('toggle-menu', { |
target: Polymer.dom(e).localTarget, |
@@ -14664,23 +10481,12 @@ Polymer({ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-/** |
- * @typedef {{device: string, |
- * lastUpdateTime: string, |
- * separatorIndexes: !Array<number>, |
- * timestamp: number, |
- * tabs: !Array<!ForeignSessionTab>, |
- * tag: string}} |
- */ |
var ForeignDeviceInternal; |
Polymer({ |
is: 'history-synced-device-manager', |
properties: { |
- /** |
- * @type {?Array<!ForeignSession>} |
- */ |
sessionList: { |
type: Array, |
observer: 'updateSyncedDevices' |
@@ -14691,10 +10497,6 @@ Polymer({ |
observer: 'searchTermChanged' |
}, |
- /** |
- * An array of synced devices with synced tab data. |
- * @type {!Array<!ForeignDeviceInternal>} |
- */ |
syncedDevices_: { |
type: Array, |
value: function() { return []; } |
@@ -14725,14 +10527,9 @@ Polymer({ |
/** @override */ |
attached: function() { |
- // Update the sign in state. |
chrome.send('otherDevicesInitialized'); |
}, |
- /** |
- * @param {!ForeignSession} session |
- * @return {!ForeignDeviceInternal} |
- */ |
createInternalDevice_: function(session) { |
var tabs = []; |
var separatorIndexes = []; |
@@ -14748,7 +10545,6 @@ Polymer({ |
var windowAdded = false; |
if (!this.searchTerm) { |
- // Add all the tabs if there is no search term. |
tabs = tabs.concat(newTabs); |
windowAdded = true; |
} else { |
@@ -14803,13 +10599,6 @@ Polymer({ |
this.syncedDevices_ = []; |
}, |
- /** |
- * Decide whether or not should display no synced tabs message. |
- * @param {boolean} signInState |
- * @param {number} syncedDevicesLength |
- * @param {boolean} guestSession |
- * @return {boolean} |
- */ |
showNoSyncedMessage: function( |
signInState, syncedDevicesLength, guestSession) { |
if (guestSession) |
@@ -14818,13 +10607,6 @@ Polymer({ |
return signInState && syncedDevicesLength == 0; |
}, |
- /** |
- * Shows the signin guide when the user is not signed in and not in a guest |
- * session. |
- * @param {boolean} signInState |
- * @param {boolean} guestSession |
- * @return {boolean} |
- */ |
showSignInGuide: function(signInState, guestSession) { |
var show = !signInState && !guestSession; |
if (show) { |
@@ -14835,32 +10617,17 @@ Polymer({ |
return show; |
}, |
- /** |
- * Decide what message should be displayed when user is logged in and there |
- * are no synced tabs. |
- * @param {boolean} fetchingSyncedTabs |
- * @return {string} |
- */ |
noSyncedTabsMessage: function(fetchingSyncedTabs) { |
return loadTimeData.getString( |
fetchingSyncedTabs ? 'loading' : 'noSyncedResults'); |
}, |
- /** |
- * Replaces the currently displayed synced tabs with |sessionList|. It is |
- * common for only a single session within the list to have changed, We try to |
- * avoid doing extra work in this case. The logic could be more intelligent |
- * about updating individual tabs rather than replacing whole sessions, but |
- * this approach seems to have acceptable performance. |
- * @param {?Array<!ForeignSession>} sessionList |
- */ |
updateSyncedDevices: function(sessionList) { |
this.fetchingSyncedTabs_ = false; |
if (!sessionList) |
return; |
- // First, update any existing devices that have changed. |
var updateCount = Math.min(sessionList.length, this.syncedDevices_.length); |
for (var i = 0; i < updateCount; i++) { |
var oldDevice = this.syncedDevices_[i]; |
@@ -14871,41 +10638,26 @@ Polymer({ |
} |
} |
- // Then, append any new devices. |
for (var i = updateCount; i < sessionList.length; i++) { |
this.push('syncedDevices_', this.createInternalDevice_(sessionList[i])); |
} |
}, |
- /** |
- * End fetching synced tabs when sync is disabled. |
- */ |
tabSyncDisabled: function() { |
this.fetchingSyncedTabs_ = false; |
this.clearDisplayedSyncedDevices_(); |
}, |
- /** |
- * Get called when user's sign in state changes, this will affect UI of synced |
- * tabs page. Sign in promo gets displayed when user is signed out, and |
- * different messages are shown when there are no synced tabs. |
- * @param {boolean} isUserSignedIn |
- */ |
updateSignInState: function(isUserSignedIn) { |
- // If user's sign in state didn't change, then don't change message or |
- // update UI. |
if (this.signInState_ == isUserSignedIn) |
return; |
this.signInState_ = isUserSignedIn; |
- // User signed out, clear synced device list and show the sign in promo. |
if (!isUserSignedIn) { |
this.clearDisplayedSyncedDevices_(); |
return; |
} |
- // User signed in, show the loading message when querying for synced |
- // devices. |
this.fetchingSyncedTabs_ = true; |
}, |
@@ -14914,66 +10666,6 @@ Polymer({ |
this.updateSyncedDevices(this.sessionList); |
} |
}); |
-/** |
- `iron-selector` is an element which can be used to manage a list of elements |
- that can be selected. Tapping on the item will make the item selected. The `selected` indicates |
- which item is being selected. The default is to use the index of the item. |
- |
- Example: |
- |
- <iron-selector selected="0"> |
- <div>Item 1</div> |
- <div>Item 2</div> |
- <div>Item 3</div> |
- </iron-selector> |
- |
- If you want to use the attribute value of an element for `selected` instead of the index, |
- set `attrForSelected` to the name of the attribute. For example, if you want to select item by |
- `name`, set `attrForSelected` to `name`. |
- |
- Example: |
- |
- <iron-selector attr-for-selected="name" selected="foo"> |
- <div name="foo">Foo</div> |
- <div name="bar">Bar</div> |
- <div name="zot">Zot</div> |
- </iron-selector> |
- |
- You can specify a default fallback with `fallbackSelection` in case the `selected` attribute does |
- not match the `attrForSelected` attribute of any elements. |
- |
- Example: |
- |
- <iron-selector attr-for-selected="name" selected="non-existing" |
- fallback-selection="default"> |
- <div name="foo">Foo</div> |
- <div name="bar">Bar</div> |
- <div name="default">Default</div> |
- </iron-selector> |
- |
- Note: When the selector is multi, the selection will set to `fallbackSelection` iff |
- the number of matching elements is zero. |
- |
- `iron-selector` is not styled. Use the `iron-selected` CSS class to style the selected element. |
- |
- Example: |
- |
- <style> |
- .iron-selected { |
- background: #eee; |
- } |
- </style> |
- |
- ... |
- |
- <iron-selector selected="0"> |
- <div>Item 1</div> |
- <div>Item 2</div> |
- <div>Item 3</div> |
- </iron-selector> |
- |
- @demo demo/index.html |
- */ |
Polymer({ |
@@ -15001,7 +10693,6 @@ Polymer({ |
showFooter: Boolean, |
- // If true, the sidebar is contained within an app-drawer. |
drawer: { |
type: Boolean, |
reflectToAttribute: true |
@@ -15013,20 +10704,11 @@ Polymer({ |
this.fire('history-close-drawer'); |
}, |
- /** |
- * Relocates the user to the clear browsing data section of the settings page. |
- * @param {Event} e |
- * @private |
- */ |
onClearBrowsingDataTap_: function(e) { |
md_history.BrowserService.getInstance().openClearBrowsingData(); |
e.preventDefault(); |
}, |
- /** |
- * @param {Object} route |
- * @private |
- */ |
getQueryString_: function(route) { |
return window.location.search; |
} |
@@ -15041,10 +10723,8 @@ Polymer({ |
properties: { |
showSidebarFooter: Boolean, |
- // The id of the currently selected page. |
selectedPage_: {type: String, value: 'history', observer: 'unselectAll'}, |
- // Whether domain-grouped history is enabled. |
grouped_: {type: Boolean, reflectToAttribute: true}, |
/** @type {!QueryState} */ |
@@ -15052,14 +10732,11 @@ Polymer({ |
type: Object, |
value: function() { |
return { |
- // Whether the most recent query was incremental. |
incremental: false, |
- // A query is initiated by page load. |
querying: true, |
queryingDisabled: false, |
_range: HistoryRange.ALL_TIME, |
searchTerm: '', |
- // TODO(calamity): Make history toolbar buttons change the offset |
groupedOffset: 0, |
set range(val) { this._range = Number(val); }, |
@@ -15080,28 +10757,22 @@ Polymer({ |
} |
}, |
- // Route data for the current page. |
routeData_: Object, |
- // The query params for the page. |
queryParams_: Object, |
- // True if the window is narrow enough for the page to have a drawer. |
hasDrawer_: Boolean, |
}, |
observers: [ |
- // routeData_.page <=> selectedPage |
'routeDataChanged_(routeData_.page)', |
'selectedPageChanged_(selectedPage_)', |
- // queryParams_.q <=> queryState.searchTerm |
'searchTermChanged_(queryState_.searchTerm)', |
'searchQueryParamChanged_(queryParams_.q)', |
], |
- // TODO(calamity): Replace these event listeners with data bound properties. |
listeners: { |
'cr-menu-tap': 'onMenuTap_', |
'history-checkbox-select': 'checkboxSelected', |
@@ -15119,7 +10790,6 @@ Polymer({ |
document.addEventListener('canExecute', this.onCanExecute_.bind(this)); |
document.addEventListener('command', this.onCommand_.bind(this)); |
- // Redirect legacy search URLs to URLs compatible with material history. |
if (window.location.hash) { |
window.location.href = window.location.href.split('#')[0] + '?' + |
window.location.hash.substr(1); |
@@ -15133,22 +10803,12 @@ Polymer({ |
drawer.toggle(); |
}, |
- /** |
- * Listens for history-item being selected or deselected (through checkbox) |
- * and changes the view of the top toolbar. |
- * @param {{detail: {countAddition: number}}} e |
- */ |
checkboxSelected: function(e) { |
var toolbar = /** @type {HistoryToolbarElement} */ (this.$.toolbar); |
toolbar.count = /** @type {HistoryListContainerElement} */ (this.$.history) |
.getSelectedItemCount(); |
}, |
- /** |
- * Listens for call to cancel selection and loops through all items to set |
- * checkbox to be unselected. |
- * @private |
- */ |
unselectAll: function() { |
var listContainer = |
/** @type {HistoryListContainerElement} */ (this.$.history); |
@@ -15161,11 +10821,6 @@ Polymer({ |
this.$.history.deleteSelectedWithPrompt(); |
}, |
- /** |
- * @param {HistoryQuery} info An object containing information about the |
- * query. |
- * @param {!Array<HistoryEntry>} results A list of results. |
- */ |
historyResult: function(info, results) { |
this.set('queryState_.querying', false); |
this.set('queryResult_.info', info); |
@@ -15175,16 +10830,8 @@ Polymer({ |
listContainer.historyResult(info, results); |
}, |
- /** |
- * Fired when the user presses 'More from this site'. |
- * @param {{detail: {domain: string}}} e |
- */ |
searchDomain_: function(e) { this.$.toolbar.setSearchTerm(e.detail.domain); }, |
- /** |
- * @param {Event} e |
- * @private |
- */ |
onCanExecute_: function(e) { |
e = /** @type {cr.ui.CanExecuteEvent} */(e); |
switch (e.command.id) { |
@@ -15200,27 +10847,15 @@ Polymer({ |
} |
}, |
- /** |
- * @param {string} searchTerm |
- * @private |
- */ |
searchTermChanged_: function(searchTerm) { |
this.set('queryParams_.q', searchTerm || null); |
this.$['history'].queryHistory(false); |
}, |
- /** |
- * @param {string} searchQuery |
- * @private |
- */ |
searchQueryParamChanged_: function(searchQuery) { |
this.$.toolbar.setSearchTerm(searchQuery || ''); |
}, |
- /** |
- * @param {Event} e |
- * @private |
- */ |
onCommand_: function(e) { |
if (e.command.id == 'find-command' || e.command.id == 'slash-command') |
this.$.toolbar.showSearchField(); |
@@ -15228,11 +10863,6 @@ Polymer({ |
this.deleteSelected(); |
}, |
- /** |
- * @param {!Array<!ForeignSession>} sessionList Array of objects describing |
- * the sessions from other devices. |
- * @param {boolean} isTabSyncEnabled Is tab sync enabled for this profile? |
- */ |
setForeignSessions: function(sessionList, isTabSyncEnabled) { |
if (!isTabSyncEnabled) { |
var syncedDeviceManagerElem = |
@@ -15246,10 +10876,6 @@ Polymer({ |
this.set('queryResult_.sessionList', sessionList); |
}, |
- /** |
- * Update sign in state of synced device manager after user logs in or out. |
- * @param {boolean} isUserSignedIn |
- */ |
updateSignInState: function(isUserSignedIn) { |
var syncedDeviceManagerElem = |
/** @type {HistorySyncedDeviceManagerElement} */this |
@@ -15258,53 +10884,22 @@ Polymer({ |
syncedDeviceManagerElem.updateSignInState(isUserSignedIn); |
}, |
- /** |
- * @param {string} selectedPage |
- * @return {boolean} |
- * @private |
- */ |
syncedTabsSelected_: function(selectedPage) { |
return selectedPage == 'syncedTabs'; |
}, |
- /** |
- * @param {boolean} querying |
- * @param {boolean} incremental |
- * @param {string} searchTerm |
- * @return {boolean} Whether a loading spinner should be shown (implies the |
- * backend is querying a new search term). |
- * @private |
- */ |
shouldShowSpinner_: function(querying, incremental, searchTerm) { |
return querying && !incremental && searchTerm != ''; |
}, |
- /** |
- * @param {string} page |
- * @private |
- */ |
routeDataChanged_: function(page) { |
this.selectedPage_ = page; |
}, |
- /** |
- * @param {string} selectedPage |
- * @private |
- */ |
selectedPageChanged_: function(selectedPage) { |
this.set('routeData_.page', selectedPage); |
}, |
- /** |
- * This computed binding is needed to make the iron-pages selector update when |
- * the synced-device-manager is instantiated for the first time. Otherwise the |
- * fallback selection will continue to be used after the corresponding item is |
- * added as a child of iron-pages. |
- * @param {string} selectedPage |
- * @param {Array} items |
- * @return {string} |
- * @private |
- */ |
getSelectedPage_: function(selectedPage, items) { |
return selectedPage; |
}, |