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

Unified Diff: chrome/browser/resources/md_history/lazy_load.crisper.js

Issue 2264983002: MD History: Lazily load element files which are not needed for first paint (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove flattenhtml, rebase Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/md_history/lazy_load.crisper.js
diff --git a/chrome/browser/resources/md_history/lazy_load.crisper.js b/chrome/browser/resources/md_history/lazy_load.crisper.js
new file mode 100644
index 0000000000000000000000000000000000000000..29502b017090db42b4004999cd9ec846036c4f53
--- /dev/null
+++ b/chrome/browser/resources/md_history/lazy_load.crisper.js
@@ -0,0 +1,3291 @@
+Polymer({
+ is: 'iron-collapse',
+ behaviors: [ Polymer.IronResizableBehavior ],
+ properties: {
+ horizontal: {
+ type: Boolean,
+ value: false,
+ observer: '_horizontalChanged'
+ },
+ opened: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ observer: '_openedChanged'
+ },
+ noAnimation: {
+ type: Boolean
+ },
+ _desiredSize: {
+ type: String,
+ value: ''
+ }
+ },
+ get dimension() {
+ return this.horizontal ? 'width' : 'height';
+ },
+ get _dimensionMax() {
+ return this.horizontal ? 'maxWidth' : 'maxHeight';
+ },
+ get _dimensionMaxCss() {
+ return this.horizontal ? 'max-width' : 'max-height';
+ },
+ hostAttributes: {
+ role: 'group',
+ 'aria-hidden': 'true',
+ 'aria-expanded': 'false'
+ },
+ listeners: {
+ transitionend: '_transitionEnd'
+ },
+ attached: function() {
+ this._transitionEnd();
+ },
+ toggle: function() {
+ this.opened = !this.opened;
+ },
+ show: function() {
+ this.opened = true;
+ },
+ hide: function() {
+ this.opened = false;
+ },
+ updateSize: function(size, animated) {
+ size = size === 'auto' ? '' : size;
+ if (this._desiredSize === size) {
+ return;
+ }
+ this._desiredSize = size;
+ this._updateTransition(false);
+ var willAnimate = animated && !this.noAnimation && this._isDisplayed;
+ if (willAnimate) {
+ var startSize = this._calcSize();
+ if (size === '') {
+ this.style[this._dimensionMax] = '';
+ size = this._calcSize();
+ }
+ this.style[this._dimensionMax] = startSize;
+ this.scrollTop = this.scrollTop;
+ this._updateTransition(true);
+ willAnimate = size !== startSize;
+ }
+ this.style[this._dimensionMax] = size;
+ if (!willAnimate) {
+ this._transitionEnd();
+ }
+ },
+ enableTransition: function(enabled) {
+ Polymer.Base._warn('`enableTransition()` is deprecated, use `noAnimation` instead.');
+ this.noAnimation = !enabled;
+ },
+ _updateTransition: function(enabled) {
+ this.style.transitionDuration = enabled && !this.noAnimation ? '' : '0s';
+ },
+ _horizontalChanged: function() {
+ this.style.transitionProperty = this._dimensionMaxCss;
+ var otherDimension = this._dimensionMax === 'maxWidth' ? 'maxHeight' : 'maxWidth';
+ this.style[otherDimension] = '';
+ this.updateSize(this.opened ? 'auto' : '0px', false);
+ },
+ _openedChanged: function() {
+ this.setAttribute('aria-expanded', this.opened);
+ this.setAttribute('aria-hidden', !this.opened);
+ this.toggleClass('iron-collapse-closed', false);
+ this.toggleClass('iron-collapse-opened', false);
+ this.updateSize(this.opened ? 'auto' : '0px', true);
+ if (this.opened) {
+ this.focus();
+ }
+ },
+ _transitionEnd: function() {
+ this.style[this._dimensionMax] = this._desiredSize;
+ this.toggleClass('iron-collapse-closed', !this.opened);
+ this.toggleClass('iron-collapse-opened', this.opened);
+ this._updateTransition(false);
+ this.notifyResize();
+ },
+ get _isDisplayed() {
+ var rect = this.getBoundingClientRect();
+ for (var prop in rect) {
+ if (rect[prop] !== 0) return true;
+ }
+ return false;
+ },
+ _calcSize: function() {
+ return this.getBoundingClientRect()[this.dimension] + 'px';
+ }
+});
+
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+var HistoryDomain;
+
+var HistoryGroup;
+
+Polymer({
+ is: 'history-grouped-list',
+ behaviors: [ HistoryListBehavior ],
+ properties: {
+ historyData: {
+ type: Array
+ },
+ groupedHistoryData_: {
+ type: Array
+ },
+ searchedTerm: {
+ type: String,
+ value: ''
+ },
+ range: {
+ type: Number
+ },
+ queryStartTime: String,
+ queryEndTime: String
+ },
+ observers: [ 'updateGroupedHistoryData_(range, historyData)' ],
+ createHistoryDomains_: function(visits) {
+ var domainIndexes = {};
+ var domains = [];
+ for (var i = 0, visit; visit = visits[i]; i++) {
+ var domain = visit.domain;
+ if (domainIndexes[domain] == undefined) {
+ domainIndexes[domain] = domains.length;
+ domains.push({
+ domain: domain,
+ visits: [],
+ expanded: false,
+ rendered: false
+ });
+ }
+ domains[domainIndexes[domain]].visits.push(visit);
+ }
+ var sortByVisits = function(a, b) {
+ return b.visits.length - a.visits.length;
+ };
+ domains.sort(sortByVisits);
+ return domains;
+ },
+ updateGroupedHistoryData_: function() {
+ if (this.historyData.length == 0) {
+ this.groupedHistoryData_ = [];
+ return;
+ }
+ if (this.range == HistoryRange.WEEK) {
+ var days = [];
+ var currentDayVisits = [ this.historyData[0] ];
+ var pushCurrentDay = function() {
+ days.push({
+ title: this.searchedTerm ? currentDayVisits[0].dateShort : currentDayVisits[0].dateRelativeDay,
+ domains: this.createHistoryDomains_(currentDayVisits)
+ });
+ }.bind(this);
+ var visitsSameDay = function(a, b) {
+ if (this.searchedTerm) return a.dateShort == b.dateShort;
+ return a.dateRelativeDay == b.dateRelativeDay;
+ }.bind(this);
+ for (var i = 1; i < this.historyData.length; i++) {
+ var visit = this.historyData[i];
+ if (!visitsSameDay(visit, currentDayVisits[0])) {
+ pushCurrentDay();
+ currentDayVisits = [];
+ }
+ currentDayVisits.push(visit);
+ }
+ pushCurrentDay();
+ this.groupedHistoryData_ = days;
+ } else if (this.range == HistoryRange.MONTH) {
+ this.groupedHistoryData_ = [ {
+ title: this.queryStartTime + ' – ' + this.queryEndTime,
+ domains: this.createHistoryDomains_(this.historyData)
+ } ];
+ }
+ },
+ toggleDomainExpanded_: function(e) {
+ var collapse = e.currentTarget.parentNode.querySelector('iron-collapse');
+ e.model.set('domain.rendered', true);
+ setTimeout(function() {
+ collapse.toggle();
+ }, 0);
+ },
+ needsTimeGap_: function(groupIndex, domainIndex, itemIndex) {
+ var visits = this.groupedHistoryData_[groupIndex].domains[domainIndex].visits;
+ return md_history.HistoryItem.needsTimeGap(visits, itemIndex, this.searchedTerm);
+ },
+ pathForItem_: function(groupIndex, domainIndex, itemIndex) {
+ return [ 'groupedHistoryData_', groupIndex, 'domains', domainIndex, 'visits', itemIndex ].join('.');
+ },
+ getWebsiteIconStyle_: function(domain) {
+ return 'background-image: ' + cr.icon.getFavicon(domain.visits[0].url);
+ },
+ getDropdownIcon_: function(expanded) {
+ return expanded ? 'cr:expand-less' : 'cr:expand-more';
+ }
+});
+
+Polymer.PaperButtonBehaviorImpl = {
+ properties: {
+ elevation: {
+ type: Number,
+ reflectToAttribute: true,
+ readOnly: true
+ }
+ },
+ observers: [ '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)', '_computeKeyboardClass(receivedFocusFromKeyboard)' ],
+ hostAttributes: {
+ role: 'button',
+ tabindex: '0',
+ animated: true
+ },
+ _calculateElevation: function() {
+ var e = 1;
+ if (this.disabled) {
+ e = 0;
+ } else if (this.active || this.pressed) {
+ e = 4;
+ } else if (this.receivedFocusFromKeyboard) {
+ e = 3;
+ }
+ this._setElevation(e);
+ },
+ _computeKeyboardClass: function(receivedFocusFromKeyboard) {
+ this.toggleClass('keyboard-focus', receivedFocusFromKeyboard);
+ },
+ _spaceKeyDownHandler: function(event) {
+ Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event);
+ if (this.hasRipple() && this.getRipple().ripples.length < 1) {
+ this._ripple.uiDownAction();
+ }
+ },
+ _spaceKeyUpHandler: function(event) {
+ Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event);
+ if (this.hasRipple()) {
+ this._ripple.uiUpAction();
+ }
+ }
+};
+
+Polymer.PaperButtonBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperRippleBehavior, Polymer.PaperButtonBehaviorImpl ];
+
+Polymer({
+ is: 'paper-button',
+ behaviors: [ Polymer.PaperButtonBehavior ],
+ properties: {
+ raised: {
+ type: Boolean,
+ reflectToAttribute: true,
+ value: false,
+ observer: '_calculateElevation'
+ }
+ },
+ _calculateElevation: function() {
+ if (!this.raised) {
+ this._setElevation(0);
+ } else {
+ Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this);
+ }
+ }
+});
+
+Polymer.PaperItemBehaviorImpl = {
+ hostAttributes: {
+ role: 'option',
+ tabindex: '0'
+ }
+};
+
+Polymer.PaperItemBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperItemBehaviorImpl ];
+
+Polymer({
+ is: 'paper-item',
+ behaviors: [ Polymer.PaperItemBehavior ]
+});
+
+Polymer.IronFitBehavior = {
+ properties: {
+ sizingTarget: {
+ type: Object,
+ value: function() {
+ return this;
+ }
+ },
+ fitInto: {
+ type: Object,
+ value: window
+ },
+ noOverlap: {
+ type: Boolean
+ },
+ positionTarget: {
+ type: Element
+ },
+ horizontalAlign: {
+ type: String
+ },
+ verticalAlign: {
+ type: String
+ },
+ dynamicAlign: {
+ type: Boolean
+ },
+ horizontalOffset: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+ verticalOffset: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+ autoFitOnAttach: {
+ type: Boolean,
+ value: false
+ },
+ _fitInfo: {
+ type: Object
+ }
+ },
+ get _fitWidth() {
+ var fitWidth;
+ if (this.fitInto === window) {
+ fitWidth = this.fitInto.innerWidth;
+ } else {
+ fitWidth = this.fitInto.getBoundingClientRect().width;
+ }
+ return fitWidth;
+ },
+ get _fitHeight() {
+ var fitHeight;
+ if (this.fitInto === window) {
+ fitHeight = this.fitInto.innerHeight;
+ } else {
+ fitHeight = this.fitInto.getBoundingClientRect().height;
+ }
+ return fitHeight;
+ },
+ get _fitLeft() {
+ var fitLeft;
+ if (this.fitInto === window) {
+ fitLeft = 0;
+ } else {
+ fitLeft = this.fitInto.getBoundingClientRect().left;
+ }
+ return fitLeft;
+ },
+ get _fitTop() {
+ var fitTop;
+ if (this.fitInto === window) {
+ fitTop = 0;
+ } else {
+ fitTop = this.fitInto.getBoundingClientRect().top;
+ }
+ return fitTop;
+ },
+ get _defaultPositionTarget() {
+ var parent = Polymer.dom(this).parentNode;
+ if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+ parent = parent.host;
+ }
+ return parent;
+ },
+ get _localeHorizontalAlign() {
+ if (this._isRTL) {
+ if (this.horizontalAlign === 'right') {
+ return 'left';
+ }
+ if (this.horizontalAlign === 'left') {
+ return 'right';
+ }
+ }
+ return this.horizontalAlign;
+ },
+ attached: function() {
+ this._isRTL = window.getComputedStyle(this).direction == 'rtl';
+ this.positionTarget = this.positionTarget || this._defaultPositionTarget;
+ if (this.autoFitOnAttach) {
+ if (window.getComputedStyle(this).display === 'none') {
+ setTimeout(function() {
+ this.fit();
+ }.bind(this));
+ } else {
+ this.fit();
+ }
+ }
+ },
+ fit: function() {
+ this.position();
+ this.constrain();
+ this.center();
+ },
+ _discoverInfo: function() {
+ if (this._fitInfo) {
+ return;
+ }
+ var target = window.getComputedStyle(this);
+ var sizer = window.getComputedStyle(this.sizingTarget);
+ this._fitInfo = {
+ inlineStyle: {
+ top: this.style.top || '',
+ left: this.style.left || '',
+ position: this.style.position || ''
+ },
+ sizerInlineStyle: {
+ maxWidth: this.sizingTarget.style.maxWidth || '',
+ maxHeight: this.sizingTarget.style.maxHeight || '',
+ boxSizing: this.sizingTarget.style.boxSizing || ''
+ },
+ positionedBy: {
+ vertically: target.top !== 'auto' ? 'top' : target.bottom !== 'auto' ? 'bottom' : null,
+ horizontally: target.left !== 'auto' ? 'left' : target.right !== 'auto' ? 'right' : null
+ },
+ sizedBy: {
+ height: sizer.maxHeight !== 'none',
+ width: sizer.maxWidth !== 'none',
+ minWidth: parseInt(sizer.minWidth, 10) || 0,
+ minHeight: parseInt(sizer.minHeight, 10) || 0
+ },
+ margin: {
+ top: parseInt(target.marginTop, 10) || 0,
+ right: parseInt(target.marginRight, 10) || 0,
+ bottom: parseInt(target.marginBottom, 10) || 0,
+ left: parseInt(target.marginLeft, 10) || 0
+ }
+ };
+ if (this.verticalOffset) {
+ this._fitInfo.margin.top = this._fitInfo.margin.bottom = this.verticalOffset;
+ this._fitInfo.inlineStyle.marginTop = this.style.marginTop || '';
+ this._fitInfo.inlineStyle.marginBottom = this.style.marginBottom || '';
+ this.style.marginTop = this.style.marginBottom = this.verticalOffset + 'px';
+ }
+ if (this.horizontalOffset) {
+ this._fitInfo.margin.left = this._fitInfo.margin.right = this.horizontalOffset;
+ this._fitInfo.inlineStyle.marginLeft = this.style.marginLeft || '';
+ this._fitInfo.inlineStyle.marginRight = this.style.marginRight || '';
+ this.style.marginLeft = this.style.marginRight = this.horizontalOffset + 'px';
+ }
+ },
+ resetFit: function() {
+ var info = this._fitInfo || {};
+ for (var property in info.sizerInlineStyle) {
+ this.sizingTarget.style[property] = info.sizerInlineStyle[property];
+ }
+ for (var property in info.inlineStyle) {
+ this.style[property] = info.inlineStyle[property];
+ }
+ this._fitInfo = null;
+ },
+ refit: function() {
+ var scrollLeft = this.sizingTarget.scrollLeft;
+ var scrollTop = this.sizingTarget.scrollTop;
+ this.resetFit();
+ this.fit();
+ this.sizingTarget.scrollLeft = scrollLeft;
+ this.sizingTarget.scrollTop = scrollTop;
+ },
+ position: function() {
+ if (!this.horizontalAlign && !this.verticalAlign) {
+ return;
+ }
+ this._discoverInfo();
+ this.style.position = 'fixed';
+ this.sizingTarget.style.boxSizing = 'border-box';
+ this.style.left = '0px';
+ this.style.top = '0px';
+ var rect = this.getBoundingClientRect();
+ var positionRect = this.__getNormalizedRect(this.positionTarget);
+ var fitRect = this.__getNormalizedRect(this.fitInto);
+ var margin = this._fitInfo.margin;
+ var size = {
+ width: rect.width + margin.left + margin.right,
+ height: rect.height + margin.top + margin.bottom
+ };
+ var position = this.__getPosition(this._localeHorizontalAlign, this.verticalAlign, size, positionRect, fitRect);
+ var left = position.left + margin.left;
+ var top = position.top + margin.top;
+ var right = Math.min(fitRect.right - margin.right, left + rect.width);
+ var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height);
+ var minWidth = this._fitInfo.sizedBy.minWidth;
+ var minHeight = this._fitInfo.sizedBy.minHeight;
+ if (left < margin.left) {
+ left = margin.left;
+ if (right - left < minWidth) {
+ left = right - minWidth;
+ }
+ }
+ if (top < margin.top) {
+ top = margin.top;
+ if (bottom - top < minHeight) {
+ top = bottom - minHeight;
+ }
+ }
+ this.sizingTarget.style.maxWidth = right - left + 'px';
+ this.sizingTarget.style.maxHeight = bottom - top + 'px';
+ this.style.left = left - rect.left + 'px';
+ this.style.top = top - rect.top + 'px';
+ },
+ constrain: function() {
+ if (this.horizontalAlign || this.verticalAlign) {
+ return;
+ }
+ this._discoverInfo();
+ var info = this._fitInfo;
+ if (!info.positionedBy.vertically) {
+ this.style.position = 'fixed';
+ this.style.top = '0px';
+ }
+ if (!info.positionedBy.horizontally) {
+ this.style.position = 'fixed';
+ this.style.left = '0px';
+ }
+ this.sizingTarget.style.boxSizing = 'border-box';
+ var rect = this.getBoundingClientRect();
+ if (!info.sizedBy.height) {
+ this.__sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height');
+ }
+ if (!info.sizedBy.width) {
+ this.__sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right', 'Width');
+ }
+ },
+ _sizeDimension: function(rect, positionedBy, start, end, extent) {
+ this.__sizeDimension(rect, positionedBy, start, end, extent);
+ },
+ __sizeDimension: function(rect, positionedBy, start, end, extent) {
+ var info = this._fitInfo;
+ var fitRect = this.__getNormalizedRect(this.fitInto);
+ var max = extent === 'Width' ? fitRect.width : fitRect.height;
+ var flip = positionedBy === end;
+ var offset = flip ? max - rect[end] : rect[start];
+ var margin = info.margin[flip ? start : end];
+ var offsetExtent = 'offset' + extent;
+ var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent];
+ this.sizingTarget.style['max' + extent] = max - margin - offset - sizingOffset + 'px';
+ },
+ center: function() {
+ if (this.horizontalAlign || this.verticalAlign) {
+ return;
+ }
+ this._discoverInfo();
+ var positionedBy = this._fitInfo.positionedBy;
+ if (positionedBy.vertically && positionedBy.horizontally) {
+ return;
+ }
+ this.style.position = 'fixed';
+ if (!positionedBy.vertically) {
+ this.style.top = '0px';
+ }
+ if (!positionedBy.horizontally) {
+ this.style.left = '0px';
+ }
+ var rect = this.getBoundingClientRect();
+ var fitRect = this.__getNormalizedRect(this.fitInto);
+ if (!positionedBy.vertically) {
+ var top = fitRect.top - rect.top + (fitRect.height - rect.height) / 2;
+ this.style.top = top + 'px';
+ }
+ if (!positionedBy.horizontally) {
+ var left = fitRect.left - rect.left + (fitRect.width - rect.width) / 2;
+ this.style.left = left + 'px';
+ }
+ },
+ __getNormalizedRect: function(target) {
+ if (target === document.documentElement || target === window) {
+ return {
+ top: 0,
+ left: 0,
+ width: window.innerWidth,
+ height: window.innerHeight,
+ right: window.innerWidth,
+ bottom: window.innerHeight
+ };
+ }
+ return target.getBoundingClientRect();
+ },
+ __getCroppedArea: function(position, size, fitRect) {
+ var verticalCrop = Math.min(0, position.top) + Math.min(0, fitRect.bottom - (position.top + size.height));
+ var horizontalCrop = Math.min(0, position.left) + Math.min(0, fitRect.right - (position.left + size.width));
+ return Math.abs(verticalCrop) * size.width + Math.abs(horizontalCrop) * size.height;
+ },
+ __getPosition: function(hAlign, vAlign, size, positionRect, fitRect) {
+ var positions = [ {
+ verticalAlign: 'top',
+ horizontalAlign: 'left',
+ top: positionRect.top,
+ left: positionRect.left
+ }, {
+ verticalAlign: 'top',
+ horizontalAlign: 'right',
+ top: positionRect.top,
+ left: positionRect.right - size.width
+ }, {
+ verticalAlign: 'bottom',
+ horizontalAlign: 'left',
+ top: positionRect.bottom - size.height,
+ left: positionRect.left
+ }, {
+ verticalAlign: 'bottom',
+ horizontalAlign: 'right',
+ top: positionRect.bottom - size.height,
+ left: positionRect.right - size.width
+ } ];
+ if (this.noOverlap) {
+ for (var i = 0, l = positions.length; i < l; i++) {
+ var copy = {};
+ for (var key in positions[i]) {
+ copy[key] = positions[i][key];
+ }
+ positions.push(copy);
+ }
+ positions[0].top = positions[1].top += positionRect.height;
+ positions[2].top = positions[3].top -= positionRect.height;
+ positions[4].left = positions[6].left += positionRect.width;
+ positions[5].left = positions[7].left -= positionRect.width;
+ }
+ vAlign = vAlign === 'auto' ? null : vAlign;
+ hAlign = hAlign === 'auto' ? null : hAlign;
+ var position;
+ for (var i = 0; i < positions.length; i++) {
+ var pos = positions[i];
+ if (!this.dynamicAlign && !this.noOverlap && pos.verticalAlign === vAlign && pos.horizontalAlign === hAlign) {
+ position = pos;
+ break;
+ }
+ var alignOk = (!vAlign || pos.verticalAlign === vAlign) && (!hAlign || pos.horizontalAlign === hAlign);
+ if (!this.dynamicAlign && !alignOk) {
+ continue;
+ }
+ position = position || pos;
+ pos.croppedArea = this.__getCroppedArea(pos, size, fitRect);
+ var diff = pos.croppedArea - position.croppedArea;
+ if (diff < 0 || diff === 0 && alignOk) {
+ position = pos;
+ }
+ if (position.croppedArea === 0 && alignOk) {
+ break;
+ }
+ }
+ return position;
+ }
+};
+
+(function() {
+ 'use strict';
+ Polymer({
+ is: 'iron-overlay-backdrop',
+ properties: {
+ opened: {
+ reflectToAttribute: true,
+ type: Boolean,
+ value: false,
+ observer: '_openedChanged'
+ }
+ },
+ listeners: {
+ transitionend: '_onTransitionend'
+ },
+ created: function() {
+ this.__openedRaf = null;
+ },
+ attached: function() {
+ this.opened && this._openedChanged(this.opened);
+ },
+ prepare: function() {
+ if (this.opened && !this.parentNode) {
+ Polymer.dom(document.body).appendChild(this);
+ }
+ },
+ open: function() {
+ this.opened = true;
+ },
+ close: function() {
+ this.opened = false;
+ },
+ complete: function() {
+ if (!this.opened && this.parentNode === document.body) {
+ Polymer.dom(this.parentNode).removeChild(this);
+ }
+ },
+ _onTransitionend: function(event) {
+ if (event && event.target === this) {
+ this.complete();
+ }
+ },
+ _openedChanged: function(opened) {
+ if (opened) {
+ this.prepare();
+ } else {
+ var cs = window.getComputedStyle(this);
+ if (cs.transitionDuration === '0s' || cs.opacity == 0) {
+ this.complete();
+ }
+ }
+ if (!this.isAttached) {
+ return;
+ }
+ if (this.__openedRaf) {
+ window.cancelAnimationFrame(this.__openedRaf);
+ this.__openedRaf = null;
+ }
+ this.scrollTop = this.scrollTop;
+ this.__openedRaf = window.requestAnimationFrame(function() {
+ this.__openedRaf = null;
+ this.toggleClass('opened', this.opened);
+ }.bind(this));
+ }
+ });
+})();
+
+Polymer.IronOverlayManagerClass = function() {
+ this._overlays = [];
+ this._minimumZ = 101;
+ this._backdropElement = null;
+ Polymer.Gestures.add(document, 'tap', this._onCaptureClick.bind(this));
+ document.addEventListener('focus', this._onCaptureFocus.bind(this), true);
+ document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true);
+};
+
+Polymer.IronOverlayManagerClass.prototype = {
+ constructor: Polymer.IronOverlayManagerClass,
+ get backdropElement() {
+ if (!this._backdropElement) {
+ this._backdropElement = document.createElement('iron-overlay-backdrop');
+ }
+ return this._backdropElement;
+ },
+ get deepActiveElement() {
+ var active = document.activeElement || document.body;
+ while (active.root && Polymer.dom(active.root).activeElement) {
+ active = Polymer.dom(active.root).activeElement;
+ }
+ return active;
+ },
+ _bringOverlayAtIndexToFront: function(i) {
+ var overlay = this._overlays[i];
+ if (!overlay) {
+ return;
+ }
+ var lastI = this._overlays.length - 1;
+ var currentOverlay = this._overlays[lastI];
+ if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) {
+ lastI--;
+ }
+ if (i >= lastI) {
+ return;
+ }
+ var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ);
+ if (this._getZ(overlay) <= minimumZ) {
+ this._applyOverlayZ(overlay, minimumZ);
+ }
+ while (i < lastI) {
+ this._overlays[i] = this._overlays[i + 1];
+ i++;
+ }
+ this._overlays[lastI] = overlay;
+ },
+ addOrRemoveOverlay: function(overlay) {
+ if (overlay.opened) {
+ this.addOverlay(overlay);
+ } else {
+ this.removeOverlay(overlay);
+ }
+ },
+ addOverlay: function(overlay) {
+ var i = this._overlays.indexOf(overlay);
+ if (i >= 0) {
+ this._bringOverlayAtIndexToFront(i);
+ this.trackBackdrop();
+ return;
+ }
+ var insertionIndex = this._overlays.length;
+ var currentOverlay = this._overlays[insertionIndex - 1];
+ var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ);
+ var newZ = this._getZ(overlay);
+ if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) {
+ this._applyOverlayZ(currentOverlay, minimumZ);
+ insertionIndex--;
+ var previousOverlay = this._overlays[insertionIndex - 1];
+ minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ);
+ }
+ if (newZ <= minimumZ) {
+ this._applyOverlayZ(overlay, minimumZ);
+ }
+ this._overlays.splice(insertionIndex, 0, overlay);
+ this.trackBackdrop();
+ },
+ removeOverlay: function(overlay) {
+ var i = this._overlays.indexOf(overlay);
+ if (i === -1) {
+ return;
+ }
+ this._overlays.splice(i, 1);
+ this.trackBackdrop();
+ },
+ currentOverlay: function() {
+ var i = this._overlays.length - 1;
+ return this._overlays[i];
+ },
+ currentOverlayZ: function() {
+ return this._getZ(this.currentOverlay());
+ },
+ ensureMinimumZ: function(minimumZ) {
+ this._minimumZ = Math.max(this._minimumZ, minimumZ);
+ },
+ focusOverlay: function() {
+ var current = this.currentOverlay();
+ if (current) {
+ current._applyFocus();
+ }
+ },
+ trackBackdrop: function() {
+ var overlay = this._overlayWithBackdrop();
+ if (!overlay && !this._backdropElement) {
+ return;
+ }
+ this.backdropElement.style.zIndex = this._getZ(overlay) - 1;
+ this.backdropElement.opened = !!overlay;
+ },
+ getBackdrops: function() {
+ var backdrops = [];
+ for (var i = 0; i < this._overlays.length; i++) {
+ if (this._overlays[i].withBackdrop) {
+ backdrops.push(this._overlays[i]);
+ }
+ }
+ return backdrops;
+ },
+ backdropZ: function() {
+ return this._getZ(this._overlayWithBackdrop()) - 1;
+ },
+ _overlayWithBackdrop: function() {
+ for (var i = 0; i < this._overlays.length; i++) {
+ if (this._overlays[i].withBackdrop) {
+ return this._overlays[i];
+ }
+ }
+ },
+ _getZ: function(overlay) {
+ var z = this._minimumZ;
+ if (overlay) {
+ var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay).zIndex);
+ if (z1 === z1) {
+ z = z1;
+ }
+ }
+ return z;
+ },
+ _setZ: function(element, z) {
+ element.style.zIndex = z;
+ },
+ _applyOverlayZ: function(overlay, aboveZ) {
+ this._setZ(overlay, aboveZ + 2);
+ },
+ _overlayInPath: function(path) {
+ path = path || [];
+ for (var i = 0; i < path.length; i++) {
+ if (path[i]._manager === this) {
+ return path[i];
+ }
+ }
+ },
+ _onCaptureClick: function(event) {
+ var overlay = this.currentOverlay();
+ if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) {
+ overlay._onCaptureClick(event);
+ }
+ },
+ _onCaptureFocus: function(event) {
+ var overlay = this.currentOverlay();
+ if (overlay) {
+ overlay._onCaptureFocus(event);
+ }
+ },
+ _onCaptureKeyDown: function(event) {
+ var overlay = this.currentOverlay();
+ if (overlay) {
+ if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc')) {
+ overlay._onCaptureEsc(event);
+ } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'tab')) {
+ overlay._onCaptureTab(event);
+ }
+ }
+ },
+ _shouldBeBehindOverlay: function(overlay1, overlay2) {
+ return !overlay1.alwaysOnTop && overlay2.alwaysOnTop;
+ }
+};
+
+Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass();
+
+(function() {
+ 'use strict';
+ Polymer.IronOverlayBehaviorImpl = {
+ properties: {
+ opened: {
+ observer: '_openedChanged',
+ type: Boolean,
+ value: false,
+ notify: true
+ },
+ canceled: {
+ observer: '_canceledChanged',
+ readOnly: true,
+ type: Boolean,
+ value: false
+ },
+ withBackdrop: {
+ observer: '_withBackdropChanged',
+ type: Boolean
+ },
+ noAutoFocus: {
+ type: Boolean,
+ value: false
+ },
+ noCancelOnEscKey: {
+ type: Boolean,
+ value: false
+ },
+ noCancelOnOutsideClick: {
+ type: Boolean,
+ value: false
+ },
+ closingReason: {
+ type: Object
+ },
+ restoreFocusOnClose: {
+ type: Boolean,
+ value: false
+ },
+ alwaysOnTop: {
+ type: Boolean
+ },
+ _manager: {
+ type: Object,
+ value: Polymer.IronOverlayManager
+ },
+ _focusedChild: {
+ type: Object
+ }
+ },
+ listeners: {
+ 'iron-resize': '_onIronResize'
+ },
+ get backdropElement() {
+ return this._manager.backdropElement;
+ },
+ get _focusNode() {
+ return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this;
+ },
+ get _focusableNodes() {
+ var FOCUSABLE_WITH_DISABLED = [ 'a[href]', 'area[href]', 'iframe', '[tabindex]', '[contentEditable=true]' ];
+ var FOCUSABLE_WITHOUT_DISABLED = [ 'input', 'select', 'textarea', 'button' ];
+ var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') + ':not([tabindex="-1"]),' + FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),') + ':not([disabled]):not([tabindex="-1"])';
+ var focusables = Polymer.dom(this).querySelectorAll(selector);
+ if (this.tabIndex >= 0) {
+ focusables.splice(0, 0, this);
+ }
+ return focusables.sort(function(a, b) {
+ if (a.tabIndex === b.tabIndex) {
+ return 0;
+ }
+ if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) {
+ return 1;
+ }
+ return -1;
+ });
+ },
+ ready: function() {
+ this.__isAnimating = false;
+ this.__shouldRemoveTabIndex = false;
+ this.__firstFocusableNode = this.__lastFocusableNode = null;
+ this.__raf = null;
+ this.__restoreFocusNode = null;
+ this._ensureSetup();
+ },
+ attached: function() {
+ if (this.opened) {
+ this._openedChanged(this.opened);
+ }
+ this._observer = Polymer.dom(this).observeNodes(this._onNodesChange);
+ },
+ detached: function() {
+ Polymer.dom(this).unobserveNodes(this._observer);
+ this._observer = null;
+ if (this.__raf) {
+ window.cancelAnimationFrame(this.__raf);
+ this.__raf = null;
+ }
+ this._manager.removeOverlay(this);
+ },
+ toggle: function() {
+ this._setCanceled(false);
+ this.opened = !this.opened;
+ },
+ open: function() {
+ this._setCanceled(false);
+ this.opened = true;
+ },
+ close: function() {
+ this._setCanceled(false);
+ this.opened = false;
+ },
+ cancel: function(event) {
+ var cancelEvent = this.fire('iron-overlay-canceled', event, {
+ cancelable: true
+ });
+ if (cancelEvent.defaultPrevented) {
+ return;
+ }
+ this._setCanceled(true);
+ this.opened = false;
+ },
+ _ensureSetup: function() {
+ if (this._overlaySetup) {
+ return;
+ }
+ this._overlaySetup = true;
+ this.style.outline = 'none';
+ this.style.display = 'none';
+ },
+ _openedChanged: function(opened) {
+ if (opened) {
+ this.removeAttribute('aria-hidden');
+ } else {
+ this.setAttribute('aria-hidden', 'true');
+ }
+ if (!this.isAttached) {
+ return;
+ }
+ this.__isAnimating = true;
+ this.__onNextAnimationFrame(this.__openedChanged);
+ },
+ _canceledChanged: function() {
+ this.closingReason = this.closingReason || {};
+ this.closingReason.canceled = this.canceled;
+ },
+ _withBackdropChanged: function() {
+ if (this.withBackdrop && !this.hasAttribute('tabindex')) {
+ this.setAttribute('tabindex', '-1');
+ this.__shouldRemoveTabIndex = true;
+ } else if (this.__shouldRemoveTabIndex) {
+ this.removeAttribute('tabindex');
+ this.__shouldRemoveTabIndex = false;
+ }
+ if (this.opened && this.isAttached) {
+ this._manager.trackBackdrop();
+ }
+ },
+ _prepareRenderOpened: function() {
+ this.__restoreFocusNode = this._manager.deepActiveElement;
+ this._preparePositioning();
+ this.refit();
+ this._finishPositioning();
+ if (this.noAutoFocus && document.activeElement === this._focusNode) {
+ this._focusNode.blur();
+ this.__restoreFocusNode.focus();
+ }
+ },
+ _renderOpened: function() {
+ this._finishRenderOpened();
+ },
+ _renderClosed: function() {
+ this._finishRenderClosed();
+ },
+ _finishRenderOpened: function() {
+ this.notifyResize();
+ this.__isAnimating = false;
+ var focusableNodes = this._focusableNodes;
+ this.__firstFocusableNode = focusableNodes[0];
+ this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1];
+ this.fire('iron-overlay-opened');
+ },
+ _finishRenderClosed: function() {
+ this.style.display = 'none';
+ this.style.zIndex = '';
+ this.notifyResize();
+ this.__isAnimating = false;
+ this.fire('iron-overlay-closed', this.closingReason);
+ },
+ _preparePositioning: function() {
+ this.style.transition = this.style.webkitTransition = 'none';
+ this.style.transform = this.style.webkitTransform = 'none';
+ this.style.display = '';
+ },
+ _finishPositioning: function() {
+ this.style.display = 'none';
+ this.scrollTop = this.scrollTop;
+ this.style.transition = this.style.webkitTransition = '';
+ this.style.transform = this.style.webkitTransform = '';
+ this.style.display = '';
+ this.scrollTop = this.scrollTop;
+ },
+ _applyFocus: function() {
+ if (this.opened) {
+ if (!this.noAutoFocus) {
+ this._focusNode.focus();
+ }
+ } else {
+ this._focusNode.blur();
+ this._focusedChild = null;
+ if (this.restoreFocusOnClose && this.__restoreFocusNode) {
+ this.__restoreFocusNode.focus();
+ }
+ this.__restoreFocusNode = null;
+ var currentOverlay = this._manager.currentOverlay();
+ if (currentOverlay && this !== currentOverlay) {
+ currentOverlay._applyFocus();
+ }
+ }
+ },
+ _onCaptureClick: function(event) {
+ if (!this.noCancelOnOutsideClick) {
+ this.cancel(event);
+ }
+ },
+ _onCaptureFocus: function(event) {
+ if (!this.withBackdrop) {
+ return;
+ }
+ var path = Polymer.dom(event).path;
+ if (path.indexOf(this) === -1) {
+ event.stopPropagation();
+ this._applyFocus();
+ } else {
+ this._focusedChild = path[0];
+ }
+ },
+ _onCaptureEsc: function(event) {
+ if (!this.noCancelOnEscKey) {
+ this.cancel(event);
+ }
+ },
+ _onCaptureTab: function(event) {
+ if (!this.withBackdrop) {
+ return;
+ }
+ var shift = event.shiftKey;
+ var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode;
+ var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode;
+ var shouldWrap = false;
+ if (nodeToCheck === nodeToSet) {
+ shouldWrap = true;
+ } else {
+ var focusedNode = this._manager.deepActiveElement;
+ shouldWrap = focusedNode === nodeToCheck || focusedNode === this;
+ }
+ if (shouldWrap) {
+ event.preventDefault();
+ this._focusedChild = nodeToSet;
+ this._applyFocus();
+ }
+ },
+ _onIronResize: function() {
+ if (this.opened && !this.__isAnimating) {
+ this.__onNextAnimationFrame(this.refit);
+ }
+ },
+ _onNodesChange: function() {
+ if (this.opened && !this.__isAnimating) {
+ this.notifyResize();
+ }
+ },
+ __openedChanged: function() {
+ if (this.opened) {
+ this._prepareRenderOpened();
+ this._manager.addOverlay(this);
+ this._applyFocus();
+ this._renderOpened();
+ } else {
+ this._manager.removeOverlay(this);
+ this._applyFocus();
+ this._renderClosed();
+ }
+ },
+ __onNextAnimationFrame: function(callback) {
+ if (this.__raf) {
+ window.cancelAnimationFrame(this.__raf);
+ }
+ var self = this;
+ this.__raf = window.requestAnimationFrame(function nextAnimationFrame() {
+ self.__raf = null;
+ callback.call(self);
+ });
+ }
+ };
+ Polymer.IronOverlayBehavior = [ Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl ];
+})();
+
+Polymer.NeonAnimatableBehavior = {
+ properties: {
+ animationConfig: {
+ type: Object
+ },
+ entryAnimation: {
+ observer: '_entryAnimationChanged',
+ type: String
+ },
+ exitAnimation: {
+ observer: '_exitAnimationChanged',
+ type: String
+ }
+ },
+ _entryAnimationChanged: function() {
+ this.animationConfig = this.animationConfig || {};
+ this.animationConfig['entry'] = [ {
+ name: this.entryAnimation,
+ node: this
+ } ];
+ },
+ _exitAnimationChanged: function() {
+ this.animationConfig = this.animationConfig || {};
+ this.animationConfig['exit'] = [ {
+ name: this.exitAnimation,
+ node: this
+ } ];
+ },
+ _copyProperties: function(config1, config2) {
+ for (var property in config2) {
+ config1[property] = config2[property];
+ }
+ },
+ _cloneConfig: function(config) {
+ var clone = {
+ isClone: true
+ };
+ this._copyProperties(clone, config);
+ return clone;
+ },
+ _getAnimationConfigRecursive: function(type, map, allConfigs) {
+ if (!this.animationConfig) {
+ return;
+ }
+ if (this.animationConfig.value && typeof this.animationConfig.value === 'function') {
+ this._warn(this._logf('playAnimation', "Please put 'animationConfig' inside of your components 'properties' object instead of outside of it."));
+ return;
+ }
+ var thisConfig;
+ if (type) {
+ thisConfig = this.animationConfig[type];
+ } else {
+ thisConfig = this.animationConfig;
+ }
+ if (!Array.isArray(thisConfig)) {
+ thisConfig = [ thisConfig ];
+ }
+ if (thisConfig) {
+ for (var config, index = 0; config = thisConfig[index]; index++) {
+ if (config.animatable) {
+ config.animatable._getAnimationConfigRecursive(config.type || type, map, allConfigs);
+ } else {
+ if (config.id) {
+ var cachedConfig = map[config.id];
+ if (cachedConfig) {
+ if (!cachedConfig.isClone) {
+ map[config.id] = this._cloneConfig(cachedConfig);
+ cachedConfig = map[config.id];
+ }
+ this._copyProperties(cachedConfig, config);
+ } else {
+ map[config.id] = config;
+ }
+ } else {
+ allConfigs.push(config);
+ }
+ }
+ }
+ }
+ },
+ getAnimationConfig: function(type) {
+ var map = {};
+ var allConfigs = [];
+ this._getAnimationConfigRecursive(type, map, allConfigs);
+ for (var key in map) {
+ allConfigs.push(map[key]);
+ }
+ return allConfigs;
+ }
+};
+
+Polymer.NeonAnimationRunnerBehaviorImpl = {
+ _configureAnimations: function(configs) {
+ var results = [];
+ if (configs.length > 0) {
+ for (var config, index = 0; config = configs[index]; index++) {
+ var neonAnimation = document.createElement(config.name);
+ if (neonAnimation.isNeonAnimation) {
+ var result = null;
+ try {
+ result = neonAnimation.configure(config);
+ if (typeof result.cancel != 'function') {
+ result = document.timeline.play(result);
+ }
+ } catch (e) {
+ result = null;
+ console.warn('Couldnt play', '(', config.name, ').', e);
+ }
+ if (result) {
+ results.push({
+ neonAnimation: neonAnimation,
+ config: config,
+ animation: result
+ });
+ }
+ } else {
+ console.warn(this.is + ':', config.name, 'not found!');
+ }
+ }
+ }
+ return results;
+ },
+ _shouldComplete: function(activeEntries) {
+ var finished = true;
+ for (var i = 0; i < activeEntries.length; i++) {
+ if (activeEntries[i].animation.playState != 'finished') {
+ finished = false;
+ break;
+ }
+ }
+ return finished;
+ },
+ _complete: function(activeEntries) {
+ for (var i = 0; i < activeEntries.length; i++) {
+ activeEntries[i].neonAnimation.complete(activeEntries[i].config);
+ }
+ for (var i = 0; i < activeEntries.length; i++) {
+ activeEntries[i].animation.cancel();
+ }
+ },
+ playAnimation: function(type, cookie) {
+ var configs = this.getAnimationConfig(type);
+ if (!configs) {
+ return;
+ }
+ this._active = this._active || {};
+ if (this._active[type]) {
+ this._complete(this._active[type]);
+ delete this._active[type];
+ }
+ var activeEntries = this._configureAnimations(configs);
+ if (activeEntries.length == 0) {
+ this.fire('neon-animation-finish', cookie, {
+ bubbles: false
+ });
+ return;
+ }
+ this._active[type] = activeEntries;
+ for (var i = 0; i < activeEntries.length; i++) {
+ activeEntries[i].animation.onfinish = function() {
+ if (this._shouldComplete(activeEntries)) {
+ this._complete(activeEntries);
+ delete this._active[type];
+ this.fire('neon-animation-finish', cookie, {
+ bubbles: false
+ });
+ }
+ }.bind(this);
+ }
+ },
+ cancelAnimation: function() {
+ for (var k in this._animations) {
+ this._animations[k].cancel();
+ }
+ this._animations = {};
+ }
+};
+
+Polymer.NeonAnimationRunnerBehavior = [ Polymer.NeonAnimatableBehavior, Polymer.NeonAnimationRunnerBehaviorImpl ];
+
+Polymer.NeonAnimationBehavior = {
+ properties: {
+ animationTiming: {
+ type: Object,
+ value: function() {
+ return {
+ duration: 500,
+ easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
+ fill: 'both'
+ };
+ }
+ }
+ },
+ isNeonAnimation: true,
+ timingFromConfig: function(config) {
+ if (config.timing) {
+ for (var property in config.timing) {
+ this.animationTiming[property] = config.timing[property];
+ }
+ }
+ return this.animationTiming;
+ },
+ setPrefixedProperty: function(node, property, value) {
+ var map = {
+ transform: [ 'webkitTransform' ],
+ transformOrigin: [ 'mozTransformOrigin', 'webkitTransformOrigin' ]
+ };
+ var prefixes = map[property];
+ for (var prefix, index = 0; prefix = prefixes[index]; index++) {
+ node.style[prefix] = value;
+ }
+ node.style[property] = value;
+ },
+ complete: function() {}
+};
+
+Polymer({
+ is: 'opaque-animation',
+ behaviors: [ Polymer.NeonAnimationBehavior ],
+ configure: function(config) {
+ var node = config.node;
+ this._effect = new KeyframeEffect(node, [ {
+ opacity: '1'
+ }, {
+ opacity: '1'
+ } ], this.timingFromConfig(config));
+ node.style.opacity = '0';
+ return this._effect;
+ },
+ complete: function(config) {
+ config.node.style.opacity = '';
+ }
+});
+
+(function() {
+ 'use strict';
+ var LAST_TOUCH_POSITION = {
+ pageX: 0,
+ pageY: 0
+ };
+ var ROOT_TARGET = null;
+ var SCROLLABLE_NODES = [];
+ Polymer.IronDropdownScrollManager = {
+ get currentLockingElement() {
+ return this._lockingElements[this._lockingElements.length - 1];
+ },
+ elementIsScrollLocked: function(element) {
+ var currentLockingElement = this.currentLockingElement;
+ if (currentLockingElement === undefined) return false;
+ var scrollLocked;
+ if (this._hasCachedLockedElement(element)) {
+ return true;
+ }
+ if (this._hasCachedUnlockedElement(element)) {
+ return false;
+ }
+ scrollLocked = !!currentLockingElement && currentLockingElement !== element && !this._composedTreeContains(currentLockingElement, element);
+ if (scrollLocked) {
+ this._lockedElementCache.push(element);
+ } else {
+ this._unlockedElementCache.push(element);
+ }
+ return scrollLocked;
+ },
+ pushScrollLock: function(element) {
+ if (this._lockingElements.indexOf(element) >= 0) {
+ return;
+ }
+ if (this._lockingElements.length === 0) {
+ this._lockScrollInteractions();
+ }
+ this._lockingElements.push(element);
+ this._lockedElementCache = [];
+ this._unlockedElementCache = [];
+ },
+ removeScrollLock: function(element) {
+ var index = this._lockingElements.indexOf(element);
+ if (index === -1) {
+ return;
+ }
+ this._lockingElements.splice(index, 1);
+ this._lockedElementCache = [];
+ this._unlockedElementCache = [];
+ if (this._lockingElements.length === 0) {
+ this._unlockScrollInteractions();
+ }
+ },
+ _lockingElements: [],
+ _lockedElementCache: null,
+ _unlockedElementCache: null,
+ _hasCachedLockedElement: function(element) {
+ return this._lockedElementCache.indexOf(element) > -1;
+ },
+ _hasCachedUnlockedElement: function(element) {
+ return this._unlockedElementCache.indexOf(element) > -1;
+ },
+ _composedTreeContains: function(element, child) {
+ var contentElements;
+ var distributedNodes;
+ var contentIndex;
+ var nodeIndex;
+ if (element.contains(child)) {
+ return true;
+ }
+ contentElements = Polymer.dom(element).querySelectorAll('content');
+ for (contentIndex = 0; contentIndex < contentElements.length; ++contentIndex) {
+ distributedNodes = Polymer.dom(contentElements[contentIndex]).getDistributedNodes();
+ for (nodeIndex = 0; nodeIndex < distributedNodes.length; ++nodeIndex) {
+ if (this._composedTreeContains(distributedNodes[nodeIndex], child)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ _scrollInteractionHandler: function(event) {
+ if (event.cancelable && this._shouldPreventScrolling(event)) {
+ event.preventDefault();
+ }
+ if (event.targetTouches) {
+ var touch = event.targetTouches[0];
+ LAST_TOUCH_POSITION.pageX = touch.pageX;
+ LAST_TOUCH_POSITION.pageY = touch.pageY;
+ }
+ },
+ _lockScrollInteractions: function() {
+ this._boundScrollHandler = this._boundScrollHandler || this._scrollInteractionHandler.bind(this);
+ document.addEventListener('wheel', this._boundScrollHandler, true);
+ document.addEventListener('mousewheel', this._boundScrollHandler, true);
+ document.addEventListener('DOMMouseScroll', this._boundScrollHandler, true);
+ document.addEventListener('touchstart', this._boundScrollHandler, true);
+ document.addEventListener('touchmove', this._boundScrollHandler, true);
+ },
+ _unlockScrollInteractions: function() {
+ document.removeEventListener('wheel', this._boundScrollHandler, true);
+ document.removeEventListener('mousewheel', this._boundScrollHandler, true);
+ document.removeEventListener('DOMMouseScroll', this._boundScrollHandler, true);
+ document.removeEventListener('touchstart', this._boundScrollHandler, true);
+ document.removeEventListener('touchmove', this._boundScrollHandler, true);
+ },
+ _shouldPreventScrolling: function(event) {
+ var target = Polymer.dom(event).rootTarget;
+ if (event.type !== 'touchmove' && ROOT_TARGET !== target) {
+ ROOT_TARGET = target;
+ SCROLLABLE_NODES = this._getScrollableNodes(Polymer.dom(event).path);
+ }
+ if (!SCROLLABLE_NODES.length) {
+ return true;
+ }
+ if (event.type === 'touchstart') {
+ return false;
+ }
+ var info = this._getScrollInfo(event);
+ return !this._getScrollingNode(SCROLLABLE_NODES, info.deltaX, info.deltaY);
+ },
+ _getScrollableNodes: function(nodes) {
+ var scrollables = [];
+ var lockingIndex = nodes.indexOf(this.currentLockingElement);
+ for (var i = 0; i <= lockingIndex; i++) {
+ var node = nodes[i];
+ if (node.nodeType === 11) {
+ continue;
+ }
+ var style = node.style;
+ if (style.overflow !== 'scroll' && style.overflow !== 'auto') {
+ style = window.getComputedStyle(node);
+ }
+ if (style.overflow === 'scroll' || style.overflow === 'auto') {
+ scrollables.push(node);
+ }
+ }
+ return scrollables;
+ },
+ _getScrollingNode: function(nodes, deltaX, deltaY) {
+ if (!deltaX && !deltaY) {
+ return;
+ }
+ 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) {
+ canScroll = deltaY < 0 ? node.scrollTop > 0 : node.scrollTop < node.scrollHeight - node.clientHeight;
+ } else {
+ canScroll = deltaX < 0 ? node.scrollLeft > 0 : node.scrollLeft < node.scrollWidth - node.clientWidth;
+ }
+ if (canScroll) {
+ return node;
+ }
+ }
+ },
+ _getScrollInfo: function(event) {
+ var info = {
+ deltaX: event.deltaX,
+ deltaY: event.deltaY
+ };
+ if ('deltaX' in event) {} else if ('wheelDeltaX' in event) {
+ info.deltaX = -event.wheelDeltaX;
+ info.deltaY = -event.wheelDeltaY;
+ } else if ('axis' in event) {
+ info.deltaX = event.axis === 1 ? event.detail : 0;
+ info.deltaY = event.axis === 2 ? event.detail : 0;
+ } else if (event.targetTouches) {
+ var touch = event.targetTouches[0];
+ info.deltaX = LAST_TOUCH_POSITION.pageX - touch.pageX;
+ info.deltaY = LAST_TOUCH_POSITION.pageY - touch.pageY;
+ }
+ return info;
+ }
+ };
+})();
+
+(function() {
+ 'use strict';
+ Polymer({
+ is: 'iron-dropdown',
+ behaviors: [ Polymer.IronControlState, Polymer.IronA11yKeysBehavior, Polymer.IronOverlayBehavior, Polymer.NeonAnimationRunnerBehavior ],
+ properties: {
+ horizontalAlign: {
+ type: String,
+ value: 'left',
+ reflectToAttribute: true
+ },
+ verticalAlign: {
+ type: String,
+ value: 'top',
+ reflectToAttribute: true
+ },
+ openAnimationConfig: {
+ type: Object
+ },
+ closeAnimationConfig: {
+ type: Object
+ },
+ focusTarget: {
+ type: Object
+ },
+ noAnimations: {
+ type: Boolean,
+ value: false
+ },
+ allowOutsideScroll: {
+ type: Boolean,
+ value: false
+ },
+ _boundOnCaptureScroll: {
+ type: Function,
+ value: function() {
+ return this._onCaptureScroll.bind(this);
+ }
+ }
+ },
+ listeners: {
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+ observers: [ '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)' ],
+ get containedElement() {
+ return Polymer.dom(this.$.content).getDistributedNodes()[0];
+ },
+ get _focusTarget() {
+ return this.focusTarget || this.containedElement;
+ },
+ ready: function() {
+ this._scrollTop = 0;
+ this._scrollLeft = 0;
+ this._refitOnScrollRAF = null;
+ },
+ attached: function() {
+ if (!this.sizingTarget || this.sizingTarget === this) {
+ this.sizingTarget = this.containedElement;
+ }
+ },
+ detached: function() {
+ this.cancelAnimation();
+ document.removeEventListener('scroll', this._boundOnCaptureScroll);
+ Polymer.IronDropdownScrollManager.removeScrollLock(this);
+ },
+ _openedChanged: function() {
+ if (this.opened && this.disabled) {
+ this.cancel();
+ } else {
+ this.cancelAnimation();
+ this._updateAnimationConfig();
+ this._saveScrollPosition();
+ if (this.opened) {
+ document.addEventListener('scroll', this._boundOnCaptureScroll);
+ !this.allowOutsideScroll && Polymer.IronDropdownScrollManager.pushScrollLock(this);
+ } else {
+ document.removeEventListener('scroll', this._boundOnCaptureScroll);
+ Polymer.IronDropdownScrollManager.removeScrollLock(this);
+ }
+ Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments);
+ }
+ },
+ _renderOpened: function() {
+ if (!this.noAnimations && this.animationConfig.open) {
+ this.$.contentWrapper.classList.add('animating');
+ this.playAnimation('open');
+ } else {
+ Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments);
+ }
+ },
+ _renderClosed: function() {
+ if (!this.noAnimations && this.animationConfig.close) {
+ this.$.contentWrapper.classList.add('animating');
+ this.playAnimation('close');
+ } else {
+ Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments);
+ }
+ },
+ _onNeonAnimationFinish: function() {
+ this.$.contentWrapper.classList.remove('animating');
+ if (this.opened) {
+ this._finishRenderOpened();
+ } else {
+ this._finishRenderClosed();
+ }
+ },
+ _onCaptureScroll: function() {
+ if (!this.allowOutsideScroll) {
+ this._restoreScrollPosition();
+ } else {
+ this._refitOnScrollRAF && window.cancelAnimationFrame(this._refitOnScrollRAF);
+ this._refitOnScrollRAF = window.requestAnimationFrame(this.refit.bind(this));
+ }
+ },
+ _saveScrollPosition: function() {
+ if (document.scrollingElement) {
+ this._scrollTop = document.scrollingElement.scrollTop;
+ this._scrollLeft = document.scrollingElement.scrollLeft;
+ } else {
+ this._scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
+ this._scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
+ }
+ },
+ _restoreScrollPosition: function() {
+ if (document.scrollingElement) {
+ document.scrollingElement.scrollTop = this._scrollTop;
+ document.scrollingElement.scrollLeft = this._scrollLeft;
+ } else {
+ document.documentElement.scrollTop = this._scrollTop;
+ document.documentElement.scrollLeft = this._scrollLeft;
+ document.body.scrollTop = this._scrollTop;
+ document.body.scrollLeft = this._scrollLeft;
+ }
+ },
+ _updateAnimationConfig: function() {
+ var animations = (this.openAnimationConfig || []).concat(this.closeAnimationConfig || []);
+ for (var i = 0; i < animations.length; i++) {
+ animations[i].node = this.containedElement;
+ }
+ this.animationConfig = {
+ open: this.openAnimationConfig,
+ close: this.closeAnimationConfig
+ };
+ },
+ _updateOverlayPosition: function() {
+ if (this.isAttached) {
+ this.notifyResize();
+ }
+ },
+ _applyFocus: function() {
+ var focusTarget = this.focusTarget || this.containedElement;
+ if (focusTarget && this.opened && !this.noAutoFocus) {
+ focusTarget.focus();
+ } else {
+ Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments);
+ }
+ }
+ });
+})();
+
+Polymer({
+ is: 'fade-in-animation',
+ behaviors: [ Polymer.NeonAnimationBehavior ],
+ configure: function(config) {
+ var node = config.node;
+ this._effect = new KeyframeEffect(node, [ {
+ opacity: '0'
+ }, {
+ opacity: '1'
+ } ], this.timingFromConfig(config));
+ return this._effect;
+ }
+});
+
+Polymer({
+ is: 'fade-out-animation',
+ behaviors: [ Polymer.NeonAnimationBehavior ],
+ configure: function(config) {
+ var node = config.node;
+ this._effect = new KeyframeEffect(node, [ {
+ opacity: '1'
+ }, {
+ opacity: '0'
+ } ], this.timingFromConfig(config));
+ return this._effect;
+ }
+});
+
+Polymer({
+ is: 'paper-menu-grow-height-animation',
+ behaviors: [ Polymer.NeonAnimationBehavior ],
+ configure: function(config) {
+ var node = config.node;
+ var rect = node.getBoundingClientRect();
+ var height = rect.height;
+ this._effect = new KeyframeEffect(node, [ {
+ height: height / 2 + 'px'
+ }, {
+ height: height + 'px'
+ } ], this.timingFromConfig(config));
+ return this._effect;
+ }
+});
+
+Polymer({
+ is: 'paper-menu-grow-width-animation',
+ behaviors: [ Polymer.NeonAnimationBehavior ],
+ configure: function(config) {
+ var node = config.node;
+ var rect = node.getBoundingClientRect();
+ var width = rect.width;
+ this._effect = new KeyframeEffect(node, [ {
+ width: width / 2 + 'px'
+ }, {
+ width: width + 'px'
+ } ], this.timingFromConfig(config));
+ return this._effect;
+ }
+});
+
+Polymer({
+ is: 'paper-menu-shrink-width-animation',
+ behaviors: [ Polymer.NeonAnimationBehavior ],
+ configure: function(config) {
+ var node = config.node;
+ var rect = node.getBoundingClientRect();
+ var width = rect.width;
+ this._effect = new KeyframeEffect(node, [ {
+ width: width + 'px'
+ }, {
+ width: width - width / 20 + 'px'
+ } ], this.timingFromConfig(config));
+ return this._effect;
+ }
+});
+
+Polymer({
+ is: 'paper-menu-shrink-height-animation',
+ behaviors: [ Polymer.NeonAnimationBehavior ],
+ configure: function(config) {
+ var node = config.node;
+ var rect = node.getBoundingClientRect();
+ var height = rect.height;
+ var top = rect.top;
+ this.setPrefixedProperty(node, 'transformOrigin', '0 0');
+ this._effect = new KeyframeEffect(node, [ {
+ height: height + 'px',
+ transform: 'translateY(0)'
+ }, {
+ height: height / 2 + 'px',
+ transform: 'translateY(-20px)'
+ } ], this.timingFromConfig(config));
+ return this._effect;
+ }
+});
+
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+var SLIDE_CUBIC_BEZIER = 'cubic-bezier(0.3, 0.95, 0.5, 1)';
+
+Polymer({
+ is: 'cr-shared-menu',
+ behaviors: [ Polymer.IronA11yKeysBehavior ],
+ properties: {
+ menuOpen: {
+ type: Boolean,
+ observer: 'menuOpenChanged_',
+ value: false,
+ notify: true
+ },
+ itemData: {
+ type: Object,
+ value: null
+ },
+ keyEventTarget: {
+ type: Object,
+ value: function() {
+ return this.$.menu;
+ }
+ },
+ openAnimationConfig: {
+ type: Object,
+ value: function() {
+ return [ {
+ name: 'fade-in-animation',
+ timing: {
+ delay: 50,
+ duration: 200
+ }
+ }, {
+ name: 'paper-menu-grow-width-animation',
+ timing: {
+ delay: 50,
+ duration: 150,
+ easing: SLIDE_CUBIC_BEZIER
+ }
+ }, {
+ name: 'paper-menu-grow-height-animation',
+ timing: {
+ delay: 100,
+ duration: 275,
+ easing: SLIDE_CUBIC_BEZIER
+ }
+ } ];
+ }
+ },
+ closeAnimationConfig: {
+ type: Object,
+ value: function() {
+ return [ {
+ name: 'fade-out-animation',
+ timing: {
+ duration: 150
+ }
+ } ];
+ }
+ }
+ },
+ keyBindings: {
+ tab: 'onTabPressed_'
+ },
+ listeners: {
+ 'dropdown.iron-overlay-canceled': 'onOverlayCanceled_'
+ },
+ lastAnchor_: null,
+ firstFocus_: null,
+ lastFocus_: null,
+ attached: function() {
+ window.addEventListener('resize', this.closeMenu.bind(this));
+ },
+ closeMenu: function() {
+ if (this.root.activeElement == null) {
+ this.$.dropdown.restoreFocusOnClose = false;
+ }
+ this.menuOpen = false;
+ },
+ openMenu: function(anchor, opt_itemData) {
+ if (this.lastAnchor_ == anchor && this.menuOpen) return;
+ if (this.menuOpen) this.closeMenu();
+ this.itemData = opt_itemData || null;
+ this.lastAnchor_ = anchor;
+ this.$.dropdown.restoreFocusOnClose = true;
+ var focusableChildren = Polymer.dom(this).querySelectorAll('[tabindex]:not([disabled]):not([hidden]),' + 'button:not([disabled]):not([hidden])');
+ if (focusableChildren.length > 0) {
+ this.$.dropdown.focusTarget = focusableChildren[0];
+ this.firstFocus_ = focusableChildren[0];
+ this.lastFocus_ = focusableChildren[focusableChildren.length - 1];
+ }
+ this.$.dropdown.positionTarget = anchor;
+ this.menuOpen = true;
+ },
+ toggleMenu: function(anchor, opt_itemData) {
+ if (anchor == this.lastAnchor_ && this.menuOpen) this.closeMenu(); else this.openMenu(anchor, opt_itemData);
+ },
+ onTabPressed_: function(e) {
+ if (!this.firstFocus_ || !this.lastFocus_) return;
+ var toFocus;
+ var keyEvent = e.detail.keyboardEvent;
+ if (keyEvent.shiftKey && keyEvent.target == this.firstFocus_) toFocus = this.lastFocus_; else if (!keyEvent.shiftKey && keyEvent.target == this.lastFocus_) toFocus = this.firstFocus_;
+ if (!toFocus) return;
+ e.preventDefault();
+ toFocus.focus();
+ },
+ menuOpenChanged_: function() {
+ if (!this.menuOpen) {
+ this.itemData = null;
+ this.lastAnchor_ = null;
+ }
+ },
+ onOverlayCanceled_: function(e) {
+ if (e.detail.type == 'tap') this.$.dropdown.restoreFocusOnClose = false;
+ }
+});
+
+Polymer({
+ is: 'paper-icon-button-light',
+ "extends": 'button',
+ behaviors: [ Polymer.PaperRippleBehavior ],
+ listeners: {
+ down: '_rippleDown',
+ up: '_rippleUp',
+ focus: '_rippleDown',
+ blur: '_rippleUp'
+ },
+ _rippleDown: function() {
+ this.getRipple().downAction();
+ },
+ _rippleUp: function() {
+ this.getRipple().upAction();
+ },
+ ensureRipple: function(var_args) {
+ var lastRipple = this._ripple;
+ Polymer.PaperRippleBehavior.ensureRipple.apply(this, arguments);
+ if (this._ripple && this._ripple !== lastRipple) {
+ this._ripple.center = true;
+ this._ripple.classList.add('circle');
+ }
+ }
+});
+
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+Polymer({
+ is: 'history-synced-device-card',
+ properties: {
+ device: String,
+ lastUpdateTime: String,
+ tabs: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ observer: 'updateIcons_'
+ },
+ separatorIndexes: Array,
+ opened: Boolean,
+ searchTerm: String,
+ sessionTag: String
+ },
+ openTab_: function(e) {
+ var tab = e.model.tab;
+ var browserService = md_history.BrowserService.getInstance();
+ browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.LINK_CLICKED, SyncedTabsHistogram.LIMIT);
+ browserService.openForeignSessionTab(this.sessionTag, tab.windowId, tab.sessionId, e);
+ e.preventDefault();
+ },
+ toggleTabCard: function() {
+ var histogramValue = this.$.collapse.opened ? SyncedTabsHistogram.COLLAPSE_SESSION : SyncedTabsHistogram.EXPAND_SESSION;
+ md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, histogramValue, SyncedTabsHistogram.LIMIT);
+ this.$.collapse.toggle();
+ this.$['dropdown-indicator'].icon = this.$.collapse.opened ? 'cr:expand-less' : 'cr:expand-more';
+ },
+ updateIcons_: function() {
+ this.async(function() {
+ var icons = Polymer.dom(this.root).querySelectorAll('.website-icon');
+ for (var i = 0; i < this.tabs.length; i++) {
+ icons[i].style.backgroundImage = cr.icon.getFavicon(this.tabs[i].url);
+ }
+ });
+ },
+ isWindowSeparatorIndex_: function(index, separatorIndexes) {
+ return this.separatorIndexes.indexOf(index) != -1;
+ },
+ getCollapseIcon_: function(opened) {
+ return opened ? 'cr:expand-less' : 'cr:expand-more';
+ },
+ getCollapseTitle_: function(opened) {
+ return opened ? loadTimeData.getString('collapseSessionButton') : loadTimeData.getString('expandSessionButton');
+ },
+ onMenuButtonTap_: function(e) {
+ this.fire('toggle-menu', {
+ target: Polymer.dom(e).localTarget,
+ tag: this.sessionTag
+ });
+ e.stopPropagation();
+ },
+ onLinkRightClick_: function() {
+ md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.LINK_RIGHT_CLICKED, SyncedTabsHistogram.LIMIT);
+ }
+});
+
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+var ForeignDeviceInternal;
+
+Polymer({
+ is: 'history-synced-device-manager',
+ properties: {
+ sessionList: {
+ type: Array,
+ observer: 'updateSyncedDevices'
+ },
+ searchTerm: {
+ type: String,
+ observer: 'searchTermChanged'
+ },
+ syncedDevices_: {
+ type: Array,
+ value: function() {
+ return [];
+ }
+ },
+ signInState: {
+ type: Boolean,
+ observer: 'signInStateChanged_'
+ },
+ guestSession_: {
+ type: Boolean,
+ value: loadTimeData.getBoolean('isGuestSession')
+ },
+ fetchingSyncedTabs_: {
+ type: Boolean,
+ value: false
+ },
+ hasSeenForeignData_: Boolean
+ },
+ listeners: {
+ 'toggle-menu': 'onToggleMenu_',
+ scroll: 'onListScroll_'
+ },
+ attached: function() {
+ chrome.send('otherDevicesInitialized');
+ md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.INITIALIZED, SyncedTabsHistogram.LIMIT);
+ },
+ getContentScrollTarget: function() {
+ return this;
+ },
+ createInternalDevice_: function(session) {
+ var tabs = [];
+ var separatorIndexes = [];
+ for (var i = 0; i < session.windows.length; i++) {
+ var windowId = session.windows[i].sessionId;
+ var newTabs = session.windows[i].tabs;
+ if (newTabs.length == 0) continue;
+ newTabs.forEach(function(tab) {
+ tab.windowId = windowId;
+ });
+ var windowAdded = false;
+ if (!this.searchTerm) {
+ tabs = tabs.concat(newTabs);
+ windowAdded = true;
+ } else {
+ var searchText = this.searchTerm.toLowerCase();
+ for (var j = 0; j < newTabs.length; j++) {
+ var tab = newTabs[j];
+ if (tab.title.toLowerCase().indexOf(searchText) != -1) {
+ tabs.push(tab);
+ windowAdded = true;
+ }
+ }
+ }
+ if (windowAdded && i != session.windows.length - 1) separatorIndexes.push(tabs.length - 1);
+ }
+ return {
+ device: session.name,
+ lastUpdateTime: '– ' + session.modifiedTime,
+ opened: true,
+ separatorIndexes: separatorIndexes,
+ timestamp: session.timestamp,
+ tabs: tabs,
+ tag: session.tag
+ };
+ },
+ onSignInTap_: function() {
+ chrome.send('startSignInFlow');
+ },
+ onListScroll_: function() {
+ var menu = this.$.menu.getIfExists();
+ if (menu) menu.closeMenu();
+ },
+ onToggleMenu_: function(e) {
+ var menu = this.$.menu.get();
+ menu.toggleMenu(e.detail.target, e.detail.tag);
+ if (menu.menuOpen) {
+ md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.SHOW_SESSION_MENU, SyncedTabsHistogram.LIMIT);
+ }
+ },
+ onOpenAllTap_: function() {
+ var menu = assert(this.$.menu.getIfExists());
+ var browserService = md_history.BrowserService.getInstance();
+ browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.OPEN_ALL, SyncedTabsHistogram.LIMIT);
+ browserService.openForeignSessionAllTabs(menu.itemData);
+ menu.closeMenu();
+ },
+ onDeleteSessionTap_: function() {
+ var menu = assert(this.$.menu.getIfExists());
+ var browserService = md_history.BrowserService.getInstance();
+ browserService.recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.HIDE_FOR_NOW, SyncedTabsHistogram.LIMIT);
+ browserService.deleteForeignSession(menu.itemData);
+ menu.closeMenu();
+ },
+ clearDisplayedSyncedDevices_: function() {
+ this.syncedDevices_ = [];
+ },
+ showNoSyncedMessage: function(signInState, syncedDevicesLength, guestSession) {
+ if (guestSession) return true;
+ return signInState && syncedDevicesLength == 0;
+ },
+ showSignInGuide: function(signInState, guestSession) {
+ var show = !signInState && !guestSession;
+ if (show) {
+ md_history.BrowserService.getInstance().recordAction('Signin_Impression_FromRecentTabs');
+ }
+ return show;
+ },
+ noSyncedTabsMessage: function(fetchingSyncedTabs) {
+ return loadTimeData.getString(fetchingSyncedTabs ? 'loading' : 'noSyncedResults');
+ },
+ updateSyncedDevices: function(sessionList) {
+ this.fetchingSyncedTabs_ = false;
+ if (!sessionList) return;
+ if (sessionList.length > 0 && !this.hasSeenForeignData_) {
+ this.hasSeenForeignData_ = true;
+ md_history.BrowserService.getInstance().recordHistogram(SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.HAS_FOREIGN_DATA, SyncedTabsHistogram.LIMIT);
+ }
+ var updateCount = Math.min(sessionList.length, this.syncedDevices_.length);
+ for (var i = 0; i < updateCount; i++) {
+ var oldDevice = this.syncedDevices_[i];
+ if (oldDevice.tag != sessionList[i].tag || oldDevice.timestamp != sessionList[i].timestamp) {
+ this.splice('syncedDevices_', i, 1, this.createInternalDevice_(sessionList[i]));
+ }
+ }
+ if (sessionList.length >= this.syncedDevices_.length) {
+ for (var i = updateCount; i < sessionList.length; i++) {
+ this.push('syncedDevices_', this.createInternalDevice_(sessionList[i]));
+ }
+ } else {
+ this.splice('syncedDevices_', updateCount, this.syncedDevices_.length - updateCount);
+ }
+ },
+ tabSyncDisabled: function() {
+ this.fetchingSyncedTabs_ = false;
+ this.clearDisplayedSyncedDevices_();
+ },
+ signInStateChanged_: function() {
+ this.fire('history-view-changed');
+ if (!this.signInState) {
+ this.clearDisplayedSyncedDevices_();
+ return;
+ }
+ this.fetchingSyncedTabs_ = true;
+ },
+ searchTermChanged: function(searchTerm) {
+ this.clearDisplayedSyncedDevices_();
+ this.updateSyncedDevices(this.sessionList);
+ }
+});
+
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+Polymer({
+ is: 'cr-dialog',
+ "extends": 'dialog',
+ created: function() {
+ window.addEventListener('popstate', function() {
+ if (this.open) this.cancel();
+ }.bind(this));
+ },
+ cancel: function() {
+ this.fire('cancel');
+ HTMLDialogElement.prototype.close.call(this, '');
+ },
+ close: function(opt_returnValue) {
+ HTMLDialogElement.prototype.close.call(this, 'success');
+ },
+ getCloseButton: function() {
+ return this.$.close;
+ }
+});
+
+Polymer({
+ is: 'app-drawer',
+ properties: {
+ opened: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ reflectToAttribute: true
+ },
+ persistent: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ },
+ align: {
+ type: String,
+ value: 'left'
+ },
+ position: {
+ type: String,
+ readOnly: true,
+ value: 'left',
+ reflectToAttribute: true
+ },
+ swipeOpen: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ },
+ noFocusTrap: {
+ type: Boolean,
+ value: false
+ }
+ },
+ observers: [ 'resetLayout(position)', '_resetPosition(align, isAttached)' ],
+ _translateOffset: 0,
+ _trackDetails: null,
+ _drawerState: 0,
+ _boundEscKeydownHandler: null,
+ _firstTabStop: null,
+ _lastTabStop: null,
+ ready: function() {
+ this.setScrollDirection('y');
+ this._setTransitionDuration('0s');
+ },
+ attached: function() {
+ Polymer.RenderStatus.afterNextRender(this, function() {
+ this._setTransitionDuration('');
+ this._boundEscKeydownHandler = this._escKeydownHandler.bind(this);
+ this._resetDrawerState();
+ this.listen(this, 'track', '_track');
+ this.addEventListener('transitionend', this._transitionend.bind(this));
+ this.addEventListener('keydown', this._tabKeydownHandler.bind(this));
+ });
+ },
+ detached: function() {
+ document.removeEventListener('keydown', this._boundEscKeydownHandler);
+ },
+ open: function() {
+ this.opened = true;
+ },
+ close: function() {
+ this.opened = false;
+ },
+ toggle: function() {
+ this.opened = !this.opened;
+ },
+ getWidth: function() {
+ return this.$.contentContainer.offsetWidth;
+ },
+ resetLayout: function() {
+ this.debounce('_resetLayout', function() {
+ this.fire('app-drawer-reset-layout');
+ }, 1);
+ },
+ _isRTL: function() {
+ return window.getComputedStyle(this).direction === 'rtl';
+ },
+ _resetPosition: function() {
+ switch (this.align) {
+ case 'start':
+ this._setPosition(this._isRTL() ? 'right' : 'left');
+ return;
+
+ case 'end':
+ this._setPosition(this._isRTL() ? 'left' : 'right');
+ return;
+ }
+ this._setPosition(this.align);
+ },
+ _escKeydownHandler: function(event) {
+ var ESC_KEYCODE = 27;
+ if (event.keyCode === ESC_KEYCODE) {
+ event.preventDefault();
+ this.close();
+ }
+ },
+ _track: function(event) {
+ if (this.persistent) {
+ return;
+ }
+ event.preventDefault();
+ switch (event.detail.state) {
+ case 'start':
+ this._trackStart(event);
+ break;
+
+ case 'track':
+ this._trackMove(event);
+ break;
+
+ case 'end':
+ this._trackEnd(event);
+ break;
+ }
+ },
+ _trackStart: function(event) {
+ this._drawerState = this._DRAWER_STATE.TRACKING;
+ this._setTransitionDuration('0s');
+ this.style.visibility = 'visible';
+ var rect = this.$.contentContainer.getBoundingClientRect();
+ if (this.position === 'left') {
+ this._translateOffset = rect.left;
+ } else {
+ this._translateOffset = rect.right - window.innerWidth;
+ }
+ this._trackDetails = [];
+ },
+ _trackMove: function(event) {
+ this._translateDrawer(event.detail.dx + this._translateOffset);
+ this._trackDetails.push({
+ dx: event.detail.dx,
+ timeStamp: Date.now()
+ });
+ },
+ _trackEnd: function(event) {
+ var x = event.detail.dx + this._translateOffset;
+ var drawerWidth = this.getWidth();
+ var isPositionLeft = this.position === 'left';
+ var isInEndState = isPositionLeft ? x >= 0 || x <= -drawerWidth : x <= 0 || x >= drawerWidth;
+ if (!isInEndState) {
+ var trackDetails = this._trackDetails;
+ this._trackDetails = null;
+ this._flingDrawer(event, trackDetails);
+ if (this._drawerState === this._DRAWER_STATE.FLINGING) {
+ return;
+ }
+ }
+ var halfWidth = drawerWidth / 2;
+ if (event.detail.dx < -halfWidth) {
+ this.opened = this.position === 'right';
+ } else if (event.detail.dx > halfWidth) {
+ this.opened = this.position === 'left';
+ }
+ if (isInEndState) {
+ this._resetDrawerState();
+ }
+ this._setTransitionDuration('');
+ this._resetDrawerTranslate();
+ this.style.visibility = '';
+ },
+ _calculateVelocity: function(event, trackDetails) {
+ var now = Date.now();
+ var timeLowerBound = now - 100;
+ var trackDetail;
+ var min = 0;
+ var max = trackDetails.length - 1;
+ while (min <= max) {
+ var mid = min + max >> 1;
+ var d = trackDetails[mid];
+ if (d.timeStamp >= timeLowerBound) {
+ trackDetail = d;
+ max = mid - 1;
+ } else {
+ min = mid + 1;
+ }
+ }
+ if (trackDetail) {
+ var dx = event.detail.dx - trackDetail.dx;
+ var dt = now - trackDetail.timeStamp || 1;
+ return dx / dt;
+ }
+ return 0;
+ },
+ _flingDrawer: function(event, trackDetails) {
+ var velocity = this._calculateVelocity(event, trackDetails);
+ if (Math.abs(velocity) < this._MIN_FLING_THRESHOLD) {
+ return;
+ }
+ this._drawerState = this._DRAWER_STATE.FLINGING;
+ var x = event.detail.dx + this._translateOffset;
+ var drawerWidth = this.getWidth();
+ var isPositionLeft = this.position === 'left';
+ var isVelocityPositive = velocity > 0;
+ var isClosingLeft = !isVelocityPositive && isPositionLeft;
+ var isClosingRight = isVelocityPositive && !isPositionLeft;
+ var dx;
+ if (isClosingLeft) {
+ dx = -(x + drawerWidth);
+ } else if (isClosingRight) {
+ dx = drawerWidth - x;
+ } else {
+ dx = -x;
+ }
+ if (isVelocityPositive) {
+ velocity = Math.max(velocity, this._MIN_TRANSITION_VELOCITY);
+ this.opened = this.position === 'left';
+ } else {
+ velocity = Math.min(velocity, -this._MIN_TRANSITION_VELOCITY);
+ this.opened = this.position === 'right';
+ }
+ this._setTransitionDuration(this._FLING_INITIAL_SLOPE * dx / velocity + 'ms');
+ this._setTransitionTimingFunction(this._FLING_TIMING_FUNCTION);
+ this._resetDrawerTranslate();
+ },
+ _transitionend: function(event) {
+ var target = Polymer.dom(event).rootTarget;
+ if (target === this.$.contentContainer || target === this.$.scrim) {
+ if (this._drawerState === this._DRAWER_STATE.FLINGING) {
+ this._setTransitionDuration('');
+ this._setTransitionTimingFunction('');
+ this.style.visibility = '';
+ }
+ this._resetDrawerState();
+ }
+ },
+ _setTransitionDuration: function(duration) {
+ this.$.contentContainer.style.transitionDuration = duration;
+ this.$.scrim.style.transitionDuration = duration;
+ },
+ _setTransitionTimingFunction: function(timingFunction) {
+ this.$.contentContainer.style.transitionTimingFunction = timingFunction;
+ this.$.scrim.style.transitionTimingFunction = timingFunction;
+ },
+ _translateDrawer: function(x) {
+ var drawerWidth = this.getWidth();
+ if (this.position === 'left') {
+ x = Math.max(-drawerWidth, Math.min(x, 0));
+ this.$.scrim.style.opacity = 1 + x / drawerWidth;
+ } else {
+ x = Math.max(0, Math.min(x, drawerWidth));
+ this.$.scrim.style.opacity = 1 - x / drawerWidth;
+ }
+ this.translate3d(x + 'px', '0', '0', this.$.contentContainer);
+ },
+ _resetDrawerTranslate: function() {
+ this.$.scrim.style.opacity = '';
+ this.transform('', this.$.contentContainer);
+ },
+ _resetDrawerState: function() {
+ var oldState = this._drawerState;
+ if (this.opened) {
+ this._drawerState = this.persistent ? this._DRAWER_STATE.OPENED_PERSISTENT : this._DRAWER_STATE.OPENED;
+ } else {
+ this._drawerState = this._DRAWER_STATE.CLOSED;
+ }
+ if (oldState !== this._drawerState) {
+ if (this._drawerState === this._DRAWER_STATE.OPENED) {
+ this._setKeyboardFocusTrap();
+ document.addEventListener('keydown', this._boundEscKeydownHandler);
+ document.body.style.overflow = 'hidden';
+ } else {
+ document.removeEventListener('keydown', this._boundEscKeydownHandler);
+ document.body.style.overflow = '';
+ }
+ if (oldState !== this._DRAWER_STATE.INIT) {
+ this.fire('app-drawer-transitioned');
+ }
+ }
+ },
+ _setKeyboardFocusTrap: function() {
+ if (this.noFocusTrap) {
+ return;
+ }
+ var focusableElementsSelector = [ 'a[href]:not([tabindex="-1"])', 'area[href]:not([tabindex="-1"])', 'input:not([disabled]):not([tabindex="-1"])', 'select:not([disabled]):not([tabindex="-1"])', 'textarea:not([disabled]):not([tabindex="-1"])', 'button:not([disabled]):not([tabindex="-1"])', 'iframe:not([tabindex="-1"])', '[tabindex]:not([tabindex="-1"])', '[contentEditable=true]:not([tabindex="-1"])' ].join(',');
+ var focusableElements = Polymer.dom(this).querySelectorAll(focusableElementsSelector);
+ if (focusableElements.length > 0) {
+ this._firstTabStop = focusableElements[0];
+ this._lastTabStop = focusableElements[focusableElements.length - 1];
+ } else {
+ this._firstTabStop = null;
+ this._lastTabStop = null;
+ }
+ var tabindex = this.getAttribute('tabindex');
+ if (tabindex && parseInt(tabindex, 10) > -1) {
+ this.focus();
+ } else if (this._firstTabStop) {
+ this._firstTabStop.focus();
+ }
+ },
+ _tabKeydownHandler: function(event) {
+ if (this.noFocusTrap) {
+ return;
+ }
+ var TAB_KEYCODE = 9;
+ if (this._drawerState === this._DRAWER_STATE.OPENED && event.keyCode === TAB_KEYCODE) {
+ if (event.shiftKey) {
+ if (this._firstTabStop && Polymer.dom(event).localTarget === this._firstTabStop) {
+ event.preventDefault();
+ this._lastTabStop.focus();
+ }
+ } else {
+ if (this._lastTabStop && Polymer.dom(event).localTarget === this._lastTabStop) {
+ event.preventDefault();
+ this._firstTabStop.focus();
+ }
+ }
+ }
+ },
+ _MIN_FLING_THRESHOLD: .2,
+ _MIN_TRANSITION_VELOCITY: 1.2,
+ _FLING_TIMING_FUNCTION: 'cubic-bezier(0.667, 1, 0.667, 1)',
+ _FLING_INITIAL_SLOPE: 1.5,
+ _DRAWER_STATE: {
+ INIT: 0,
+ OPENED: 1,
+ OPENED_PERSISTENT: 2,
+ CLOSED: 3,
+ TRACKING: 4,
+ FLINGING: 5
+ }
+});
+
+Polymer.IronFormElementBehavior = {
+ properties: {
+ name: {
+ type: String
+ },
+ value: {
+ notify: true,
+ type: String
+ },
+ required: {
+ type: Boolean,
+ value: false
+ },
+ _parentForm: {
+ type: Object
+ }
+ },
+ attached: function() {
+ this.fire('iron-form-element-register');
+ },
+ detached: function() {
+ if (this._parentForm) {
+ this._parentForm.fire('iron-form-element-unregister', {
+ target: this
+ });
+ }
+ }
+};
+
+Polymer.IronCheckedElementBehaviorImpl = {
+ properties: {
+ checked: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ notify: true,
+ observer: '_checkedChanged'
+ },
+ toggles: {
+ type: Boolean,
+ value: true,
+ reflectToAttribute: true
+ },
+ value: {
+ type: String,
+ value: 'on',
+ observer: '_valueChanged'
+ }
+ },
+ observers: [ '_requiredChanged(required)' ],
+ created: function() {
+ this._hasIronCheckedElementBehavior = true;
+ },
+ _getValidity: function(_value) {
+ return this.disabled || !this.required || this.checked;
+ },
+ _requiredChanged: function() {
+ if (this.required) {
+ this.setAttribute('aria-required', 'true');
+ } else {
+ this.removeAttribute('aria-required');
+ }
+ },
+ _checkedChanged: function() {
+ this.active = this.checked;
+ this.fire('iron-change');
+ },
+ _valueChanged: function() {
+ if (this.value === undefined || this.value === null) {
+ this.value = 'on';
+ }
+ }
+};
+
+Polymer.IronCheckedElementBehavior = [ Polymer.IronFormElementBehavior, Polymer.IronValidatableBehavior, Polymer.IronCheckedElementBehaviorImpl ];
+
+Polymer.PaperCheckedElementBehaviorImpl = {
+ _checkedChanged: function() {
+ Polymer.IronCheckedElementBehaviorImpl._checkedChanged.call(this);
+ if (this.hasRipple()) {
+ if (this.checked) {
+ this._ripple.setAttribute('checked', '');
+ } else {
+ this._ripple.removeAttribute('checked');
+ }
+ }
+ },
+ _buttonStateChanged: function() {
+ Polymer.PaperRippleBehavior._buttonStateChanged.call(this);
+ if (this.disabled) {
+ return;
+ }
+ if (this.isAttached) {
+ this.checked = this.active;
+ }
+ }
+};
+
+Polymer.PaperCheckedElementBehavior = [ Polymer.PaperInkyFocusBehavior, Polymer.IronCheckedElementBehavior, Polymer.PaperCheckedElementBehaviorImpl ];
+
+Polymer({
+ is: 'paper-checkbox',
+ behaviors: [ Polymer.PaperCheckedElementBehavior ],
+ hostAttributes: {
+ role: 'checkbox',
+ 'aria-checked': false,
+ tabindex: 0
+ },
+ properties: {
+ ariaActiveAttribute: {
+ type: String,
+ value: 'aria-checked'
+ }
+ },
+ attached: function() {
+ var inkSize = this.getComputedStyleValue('--calculated-paper-checkbox-ink-size');
+ if (inkSize === '-1px') {
+ var checkboxSize = parseFloat(this.getComputedStyleValue('--calculated-paper-checkbox-size'));
+ var defaultInkSize = Math.floor(8 / 3 * checkboxSize);
+ if (defaultInkSize % 2 !== checkboxSize % 2) {
+ defaultInkSize++;
+ }
+ this.customStyle['--paper-checkbox-ink-size'] = defaultInkSize + 'px';
+ this.updateStyles();
+ }
+ },
+ _computeCheckboxClass: function(checked, invalid) {
+ var className = '';
+ if (checked) {
+ className += 'checked ';
+ }
+ if (invalid) {
+ className += 'invalid';
+ }
+ return className;
+ },
+ _computeCheckmarkClass: function(checked) {
+ return checked ? '' : 'hidden';
+ },
+ _createRipple: function() {
+ this._rippleContainer = this.$.checkboxContainer;
+ return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this);
+ }
+});
+
+Polymer({
+ is: 'paper-tab',
+ behaviors: [ Polymer.IronControlState, Polymer.IronButtonState, Polymer.PaperRippleBehavior ],
+ properties: {
+ link: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ }
+ },
+ hostAttributes: {
+ role: 'tab'
+ },
+ listeners: {
+ down: '_updateNoink',
+ tap: '_onTap'
+ },
+ attached: function() {
+ this._updateNoink();
+ },
+ get _parentNoink() {
+ var parent = Polymer.dom(this).parentNode;
+ return !!parent && !!parent.noink;
+ },
+ _updateNoink: function() {
+ this.noink = !!this.noink || !!this._parentNoink;
+ },
+ _onTap: function(event) {
+ if (this.link) {
+ var anchor = this.queryEffectiveChildren('a');
+ if (!anchor) {
+ return;
+ }
+ if (event.target === anchor) {
+ return;
+ }
+ anchor.click();
+ }
+ }
+});
+
+Polymer.IronMenuBehaviorImpl = {
+ properties: {
+ focusedItem: {
+ observer: '_focusedItemChanged',
+ readOnly: true,
+ type: Object
+ },
+ attrForItemTitle: {
+ type: String
+ }
+ },
+ hostAttributes: {
+ role: 'menu',
+ tabindex: '0'
+ },
+ observers: [ '_updateMultiselectable(multi)' ],
+ listeners: {
+ focus: '_onFocus',
+ keydown: '_onKeydown',
+ 'iron-items-changed': '_onIronItemsChanged'
+ },
+ keyBindings: {
+ up: '_onUpKey',
+ down: '_onDownKey',
+ esc: '_onEscKey',
+ 'shift+tab:keydown': '_onShiftTabDown'
+ },
+ attached: function() {
+ this._resetTabindices();
+ },
+ select: function(value) {
+ if (this._defaultFocusAsync) {
+ this.cancelAsync(this._defaultFocusAsync);
+ this._defaultFocusAsync = null;
+ }
+ var item = this._valueToItem(value);
+ if (item && item.hasAttribute('disabled')) return;
+ this._setFocusedItem(item);
+ Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments);
+ },
+ _resetTabindices: function() {
+ var selectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem;
+ this.items.forEach(function(item) {
+ item.setAttribute('tabindex', item === selectedItem ? '0' : '-1');
+ }, this);
+ },
+ _updateMultiselectable: function(multi) {
+ if (multi) {
+ this.setAttribute('aria-multiselectable', 'true');
+ } else {
+ this.removeAttribute('aria-multiselectable');
+ }
+ },
+ _focusWithKeyboardEvent: function(event) {
+ for (var i = 0, item; item = this.items[i]; i++) {
+ var attr = this.attrForItemTitle || 'textContent';
+ var title = item[attr] || item.getAttribute(attr);
+ if (!item.hasAttribute('disabled') && title && title.trim().charAt(0).toLowerCase() === String.fromCharCode(event.keyCode).toLowerCase()) {
+ this._setFocusedItem(item);
+ break;
+ }
+ }
+ },
+ _focusPrevious: function() {
+ var length = this.items.length;
+ var curFocusIndex = Number(this.indexOf(this.focusedItem));
+ for (var i = 1; i < length + 1; i++) {
+ var item = this.items[(curFocusIndex - i + length) % length];
+ if (!item.hasAttribute('disabled')) {
+ var owner = Polymer.dom(item).getOwnerRoot() || document;
+ this._setFocusedItem(item);
+ if (Polymer.dom(owner).activeElement == item) {
+ return;
+ }
+ }
+ }
+ },
+ _focusNext: function() {
+ var length = this.items.length;
+ var curFocusIndex = Number(this.indexOf(this.focusedItem));
+ for (var i = 1; i < length + 1; i++) {
+ var item = this.items[(curFocusIndex + i) % length];
+ if (!item.hasAttribute('disabled')) {
+ var owner = Polymer.dom(item).getOwnerRoot() || document;
+ this._setFocusedItem(item);
+ if (Polymer.dom(owner).activeElement == item) {
+ return;
+ }
+ }
+ }
+ },
+ _applySelection: function(item, isSelected) {
+ if (isSelected) {
+ item.setAttribute('aria-selected', 'true');
+ } else {
+ item.removeAttribute('aria-selected');
+ }
+ Polymer.IronSelectableBehavior._applySelection.apply(this, arguments);
+ },
+ _focusedItemChanged: function(focusedItem, old) {
+ old && old.setAttribute('tabindex', '-1');
+ if (focusedItem) {
+ focusedItem.setAttribute('tabindex', '0');
+ focusedItem.focus();
+ }
+ },
+ _onIronItemsChanged: function(event) {
+ if (event.detail.addedNodes.length) {
+ this._resetTabindices();
+ }
+ },
+ _onShiftTabDown: function(event) {
+ var oldTabIndex = this.getAttribute('tabindex');
+ Polymer.IronMenuBehaviorImpl._shiftTabPressed = true;
+ this._setFocusedItem(null);
+ this.setAttribute('tabindex', '-1');
+ this.async(function() {
+ this.setAttribute('tabindex', oldTabIndex);
+ Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
+ }, 1);
+ },
+ _onFocus: function(event) {
+ if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) {
+ return;
+ }
+ var rootTarget = Polymer.dom(event).rootTarget;
+ if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !this.isLightDescendant(rootTarget)) {
+ return;
+ }
+ this._defaultFocusAsync = this.async(function() {
+ var selectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem;
+ this._setFocusedItem(null);
+ if (selectedItem) {
+ this._setFocusedItem(selectedItem);
+ } else if (this.items[0]) {
+ this._focusNext();
+ }
+ });
+ },
+ _onUpKey: function(event) {
+ this._focusPrevious();
+ event.detail.keyboardEvent.preventDefault();
+ },
+ _onDownKey: function(event) {
+ this._focusNext();
+ event.detail.keyboardEvent.preventDefault();
+ },
+ _onEscKey: function(event) {
+ this.focusedItem.blur();
+ },
+ _onKeydown: function(event) {
+ if (!this.keyboardEventMatchesKeys(event, 'up down esc')) {
+ this._focusWithKeyboardEvent(event);
+ }
+ event.stopPropagation();
+ },
+ _activateHandler: function(event) {
+ Polymer.IronSelectableBehavior._activateHandler.call(this, event);
+ event.stopPropagation();
+ }
+};
+
+Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
+
+Polymer.IronMenuBehavior = [ Polymer.IronMultiSelectableBehavior, Polymer.IronA11yKeysBehavior, Polymer.IronMenuBehaviorImpl ];
+
+Polymer.IronMenubarBehaviorImpl = {
+ hostAttributes: {
+ role: 'menubar'
+ },
+ keyBindings: {
+ left: '_onLeftKey',
+ right: '_onRightKey'
+ },
+ _onUpKey: function(event) {
+ this.focusedItem.click();
+ event.detail.keyboardEvent.preventDefault();
+ },
+ _onDownKey: function(event) {
+ this.focusedItem.click();
+ event.detail.keyboardEvent.preventDefault();
+ },
+ get _isRTL() {
+ return window.getComputedStyle(this)['direction'] === 'rtl';
+ },
+ _onLeftKey: function(event) {
+ if (this._isRTL) {
+ this._focusNext();
+ } else {
+ this._focusPrevious();
+ }
+ event.detail.keyboardEvent.preventDefault();
+ },
+ _onRightKey: function(event) {
+ if (this._isRTL) {
+ this._focusPrevious();
+ } else {
+ this._focusNext();
+ }
+ event.detail.keyboardEvent.preventDefault();
+ },
+ _onKeydown: function(event) {
+ if (this.keyboardEventMatchesKeys(event, 'up down left right esc')) {
+ return;
+ }
+ this._focusWithKeyboardEvent(event);
+ }
+};
+
+Polymer.IronMenubarBehavior = [ Polymer.IronMenuBehavior, Polymer.IronMenubarBehaviorImpl ];
+
+Polymer({
+ is: 'paper-tabs',
+ behaviors: [ Polymer.IronResizableBehavior, Polymer.IronMenubarBehavior ],
+ properties: {
+ noink: {
+ type: Boolean,
+ value: false,
+ observer: '_noinkChanged'
+ },
+ noBar: {
+ type: Boolean,
+ value: false
+ },
+ noSlide: {
+ type: Boolean,
+ value: false
+ },
+ scrollable: {
+ type: Boolean,
+ value: false
+ },
+ fitContainer: {
+ type: Boolean,
+ value: false
+ },
+ disableDrag: {
+ type: Boolean,
+ value: false
+ },
+ hideScrollButtons: {
+ type: Boolean,
+ value: false
+ },
+ alignBottom: {
+ type: Boolean,
+ value: false
+ },
+ selectable: {
+ type: String,
+ value: 'paper-tab'
+ },
+ autoselect: {
+ type: Boolean,
+ value: false
+ },
+ autoselectDelay: {
+ type: Number,
+ value: 0
+ },
+ _step: {
+ type: Number,
+ value: 10
+ },
+ _holdDelay: {
+ type: Number,
+ value: 1
+ },
+ _leftHidden: {
+ type: Boolean,
+ value: false
+ },
+ _rightHidden: {
+ type: Boolean,
+ value: false
+ },
+ _previousTab: {
+ type: Object
+ }
+ },
+ hostAttributes: {
+ role: 'tablist'
+ },
+ listeners: {
+ 'iron-resize': '_onTabSizingChanged',
+ 'iron-items-changed': '_onTabSizingChanged',
+ 'iron-select': '_onIronSelect',
+ 'iron-deselect': '_onIronDeselect'
+ },
+ keyBindings: {
+ 'left:keyup right:keyup': '_onArrowKeyup'
+ },
+ created: function() {
+ this._holdJob = null;
+ this._pendingActivationItem = undefined;
+ this._pendingActivationTimeout = undefined;
+ this._bindDelayedActivationHandler = this._delayedActivationHandler.bind(this);
+ this.addEventListener('blur', this._onBlurCapture.bind(this), true);
+ },
+ ready: function() {
+ this.setScrollDirection('y', this.$.tabsContainer);
+ },
+ detached: function() {
+ this._cancelPendingActivation();
+ },
+ _noinkChanged: function(noink) {
+ var childTabs = Polymer.dom(this).querySelectorAll('paper-tab');
+ childTabs.forEach(noink ? this._setNoinkAttribute : this._removeNoinkAttribute);
+ },
+ _setNoinkAttribute: function(element) {
+ element.setAttribute('noink', '');
+ },
+ _removeNoinkAttribute: function(element) {
+ element.removeAttribute('noink');
+ },
+ _computeScrollButtonClass: function(hideThisButton, scrollable, hideScrollButtons) {
+ if (!scrollable || hideScrollButtons) {
+ return 'hidden';
+ }
+ if (hideThisButton) {
+ return 'not-visible';
+ }
+ return '';
+ },
+ _computeTabsContentClass: function(scrollable, fitContainer) {
+ return scrollable ? 'scrollable' + (fitContainer ? ' fit-container' : '') : ' fit-container';
+ },
+ _computeSelectionBarClass: function(noBar, alignBottom) {
+ if (noBar) {
+ return 'hidden';
+ } else if (alignBottom) {
+ return 'align-bottom';
+ }
+ return '';
+ },
+ _onTabSizingChanged: function() {
+ this.debounce('_onTabSizingChanged', function() {
+ this._scroll();
+ this._tabChanged(this.selectedItem);
+ }, 10);
+ },
+ _onIronSelect: function(event) {
+ this._tabChanged(event.detail.item, this._previousTab);
+ this._previousTab = event.detail.item;
+ this.cancelDebouncer('tab-changed');
+ },
+ _onIronDeselect: function(event) {
+ this.debounce('tab-changed', function() {
+ this._tabChanged(null, this._previousTab);
+ this._previousTab = null;
+ }, 1);
+ },
+ _activateHandler: function() {
+ this._cancelPendingActivation();
+ Polymer.IronMenuBehaviorImpl._activateHandler.apply(this, arguments);
+ },
+ _scheduleActivation: function(item, delay) {
+ this._pendingActivationItem = item;
+ this._pendingActivationTimeout = this.async(this._bindDelayedActivationHandler, delay);
+ },
+ _delayedActivationHandler: function() {
+ var item = this._pendingActivationItem;
+ this._pendingActivationItem = undefined;
+ this._pendingActivationTimeout = undefined;
+ item.fire(this.activateEvent, null, {
+ bubbles: true,
+ cancelable: true
+ });
+ },
+ _cancelPendingActivation: function() {
+ if (this._pendingActivationTimeout !== undefined) {
+ this.cancelAsync(this._pendingActivationTimeout);
+ this._pendingActivationItem = undefined;
+ this._pendingActivationTimeout = undefined;
+ }
+ },
+ _onArrowKeyup: function(event) {
+ if (this.autoselect) {
+ this._scheduleActivation(this.focusedItem, this.autoselectDelay);
+ }
+ },
+ _onBlurCapture: function(event) {
+ if (event.target === this._pendingActivationItem) {
+ this._cancelPendingActivation();
+ }
+ },
+ get _tabContainerScrollSize() {
+ return Math.max(0, this.$.tabsContainer.scrollWidth - this.$.tabsContainer.offsetWidth);
+ },
+ _scroll: function(e, detail) {
+ if (!this.scrollable) {
+ return;
+ }
+ var ddx = detail && -detail.ddx || 0;
+ this._affectScroll(ddx);
+ },
+ _down: function(e) {
+ this.async(function() {
+ if (this._defaultFocusAsync) {
+ this.cancelAsync(this._defaultFocusAsync);
+ this._defaultFocusAsync = null;
+ }
+ }, 1);
+ },
+ _affectScroll: function(dx) {
+ this.$.tabsContainer.scrollLeft += dx;
+ var scrollLeft = this.$.tabsContainer.scrollLeft;
+ this._leftHidden = scrollLeft === 0;
+ this._rightHidden = scrollLeft === this._tabContainerScrollSize;
+ },
+ _onLeftScrollButtonDown: function() {
+ this._scrollToLeft();
+ this._holdJob = setInterval(this._scrollToLeft.bind(this), this._holdDelay);
+ },
+ _onRightScrollButtonDown: function() {
+ this._scrollToRight();
+ this._holdJob = setInterval(this._scrollToRight.bind(this), this._holdDelay);
+ },
+ _onScrollButtonUp: function() {
+ clearInterval(this._holdJob);
+ this._holdJob = null;
+ },
+ _scrollToLeft: function() {
+ this._affectScroll(-this._step);
+ },
+ _scrollToRight: function() {
+ this._affectScroll(this._step);
+ },
+ _tabChanged: function(tab, old) {
+ if (!tab) {
+ this.$.selectionBar.classList.remove('expand');
+ this.$.selectionBar.classList.remove('contract');
+ this._positionBar(0, 0);
+ return;
+ }
+ var r = this.$.tabsContent.getBoundingClientRect();
+ var w = r.width;
+ var tabRect = tab.getBoundingClientRect();
+ var tabOffsetLeft = tabRect.left - r.left;
+ this._pos = {
+ width: this._calcPercent(tabRect.width, w),
+ left: this._calcPercent(tabOffsetLeft, w)
+ };
+ if (this.noSlide || old == null) {
+ this.$.selectionBar.classList.remove('expand');
+ this.$.selectionBar.classList.remove('contract');
+ this._positionBar(this._pos.width, this._pos.left);
+ return;
+ }
+ var oldRect = old.getBoundingClientRect();
+ var oldIndex = this.items.indexOf(old);
+ var index = this.items.indexOf(tab);
+ var m = 5;
+ this.$.selectionBar.classList.add('expand');
+ var moveRight = oldIndex < index;
+ var isRTL = this._isRTL;
+ if (isRTL) {
+ moveRight = !moveRight;
+ }
+ if (moveRight) {
+ this._positionBar(this._calcPercent(tabRect.left + tabRect.width - oldRect.left, w) - m, this._left);
+ } else {
+ this._positionBar(this._calcPercent(oldRect.left + oldRect.width - tabRect.left, w) - m, this._calcPercent(tabOffsetLeft, w) + m);
+ }
+ if (this.scrollable) {
+ this._scrollToSelectedIfNeeded(tabRect.width, tabOffsetLeft);
+ }
+ },
+ _scrollToSelectedIfNeeded: function(tabWidth, tabOffsetLeft) {
+ var l = tabOffsetLeft - this.$.tabsContainer.scrollLeft;
+ if (l < 0) {
+ this.$.tabsContainer.scrollLeft += l;
+ } else {
+ l += tabWidth - this.$.tabsContainer.offsetWidth;
+ if (l > 0) {
+ this.$.tabsContainer.scrollLeft += l;
+ }
+ }
+ },
+ _calcPercent: function(w, w0) {
+ return 100 * w / w0;
+ },
+ _positionBar: function(width, left) {
+ width = width || 0;
+ left = left || 0;
+ this._width = width;
+ this._left = left;
+ this.transform('translateX(' + left + '%) scaleX(' + width / 100 + ')', this.$.selectionBar);
+ },
+ _onBarTransitionEnd: function(e) {
+ var cl = this.$.selectionBar.classList;
+ if (cl.contains('expand')) {
+ cl.remove('expand');
+ cl.add('contract');
+ this._positionBar(this._pos.width, this._pos.left);
+ } else if (cl.contains('contract')) {
+ cl.remove('contract');
+ }
+ }
+});
« no previous file with comments | « chrome/browser/resources/md_history/lazy_load.html ('k') | chrome/browser/resources/md_history/lazy_load.vulcanized.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698