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

Side by Side Diff: chrome/browser/resources/md_history/app.crisper.js

Issue 2224003003: Vulcanize MD History to improve page-load performance (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase, revulcanize, avoid duplicating files Created 4 years, 4 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 function assert(condition, opt_message) {
5 if (!condition) {
6 var message = 'Assertion failed';
7 if (opt_message) message = message + ': ' + opt_message;
8 var error = new Error(message);
9 var global = function() {
10 return this;
11 }();
12 if (global.traceAssertionsForTesting) console.warn(error.stack);
13 throw error;
14 }
15 return condition;
16 }
17
18 function assertNotReached(opt_message) {
19 assert(false, opt_message || 'Unreachable code hit');
20 }
21
22 function assertInstanceof(value, type, opt_message) {
23 if (!(value instanceof type)) {
24 assertNotReached(opt_message || 'Value ' + value + ' is not a[n] ' + (type.n ame || typeof type));
25 }
26 return value;
27 }
28
29 // Copyright 2016 The Chromium Authors. All rights reserved.
30 // Use of this source code is governed by a BSD-style license that can be
31 // found in the LICENSE file.
32 function PromiseResolver() {
33 this.resolve_;
34 this.reject_;
35 this.promise_ = new Promise(function(resolve, reject) {
36 this.resolve_ = resolve;
37 this.reject_ = reject;
38 }.bind(this));
39 }
40
41 PromiseResolver.prototype = {
42 get promise() {
43 return this.promise_;
44 },
45 set promise(p) {
46 assertNotReached();
47 },
48 get resolve() {
49 return this.resolve_;
50 },
51 set resolve(r) {
52 assertNotReached();
53 },
54 get reject() {
55 return this.reject_;
56 },
57 set reject(s) {
58 assertNotReached();
59 }
60 };
61
62 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
63 // Use of this source code is governed by a BSD-style license that can be
64 // found in the LICENSE file.
65 var global = this;
66
67 var WebUIListener;
68
69 var cr = cr || function() {
70 'use strict';
71 function exportPath(name, opt_object, opt_objectToExportTo) {
72 var parts = name.split('.');
73 var cur = opt_objectToExportTo || global;
74 for (var part; parts.length && (part = parts.shift()); ) {
75 if (!parts.length && opt_object !== undefined) {
76 cur[part] = opt_object;
77 } else if (part in cur) {
78 cur = cur[part];
79 } else {
80 cur = cur[part] = {};
81 }
82 }
83 return cur;
84 }
85 function dispatchPropertyChange(target, propertyName, newValue, oldValue) {
86 var e = new Event(propertyName + 'Change');
87 e.propertyName = propertyName;
88 e.newValue = newValue;
89 e.oldValue = oldValue;
90 target.dispatchEvent(e);
91 }
92 function getAttributeName(jsName) {
93 return jsName.replace(/([A-Z])/g, '-$1').toLowerCase();
94 }
95 var PropertyKind = {
96 JS: 'js',
97 ATTR: 'attr',
98 BOOL_ATTR: 'boolAttr'
99 };
100 function getGetter(name, kind) {
101 switch (kind) {
102 case PropertyKind.JS:
103 var privateName = name + '_';
104 return function() {
105 return this[privateName];
106 };
107
108 case PropertyKind.ATTR:
109 var attributeName = getAttributeName(name);
110 return function() {
111 return this.getAttribute(attributeName);
112 };
113
114 case PropertyKind.BOOL_ATTR:
115 var attributeName = getAttributeName(name);
116 return function() {
117 return this.hasAttribute(attributeName);
118 };
119 }
120 throw 'not reached';
121 }
122 function getSetter(name, kind, opt_setHook) {
123 switch (kind) {
124 case PropertyKind.JS:
125 var privateName = name + '_';
126 return function(value) {
127 var oldValue = this[name];
128 if (value !== oldValue) {
129 this[privateName] = value;
130 if (opt_setHook) opt_setHook.call(this, value, oldValue);
131 dispatchPropertyChange(this, name, value, oldValue);
132 }
133 };
134
135 case PropertyKind.ATTR:
136 var attributeName = getAttributeName(name);
137 return function(value) {
138 var oldValue = this[name];
139 if (value !== oldValue) {
140 if (value == undefined) this.removeAttribute(attributeName); else this .setAttribute(attributeName, value);
141 if (opt_setHook) opt_setHook.call(this, value, oldValue);
142 dispatchPropertyChange(this, name, value, oldValue);
143 }
144 };
145
146 case PropertyKind.BOOL_ATTR:
147 var attributeName = getAttributeName(name);
148 return function(value) {
149 var oldValue = this[name];
150 if (value !== oldValue) {
151 if (value) this.setAttribute(attributeName, name); else this.removeAtt ribute(attributeName);
152 if (opt_setHook) opt_setHook.call(this, value, oldValue);
153 dispatchPropertyChange(this, name, value, oldValue);
154 }
155 };
156 }
157 throw 'not reached';
158 }
159 function defineProperty(obj, name, opt_kind, opt_setHook) {
160 if (typeof obj == 'function') obj = obj.prototype;
161 var kind = opt_kind || PropertyKind.JS;
162 if (!obj.__lookupGetter__(name)) obj.__defineGetter__(name, getGetter(name, kind));
163 if (!obj.__lookupSetter__(name)) obj.__defineSetter__(name, getSetter(name, kind, opt_setHook));
164 }
165 var uidCounter = 1;
166 function createUid() {
167 return uidCounter++;
168 }
169 function getUid(item) {
170 if (item.hasOwnProperty('uid')) return item.uid;
171 return item.uid = createUid();
172 }
173 function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) {
174 var e = new Event(type, {
175 bubbles: opt_bubbles,
176 cancelable: opt_cancelable === undefined || opt_cancelable
177 });
178 return target.dispatchEvent(e);
179 }
180 function define(name, fun) {
181 var obj = exportPath(name);
182 var exports = fun();
183 for (var propertyName in exports) {
184 var propertyDescriptor = Object.getOwnPropertyDescriptor(exports, property Name);
185 if (propertyDescriptor) Object.defineProperty(obj, propertyName, propertyD escriptor);
186 }
187 }
188 function addSingletonGetter(ctor) {
189 ctor.getInstance = function() {
190 return ctor.instance_ || (ctor.instance_ = new ctor());
191 };
192 }
193 function makePublic(ctor, methods, opt_target) {
194 methods.forEach(function(method) {
195 ctor[method] = function() {
196 var target = opt_target ? document.getElementById(opt_target) : ctor.get Instance();
197 return target[method + '_'].apply(target, arguments);
198 };
199 });
200 }
201 var chromeSendResolverMap = {};
202 function webUIResponse(id, isSuccess, response) {
203 var resolver = chromeSendResolverMap[id];
204 delete chromeSendResolverMap[id];
205 if (isSuccess) resolver.resolve(response); else resolver.reject(response);
206 }
207 function sendWithPromise(methodName, var_args) {
208 var args = Array.prototype.slice.call(arguments, 1);
209 var promiseResolver = new PromiseResolver();
210 var id = methodName + '_' + createUid();
211 chromeSendResolverMap[id] = promiseResolver;
212 chrome.send(methodName, [ id ].concat(args));
213 return promiseResolver.promise;
214 }
215 var webUIListenerMap = {};
216 function webUIListenerCallback(event, var_args) {
217 var eventListenersMap = webUIListenerMap[event];
218 if (!eventListenersMap) {
219 return;
220 }
221 var args = Array.prototype.slice.call(arguments, 1);
222 for (var listenerId in eventListenersMap) {
223 eventListenersMap[listenerId].apply(null, args);
224 }
225 }
226 function addWebUIListener(eventName, callback) {
227 webUIListenerMap[eventName] = webUIListenerMap[eventName] || {};
228 var uid = createUid();
229 webUIListenerMap[eventName][uid] = callback;
230 return {
231 eventName: eventName,
232 uid: uid
233 };
234 }
235 function removeWebUIListener(listener) {
236 var listenerExists = webUIListenerMap[listener.eventName] && webUIListenerMa p[listener.eventName][listener.uid];
237 if (listenerExists) {
238 delete webUIListenerMap[listener.eventName][listener.uid];
239 return true;
240 }
241 return false;
242 }
243 return {
244 addSingletonGetter: addSingletonGetter,
245 createUid: createUid,
246 define: define,
247 defineProperty: defineProperty,
248 dispatchPropertyChange: dispatchPropertyChange,
249 dispatchSimpleEvent: dispatchSimpleEvent,
250 exportPath: exportPath,
251 getUid: getUid,
252 makePublic: makePublic,
253 PropertyKind: PropertyKind,
254 addWebUIListener: addWebUIListener,
255 removeWebUIListener: removeWebUIListener,
256 sendWithPromise: sendWithPromise,
257 webUIListenerCallback: webUIListenerCallback,
258 webUIResponse: webUIResponse,
259 get doc() {
260 return document;
261 },
262 get isMac() {
263 return /Mac/.test(navigator.platform);
264 },
265 get isWindows() {
266 return /Win/.test(navigator.platform);
267 },
268 get isChromeOS() {
269 return /CrOS/.test(navigator.userAgent);
270 },
271 get isLinux() {
272 return /Linux/.test(navigator.userAgent);
273 },
274 get isAndroid() {
275 return /Android/.test(navigator.userAgent);
276 },
277 get isIOS() {
278 return /iPad|iPhone|iPod/.test(navigator.platform);
279 }
280 };
281 }();
282
283 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
284 // Use of this source code is governed by a BSD-style license that can be
285 // found in the LICENSE file.
286 cr.define('cr.ui', function() {
287 function decorate(source, constr) {
288 var elements;
289 if (typeof source == 'string') elements = cr.doc.querySelectorAll(source); e lse elements = [ source ];
290 for (var i = 0, el; el = elements[i]; i++) {
291 if (!(el instanceof constr)) constr.decorate(el);
292 }
293 }
294 function createElementHelper(tagName, opt_bag) {
295 var doc;
296 if (opt_bag && opt_bag.ownerDocument) doc = opt_bag.ownerDocument; else doc = cr.doc;
297 return doc.createElement(tagName);
298 }
299 function define(tagNameOrFunction) {
300 var createFunction, tagName;
301 if (typeof tagNameOrFunction == 'function') {
302 createFunction = tagNameOrFunction;
303 tagName = '';
304 } else {
305 createFunction = createElementHelper;
306 tagName = tagNameOrFunction;
307 }
308 function f(opt_propertyBag) {
309 var el = createFunction(tagName, opt_propertyBag);
310 f.decorate(el);
311 for (var propertyName in opt_propertyBag) {
312 el[propertyName] = opt_propertyBag[propertyName];
313 }
314 return el;
315 }
316 f.decorate = function(el) {
317 el.__proto__ = f.prototype;
318 el.decorate();
319 };
320 return f;
321 }
322 function limitInputWidth(el, parentEl, min, opt_scale) {
323 el.style.width = '10px';
324 var doc = el.ownerDocument;
325 var win = doc.defaultView;
326 var computedStyle = win.getComputedStyle(el);
327 var parentComputedStyle = win.getComputedStyle(parentEl);
328 var rtl = computedStyle.direction == 'rtl';
329 var inputRect = el.getBoundingClientRect();
330 var parentRect = parentEl.getBoundingClientRect();
331 var startPos = rtl ? parentRect.right - inputRect.right : inputRect.left - p arentRect.left;
332 var inner = parseInt(computedStyle.borderLeftWidth, 10) + parseInt(computedS tyle.paddingLeft, 10) + parseInt(computedStyle.paddingRight, 10) + parseInt(comp utedStyle.borderRightWidth, 10);
333 var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) : pa rseInt(parentComputedStyle.paddingRight, 10);
334 var max = parentEl.clientWidth - startPos - inner - parentPadding;
335 if (opt_scale) max *= opt_scale;
336 function limit() {
337 if (el.scrollWidth > max) {
338 el.style.width = max + 'px';
339 } else {
340 el.style.width = 0;
341 var sw = el.scrollWidth;
342 if (sw < min) {
343 el.style.width = min + 'px';
344 } else {
345 el.style.width = sw + 'px';
346 }
347 }
348 }
349 el.addEventListener('input', limit);
350 limit();
351 }
352 function toCssPx(pixels) {
353 if (!window.isFinite(pixels)) console.error('Pixel value is not a number: ' + pixels);
354 return Math.round(pixels) + 'px';
355 }
356 function swallowDoubleClick(e) {
357 var doc = e.target.ownerDocument;
358 var counter = Math.min(1, e.detail);
359 function swallow(e) {
360 e.stopPropagation();
361 e.preventDefault();
362 }
363 function onclick(e) {
364 if (e.detail > counter) {
365 counter = e.detail;
366 swallow(e);
367 } else {
368 doc.removeEventListener('dblclick', swallow, true);
369 doc.removeEventListener('click', onclick, true);
370 }
371 }
372 setTimeout(function() {
373 doc.addEventListener('click', onclick, true);
374 doc.addEventListener('dblclick', swallow, true);
375 }, 0);
376 }
377 return {
378 decorate: decorate,
379 define: define,
380 limitInputWidth: limitInputWidth,
381 toCssPx: toCssPx,
382 swallowDoubleClick: swallowDoubleClick
383 };
384 });
385
386 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
387 // Use of this source code is governed by a BSD-style license that can be
388 // found in the LICENSE file.
389 cr.define('cr.ui', function() {
390 function KeyboardShortcut(shortcut) {
391 var mods = {};
392 var ident = '';
393 shortcut.split('|').forEach(function(part) {
394 var partLc = part.toLowerCase();
395 switch (partLc) {
396 case 'alt':
397 case 'ctrl':
398 case 'meta':
399 case 'shift':
400 mods[partLc + 'Key'] = true;
401 break;
402
403 default:
404 if (ident) throw Error('Invalid shortcut');
405 ident = part;
406 }
407 });
408 this.ident_ = ident;
409 this.mods_ = mods;
410 }
411 KeyboardShortcut.prototype = {
412 matchesEvent: function(e) {
413 if (e.key == this.ident_) {
414 var mods = this.mods_;
415 return [ 'altKey', 'ctrlKey', 'metaKey', 'shiftKey' ].every(function(k) {
416 return e[k] == !!mods[k];
417 });
418 }
419 return false;
420 }
421 };
422 var Command = cr.ui.define('command');
423 Command.prototype = {
424 __proto__: HTMLElement.prototype,
425 decorate: function() {
426 CommandManager.init(assert(this.ownerDocument));
427 if (this.hasAttribute('shortcut')) this.shortcut = this.getAttribute('shor tcut');
428 },
429 execute: function(opt_element) {
430 if (this.disabled) return;
431 var doc = this.ownerDocument;
432 if (doc.activeElement) {
433 var e = new Event('command', {
434 bubbles: true
435 });
436 e.command = this;
437 (opt_element || doc.activeElement).dispatchEvent(e);
438 }
439 },
440 canExecuteChange: function(opt_node) {
441 dispatchCanExecuteEvent(this, opt_node || this.ownerDocument.activeElement );
442 },
443 shortcut_: '',
444 get shortcut() {
445 return this.shortcut_;
446 },
447 set shortcut(shortcut) {
448 var oldShortcut = this.shortcut_;
449 if (shortcut !== oldShortcut) {
450 this.keyboardShortcuts_ = shortcut.split(/\s+/).map(function(shortcut) {
451 return new KeyboardShortcut(shortcut);
452 });
453 this.shortcut_ = shortcut;
454 cr.dispatchPropertyChange(this, 'shortcut', this.shortcut_, oldShortcut) ;
455 }
456 },
457 matchesEvent: function(e) {
458 if (!this.keyboardShortcuts_) return false;
459 return this.keyboardShortcuts_.some(function(keyboardShortcut) {
460 return keyboardShortcut.matchesEvent(e);
461 });
462 }
463 };
464 cr.defineProperty(Command, 'label', cr.PropertyKind.ATTR);
465 cr.defineProperty(Command, 'disabled', cr.PropertyKind.BOOL_ATTR);
466 cr.defineProperty(Command, 'hidden', cr.PropertyKind.BOOL_ATTR);
467 cr.defineProperty(Command, 'checked', cr.PropertyKind.BOOL_ATTR);
468 cr.defineProperty(Command, 'hideShortcutText', cr.PropertyKind.BOOL_ATTR);
469 function dispatchCanExecuteEvent(command, target) {
470 var e = new CanExecuteEvent(command);
471 target.dispatchEvent(e);
472 command.disabled = !e.canExecute;
473 }
474 var commandManagers = {};
475 function CommandManager(doc) {
476 doc.addEventListener('focus', this.handleFocus_.bind(this), true);
477 doc.addEventListener('keydown', this.handleKeyDown_.bind(this), false);
478 }
479 CommandManager.init = function(doc) {
480 var uid = cr.getUid(doc);
481 if (!(uid in commandManagers)) {
482 commandManagers[uid] = new CommandManager(doc);
483 }
484 };
485 CommandManager.prototype = {
486 handleFocus_: function(e) {
487 var target = e.target;
488 if (target.menu || target.command) return;
489 var commands = Array.prototype.slice.call(target.ownerDocument.querySelect orAll('command'));
490 commands.forEach(function(command) {
491 dispatchCanExecuteEvent(command, target);
492 });
493 },
494 handleKeyDown_: function(e) {
495 var target = e.target;
496 var commands = Array.prototype.slice.call(target.ownerDocument.querySelect orAll('command'));
497 for (var i = 0, command; command = commands[i]; i++) {
498 if (command.matchesEvent(e)) {
499 command.canExecuteChange();
500 if (!command.disabled) {
501 e.preventDefault();
502 e.stopPropagation();
503 command.execute();
504 return;
505 }
506 }
507 }
508 }
509 };
510 function CanExecuteEvent(command) {
511 var e = new Event('canExecute', {
512 bubbles: true,
513 cancelable: true
514 });
515 e.__proto__ = CanExecuteEvent.prototype;
516 e.command = command;
517 return e;
518 }
519 CanExecuteEvent.prototype = {
520 __proto__: Event.prototype,
521 command: null,
522 canExecute_: false,
523 get canExecute() {
524 return this.canExecute_;
525 },
526 set canExecute(canExecute) {
527 this.canExecute_ = !!canExecute;
528 this.stopPropagation();
529 this.preventDefault();
530 }
531 };
532 return {
533 Command: Command,
534 CanExecuteEvent: CanExecuteEvent
535 };
536 });
537
538 Polymer({
539 is: 'app-drawer',
540 properties: {
541 opened: {
542 type: Boolean,
543 value: false,
544 notify: true,
545 reflectToAttribute: true
546 },
547 persistent: {
548 type: Boolean,
549 value: false,
550 reflectToAttribute: true
551 },
552 align: {
553 type: String,
554 value: 'left'
555 },
556 position: {
557 type: String,
558 readOnly: true,
559 value: 'left',
560 reflectToAttribute: true
561 },
562 swipeOpen: {
563 type: Boolean,
564 value: false,
565 reflectToAttribute: true
566 },
567 noFocusTrap: {
568 type: Boolean,
569 value: false
570 }
571 },
572 observers: [ 'resetLayout(position)', '_resetPosition(align, isAttached)' ],
573 _translateOffset: 0,
574 _trackDetails: null,
575 _drawerState: 0,
576 _boundEscKeydownHandler: null,
577 _firstTabStop: null,
578 _lastTabStop: null,
579 ready: function() {
580 this.setScrollDirection('y');
581 this._setTransitionDuration('0s');
582 },
583 attached: function() {
584 Polymer.RenderStatus.afterNextRender(this, function() {
585 this._setTransitionDuration('');
586 this._boundEscKeydownHandler = this._escKeydownHandler.bind(this);
587 this._resetDrawerState();
588 this.listen(this, 'track', '_track');
589 this.addEventListener('transitionend', this._transitionend.bind(this));
590 this.addEventListener('keydown', this._tabKeydownHandler.bind(this));
591 });
592 },
593 detached: function() {
594 document.removeEventListener('keydown', this._boundEscKeydownHandler);
595 },
596 open: function() {
597 this.opened = true;
598 },
599 close: function() {
600 this.opened = false;
601 },
602 toggle: function() {
603 this.opened = !this.opened;
604 },
605 getWidth: function() {
606 return this.$.contentContainer.offsetWidth;
607 },
608 resetLayout: function() {
609 this.debounce('_resetLayout', function() {
610 this.fire('app-drawer-reset-layout');
611 }, 1);
612 },
613 _isRTL: function() {
614 return window.getComputedStyle(this).direction === 'rtl';
615 },
616 _resetPosition: function() {
617 switch (this.align) {
618 case 'start':
619 this._setPosition(this._isRTL() ? 'right' : 'left');
620 return;
621
622 case 'end':
623 this._setPosition(this._isRTL() ? 'left' : 'right');
624 return;
625 }
626 this._setPosition(this.align);
627 },
628 _escKeydownHandler: function(event) {
629 var ESC_KEYCODE = 27;
630 if (event.keyCode === ESC_KEYCODE) {
631 event.preventDefault();
632 this.close();
633 }
634 },
635 _track: function(event) {
636 if (this.persistent) {
637 return;
638 }
639 event.preventDefault();
640 switch (event.detail.state) {
641 case 'start':
642 this._trackStart(event);
643 break;
644
645 case 'track':
646 this._trackMove(event);
647 break;
648
649 case 'end':
650 this._trackEnd(event);
651 break;
652 }
653 },
654 _trackStart: function(event) {
655 this._drawerState = this._DRAWER_STATE.TRACKING;
656 this._setTransitionDuration('0s');
657 this.style.visibility = 'visible';
658 var rect = this.$.contentContainer.getBoundingClientRect();
659 if (this.position === 'left') {
660 this._translateOffset = rect.left;
661 } else {
662 this._translateOffset = rect.right - window.innerWidth;
663 }
664 this._trackDetails = [];
665 },
666 _trackMove: function(event) {
667 this._translateDrawer(event.detail.dx + this._translateOffset);
668 this._trackDetails.push({
669 dx: event.detail.dx,
670 timeStamp: Date.now()
671 });
672 },
673 _trackEnd: function(event) {
674 var x = event.detail.dx + this._translateOffset;
675 var drawerWidth = this.getWidth();
676 var isPositionLeft = this.position === 'left';
677 var isInEndState = isPositionLeft ? x >= 0 || x <= -drawerWidth : x <= 0 || x >= drawerWidth;
678 if (!isInEndState) {
679 var trackDetails = this._trackDetails;
680 this._trackDetails = null;
681 this._flingDrawer(event, trackDetails);
682 if (this._drawerState === this._DRAWER_STATE.FLINGING) {
683 return;
684 }
685 }
686 var halfWidth = drawerWidth / 2;
687 if (event.detail.dx < -halfWidth) {
688 this.opened = this.position === 'right';
689 } else if (event.detail.dx > halfWidth) {
690 this.opened = this.position === 'left';
691 }
692 if (isInEndState) {
693 this._resetDrawerState();
694 }
695 this._setTransitionDuration('');
696 this._resetDrawerTranslate();
697 this.style.visibility = '';
698 },
699 _calculateVelocity: function(event, trackDetails) {
700 var now = Date.now();
701 var timeLowerBound = now - 100;
702 var trackDetail;
703 var min = 0;
704 var max = trackDetails.length - 1;
705 while (min <= max) {
706 var mid = min + max >> 1;
707 var d = trackDetails[mid];
708 if (d.timeStamp >= timeLowerBound) {
709 trackDetail = d;
710 max = mid - 1;
711 } else {
712 min = mid + 1;
713 }
714 }
715 if (trackDetail) {
716 var dx = event.detail.dx - trackDetail.dx;
717 var dt = now - trackDetail.timeStamp || 1;
718 return dx / dt;
719 }
720 return 0;
721 },
722 _flingDrawer: function(event, trackDetails) {
723 var velocity = this._calculateVelocity(event, trackDetails);
724 if (Math.abs(velocity) < this._MIN_FLING_THRESHOLD) {
725 return;
726 }
727 this._drawerState = this._DRAWER_STATE.FLINGING;
728 var x = event.detail.dx + this._translateOffset;
729 var drawerWidth = this.getWidth();
730 var isPositionLeft = this.position === 'left';
731 var isVelocityPositive = velocity > 0;
732 var isClosingLeft = !isVelocityPositive && isPositionLeft;
733 var isClosingRight = isVelocityPositive && !isPositionLeft;
734 var dx;
735 if (isClosingLeft) {
736 dx = -(x + drawerWidth);
737 } else if (isClosingRight) {
738 dx = drawerWidth - x;
739 } else {
740 dx = -x;
741 }
742 if (isVelocityPositive) {
743 velocity = Math.max(velocity, this._MIN_TRANSITION_VELOCITY);
744 this.opened = this.position === 'left';
745 } else {
746 velocity = Math.min(velocity, -this._MIN_TRANSITION_VELOCITY);
747 this.opened = this.position === 'right';
748 }
749 this._setTransitionDuration(this._FLING_INITIAL_SLOPE * dx / velocity + 'ms' );
750 this._setTransitionTimingFunction(this._FLING_TIMING_FUNCTION);
751 this._resetDrawerTranslate();
752 },
753 _transitionend: function(event) {
754 var target = Polymer.dom(event).rootTarget;
755 if (target === this.$.contentContainer || target === this.$.scrim) {
756 if (this._drawerState === this._DRAWER_STATE.FLINGING) {
757 this._setTransitionDuration('');
758 this._setTransitionTimingFunction('');
759 this.style.visibility = '';
760 }
761 this._resetDrawerState();
762 }
763 },
764 _setTransitionDuration: function(duration) {
765 this.$.contentContainer.style.transitionDuration = duration;
766 this.$.scrim.style.transitionDuration = duration;
767 },
768 _setTransitionTimingFunction: function(timingFunction) {
769 this.$.contentContainer.style.transitionTimingFunction = timingFunction;
770 this.$.scrim.style.transitionTimingFunction = timingFunction;
771 },
772 _translateDrawer: function(x) {
773 var drawerWidth = this.getWidth();
774 if (this.position === 'left') {
775 x = Math.max(-drawerWidth, Math.min(x, 0));
776 this.$.scrim.style.opacity = 1 + x / drawerWidth;
777 } else {
778 x = Math.max(0, Math.min(x, drawerWidth));
779 this.$.scrim.style.opacity = 1 - x / drawerWidth;
780 }
781 this.translate3d(x + 'px', '0', '0', this.$.contentContainer);
782 },
783 _resetDrawerTranslate: function() {
784 this.$.scrim.style.opacity = '';
785 this.transform('', this.$.contentContainer);
786 },
787 _resetDrawerState: function() {
788 var oldState = this._drawerState;
789 if (this.opened) {
790 this._drawerState = this.persistent ? this._DRAWER_STATE.OPENED_PERSISTENT : this._DRAWER_STATE.OPENED;
791 } else {
792 this._drawerState = this._DRAWER_STATE.CLOSED;
793 }
794 if (oldState !== this._drawerState) {
795 if (this._drawerState === this._DRAWER_STATE.OPENED) {
796 this._setKeyboardFocusTrap();
797 document.addEventListener('keydown', this._boundEscKeydownHandler);
798 document.body.style.overflow = 'hidden';
799 } else {
800 document.removeEventListener('keydown', this._boundEscKeydownHandler);
801 document.body.style.overflow = '';
802 }
803 if (oldState !== this._DRAWER_STATE.INIT) {
804 this.fire('app-drawer-transitioned');
805 }
806 }
807 },
808 _setKeyboardFocusTrap: function() {
809 if (this.noFocusTrap) {
810 return;
811 }
812 var focusableElementsSelector = [ 'a[href]:not([tabindex="-1"])', 'area[href ]:not([tabindex="-1"])', 'input:not([disabled]):not([tabindex="-1"])', 'select:n ot([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(',');
813 var focusableElements = Polymer.dom(this).querySelectorAll(focusableElements Selector);
814 if (focusableElements.length > 0) {
815 this._firstTabStop = focusableElements[0];
816 this._lastTabStop = focusableElements[focusableElements.length - 1];
817 } else {
818 this._firstTabStop = null;
819 this._lastTabStop = null;
820 }
821 var tabindex = this.getAttribute('tabindex');
822 if (tabindex && parseInt(tabindex, 10) > -1) {
823 this.focus();
824 } else if (this._firstTabStop) {
825 this._firstTabStop.focus();
826 }
827 },
828 _tabKeydownHandler: function(event) {
829 if (this.noFocusTrap) {
830 return;
831 }
832 var TAB_KEYCODE = 9;
833 if (this._drawerState === this._DRAWER_STATE.OPENED && event.keyCode === TAB _KEYCODE) {
834 if (event.shiftKey) {
835 if (this._firstTabStop && Polymer.dom(event).localTarget === this._first TabStop) {
836 event.preventDefault();
837 this._lastTabStop.focus();
838 }
839 } else {
840 if (this._lastTabStop && Polymer.dom(event).localTarget === this._lastTa bStop) {
841 event.preventDefault();
842 this._firstTabStop.focus();
843 }
844 }
845 }
846 },
847 _MIN_FLING_THRESHOLD: .2,
848 _MIN_TRANSITION_VELOCITY: 1.2,
849 _FLING_TIMING_FUNCTION: 'cubic-bezier(0.667, 1, 0.667, 1)',
850 _FLING_INITIAL_SLOPE: 1.5,
851 _DRAWER_STATE: {
852 INIT: 0,
853 OPENED: 1,
854 OPENED_PERSISTENT: 2,
855 CLOSED: 3,
856 TRACKING: 4,
857 FLINGING: 5
858 }
859 });
860
861 (function() {
862 'use strict';
863 Polymer({
864 is: 'iron-location',
865 properties: {
866 path: {
867 type: String,
868 notify: true,
869 value: function() {
870 return window.decodeURIComponent(window.location.pathname);
871 }
872 },
873 query: {
874 type: String,
875 notify: true,
876 value: function() {
877 return window.decodeURIComponent(window.location.search.slice(1));
878 }
879 },
880 hash: {
881 type: String,
882 notify: true,
883 value: function() {
884 return window.decodeURIComponent(window.location.hash.slice(1));
885 }
886 },
887 dwellTime: {
888 type: Number,
889 value: 2e3
890 },
891 urlSpaceRegex: {
892 type: String,
893 value: ''
894 },
895 _urlSpaceRegExp: {
896 computed: '_makeRegExp(urlSpaceRegex)'
897 },
898 _lastChangedAt: {
899 type: Number
900 },
901 _initialized: {
902 type: Boolean,
903 value: false
904 }
905 },
906 hostAttributes: {
907 hidden: true
908 },
909 observers: [ '_updateUrl(path, query, hash)' ],
910 attached: function() {
911 this.listen(window, 'hashchange', '_hashChanged');
912 this.listen(window, 'location-changed', '_urlChanged');
913 this.listen(window, 'popstate', '_urlChanged');
914 this.listen(document.body, 'click', '_globalOnClick');
915 this._lastChangedAt = window.performance.now() - (this.dwellTime - 200);
916 this._initialized = true;
917 this._urlChanged();
918 },
919 detached: function() {
920 this.unlisten(window, 'hashchange', '_hashChanged');
921 this.unlisten(window, 'location-changed', '_urlChanged');
922 this.unlisten(window, 'popstate', '_urlChanged');
923 this.unlisten(document.body, 'click', '_globalOnClick');
924 this._initialized = false;
925 },
926 _hashChanged: function() {
927 this.hash = window.decodeURIComponent(window.location.hash.substring(1));
928 },
929 _urlChanged: function() {
930 this._dontUpdateUrl = true;
931 this._hashChanged();
932 this.path = window.decodeURIComponent(window.location.pathname);
933 this.query = window.decodeURIComponent(window.location.search.substring(1) );
934 this._dontUpdateUrl = false;
935 this._updateUrl();
936 },
937 _getUrl: function() {
938 var partiallyEncodedPath = window.encodeURI(this.path).replace(/\#/g, '%23 ').replace(/\?/g, '%3F');
939 var partiallyEncodedQuery = '';
940 if (this.query) {
941 partiallyEncodedQuery = '?' + window.encodeURI(this.query).replace(/\#/g , '%23');
942 }
943 var partiallyEncodedHash = '';
944 if (this.hash) {
945 partiallyEncodedHash = '#' + window.encodeURI(this.hash);
946 }
947 return partiallyEncodedPath + partiallyEncodedQuery + partiallyEncodedHash ;
948 },
949 _updateUrl: function() {
950 if (this._dontUpdateUrl || !this._initialized) {
951 return;
952 }
953 if (this.path === window.decodeURIComponent(window.location.pathname) && t his.query === window.decodeURIComponent(window.location.search.substring(1)) && this.hash === window.decodeURIComponent(window.location.hash.substring(1))) {
954 return;
955 }
956 var newUrl = this._getUrl();
957 var fullNewUrl = new URL(newUrl, window.location.protocol + '//' + window. location.host).href;
958 var now = window.performance.now();
959 var shouldReplace = this._lastChangedAt + this.dwellTime > now;
960 this._lastChangedAt = now;
961 if (shouldReplace) {
962 window.history.replaceState({}, '', fullNewUrl);
963 } else {
964 window.history.pushState({}, '', fullNewUrl);
965 }
966 this.fire('location-changed', {}, {
967 node: window
968 });
969 },
970 _globalOnClick: function(event) {
971 if (event.defaultPrevented) {
972 return;
973 }
974 var href = this._getSameOriginLinkHref(event);
975 if (!href) {
976 return;
977 }
978 event.preventDefault();
979 if (href === window.location.href) {
980 return;
981 }
982 window.history.pushState({}, '', href);
983 this.fire('location-changed', {}, {
984 node: window
985 });
986 },
987 _getSameOriginLinkHref: function(event) {
988 if (event.button !== 0) {
989 return null;
990 }
991 if (event.metaKey || event.ctrlKey) {
992 return null;
993 }
994 var eventPath = Polymer.dom(event).path;
995 var anchor = null;
996 for (var i = 0; i < eventPath.length; i++) {
997 var element = eventPath[i];
998 if (element.tagName === 'A' && element.href) {
999 anchor = element;
1000 break;
1001 }
1002 }
1003 if (!anchor) {
1004 return null;
1005 }
1006 if (anchor.target === '_blank') {
1007 return null;
1008 }
1009 if ((anchor.target === '_top' || anchor.target === '_parent') && window.to p !== window) {
1010 return null;
1011 }
1012 var href = anchor.href;
1013 var url;
1014 if (document.baseURI != null) {
1015 url = new URL(href, document.baseURI);
1016 } else {
1017 url = new URL(href);
1018 }
1019 var origin;
1020 if (window.location.origin) {
1021 origin = window.location.origin;
1022 } else {
1023 origin = window.location.protocol + '//' + window.location.hostname;
1024 if (window.location.port) {
1025 origin += ':' + window.location.port;
1026 }
1027 }
1028 if (url.origin !== origin) {
1029 return null;
1030 }
1031 var normalizedHref = url.pathname + url.search + url.hash;
1032 if (this._urlSpaceRegExp && !this._urlSpaceRegExp.test(normalizedHref)) {
1033 return null;
1034 }
1035 var fullNormalizedHref = new URL(normalizedHref, window.location.href).hre f;
1036 return fullNormalizedHref;
1037 },
1038 _makeRegExp: function(urlSpaceRegex) {
1039 return RegExp(urlSpaceRegex);
1040 }
1041 });
1042 })();
1043
1044 'use strict';
1045
1046 Polymer({
1047 is: 'iron-query-params',
1048 properties: {
1049 paramsString: {
1050 type: String,
1051 notify: true,
1052 observer: 'paramsStringChanged'
1053 },
1054 paramsObject: {
1055 type: Object,
1056 notify: true,
1057 value: function() {
1058 return {};
1059 }
1060 },
1061 _dontReact: {
1062 type: Boolean,
1063 value: false
1064 }
1065 },
1066 hostAttributes: {
1067 hidden: true
1068 },
1069 observers: [ 'paramsObjectChanged(paramsObject.*)' ],
1070 paramsStringChanged: function() {
1071 this._dontReact = true;
1072 this.paramsObject = this._decodeParams(this.paramsString);
1073 this._dontReact = false;
1074 },
1075 paramsObjectChanged: function() {
1076 if (this._dontReact) {
1077 return;
1078 }
1079 this.paramsString = this._encodeParams(this.paramsObject);
1080 },
1081 _encodeParams: function(params) {
1082 var encodedParams = [];
1083 for (var key in params) {
1084 var value = params[key];
1085 if (value === '') {
1086 encodedParams.push(encodeURIComponent(key));
1087 } else if (value) {
1088 encodedParams.push(encodeURIComponent(key) + '=' + encodeURIComponent(va lue.toString()));
1089 }
1090 }
1091 return encodedParams.join('&');
1092 },
1093 _decodeParams: function(paramString) {
1094 var params = {};
1095 paramString = (paramString || '').replace(/\+/g, '%20');
1096 var paramList = paramString.split('&');
1097 for (var i = 0; i < paramList.length; i++) {
1098 var param = paramList[i].split('=');
1099 if (param[0]) {
1100 params[decodeURIComponent(param[0])] = decodeURIComponent(param[1] || '' );
1101 }
1102 }
1103 return params;
1104 }
1105 });
1106
1107 'use strict';
1108
1109 Polymer.AppRouteConverterBehavior = {
1110 properties: {
1111 route: {
1112 type: Object,
1113 notify: true
1114 },
1115 queryParams: {
1116 type: Object,
1117 notify: true
1118 },
1119 path: {
1120 type: String,
1121 notify: true
1122 }
1123 },
1124 observers: [ '_locationChanged(path, queryParams)', '_routeChanged(route.prefi x, route.path)', '_routeQueryParamsChanged(route.__queryParams)' ],
1125 created: function() {
1126 this.linkPaths('route.__queryParams', 'queryParams');
1127 this.linkPaths('queryParams', 'route.__queryParams');
1128 },
1129 _locationChanged: function() {
1130 if (this.route && this.route.path === this.path && this.queryParams === this .route.__queryParams) {
1131 return;
1132 }
1133 this.route = {
1134 prefix: '',
1135 path: this.path,
1136 __queryParams: this.queryParams
1137 };
1138 },
1139 _routeChanged: function() {
1140 if (!this.route) {
1141 return;
1142 }
1143 this.path = this.route.prefix + this.route.path;
1144 },
1145 _routeQueryParamsChanged: function(queryParams) {
1146 if (!this.route) {
1147 return;
1148 }
1149 this.queryParams = queryParams;
1150 }
1151 };
1152
1153 'use strict';
1154
1155 Polymer({
1156 is: 'app-location',
1157 properties: {
1158 route: {
1159 type: Object,
1160 notify: true
1161 },
1162 useHashAsPath: {
1163 type: Boolean,
1164 value: false
1165 },
1166 urlSpaceRegex: {
1167 type: String,
1168 notify: true
1169 },
1170 __queryParams: {
1171 type: Object
1172 },
1173 __path: {
1174 type: String
1175 },
1176 __query: {
1177 type: String
1178 },
1179 __hash: {
1180 type: String
1181 },
1182 path: {
1183 type: String,
1184 observer: '__onPathChanged'
1185 }
1186 },
1187 behaviors: [ Polymer.AppRouteConverterBehavior ],
1188 observers: [ '__computeRoutePath(useHashAsPath, __hash, __path)' ],
1189 __computeRoutePath: function() {
1190 this.path = this.useHashAsPath ? this.__hash : this.__path;
1191 },
1192 __onPathChanged: function() {
1193 if (!this._readied) {
1194 return;
1195 }
1196 if (this.useHashAsPath) {
1197 this.__hash = this.path;
1198 } else {
1199 this.__path = this.path;
1200 }
1201 }
1202 });
1203
1204 'use strict';
1205
1206 Polymer({
1207 is: 'app-route',
1208 properties: {
1209 route: {
1210 type: Object,
1211 notify: true
1212 },
1213 pattern: {
1214 type: String
1215 },
1216 data: {
1217 type: Object,
1218 value: function() {
1219 return {};
1220 },
1221 notify: true
1222 },
1223 queryParams: {
1224 type: Object,
1225 value: function() {
1226 return {};
1227 },
1228 notify: true
1229 },
1230 tail: {
1231 type: Object,
1232 value: function() {
1233 return {
1234 path: null,
1235 prefix: null,
1236 __queryParams: null
1237 };
1238 },
1239 notify: true
1240 },
1241 active: {
1242 type: Boolean,
1243 notify: true,
1244 readOnly: true
1245 },
1246 _queryParamsUpdating: {
1247 type: Boolean,
1248 value: false
1249 },
1250 _matched: {
1251 type: String,
1252 value: ''
1253 }
1254 },
1255 observers: [ '__tryToMatch(route.path, pattern)', '__updatePathOnDataChange(da ta.*)', '__tailPathChanged(tail.path)', '__routeQueryParamsChanged(route.__query Params)', '__tailQueryParamsChanged(tail.__queryParams)', '__queryParamsChanged( queryParams.*)' ],
1256 created: function() {
1257 this.linkPaths('route.__queryParams', 'tail.__queryParams');
1258 this.linkPaths('tail.__queryParams', 'route.__queryParams');
1259 },
1260 __routeQueryParamsChanged: function(queryParams) {
1261 if (queryParams && this.tail) {
1262 this.set('tail.__queryParams', queryParams);
1263 if (!this.active || this._queryParamsUpdating) {
1264 return;
1265 }
1266 var copyOfQueryParams = {};
1267 var anythingChanged = false;
1268 for (var key in queryParams) {
1269 copyOfQueryParams[key] = queryParams[key];
1270 if (anythingChanged || !this.queryParams || queryParams[key] !== this.qu eryParams[key]) {
1271 anythingChanged = true;
1272 }
1273 }
1274 for (var key in this.queryParams) {
1275 if (anythingChanged || !(key in queryParams)) {
1276 anythingChanged = true;
1277 break;
1278 }
1279 }
1280 if (!anythingChanged) {
1281 return;
1282 }
1283 this._queryParamsUpdating = true;
1284 this.set('queryParams', copyOfQueryParams);
1285 this._queryParamsUpdating = false;
1286 }
1287 },
1288 __tailQueryParamsChanged: function(queryParams) {
1289 if (queryParams && this.route) {
1290 this.set('route.__queryParams', queryParams);
1291 }
1292 },
1293 __queryParamsChanged: function(changes) {
1294 if (!this.active || this._queryParamsUpdating) {
1295 return;
1296 }
1297 this.set('route.__' + changes.path, changes.value);
1298 },
1299 __resetProperties: function() {
1300 this._setActive(false);
1301 this._matched = null;
1302 },
1303 __tryToMatch: function() {
1304 if (!this.route) {
1305 return;
1306 }
1307 var path = this.route.path;
1308 var pattern = this.pattern;
1309 if (!pattern) {
1310 return;
1311 }
1312 if (!path) {
1313 this.__resetProperties();
1314 return;
1315 }
1316 var remainingPieces = path.split('/');
1317 var patternPieces = pattern.split('/');
1318 var matched = [];
1319 var namedMatches = {};
1320 for (var i = 0; i < patternPieces.length; i++) {
1321 var patternPiece = patternPieces[i];
1322 if (!patternPiece && patternPiece !== '') {
1323 break;
1324 }
1325 var pathPiece = remainingPieces.shift();
1326 if (!pathPiece && pathPiece !== '') {
1327 this.__resetProperties();
1328 return;
1329 }
1330 matched.push(pathPiece);
1331 if (patternPiece.charAt(0) == ':') {
1332 namedMatches[patternPiece.slice(1)] = pathPiece;
1333 } else if (patternPiece !== pathPiece) {
1334 this.__resetProperties();
1335 return;
1336 }
1337 }
1338 this._matched = matched.join('/');
1339 var propertyUpdates = {};
1340 if (!this.active) {
1341 propertyUpdates.active = true;
1342 }
1343 var tailPrefix = this.route.prefix + this._matched;
1344 var tailPath = remainingPieces.join('/');
1345 if (remainingPieces.length > 0) {
1346 tailPath = '/' + tailPath;
1347 }
1348 if (!this.tail || this.tail.prefix !== tailPrefix || this.tail.path !== tail Path) {
1349 propertyUpdates.tail = {
1350 prefix: tailPrefix,
1351 path: tailPath,
1352 __queryParams: this.route.__queryParams
1353 };
1354 }
1355 propertyUpdates.data = namedMatches;
1356 this._dataInUrl = {};
1357 for (var key in namedMatches) {
1358 this._dataInUrl[key] = namedMatches[key];
1359 }
1360 this.__setMulti(propertyUpdates);
1361 },
1362 __tailPathChanged: function() {
1363 if (!this.active) {
1364 return;
1365 }
1366 var tailPath = this.tail.path;
1367 var newPath = this._matched;
1368 if (tailPath) {
1369 if (tailPath.charAt(0) !== '/') {
1370 tailPath = '/' + tailPath;
1371 }
1372 newPath += tailPath;
1373 }
1374 this.set('route.path', newPath);
1375 },
1376 __updatePathOnDataChange: function() {
1377 if (!this.route || !this.active) {
1378 return;
1379 }
1380 var newPath = this.__getLink({});
1381 var oldPath = this.__getLink(this._dataInUrl);
1382 if (newPath === oldPath) {
1383 return;
1384 }
1385 this.set('route.path', newPath);
1386 },
1387 __getLink: function(overrideValues) {
1388 var values = {
1389 tail: null
1390 };
1391 for (var key in this.data) {
1392 values[key] = this.data[key];
1393 }
1394 for (var key in overrideValues) {
1395 values[key] = overrideValues[key];
1396 }
1397 var patternPieces = this.pattern.split('/');
1398 var interp = patternPieces.map(function(value) {
1399 if (value[0] == ':') {
1400 value = values[value.slice(1)];
1401 }
1402 return value;
1403 }, this);
1404 if (values.tail && values.tail.path) {
1405 if (interp.length > 0 && values.tail.path.charAt(0) === '/') {
1406 interp.push(values.tail.path.slice(1));
1407 } else {
1408 interp.push(values.tail.path);
1409 }
1410 }
1411 return interp.join('/');
1412 },
1413 __setMulti: function(setObj) {
1414 for (var property in setObj) {
1415 this._propertySetter(property, setObj[property]);
1416 }
1417 for (var property in setObj) {
1418 this._pathEffector(property, this[property]);
1419 this._notifyPathUp(property, this[property]);
1420 }
1421 }
1422 });
1423
1424 Polymer({
1425 is: 'iron-media-query',
1426 properties: {
1427 queryMatches: {
1428 type: Boolean,
1429 value: false,
1430 readOnly: true,
1431 notify: true
1432 },
1433 query: {
1434 type: String,
1435 observer: 'queryChanged'
1436 },
1437 full: {
1438 type: Boolean,
1439 value: false
1440 },
1441 _boundMQHandler: {
1442 value: function() {
1443 return this.queryHandler.bind(this);
1444 }
1445 },
1446 _mq: {
1447 value: null
1448 }
1449 },
1450 attached: function() {
1451 this.style.display = 'none';
1452 this.queryChanged();
1453 },
1454 detached: function() {
1455 this._remove();
1456 },
1457 _add: function() {
1458 if (this._mq) {
1459 this._mq.addListener(this._boundMQHandler);
1460 }
1461 },
1462 _remove: function() {
1463 if (this._mq) {
1464 this._mq.removeListener(this._boundMQHandler);
1465 }
1466 this._mq = null;
1467 },
1468 queryChanged: function() {
1469 this._remove();
1470 var query = this.query;
1471 if (!query) {
1472 return;
1473 }
1474 if (!this.full && query[0] !== '(') {
1475 query = '(' + query + ')';
1476 }
1477 this._mq = window.matchMedia(query);
1478 this._add();
1479 this.queryHandler(this._mq);
1480 },
1481 queryHandler: function(mq) {
1482 this._setQueryMatches(mq.matches);
1483 }
1484 });
1485
1486 Polymer.IronResizableBehavior = {
1487 properties: {
1488 _parentResizable: {
1489 type: Object,
1490 observer: '_parentResizableChanged'
1491 },
1492 _notifyingDescendant: {
1493 type: Boolean,
1494 value: false
1495 }
1496 },
1497 listeners: {
1498 'iron-request-resize-notifications': '_onIronRequestResizeNotifications'
1499 },
1500 created: function() {
1501 this._interestedResizables = [];
1502 this._boundNotifyResize = this.notifyResize.bind(this);
1503 },
1504 attached: function() {
1505 this.fire('iron-request-resize-notifications', null, {
1506 node: this,
1507 bubbles: true,
1508 cancelable: true
1509 });
1510 if (!this._parentResizable) {
1511 window.addEventListener('resize', this._boundNotifyResize);
1512 this.notifyResize();
1513 }
1514 },
1515 detached: function() {
1516 if (this._parentResizable) {
1517 this._parentResizable.stopResizeNotificationsFor(this);
1518 } else {
1519 window.removeEventListener('resize', this._boundNotifyResize);
1520 }
1521 this._parentResizable = null;
1522 },
1523 notifyResize: function() {
1524 if (!this.isAttached) {
1525 return;
1526 }
1527 this._interestedResizables.forEach(function(resizable) {
1528 if (this.resizerShouldNotify(resizable)) {
1529 this._notifyDescendant(resizable);
1530 }
1531 }, this);
1532 this._fireResize();
1533 },
1534 assignParentResizable: function(parentResizable) {
1535 this._parentResizable = parentResizable;
1536 },
1537 stopResizeNotificationsFor: function(target) {
1538 var index = this._interestedResizables.indexOf(target);
1539 if (index > -1) {
1540 this._interestedResizables.splice(index, 1);
1541 this.unlisten(target, 'iron-resize', '_onDescendantIronResize');
1542 }
1543 },
1544 resizerShouldNotify: function(element) {
1545 return true;
1546 },
1547 _onDescendantIronResize: function(event) {
1548 if (this._notifyingDescendant) {
1549 event.stopPropagation();
1550 return;
1551 }
1552 if (!Polymer.Settings.useShadow) {
1553 this._fireResize();
1554 }
1555 },
1556 _fireResize: function() {
1557 this.fire('iron-resize', null, {
1558 node: this,
1559 bubbles: false
1560 });
1561 },
1562 _onIronRequestResizeNotifications: function(event) {
1563 var target = event.path ? event.path[0] : event.target;
1564 if (target === this) {
1565 return;
1566 }
1567 if (this._interestedResizables.indexOf(target) === -1) {
1568 this._interestedResizables.push(target);
1569 this.listen(target, 'iron-resize', '_onDescendantIronResize');
1570 }
1571 target.assignParentResizable(this);
1572 this._notifyDescendant(target);
1573 event.stopPropagation();
1574 },
1575 _parentResizableChanged: function(parentResizable) {
1576 if (parentResizable) {
1577 window.removeEventListener('resize', this._boundNotifyResize);
1578 }
1579 },
1580 _notifyDescendant: function(descendant) {
1581 if (!this.isAttached) {
1582 return;
1583 }
1584 this._notifyingDescendant = true;
1585 descendant.notifyResize();
1586 this._notifyingDescendant = false;
1587 }
1588 };
1589
1590 Polymer.IronSelection = function(selectCallback) {
1591 this.selection = [];
1592 this.selectCallback = selectCallback;
1593 };
1594
1595 Polymer.IronSelection.prototype = {
1596 get: function() {
1597 return this.multi ? this.selection.slice() : this.selection[0];
1598 },
1599 clear: function(excludes) {
1600 this.selection.slice().forEach(function(item) {
1601 if (!excludes || excludes.indexOf(item) < 0) {
1602 this.setItemSelected(item, false);
1603 }
1604 }, this);
1605 },
1606 isSelected: function(item) {
1607 return this.selection.indexOf(item) >= 0;
1608 },
1609 setItemSelected: function(item, isSelected) {
1610 if (item != null) {
1611 if (isSelected !== this.isSelected(item)) {
1612 if (isSelected) {
1613 this.selection.push(item);
1614 } else {
1615 var i = this.selection.indexOf(item);
1616 if (i >= 0) {
1617 this.selection.splice(i, 1);
1618 }
1619 }
1620 if (this.selectCallback) {
1621 this.selectCallback(item, isSelected);
1622 }
1623 }
1624 }
1625 },
1626 select: function(item) {
1627 if (this.multi) {
1628 this.toggle(item);
1629 } else if (this.get() !== item) {
1630 this.setItemSelected(this.get(), false);
1631 this.setItemSelected(item, true);
1632 }
1633 },
1634 toggle: function(item) {
1635 this.setItemSelected(item, !this.isSelected(item));
1636 }
1637 };
1638
1639 Polymer.IronSelectableBehavior = {
1640 properties: {
1641 attrForSelected: {
1642 type: String,
1643 value: null
1644 },
1645 selected: {
1646 type: String,
1647 notify: true
1648 },
1649 selectedItem: {
1650 type: Object,
1651 readOnly: true,
1652 notify: true
1653 },
1654 activateEvent: {
1655 type: String,
1656 value: 'tap',
1657 observer: '_activateEventChanged'
1658 },
1659 selectable: String,
1660 selectedClass: {
1661 type: String,
1662 value: 'iron-selected'
1663 },
1664 selectedAttribute: {
1665 type: String,
1666 value: null
1667 },
1668 fallbackSelection: {
1669 type: String,
1670 value: null
1671 },
1672 items: {
1673 type: Array,
1674 readOnly: true,
1675 notify: true,
1676 value: function() {
1677 return [];
1678 }
1679 },
1680 _excludedLocalNames: {
1681 type: Object,
1682 value: function() {
1683 return {
1684 template: 1
1685 };
1686 }
1687 }
1688 },
1689 observers: [ '_updateAttrForSelected(attrForSelected)', '_updateSelected(selec ted)', '_checkFallback(fallbackSelection)' ],
1690 created: function() {
1691 this._bindFilterItem = this._filterItem.bind(this);
1692 this._selection = new Polymer.IronSelection(this._applySelection.bind(this)) ;
1693 },
1694 attached: function() {
1695 this._observer = this._observeItems(this);
1696 this._updateItems();
1697 if (!this._shouldUpdateSelection) {
1698 this._updateSelected();
1699 }
1700 this._addListener(this.activateEvent);
1701 },
1702 detached: function() {
1703 if (this._observer) {
1704 Polymer.dom(this).unobserveNodes(this._observer);
1705 }
1706 this._removeListener(this.activateEvent);
1707 },
1708 indexOf: function(item) {
1709 return this.items.indexOf(item);
1710 },
1711 select: function(value) {
1712 this.selected = value;
1713 },
1714 selectPrevious: function() {
1715 var length = this.items.length;
1716 var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % lengt h;
1717 this.selected = this._indexToValue(index);
1718 },
1719 selectNext: function() {
1720 var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.len gth;
1721 this.selected = this._indexToValue(index);
1722 },
1723 selectIndex: function(index) {
1724 this.select(this._indexToValue(index));
1725 },
1726 forceSynchronousItemUpdate: function() {
1727 this._updateItems();
1728 },
1729 get _shouldUpdateSelection() {
1730 return this.selected != null;
1731 },
1732 _checkFallback: function() {
1733 if (this._shouldUpdateSelection) {
1734 this._updateSelected();
1735 }
1736 },
1737 _addListener: function(eventName) {
1738 this.listen(this, eventName, '_activateHandler');
1739 },
1740 _removeListener: function(eventName) {
1741 this.unlisten(this, eventName, '_activateHandler');
1742 },
1743 _activateEventChanged: function(eventName, old) {
1744 this._removeListener(old);
1745 this._addListener(eventName);
1746 },
1747 _updateItems: function() {
1748 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '* ');
1749 nodes = Array.prototype.filter.call(nodes, this._bindFilterItem);
1750 this._setItems(nodes);
1751 },
1752 _updateAttrForSelected: function() {
1753 if (this._shouldUpdateSelection) {
1754 this.selected = this._indexToValue(this.indexOf(this.selectedItem));
1755 }
1756 },
1757 _updateSelected: function() {
1758 this._selectSelected(this.selected);
1759 },
1760 _selectSelected: function(selected) {
1761 this._selection.select(this._valueToItem(this.selected));
1762 if (this.fallbackSelection && this.items.length && this._selection.get() === undefined) {
1763 this.selected = this.fallbackSelection;
1764 }
1765 },
1766 _filterItem: function(node) {
1767 return !this._excludedLocalNames[node.localName];
1768 },
1769 _valueToItem: function(value) {
1770 return value == null ? null : this.items[this._valueToIndex(value)];
1771 },
1772 _valueToIndex: function(value) {
1773 if (this.attrForSelected) {
1774 for (var i = 0, item; item = this.items[i]; i++) {
1775 if (this._valueForItem(item) == value) {
1776 return i;
1777 }
1778 }
1779 } else {
1780 return Number(value);
1781 }
1782 },
1783 _indexToValue: function(index) {
1784 if (this.attrForSelected) {
1785 var item = this.items[index];
1786 if (item) {
1787 return this._valueForItem(item);
1788 }
1789 } else {
1790 return index;
1791 }
1792 },
1793 _valueForItem: function(item) {
1794 var propValue = item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)];
1795 return propValue != undefined ? propValue : item.getAttribute(this.attrForSe lected);
1796 },
1797 _applySelection: function(item, isSelected) {
1798 if (this.selectedClass) {
1799 this.toggleClass(this.selectedClass, isSelected, item);
1800 }
1801 if (this.selectedAttribute) {
1802 this.toggleAttribute(this.selectedAttribute, isSelected, item);
1803 }
1804 this._selectionChange();
1805 this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {
1806 item: item
1807 });
1808 },
1809 _selectionChange: function() {
1810 this._setSelectedItem(this._selection.get());
1811 },
1812 _observeItems: function(node) {
1813 return Polymer.dom(node).observeNodes(function(mutation) {
1814 this._updateItems();
1815 if (this._shouldUpdateSelection) {
1816 this._updateSelected();
1817 }
1818 this.fire('iron-items-changed', mutation, {
1819 bubbles: false,
1820 cancelable: false
1821 });
1822 });
1823 },
1824 _activateHandler: function(e) {
1825 var t = e.target;
1826 var items = this.items;
1827 while (t && t != this) {
1828 var i = items.indexOf(t);
1829 if (i >= 0) {
1830 var value = this._indexToValue(i);
1831 this._itemActivate(value, t);
1832 return;
1833 }
1834 t = t.parentNode;
1835 }
1836 },
1837 _itemActivate: function(value, item) {
1838 if (!this.fire('iron-activate', {
1839 selected: value,
1840 item: item
1841 }, {
1842 cancelable: true
1843 }).defaultPrevented) {
1844 this.select(value);
1845 }
1846 }
1847 };
1848
1849 Polymer({
1850 is: 'iron-pages',
1851 behaviors: [ Polymer.IronResizableBehavior, Polymer.IronSelectableBehavior ],
1852 properties: {
1853 activateEvent: {
1854 type: String,
1855 value: null
1856 }
1857 },
1858 observers: [ '_selectedPageChanged(selected)' ],
1859 _selectedPageChanged: function(selected, old) {
1860 this.async(this.notifyResize);
1861 }
1862 });
1863
1864 (function() {
1865 'use strict';
1866 var KEY_IDENTIFIER = {
1867 'U+0008': 'backspace',
1868 'U+0009': 'tab',
1869 'U+001B': 'esc',
1870 'U+0020': 'space',
1871 'U+007F': 'del'
1872 };
1873 var KEY_CODE = {
1874 8: 'backspace',
1875 9: 'tab',
1876 13: 'enter',
1877 27: 'esc',
1878 33: 'pageup',
1879 34: 'pagedown',
1880 35: 'end',
1881 36: 'home',
1882 32: 'space',
1883 37: 'left',
1884 38: 'up',
1885 39: 'right',
1886 40: 'down',
1887 46: 'del',
1888 106: '*'
1889 };
1890 var MODIFIER_KEYS = {
1891 shift: 'shiftKey',
1892 ctrl: 'ctrlKey',
1893 alt: 'altKey',
1894 meta: 'metaKey'
1895 };
1896 var KEY_CHAR = /[a-z0-9*]/;
1897 var IDENT_CHAR = /U\+/;
1898 var ARROW_KEY = /^arrow/;
1899 var SPACE_KEY = /^space(bar)?/;
1900 var ESC_KEY = /^escape$/;
1901 function transformKey(key, noSpecialChars) {
1902 var validKey = '';
1903 if (key) {
1904 var lKey = key.toLowerCase();
1905 if (lKey === ' ' || SPACE_KEY.test(lKey)) {
1906 validKey = 'space';
1907 } else if (ESC_KEY.test(lKey)) {
1908 validKey = 'esc';
1909 } else if (lKey.length == 1) {
1910 if (!noSpecialChars || KEY_CHAR.test(lKey)) {
1911 validKey = lKey;
1912 }
1913 } else if (ARROW_KEY.test(lKey)) {
1914 validKey = lKey.replace('arrow', '');
1915 } else if (lKey == 'multiply') {
1916 validKey = '*';
1917 } else {
1918 validKey = lKey;
1919 }
1920 }
1921 return validKey;
1922 }
1923 function transformKeyIdentifier(keyIdent) {
1924 var validKey = '';
1925 if (keyIdent) {
1926 if (keyIdent in KEY_IDENTIFIER) {
1927 validKey = KEY_IDENTIFIER[keyIdent];
1928 } else if (IDENT_CHAR.test(keyIdent)) {
1929 keyIdent = parseInt(keyIdent.replace('U+', '0x'), 16);
1930 validKey = String.fromCharCode(keyIdent).toLowerCase();
1931 } else {
1932 validKey = keyIdent.toLowerCase();
1933 }
1934 }
1935 return validKey;
1936 }
1937 function transformKeyCode(keyCode) {
1938 var validKey = '';
1939 if (Number(keyCode)) {
1940 if (keyCode >= 65 && keyCode <= 90) {
1941 validKey = String.fromCharCode(32 + keyCode);
1942 } else if (keyCode >= 112 && keyCode <= 123) {
1943 validKey = 'f' + (keyCode - 112);
1944 } else if (keyCode >= 48 && keyCode <= 57) {
1945 validKey = String(keyCode - 48);
1946 } else if (keyCode >= 96 && keyCode <= 105) {
1947 validKey = String(keyCode - 96);
1948 } else {
1949 validKey = KEY_CODE[keyCode];
1950 }
1951 }
1952 return validKey;
1953 }
1954 function normalizedKeyForEvent(keyEvent, noSpecialChars) {
1955 return transformKey(keyEvent.key, noSpecialChars) || transformKeyIdentifier( keyEvent.keyIdentifier) || transformKeyCode(keyEvent.keyCode) || transformKey(ke yEvent.detail ? keyEvent.detail.key : keyEvent.detail, noSpecialChars) || '';
1956 }
1957 function keyComboMatchesEvent(keyCombo, event) {
1958 var keyEvent = normalizedKeyForEvent(event, keyCombo.hasModifiers);
1959 return keyEvent === keyCombo.key && (!keyCombo.hasModifiers || !!event.shift Key === !!keyCombo.shiftKey && !!event.ctrlKey === !!keyCombo.ctrlKey && !!event .altKey === !!keyCombo.altKey && !!event.metaKey === !!keyCombo.metaKey);
1960 }
1961 function parseKeyComboString(keyComboString) {
1962 if (keyComboString.length === 1) {
1963 return {
1964 combo: keyComboString,
1965 key: keyComboString,
1966 event: 'keydown'
1967 };
1968 }
1969 return keyComboString.split('+').reduce(function(parsedKeyCombo, keyComboPar t) {
1970 var eventParts = keyComboPart.split(':');
1971 var keyName = eventParts[0];
1972 var event = eventParts[1];
1973 if (keyName in MODIFIER_KEYS) {
1974 parsedKeyCombo[MODIFIER_KEYS[keyName]] = true;
1975 parsedKeyCombo.hasModifiers = true;
1976 } else {
1977 parsedKeyCombo.key = keyName;
1978 parsedKeyCombo.event = event || 'keydown';
1979 }
1980 return parsedKeyCombo;
1981 }, {
1982 combo: keyComboString.split(':').shift()
1983 });
1984 }
1985 function parseEventString(eventString) {
1986 return eventString.trim().split(' ').map(function(keyComboString) {
1987 return parseKeyComboString(keyComboString);
1988 });
1989 }
1990 Polymer.IronA11yKeysBehavior = {
1991 properties: {
1992 keyEventTarget: {
1993 type: Object,
1994 value: function() {
1995 return this;
1996 }
1997 },
1998 stopKeyboardEventPropagation: {
1999 type: Boolean,
2000 value: false
2001 },
2002 _boundKeyHandlers: {
2003 type: Array,
2004 value: function() {
2005 return [];
2006 }
2007 },
2008 _imperativeKeyBindings: {
2009 type: Object,
2010 value: function() {
2011 return {};
2012 }
2013 }
2014 },
2015 observers: [ '_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)' ],
2016 keyBindings: {},
2017 registered: function() {
2018 this._prepKeyBindings();
2019 },
2020 attached: function() {
2021 this._listenKeyEventListeners();
2022 },
2023 detached: function() {
2024 this._unlistenKeyEventListeners();
2025 },
2026 addOwnKeyBinding: function(eventString, handlerName) {
2027 this._imperativeKeyBindings[eventString] = handlerName;
2028 this._prepKeyBindings();
2029 this._resetKeyEventListeners();
2030 },
2031 removeOwnKeyBindings: function() {
2032 this._imperativeKeyBindings = {};
2033 this._prepKeyBindings();
2034 this._resetKeyEventListeners();
2035 },
2036 keyboardEventMatchesKeys: function(event, eventString) {
2037 var keyCombos = parseEventString(eventString);
2038 for (var i = 0; i < keyCombos.length; ++i) {
2039 if (keyComboMatchesEvent(keyCombos[i], event)) {
2040 return true;
2041 }
2042 }
2043 return false;
2044 },
2045 _collectKeyBindings: function() {
2046 var keyBindings = this.behaviors.map(function(behavior) {
2047 return behavior.keyBindings;
2048 });
2049 if (keyBindings.indexOf(this.keyBindings) === -1) {
2050 keyBindings.push(this.keyBindings);
2051 }
2052 return keyBindings;
2053 },
2054 _prepKeyBindings: function() {
2055 this._keyBindings = {};
2056 this._collectKeyBindings().forEach(function(keyBindings) {
2057 for (var eventString in keyBindings) {
2058 this._addKeyBinding(eventString, keyBindings[eventString]);
2059 }
2060 }, this);
2061 for (var eventString in this._imperativeKeyBindings) {
2062 this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString ]);
2063 }
2064 for (var eventName in this._keyBindings) {
2065 this._keyBindings[eventName].sort(function(kb1, kb2) {
2066 var b1 = kb1[0].hasModifiers;
2067 var b2 = kb2[0].hasModifiers;
2068 return b1 === b2 ? 0 : b1 ? -1 : 1;
2069 });
2070 }
2071 },
2072 _addKeyBinding: function(eventString, handlerName) {
2073 parseEventString(eventString).forEach(function(keyCombo) {
2074 this._keyBindings[keyCombo.event] = this._keyBindings[keyCombo.event] || [];
2075 this._keyBindings[keyCombo.event].push([ keyCombo, handlerName ]);
2076 }, this);
2077 },
2078 _resetKeyEventListeners: function() {
2079 this._unlistenKeyEventListeners();
2080 if (this.isAttached) {
2081 this._listenKeyEventListeners();
2082 }
2083 },
2084 _listenKeyEventListeners: function() {
2085 if (!this.keyEventTarget) {
2086 return;
2087 }
2088 Object.keys(this._keyBindings).forEach(function(eventName) {
2089 var keyBindings = this._keyBindings[eventName];
2090 var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings);
2091 this._boundKeyHandlers.push([ this.keyEventTarget, eventName, boundKeyHa ndler ]);
2092 this.keyEventTarget.addEventListener(eventName, boundKeyHandler);
2093 }, this);
2094 },
2095 _unlistenKeyEventListeners: function() {
2096 var keyHandlerTuple;
2097 var keyEventTarget;
2098 var eventName;
2099 var boundKeyHandler;
2100 while (this._boundKeyHandlers.length) {
2101 keyHandlerTuple = this._boundKeyHandlers.pop();
2102 keyEventTarget = keyHandlerTuple[0];
2103 eventName = keyHandlerTuple[1];
2104 boundKeyHandler = keyHandlerTuple[2];
2105 keyEventTarget.removeEventListener(eventName, boundKeyHandler);
2106 }
2107 },
2108 _onKeyBindingEvent: function(keyBindings, event) {
2109 if (this.stopKeyboardEventPropagation) {
2110 event.stopPropagation();
2111 }
2112 if (event.defaultPrevented) {
2113 return;
2114 }
2115 for (var i = 0; i < keyBindings.length; i++) {
2116 var keyCombo = keyBindings[i][0];
2117 var handlerName = keyBindings[i][1];
2118 if (keyComboMatchesEvent(keyCombo, event)) {
2119 this._triggerKeyHandler(keyCombo, handlerName, event);
2120 if (event.defaultPrevented) {
2121 return;
2122 }
2123 }
2124 }
2125 },
2126 _triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) {
2127 var detail = Object.create(keyCombo);
2128 detail.keyboardEvent = keyboardEvent;
2129 var event = new CustomEvent(keyCombo.event, {
2130 detail: detail,
2131 cancelable: true
2132 });
2133 this[handlerName].call(this, event);
2134 if (event.defaultPrevented) {
2135 keyboardEvent.preventDefault();
2136 }
2137 }
2138 };
2139 })();
2140
2141 Polymer.IronControlState = {
2142 properties: {
2143 focused: {
2144 type: Boolean,
2145 value: false,
2146 notify: true,
2147 readOnly: true,
2148 reflectToAttribute: true
2149 },
2150 disabled: {
2151 type: Boolean,
2152 value: false,
2153 notify: true,
2154 observer: '_disabledChanged',
2155 reflectToAttribute: true
2156 },
2157 _oldTabIndex: {
2158 type: Number
2159 },
2160 _boundFocusBlurHandler: {
2161 type: Function,
2162 value: function() {
2163 return this._focusBlurHandler.bind(this);
2164 }
2165 }
2166 },
2167 observers: [ '_changedControlState(focused, disabled)' ],
2168 ready: function() {
2169 this.addEventListener('focus', this._boundFocusBlurHandler, true);
2170 this.addEventListener('blur', this._boundFocusBlurHandler, true);
2171 },
2172 _focusBlurHandler: function(event) {
2173 if (event.target === this) {
2174 this._setFocused(event.type === 'focus');
2175 } else if (!this.shadowRoot) {
2176 var target = Polymer.dom(event).localTarget;
2177 if (!this.isLightDescendant(target)) {
2178 this.fire(event.type, {
2179 sourceEvent: event
2180 }, {
2181 node: this,
2182 bubbles: event.bubbles,
2183 cancelable: event.cancelable
2184 });
2185 }
2186 }
2187 },
2188 _disabledChanged: function(disabled, old) {
2189 this.setAttribute('aria-disabled', disabled ? 'true' : 'false');
2190 this.style.pointerEvents = disabled ? 'none' : '';
2191 if (disabled) {
2192 this._oldTabIndex = this.tabIndex;
2193 this._setFocused(false);
2194 this.tabIndex = -1;
2195 this.blur();
2196 } else if (this._oldTabIndex !== undefined) {
2197 this.tabIndex = this._oldTabIndex;
2198 }
2199 },
2200 _changedControlState: function() {
2201 if (this._controlStateChanged) {
2202 this._controlStateChanged();
2203 }
2204 }
2205 };
2206
2207 Polymer.IronButtonStateImpl = {
2208 properties: {
2209 pressed: {
2210 type: Boolean,
2211 readOnly: true,
2212 value: false,
2213 reflectToAttribute: true,
2214 observer: '_pressedChanged'
2215 },
2216 toggles: {
2217 type: Boolean,
2218 value: false,
2219 reflectToAttribute: true
2220 },
2221 active: {
2222 type: Boolean,
2223 value: false,
2224 notify: true,
2225 reflectToAttribute: true
2226 },
2227 pointerDown: {
2228 type: Boolean,
2229 readOnly: true,
2230 value: false
2231 },
2232 receivedFocusFromKeyboard: {
2233 type: Boolean,
2234 readOnly: true
2235 },
2236 ariaActiveAttribute: {
2237 type: String,
2238 value: 'aria-pressed',
2239 observer: '_ariaActiveAttributeChanged'
2240 }
2241 },
2242 listeners: {
2243 down: '_downHandler',
2244 up: '_upHandler',
2245 tap: '_tapHandler'
2246 },
2247 observers: [ '_detectKeyboardFocus(focused)', '_activeChanged(active, ariaActi veAttribute)' ],
2248 keyBindings: {
2249 'enter:keydown': '_asyncClick',
2250 'space:keydown': '_spaceKeyDownHandler',
2251 'space:keyup': '_spaceKeyUpHandler'
2252 },
2253 _mouseEventRe: /^mouse/,
2254 _tapHandler: function() {
2255 if (this.toggles) {
2256 this._userActivate(!this.active);
2257 } else {
2258 this.active = false;
2259 }
2260 },
2261 _detectKeyboardFocus: function(focused) {
2262 this._setReceivedFocusFromKeyboard(!this.pointerDown && focused);
2263 },
2264 _userActivate: function(active) {
2265 if (this.active !== active) {
2266 this.active = active;
2267 this.fire('change');
2268 }
2269 },
2270 _downHandler: function(event) {
2271 this._setPointerDown(true);
2272 this._setPressed(true);
2273 this._setReceivedFocusFromKeyboard(false);
2274 },
2275 _upHandler: function() {
2276 this._setPointerDown(false);
2277 this._setPressed(false);
2278 },
2279 _spaceKeyDownHandler: function(event) {
2280 var keyboardEvent = event.detail.keyboardEvent;
2281 var target = Polymer.dom(keyboardEvent).localTarget;
2282 if (this.isLightDescendant(target)) return;
2283 keyboardEvent.preventDefault();
2284 keyboardEvent.stopImmediatePropagation();
2285 this._setPressed(true);
2286 },
2287 _spaceKeyUpHandler: function(event) {
2288 var keyboardEvent = event.detail.keyboardEvent;
2289 var target = Polymer.dom(keyboardEvent).localTarget;
2290 if (this.isLightDescendant(target)) return;
2291 if (this.pressed) {
2292 this._asyncClick();
2293 }
2294 this._setPressed(false);
2295 },
2296 _asyncClick: function() {
2297 this.async(function() {
2298 this.click();
2299 }, 1);
2300 },
2301 _pressedChanged: function(pressed) {
2302 this._changedButtonState();
2303 },
2304 _ariaActiveAttributeChanged: function(value, oldValue) {
2305 if (oldValue && oldValue != value && this.hasAttribute(oldValue)) {
2306 this.removeAttribute(oldValue);
2307 }
2308 },
2309 _activeChanged: function(active, ariaActiveAttribute) {
2310 if (this.toggles) {
2311 this.setAttribute(this.ariaActiveAttribute, active ? 'true' : 'false');
2312 } else {
2313 this.removeAttribute(this.ariaActiveAttribute);
2314 }
2315 this._changedButtonState();
2316 },
2317 _controlStateChanged: function() {
2318 if (this.disabled) {
2319 this._setPressed(false);
2320 } else {
2321 this._changedButtonState();
2322 }
2323 },
2324 _changedButtonState: function() {
2325 if (this._buttonStateChanged) {
2326 this._buttonStateChanged();
2327 }
2328 }
2329 };
2330
2331 Polymer.IronButtonState = [ Polymer.IronA11yKeysBehavior, Polymer.IronButtonStat eImpl ];
2332
2333 (function() {
2334 var Utility = {
2335 distance: function(x1, y1, x2, y2) {
2336 var xDelta = x1 - x2;
2337 var yDelta = y1 - y2;
2338 return Math.sqrt(xDelta * xDelta + yDelta * yDelta);
2339 },
2340 now: window.performance && window.performance.now ? window.performance.now.b ind(window.performance) : Date.now
2341 };
2342 function ElementMetrics(element) {
2343 this.element = element;
2344 this.width = this.boundingRect.width;
2345 this.height = this.boundingRect.height;
2346 this.size = Math.max(this.width, this.height);
2347 }
2348 ElementMetrics.prototype = {
2349 get boundingRect() {
2350 return this.element.getBoundingClientRect();
2351 },
2352 furthestCornerDistanceFrom: function(x, y) {
2353 var topLeft = Utility.distance(x, y, 0, 0);
2354 var topRight = Utility.distance(x, y, this.width, 0);
2355 var bottomLeft = Utility.distance(x, y, 0, this.height);
2356 var bottomRight = Utility.distance(x, y, this.width, this.height);
2357 return Math.max(topLeft, topRight, bottomLeft, bottomRight);
2358 }
2359 };
2360 function Ripple(element) {
2361 this.element = element;
2362 this.color = window.getComputedStyle(element).color;
2363 this.wave = document.createElement('div');
2364 this.waveContainer = document.createElement('div');
2365 this.wave.style.backgroundColor = this.color;
2366 this.wave.classList.add('wave');
2367 this.waveContainer.classList.add('wave-container');
2368 Polymer.dom(this.waveContainer).appendChild(this.wave);
2369 this.resetInteractionState();
2370 }
2371 Ripple.MAX_RADIUS = 300;
2372 Ripple.prototype = {
2373 get recenters() {
2374 return this.element.recenters;
2375 },
2376 get center() {
2377 return this.element.center;
2378 },
2379 get mouseDownElapsed() {
2380 var elapsed;
2381 if (!this.mouseDownStart) {
2382 return 0;
2383 }
2384 elapsed = Utility.now() - this.mouseDownStart;
2385 if (this.mouseUpStart) {
2386 elapsed -= this.mouseUpElapsed;
2387 }
2388 return elapsed;
2389 },
2390 get mouseUpElapsed() {
2391 return this.mouseUpStart ? Utility.now() - this.mouseUpStart : 0;
2392 },
2393 get mouseDownElapsedSeconds() {
2394 return this.mouseDownElapsed / 1e3;
2395 },
2396 get mouseUpElapsedSeconds() {
2397 return this.mouseUpElapsed / 1e3;
2398 },
2399 get mouseInteractionSeconds() {
2400 return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds;
2401 },
2402 get initialOpacity() {
2403 return this.element.initialOpacity;
2404 },
2405 get opacityDecayVelocity() {
2406 return this.element.opacityDecayVelocity;
2407 },
2408 get radius() {
2409 var width2 = this.containerMetrics.width * this.containerMetrics.width;
2410 var height2 = this.containerMetrics.height * this.containerMetrics.height;
2411 var waveRadius = Math.min(Math.sqrt(width2 + height2), Ripple.MAX_RADIUS) * 1.1 + 5;
2412 var duration = 1.1 - .2 * (waveRadius / Ripple.MAX_RADIUS);
2413 var timeNow = this.mouseInteractionSeconds / duration;
2414 var size = waveRadius * (1 - Math.pow(80, -timeNow));
2415 return Math.abs(size);
2416 },
2417 get opacity() {
2418 if (!this.mouseUpStart) {
2419 return this.initialOpacity;
2420 }
2421 return Math.max(0, this.initialOpacity - this.mouseUpElapsedSeconds * this .opacityDecayVelocity);
2422 },
2423 get outerOpacity() {
2424 var outerOpacity = this.mouseUpElapsedSeconds * .3;
2425 var waveOpacity = this.opacity;
2426 return Math.max(0, Math.min(outerOpacity, waveOpacity));
2427 },
2428 get isOpacityFullyDecayed() {
2429 return this.opacity < .01 && this.radius >= Math.min(this.maxRadius, Rippl e.MAX_RADIUS);
2430 },
2431 get isRestingAtMaxRadius() {
2432 return this.opacity >= this.initialOpacity && this.radius >= Math.min(this .maxRadius, Ripple.MAX_RADIUS);
2433 },
2434 get isAnimationComplete() {
2435 return this.mouseUpStart ? this.isOpacityFullyDecayed : this.isRestingAtMa xRadius;
2436 },
2437 get translationFraction() {
2438 return Math.min(1, this.radius / this.containerMetrics.size * 2 / Math.sqr t(2));
2439 },
2440 get xNow() {
2441 if (this.xEnd) {
2442 return this.xStart + this.translationFraction * (this.xEnd - this.xStart );
2443 }
2444 return this.xStart;
2445 },
2446 get yNow() {
2447 if (this.yEnd) {
2448 return this.yStart + this.translationFraction * (this.yEnd - this.yStart );
2449 }
2450 return this.yStart;
2451 },
2452 get isMouseDown() {
2453 return this.mouseDownStart && !this.mouseUpStart;
2454 },
2455 resetInteractionState: function() {
2456 this.maxRadius = 0;
2457 this.mouseDownStart = 0;
2458 this.mouseUpStart = 0;
2459 this.xStart = 0;
2460 this.yStart = 0;
2461 this.xEnd = 0;
2462 this.yEnd = 0;
2463 this.slideDistance = 0;
2464 this.containerMetrics = new ElementMetrics(this.element);
2465 },
2466 draw: function() {
2467 var scale;
2468 var translateString;
2469 var dx;
2470 var dy;
2471 this.wave.style.opacity = this.opacity;
2472 scale = this.radius / (this.containerMetrics.size / 2);
2473 dx = this.xNow - this.containerMetrics.width / 2;
2474 dy = this.yNow - this.containerMetrics.height / 2;
2475 this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' + dy + 'px)';
2476 this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy + ' px, 0)';
2477 this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')';
2478 this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)';
2479 },
2480 downAction: function(event) {
2481 var xCenter = this.containerMetrics.width / 2;
2482 var yCenter = this.containerMetrics.height / 2;
2483 this.resetInteractionState();
2484 this.mouseDownStart = Utility.now();
2485 if (this.center) {
2486 this.xStart = xCenter;
2487 this.yStart = yCenter;
2488 this.slideDistance = Utility.distance(this.xStart, this.yStart, this.xEn d, this.yEnd);
2489 } else {
2490 this.xStart = event ? event.detail.x - this.containerMetrics.boundingRec t.left : this.containerMetrics.width / 2;
2491 this.yStart = event ? event.detail.y - this.containerMetrics.boundingRec t.top : this.containerMetrics.height / 2;
2492 }
2493 if (this.recenters) {
2494 this.xEnd = xCenter;
2495 this.yEnd = yCenter;
2496 this.slideDistance = Utility.distance(this.xStart, this.yStart, this.xEn d, this.yEnd);
2497 }
2498 this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(this.xSt art, this.yStart);
2499 this.waveContainer.style.top = (this.containerMetrics.height - this.contai nerMetrics.size) / 2 + 'px';
2500 this.waveContainer.style.left = (this.containerMetrics.width - this.contai nerMetrics.size) / 2 + 'px';
2501 this.waveContainer.style.width = this.containerMetrics.size + 'px';
2502 this.waveContainer.style.height = this.containerMetrics.size + 'px';
2503 },
2504 upAction: function(event) {
2505 if (!this.isMouseDown) {
2506 return;
2507 }
2508 this.mouseUpStart = Utility.now();
2509 },
2510 remove: function() {
2511 Polymer.dom(this.waveContainer.parentNode).removeChild(this.waveContainer) ;
2512 }
2513 };
2514 Polymer({
2515 is: 'paper-ripple',
2516 behaviors: [ Polymer.IronA11yKeysBehavior ],
2517 properties: {
2518 initialOpacity: {
2519 type: Number,
2520 value: .25
2521 },
2522 opacityDecayVelocity: {
2523 type: Number,
2524 value: .8
2525 },
2526 recenters: {
2527 type: Boolean,
2528 value: false
2529 },
2530 center: {
2531 type: Boolean,
2532 value: false
2533 },
2534 ripples: {
2535 type: Array,
2536 value: function() {
2537 return [];
2538 }
2539 },
2540 animating: {
2541 type: Boolean,
2542 readOnly: true,
2543 reflectToAttribute: true,
2544 value: false
2545 },
2546 holdDown: {
2547 type: Boolean,
2548 value: false,
2549 observer: '_holdDownChanged'
2550 },
2551 noink: {
2552 type: Boolean,
2553 value: false
2554 },
2555 _animating: {
2556 type: Boolean
2557 },
2558 _boundAnimate: {
2559 type: Function,
2560 value: function() {
2561 return this.animate.bind(this);
2562 }
2563 }
2564 },
2565 get target() {
2566 return this.keyEventTarget;
2567 },
2568 keyBindings: {
2569 'enter:keydown': '_onEnterKeydown',
2570 'space:keydown': '_onSpaceKeydown',
2571 'space:keyup': '_onSpaceKeyup'
2572 },
2573 attached: function() {
2574 if (this.parentNode.nodeType == 11) {
2575 this.keyEventTarget = Polymer.dom(this).getOwnerRoot().host;
2576 } else {
2577 this.keyEventTarget = this.parentNode;
2578 }
2579 var keyEventTarget = this.keyEventTarget;
2580 this.listen(keyEventTarget, 'up', 'uiUpAction');
2581 this.listen(keyEventTarget, 'down', 'uiDownAction');
2582 },
2583 detached: function() {
2584 this.unlisten(this.keyEventTarget, 'up', 'uiUpAction');
2585 this.unlisten(this.keyEventTarget, 'down', 'uiDownAction');
2586 this.keyEventTarget = null;
2587 },
2588 get shouldKeepAnimating() {
2589 for (var index = 0; index < this.ripples.length; ++index) {
2590 if (!this.ripples[index].isAnimationComplete) {
2591 return true;
2592 }
2593 }
2594 return false;
2595 },
2596 simulatedRipple: function() {
2597 this.downAction(null);
2598 this.async(function() {
2599 this.upAction();
2600 }, 1);
2601 },
2602 uiDownAction: function(event) {
2603 if (!this.noink) {
2604 this.downAction(event);
2605 }
2606 },
2607 downAction: function(event) {
2608 if (this.holdDown && this.ripples.length > 0) {
2609 return;
2610 }
2611 var ripple = this.addRipple();
2612 ripple.downAction(event);
2613 if (!this._animating) {
2614 this._animating = true;
2615 this.animate();
2616 }
2617 },
2618 uiUpAction: function(event) {
2619 if (!this.noink) {
2620 this.upAction(event);
2621 }
2622 },
2623 upAction: function(event) {
2624 if (this.holdDown) {
2625 return;
2626 }
2627 this.ripples.forEach(function(ripple) {
2628 ripple.upAction(event);
2629 });
2630 this._animating = true;
2631 this.animate();
2632 },
2633 onAnimationComplete: function() {
2634 this._animating = false;
2635 this.$.background.style.backgroundColor = null;
2636 this.fire('transitionend');
2637 },
2638 addRipple: function() {
2639 var ripple = new Ripple(this);
2640 Polymer.dom(this.$.waves).appendChild(ripple.waveContainer);
2641 this.$.background.style.backgroundColor = ripple.color;
2642 this.ripples.push(ripple);
2643 this._setAnimating(true);
2644 return ripple;
2645 },
2646 removeRipple: function(ripple) {
2647 var rippleIndex = this.ripples.indexOf(ripple);
2648 if (rippleIndex < 0) {
2649 return;
2650 }
2651 this.ripples.splice(rippleIndex, 1);
2652 ripple.remove();
2653 if (!this.ripples.length) {
2654 this._setAnimating(false);
2655 }
2656 },
2657 animate: function() {
2658 if (!this._animating) {
2659 return;
2660 }
2661 var index;
2662 var ripple;
2663 for (index = 0; index < this.ripples.length; ++index) {
2664 ripple = this.ripples[index];
2665 ripple.draw();
2666 this.$.background.style.opacity = ripple.outerOpacity;
2667 if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) {
2668 this.removeRipple(ripple);
2669 }
2670 }
2671 if (!this.shouldKeepAnimating && this.ripples.length === 0) {
2672 this.onAnimationComplete();
2673 } else {
2674 window.requestAnimationFrame(this._boundAnimate);
2675 }
2676 },
2677 _onEnterKeydown: function() {
2678 this.uiDownAction();
2679 this.async(this.uiUpAction, 1);
2680 },
2681 _onSpaceKeydown: function() {
2682 this.uiDownAction();
2683 },
2684 _onSpaceKeyup: function() {
2685 this.uiUpAction();
2686 },
2687 _holdDownChanged: function(newVal, oldVal) {
2688 if (oldVal === undefined) {
2689 return;
2690 }
2691 if (newVal) {
2692 this.downAction();
2693 } else {
2694 this.upAction();
2695 }
2696 }
2697 });
2698 })();
2699
2700 Polymer.PaperRippleBehavior = {
2701 properties: {
2702 noink: {
2703 type: Boolean,
2704 observer: '_noinkChanged'
2705 },
2706 _rippleContainer: {
2707 type: Object
2708 }
2709 },
2710 _buttonStateChanged: function() {
2711 if (this.focused) {
2712 this.ensureRipple();
2713 }
2714 },
2715 _downHandler: function(event) {
2716 Polymer.IronButtonStateImpl._downHandler.call(this, event);
2717 if (this.pressed) {
2718 this.ensureRipple(event);
2719 }
2720 },
2721 ensureRipple: function(optTriggeringEvent) {
2722 if (!this.hasRipple()) {
2723 this._ripple = this._createRipple();
2724 this._ripple.noink = this.noink;
2725 var rippleContainer = this._rippleContainer || this.root;
2726 if (rippleContainer) {
2727 Polymer.dom(rippleContainer).appendChild(this._ripple);
2728 }
2729 if (optTriggeringEvent) {
2730 var domContainer = Polymer.dom(this._rippleContainer || this);
2731 var target = Polymer.dom(optTriggeringEvent).rootTarget;
2732 if (domContainer.deepContains(target)) {
2733 this._ripple.uiDownAction(optTriggeringEvent);
2734 }
2735 }
2736 }
2737 },
2738 getRipple: function() {
2739 this.ensureRipple();
2740 return this._ripple;
2741 },
2742 hasRipple: function() {
2743 return Boolean(this._ripple);
2744 },
2745 _createRipple: function() {
2746 return document.createElement('paper-ripple');
2747 },
2748 _noinkChanged: function(noink) {
2749 if (this.hasRipple()) {
2750 this._ripple.noink = noink;
2751 }
2752 }
2753 };
2754
2755 Polymer.PaperButtonBehaviorImpl = {
2756 properties: {
2757 elevation: {
2758 type: Number,
2759 reflectToAttribute: true,
2760 readOnly: true
2761 }
2762 },
2763 observers: [ '_calculateElevation(focused, disabled, active, pressed, received FocusFromKeyboard)', '_computeKeyboardClass(receivedFocusFromKeyboard)' ],
2764 hostAttributes: {
2765 role: 'button',
2766 tabindex: '0',
2767 animated: true
2768 },
2769 _calculateElevation: function() {
2770 var e = 1;
2771 if (this.disabled) {
2772 e = 0;
2773 } else if (this.active || this.pressed) {
2774 e = 4;
2775 } else if (this.receivedFocusFromKeyboard) {
2776 e = 3;
2777 }
2778 this._setElevation(e);
2779 },
2780 _computeKeyboardClass: function(receivedFocusFromKeyboard) {
2781 this.toggleClass('keyboard-focus', receivedFocusFromKeyboard);
2782 },
2783 _spaceKeyDownHandler: function(event) {
2784 Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event);
2785 if (this.hasRipple() && this.getRipple().ripples.length < 1) {
2786 this._ripple.uiDownAction();
2787 }
2788 },
2789 _spaceKeyUpHandler: function(event) {
2790 Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event);
2791 if (this.hasRipple()) {
2792 this._ripple.uiUpAction();
2793 }
2794 }
2795 };
2796
2797 Polymer.PaperButtonBehavior = [ Polymer.IronButtonState, Polymer.IronControlStat e, Polymer.PaperRippleBehavior, Polymer.PaperButtonBehaviorImpl ];
2798
2799 Polymer({
2800 is: 'paper-button',
2801 behaviors: [ Polymer.PaperButtonBehavior ],
2802 properties: {
2803 raised: {
2804 type: Boolean,
2805 reflectToAttribute: true,
2806 value: false,
2807 observer: '_calculateElevation'
2808 }
2809 },
2810 _calculateElevation: function() {
2811 if (!this.raised) {
2812 this._setElevation(0);
2813 } else {
2814 Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this);
2815 }
2816 }
2817 });
2818
2819 (function() {
2820 var metaDatas = {};
2821 var metaArrays = {};
2822 var singleton = null;
2823 Polymer.IronMeta = Polymer({
2824 is: 'iron-meta',
2825 properties: {
2826 type: {
2827 type: String,
2828 value: 'default',
2829 observer: '_typeChanged'
2830 },
2831 key: {
2832 type: String,
2833 observer: '_keyChanged'
2834 },
2835 value: {
2836 type: Object,
2837 notify: true,
2838 observer: '_valueChanged'
2839 },
2840 self: {
2841 type: Boolean,
2842 observer: '_selfChanged'
2843 },
2844 list: {
2845 type: Array,
2846 notify: true
2847 }
2848 },
2849 hostAttributes: {
2850 hidden: true
2851 },
2852 factoryImpl: function(config) {
2853 if (config) {
2854 for (var n in config) {
2855 switch (n) {
2856 case 'type':
2857 case 'key':
2858 case 'value':
2859 this[n] = config[n];
2860 break;
2861 }
2862 }
2863 }
2864 },
2865 created: function() {
2866 this._metaDatas = metaDatas;
2867 this._metaArrays = metaArrays;
2868 },
2869 _keyChanged: function(key, old) {
2870 this._resetRegistration(old);
2871 },
2872 _valueChanged: function(value) {
2873 this._resetRegistration(this.key);
2874 },
2875 _selfChanged: function(self) {
2876 if (self) {
2877 this.value = this;
2878 }
2879 },
2880 _typeChanged: function(type) {
2881 this._unregisterKey(this.key);
2882 if (!metaDatas[type]) {
2883 metaDatas[type] = {};
2884 }
2885 this._metaData = metaDatas[type];
2886 if (!metaArrays[type]) {
2887 metaArrays[type] = [];
2888 }
2889 this.list = metaArrays[type];
2890 this._registerKeyValue(this.key, this.value);
2891 },
2892 byKey: function(key) {
2893 return this._metaData && this._metaData[key];
2894 },
2895 _resetRegistration: function(oldKey) {
2896 this._unregisterKey(oldKey);
2897 this._registerKeyValue(this.key, this.value);
2898 },
2899 _unregisterKey: function(key) {
2900 this._unregister(key, this._metaData, this.list);
2901 },
2902 _registerKeyValue: function(key, value) {
2903 this._register(key, value, this._metaData, this.list);
2904 },
2905 _register: function(key, value, data, list) {
2906 if (key && data && value !== undefined) {
2907 data[key] = value;
2908 list.push(value);
2909 }
2910 },
2911 _unregister: function(key, data, list) {
2912 if (key && data) {
2913 if (key in data) {
2914 var value = data[key];
2915 delete data[key];
2916 this.arrayDelete(list, value);
2917 }
2918 }
2919 }
2920 });
2921 Polymer.IronMeta.getIronMeta = function getIronMeta() {
2922 if (singleton === null) {
2923 singleton = new Polymer.IronMeta();
2924 }
2925 return singleton;
2926 };
2927 Polymer.IronMetaQuery = Polymer({
2928 is: 'iron-meta-query',
2929 properties: {
2930 type: {
2931 type: String,
2932 value: 'default',
2933 observer: '_typeChanged'
2934 },
2935 key: {
2936 type: String,
2937 observer: '_keyChanged'
2938 },
2939 value: {
2940 type: Object,
2941 notify: true,
2942 readOnly: true
2943 },
2944 list: {
2945 type: Array,
2946 notify: true
2947 }
2948 },
2949 factoryImpl: function(config) {
2950 if (config) {
2951 for (var n in config) {
2952 switch (n) {
2953 case 'type':
2954 case 'key':
2955 this[n] = config[n];
2956 break;
2957 }
2958 }
2959 }
2960 },
2961 created: function() {
2962 this._metaDatas = metaDatas;
2963 this._metaArrays = metaArrays;
2964 },
2965 _keyChanged: function(key) {
2966 this._setValue(this._metaData && this._metaData[key]);
2967 },
2968 _typeChanged: function(type) {
2969 this._metaData = metaDatas[type];
2970 this.list = metaArrays[type];
2971 if (this.key) {
2972 this._keyChanged(this.key);
2973 }
2974 },
2975 byKey: function(key) {
2976 return this._metaData && this._metaData[key];
2977 }
2978 });
2979 })();
2980
2981 Polymer({
2982 is: 'iron-icon',
2983 properties: {
2984 icon: {
2985 type: String,
2986 observer: '_iconChanged'
2987 },
2988 theme: {
2989 type: String,
2990 observer: '_updateIcon'
2991 },
2992 src: {
2993 type: String,
2994 observer: '_srcChanged'
2995 },
2996 _meta: {
2997 value: Polymer.Base.create('iron-meta', {
2998 type: 'iconset'
2999 }),
3000 observer: '_updateIcon'
3001 }
3002 },
3003 _DEFAULT_ICONSET: 'icons',
3004 _iconChanged: function(icon) {
3005 var parts = (icon || '').split(':');
3006 this._iconName = parts.pop();
3007 this._iconsetName = parts.pop() || this._DEFAULT_ICONSET;
3008 this._updateIcon();
3009 },
3010 _srcChanged: function(src) {
3011 this._updateIcon();
3012 },
3013 _usesIconset: function() {
3014 return this.icon || !this.src;
3015 },
3016 _updateIcon: function() {
3017 if (this._usesIconset()) {
3018 if (this._img && this._img.parentNode) {
3019 Polymer.dom(this.root).removeChild(this._img);
3020 }
3021 if (this._iconName === "") {
3022 if (this._iconset) {
3023 this._iconset.removeIcon(this);
3024 }
3025 } else if (this._iconsetName && this._meta) {
3026 this._iconset = this._meta.byKey(this._iconsetName);
3027 if (this._iconset) {
3028 this._iconset.applyIcon(this, this._iconName, this.theme);
3029 this.unlisten(window, 'iron-iconset-added', '_updateIcon');
3030 } else {
3031 this.listen(window, 'iron-iconset-added', '_updateIcon');
3032 }
3033 }
3034 } else {
3035 if (this._iconset) {
3036 this._iconset.removeIcon(this);
3037 }
3038 if (!this._img) {
3039 this._img = document.createElement('img');
3040 this._img.style.width = '100%';
3041 this._img.style.height = '100%';
3042 this._img.draggable = false;
3043 }
3044 this._img.src = this.src;
3045 Polymer.dom(this.root).appendChild(this._img);
3046 }
3047 }
3048 });
3049
3050 Polymer.PaperInkyFocusBehaviorImpl = {
3051 observers: [ '_focusedChanged(receivedFocusFromKeyboard)' ],
3052 _focusedChanged: function(receivedFocusFromKeyboard) {
3053 if (receivedFocusFromKeyboard) {
3054 this.ensureRipple();
3055 }
3056 if (this.hasRipple()) {
3057 this._ripple.holdDown = receivedFocusFromKeyboard;
3058 }
3059 },
3060 _createRipple: function() {
3061 var ripple = Polymer.PaperRippleBehavior._createRipple();
3062 ripple.id = 'ink';
3063 ripple.setAttribute('center', '');
3064 ripple.classList.add('circle');
3065 return ripple;
3066 }
3067 };
3068
3069 Polymer.PaperInkyFocusBehavior = [ Polymer.IronButtonState, Polymer.IronControlS tate, Polymer.PaperRippleBehavior, Polymer.PaperInkyFocusBehaviorImpl ];
3070
3071 Polymer({
3072 is: 'paper-icon-button',
3073 hostAttributes: {
3074 role: 'button',
3075 tabindex: '0'
3076 },
3077 behaviors: [ Polymer.PaperInkyFocusBehavior ],
3078 properties: {
3079 src: {
3080 type: String
3081 },
3082 icon: {
3083 type: String
3084 },
3085 alt: {
3086 type: String,
3087 observer: "_altChanged"
3088 }
3089 },
3090 _altChanged: function(newValue, oldValue) {
3091 var label = this.getAttribute('aria-label');
3092 if (!label || oldValue == label) {
3093 this.setAttribute('aria-label', newValue);
3094 }
3095 }
3096 });
3097
3098 Polymer({
3099 is: 'paper-tab',
3100 behaviors: [ Polymer.IronControlState, Polymer.IronButtonState, Polymer.PaperR ippleBehavior ],
3101 properties: {
3102 link: {
3103 type: Boolean,
3104 value: false,
3105 reflectToAttribute: true
3106 }
3107 },
3108 hostAttributes: {
3109 role: 'tab'
3110 },
3111 listeners: {
3112 down: '_updateNoink',
3113 tap: '_onTap'
3114 },
3115 attached: function() {
3116 this._updateNoink();
3117 },
3118 get _parentNoink() {
3119 var parent = Polymer.dom(this).parentNode;
3120 return !!parent && !!parent.noink;
3121 },
3122 _updateNoink: function() {
3123 this.noink = !!this.noink || !!this._parentNoink;
3124 },
3125 _onTap: function(event) {
3126 if (this.link) {
3127 var anchor = this.queryEffectiveChildren('a');
3128 if (!anchor) {
3129 return;
3130 }
3131 if (event.target === anchor) {
3132 return;
3133 }
3134 anchor.click();
3135 }
3136 }
3137 });
3138
3139 Polymer.IronMultiSelectableBehaviorImpl = {
3140 properties: {
3141 multi: {
3142 type: Boolean,
3143 value: false,
3144 observer: 'multiChanged'
3145 },
3146 selectedValues: {
3147 type: Array,
3148 notify: true
3149 },
3150 selectedItems: {
3151 type: Array,
3152 readOnly: true,
3153 notify: true
3154 }
3155 },
3156 observers: [ '_updateSelected(selectedValues.splices)' ],
3157 select: function(value) {
3158 if (this.multi) {
3159 if (this.selectedValues) {
3160 this._toggleSelected(value);
3161 } else {
3162 this.selectedValues = [ value ];
3163 }
3164 } else {
3165 this.selected = value;
3166 }
3167 },
3168 multiChanged: function(multi) {
3169 this._selection.multi = multi;
3170 },
3171 get _shouldUpdateSelection() {
3172 return this.selected != null || this.selectedValues != null && this.selected Values.length;
3173 },
3174 _updateAttrForSelected: function() {
3175 if (!this.multi) {
3176 Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this);
3177 } else if (this._shouldUpdateSelection) {
3178 this.selectedValues = this.selectedItems.map(function(selectedItem) {
3179 return this._indexToValue(this.indexOf(selectedItem));
3180 }, this).filter(function(unfilteredValue) {
3181 return unfilteredValue != null;
3182 }, this);
3183 }
3184 },
3185 _updateSelected: function() {
3186 if (this.multi) {
3187 this._selectMulti(this.selectedValues);
3188 } else {
3189 this._selectSelected(this.selected);
3190 }
3191 },
3192 _selectMulti: function(values) {
3193 if (values) {
3194 var selectedItems = this._valuesToItems(values);
3195 this._selection.clear(selectedItems);
3196 for (var i = 0; i < selectedItems.length; i++) {
3197 this._selection.setItemSelected(selectedItems[i], true);
3198 }
3199 if (this.fallbackSelection && this.items.length && !this._selection.get(). length) {
3200 var fallback = this._valueToItem(this.fallbackSelection);
3201 if (fallback) {
3202 this.selectedValues = [ this.fallbackSelection ];
3203 }
3204 }
3205 } else {
3206 this._selection.clear();
3207 }
3208 },
3209 _selectionChange: function() {
3210 var s = this._selection.get();
3211 if (this.multi) {
3212 this._setSelectedItems(s);
3213 } else {
3214 this._setSelectedItems([ s ]);
3215 this._setSelectedItem(s);
3216 }
3217 },
3218 _toggleSelected: function(value) {
3219 var i = this.selectedValues.indexOf(value);
3220 var unselected = i < 0;
3221 if (unselected) {
3222 this.push('selectedValues', value);
3223 } else {
3224 this.splice('selectedValues', i, 1);
3225 }
3226 },
3227 _valuesToItems: function(values) {
3228 return values == null ? null : values.map(function(value) {
3229 return this._valueToItem(value);
3230 }, this);
3231 }
3232 };
3233
3234 Polymer.IronMultiSelectableBehavior = [ Polymer.IronSelectableBehavior, Polymer. IronMultiSelectableBehaviorImpl ];
3235
3236 Polymer.IronMenuBehaviorImpl = {
3237 properties: {
3238 focusedItem: {
3239 observer: '_focusedItemChanged',
3240 readOnly: true,
3241 type: Object
3242 },
3243 attrForItemTitle: {
3244 type: String
3245 }
3246 },
3247 hostAttributes: {
3248 role: 'menu',
3249 tabindex: '0'
3250 },
3251 observers: [ '_updateMultiselectable(multi)' ],
3252 listeners: {
3253 focus: '_onFocus',
3254 keydown: '_onKeydown',
3255 'iron-items-changed': '_onIronItemsChanged'
3256 },
3257 keyBindings: {
3258 up: '_onUpKey',
3259 down: '_onDownKey',
3260 esc: '_onEscKey',
3261 'shift+tab:keydown': '_onShiftTabDown'
3262 },
3263 attached: function() {
3264 this._resetTabindices();
3265 },
3266 select: function(value) {
3267 if (this._defaultFocusAsync) {
3268 this.cancelAsync(this._defaultFocusAsync);
3269 this._defaultFocusAsync = null;
3270 }
3271 var item = this._valueToItem(value);
3272 if (item && item.hasAttribute('disabled')) return;
3273 this._setFocusedItem(item);
3274 Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments);
3275 },
3276 _resetTabindices: function() {
3277 var selectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem;
3278 this.items.forEach(function(item) {
3279 item.setAttribute('tabindex', item === selectedItem ? '0' : '-1');
3280 }, this);
3281 },
3282 _updateMultiselectable: function(multi) {
3283 if (multi) {
3284 this.setAttribute('aria-multiselectable', 'true');
3285 } else {
3286 this.removeAttribute('aria-multiselectable');
3287 }
3288 },
3289 _focusWithKeyboardEvent: function(event) {
3290 for (var i = 0, item; item = this.items[i]; i++) {
3291 var attr = this.attrForItemTitle || 'textContent';
3292 var title = item[attr] || item.getAttribute(attr);
3293 if (!item.hasAttribute('disabled') && title && title.trim().charAt(0).toLo werCase() === String.fromCharCode(event.keyCode).toLowerCase()) {
3294 this._setFocusedItem(item);
3295 break;
3296 }
3297 }
3298 },
3299 _focusPrevious: function() {
3300 var length = this.items.length;
3301 var curFocusIndex = Number(this.indexOf(this.focusedItem));
3302 for (var i = 1; i < length + 1; i++) {
3303 var item = this.items[(curFocusIndex - i + length) % length];
3304 if (!item.hasAttribute('disabled')) {
3305 this._setFocusedItem(item);
3306 return;
3307 }
3308 }
3309 },
3310 _focusNext: function() {
3311 var length = this.items.length;
3312 var curFocusIndex = Number(this.indexOf(this.focusedItem));
3313 for (var i = 1; i < length + 1; i++) {
3314 var item = this.items[(curFocusIndex + i) % length];
3315 if (!item.hasAttribute('disabled')) {
3316 this._setFocusedItem(item);
3317 return;
3318 }
3319 }
3320 },
3321 _applySelection: function(item, isSelected) {
3322 if (isSelected) {
3323 item.setAttribute('aria-selected', 'true');
3324 } else {
3325 item.removeAttribute('aria-selected');
3326 }
3327 Polymer.IronSelectableBehavior._applySelection.apply(this, arguments);
3328 },
3329 _focusedItemChanged: function(focusedItem, old) {
3330 old && old.setAttribute('tabindex', '-1');
3331 if (focusedItem) {
3332 focusedItem.setAttribute('tabindex', '0');
3333 focusedItem.focus();
3334 }
3335 },
3336 _onIronItemsChanged: function(event) {
3337 if (event.detail.addedNodes.length) {
3338 this._resetTabindices();
3339 }
3340 },
3341 _onShiftTabDown: function(event) {
3342 var oldTabIndex = this.getAttribute('tabindex');
3343 Polymer.IronMenuBehaviorImpl._shiftTabPressed = true;
3344 this._setFocusedItem(null);
3345 this.setAttribute('tabindex', '-1');
3346 this.async(function() {
3347 this.setAttribute('tabindex', oldTabIndex);
3348 Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
3349 }, 1);
3350 },
3351 _onFocus: function(event) {
3352 if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) {
3353 return;
3354 }
3355 var rootTarget = Polymer.dom(event).rootTarget;
3356 if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !th is.isLightDescendant(rootTarget)) {
3357 return;
3358 }
3359 this._defaultFocusAsync = this.async(function() {
3360 var selectedItem = this.multi ? this.selectedItems && this.selectedItems[0 ] : this.selectedItem;
3361 this._setFocusedItem(null);
3362 if (selectedItem) {
3363 this._setFocusedItem(selectedItem);
3364 } else if (this.items[0]) {
3365 this._focusNext();
3366 }
3367 });
3368 },
3369 _onUpKey: function(event) {
3370 this._focusPrevious();
3371 event.detail.keyboardEvent.preventDefault();
3372 },
3373 _onDownKey: function(event) {
3374 this._focusNext();
3375 event.detail.keyboardEvent.preventDefault();
3376 },
3377 _onEscKey: function(event) {
3378 this.focusedItem.blur();
3379 },
3380 _onKeydown: function(event) {
3381 if (!this.keyboardEventMatchesKeys(event, 'up down esc')) {
3382 this._focusWithKeyboardEvent(event);
3383 }
3384 event.stopPropagation();
3385 },
3386 _activateHandler: function(event) {
3387 Polymer.IronSelectableBehavior._activateHandler.call(this, event);
3388 event.stopPropagation();
3389 }
3390 };
3391
3392 Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
3393
3394 Polymer.IronMenuBehavior = [ Polymer.IronMultiSelectableBehavior, Polymer.IronA1 1yKeysBehavior, Polymer.IronMenuBehaviorImpl ];
3395
3396 Polymer.IronMenubarBehaviorImpl = {
3397 hostAttributes: {
3398 role: 'menubar'
3399 },
3400 keyBindings: {
3401 left: '_onLeftKey',
3402 right: '_onRightKey'
3403 },
3404 _onUpKey: function(event) {
3405 this.focusedItem.click();
3406 event.detail.keyboardEvent.preventDefault();
3407 },
3408 _onDownKey: function(event) {
3409 this.focusedItem.click();
3410 event.detail.keyboardEvent.preventDefault();
3411 },
3412 get _isRTL() {
3413 return window.getComputedStyle(this)['direction'] === 'rtl';
3414 },
3415 _onLeftKey: function(event) {
3416 if (this._isRTL) {
3417 this._focusNext();
3418 } else {
3419 this._focusPrevious();
3420 }
3421 event.detail.keyboardEvent.preventDefault();
3422 },
3423 _onRightKey: function(event) {
3424 if (this._isRTL) {
3425 this._focusPrevious();
3426 } else {
3427 this._focusNext();
3428 }
3429 event.detail.keyboardEvent.preventDefault();
3430 },
3431 _onKeydown: function(event) {
3432 if (this.keyboardEventMatchesKeys(event, 'up down left right esc')) {
3433 return;
3434 }
3435 this._focusWithKeyboardEvent(event);
3436 }
3437 };
3438
3439 Polymer.IronMenubarBehavior = [ Polymer.IronMenuBehavior, Polymer.IronMenubarBeh aviorImpl ];
3440
3441 Polymer({
3442 is: 'iron-iconset-svg',
3443 properties: {
3444 name: {
3445 type: String,
3446 observer: '_nameChanged'
3447 },
3448 size: {
3449 type: Number,
3450 value: 24
3451 }
3452 },
3453 attached: function() {
3454 this.style.display = 'none';
3455 },
3456 getIconNames: function() {
3457 this._icons = this._createIconMap();
3458 return Object.keys(this._icons).map(function(n) {
3459 return this.name + ':' + n;
3460 }, this);
3461 },
3462 applyIcon: function(element, iconName) {
3463 element = element.root || element;
3464 this.removeIcon(element);
3465 var svg = this._cloneIcon(iconName);
3466 if (svg) {
3467 var pde = Polymer.dom(element);
3468 pde.insertBefore(svg, pde.childNodes[0]);
3469 return element._svgIcon = svg;
3470 }
3471 return null;
3472 },
3473 removeIcon: function(element) {
3474 if (element._svgIcon) {
3475 Polymer.dom(element).removeChild(element._svgIcon);
3476 element._svgIcon = null;
3477 }
3478 },
3479 _nameChanged: function() {
3480 new Polymer.IronMeta({
3481 type: 'iconset',
3482 key: this.name,
3483 value: this
3484 });
3485 this.async(function() {
3486 this.fire('iron-iconset-added', this, {
3487 node: window
3488 });
3489 });
3490 },
3491 _createIconMap: function() {
3492 var icons = Object.create(null);
3493 Polymer.dom(this).querySelectorAll('[id]').forEach(function(icon) {
3494 icons[icon.id] = icon;
3495 });
3496 return icons;
3497 },
3498 _cloneIcon: function(id) {
3499 this._icons = this._icons || this._createIconMap();
3500 return this._prepareSvgClone(this._icons[id], this.size);
3501 },
3502 _prepareSvgClone: function(sourceSvg, size) {
3503 if (sourceSvg) {
3504 var content = sourceSvg.cloneNode(true), svg = document.createElementNS('h ttp://www.w3.org/2000/svg', 'svg'), viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size;
3505 svg.setAttribute('viewBox', viewBox);
3506 svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
3507 svg.style.cssText = 'pointer-events: none; display: block; width: 100%; he ight: 100%;';
3508 svg.appendChild(content).removeAttribute('id');
3509 return svg;
3510 }
3511 return null;
3512 }
3513 });
3514
3515 Polymer({
3516 is: 'paper-tabs',
3517 behaviors: [ Polymer.IronResizableBehavior, Polymer.IronMenubarBehavior ],
3518 properties: {
3519 noink: {
3520 type: Boolean,
3521 value: false,
3522 observer: '_noinkChanged'
3523 },
3524 noBar: {
3525 type: Boolean,
3526 value: false
3527 },
3528 noSlide: {
3529 type: Boolean,
3530 value: false
3531 },
3532 scrollable: {
3533 type: Boolean,
3534 value: false
3535 },
3536 fitContainer: {
3537 type: Boolean,
3538 value: false
3539 },
3540 disableDrag: {
3541 type: Boolean,
3542 value: false
3543 },
3544 hideScrollButtons: {
3545 type: Boolean,
3546 value: false
3547 },
3548 alignBottom: {
3549 type: Boolean,
3550 value: false
3551 },
3552 selectable: {
3553 type: String,
3554 value: 'paper-tab'
3555 },
3556 autoselect: {
3557 type: Boolean,
3558 value: false
3559 },
3560 autoselectDelay: {
3561 type: Number,
3562 value: 0
3563 },
3564 _step: {
3565 type: Number,
3566 value: 10
3567 },
3568 _holdDelay: {
3569 type: Number,
3570 value: 1
3571 },
3572 _leftHidden: {
3573 type: Boolean,
3574 value: false
3575 },
3576 _rightHidden: {
3577 type: Boolean,
3578 value: false
3579 },
3580 _previousTab: {
3581 type: Object
3582 }
3583 },
3584 hostAttributes: {
3585 role: 'tablist'
3586 },
3587 listeners: {
3588 'iron-resize': '_onTabSizingChanged',
3589 'iron-items-changed': '_onTabSizingChanged',
3590 'iron-select': '_onIronSelect',
3591 'iron-deselect': '_onIronDeselect'
3592 },
3593 keyBindings: {
3594 'left:keyup right:keyup': '_onArrowKeyup'
3595 },
3596 created: function() {
3597 this._holdJob = null;
3598 this._pendingActivationItem = undefined;
3599 this._pendingActivationTimeout = undefined;
3600 this._bindDelayedActivationHandler = this._delayedActivationHandler.bind(thi s);
3601 this.addEventListener('blur', this._onBlurCapture.bind(this), true);
3602 },
3603 ready: function() {
3604 this.setScrollDirection('y', this.$.tabsContainer);
3605 },
3606 detached: function() {
3607 this._cancelPendingActivation();
3608 },
3609 _noinkChanged: function(noink) {
3610 var childTabs = Polymer.dom(this).querySelectorAll('paper-tab');
3611 childTabs.forEach(noink ? this._setNoinkAttribute : this._removeNoinkAttribu te);
3612 },
3613 _setNoinkAttribute: function(element) {
3614 element.setAttribute('noink', '');
3615 },
3616 _removeNoinkAttribute: function(element) {
3617 element.removeAttribute('noink');
3618 },
3619 _computeScrollButtonClass: function(hideThisButton, scrollable, hideScrollButt ons) {
3620 if (!scrollable || hideScrollButtons) {
3621 return 'hidden';
3622 }
3623 if (hideThisButton) {
3624 return 'not-visible';
3625 }
3626 return '';
3627 },
3628 _computeTabsContentClass: function(scrollable, fitContainer) {
3629 return scrollable ? 'scrollable' + (fitContainer ? ' fit-container' : '') : ' fit-container';
3630 },
3631 _computeSelectionBarClass: function(noBar, alignBottom) {
3632 if (noBar) {
3633 return 'hidden';
3634 } else if (alignBottom) {
3635 return 'align-bottom';
3636 }
3637 return '';
3638 },
3639 _onTabSizingChanged: function() {
3640 this.debounce('_onTabSizingChanged', function() {
3641 this._scroll();
3642 this._tabChanged(this.selectedItem);
3643 }, 10);
3644 },
3645 _onIronSelect: function(event) {
3646 this._tabChanged(event.detail.item, this._previousTab);
3647 this._previousTab = event.detail.item;
3648 this.cancelDebouncer('tab-changed');
3649 },
3650 _onIronDeselect: function(event) {
3651 this.debounce('tab-changed', function() {
3652 this._tabChanged(null, this._previousTab);
3653 this._previousTab = null;
3654 }, 1);
3655 },
3656 _activateHandler: function() {
3657 this._cancelPendingActivation();
3658 Polymer.IronMenuBehaviorImpl._activateHandler.apply(this, arguments);
3659 },
3660 _scheduleActivation: function(item, delay) {
3661 this._pendingActivationItem = item;
3662 this._pendingActivationTimeout = this.async(this._bindDelayedActivationHandl er, delay);
3663 },
3664 _delayedActivationHandler: function() {
3665 var item = this._pendingActivationItem;
3666 this._pendingActivationItem = undefined;
3667 this._pendingActivationTimeout = undefined;
3668 item.fire(this.activateEvent, null, {
3669 bubbles: true,
3670 cancelable: true
3671 });
3672 },
3673 _cancelPendingActivation: function() {
3674 if (this._pendingActivationTimeout !== undefined) {
3675 this.cancelAsync(this._pendingActivationTimeout);
3676 this._pendingActivationItem = undefined;
3677 this._pendingActivationTimeout = undefined;
3678 }
3679 },
3680 _onArrowKeyup: function(event) {
3681 if (this.autoselect) {
3682 this._scheduleActivation(this.focusedItem, this.autoselectDelay);
3683 }
3684 },
3685 _onBlurCapture: function(event) {
3686 if (event.target === this._pendingActivationItem) {
3687 this._cancelPendingActivation();
3688 }
3689 },
3690 get _tabContainerScrollSize() {
3691 return Math.max(0, this.$.tabsContainer.scrollWidth - this.$.tabsContainer.o ffsetWidth);
3692 },
3693 _scroll: function(e, detail) {
3694 if (!this.scrollable) {
3695 return;
3696 }
3697 var ddx = detail && -detail.ddx || 0;
3698 this._affectScroll(ddx);
3699 },
3700 _down: function(e) {
3701 this.async(function() {
3702 if (this._defaultFocusAsync) {
3703 this.cancelAsync(this._defaultFocusAsync);
3704 this._defaultFocusAsync = null;
3705 }
3706 }, 1);
3707 },
3708 _affectScroll: function(dx) {
3709 this.$.tabsContainer.scrollLeft += dx;
3710 var scrollLeft = this.$.tabsContainer.scrollLeft;
3711 this._leftHidden = scrollLeft === 0;
3712 this._rightHidden = scrollLeft === this._tabContainerScrollSize;
3713 },
3714 _onLeftScrollButtonDown: function() {
3715 this._scrollToLeft();
3716 this._holdJob = setInterval(this._scrollToLeft.bind(this), this._holdDelay);
3717 },
3718 _onRightScrollButtonDown: function() {
3719 this._scrollToRight();
3720 this._holdJob = setInterval(this._scrollToRight.bind(this), this._holdDelay) ;
3721 },
3722 _onScrollButtonUp: function() {
3723 clearInterval(this._holdJob);
3724 this._holdJob = null;
3725 },
3726 _scrollToLeft: function() {
3727 this._affectScroll(-this._step);
3728 },
3729 _scrollToRight: function() {
3730 this._affectScroll(this._step);
3731 },
3732 _tabChanged: function(tab, old) {
3733 if (!tab) {
3734 this.$.selectionBar.classList.remove('expand');
3735 this.$.selectionBar.classList.remove('contract');
3736 this._positionBar(0, 0);
3737 return;
3738 }
3739 var r = this.$.tabsContent.getBoundingClientRect();
3740 var w = r.width;
3741 var tabRect = tab.getBoundingClientRect();
3742 var tabOffsetLeft = tabRect.left - r.left;
3743 this._pos = {
3744 width: this._calcPercent(tabRect.width, w),
3745 left: this._calcPercent(tabOffsetLeft, w)
3746 };
3747 if (this.noSlide || old == null) {
3748 this.$.selectionBar.classList.remove('expand');
3749 this.$.selectionBar.classList.remove('contract');
3750 this._positionBar(this._pos.width, this._pos.left);
3751 return;
3752 }
3753 var oldRect = old.getBoundingClientRect();
3754 var oldIndex = this.items.indexOf(old);
3755 var index = this.items.indexOf(tab);
3756 var m = 5;
3757 this.$.selectionBar.classList.add('expand');
3758 var moveRight = oldIndex < index;
3759 var isRTL = this._isRTL;
3760 if (isRTL) {
3761 moveRight = !moveRight;
3762 }
3763 if (moveRight) {
3764 this._positionBar(this._calcPercent(tabRect.left + tabRect.width - oldRect .left, w) - m, this._left);
3765 } else {
3766 this._positionBar(this._calcPercent(oldRect.left + oldRect.width - tabRect .left, w) - m, this._calcPercent(tabOffsetLeft, w) + m);
3767 }
3768 if (this.scrollable) {
3769 this._scrollToSelectedIfNeeded(tabRect.width, tabOffsetLeft);
3770 }
3771 },
3772 _scrollToSelectedIfNeeded: function(tabWidth, tabOffsetLeft) {
3773 var l = tabOffsetLeft - this.$.tabsContainer.scrollLeft;
3774 if (l < 0) {
3775 this.$.tabsContainer.scrollLeft += l;
3776 } else {
3777 l += tabWidth - this.$.tabsContainer.offsetWidth;
3778 if (l > 0) {
3779 this.$.tabsContainer.scrollLeft += l;
3780 }
3781 }
3782 },
3783 _calcPercent: function(w, w0) {
3784 return 100 * w / w0;
3785 },
3786 _positionBar: function(width, left) {
3787 width = width || 0;
3788 left = left || 0;
3789 this._width = width;
3790 this._left = left;
3791 this.transform('translateX(' + left + '%) scaleX(' + width / 100 + ')', this .$.selectionBar);
3792 },
3793 _onBarTransitionEnd: function(e) {
3794 var cl = this.$.selectionBar.classList;
3795 if (cl.contains('expand')) {
3796 cl.remove('expand');
3797 cl.add('contract');
3798 this._positionBar(this._pos.width, this._pos.left);
3799 } else if (cl.contains('contract')) {
3800 cl.remove('contract');
3801 }
3802 }
3803 });
3804
3805 (function() {
3806 'use strict';
3807 Polymer.IronA11yAnnouncer = Polymer({
3808 is: 'iron-a11y-announcer',
3809 properties: {
3810 mode: {
3811 type: String,
3812 value: 'polite'
3813 },
3814 _text: {
3815 type: String,
3816 value: ''
3817 }
3818 },
3819 created: function() {
3820 if (!Polymer.IronA11yAnnouncer.instance) {
3821 Polymer.IronA11yAnnouncer.instance = this;
3822 }
3823 document.body.addEventListener('iron-announce', this._onIronAnnounce.bind( this));
3824 },
3825 announce: function(text) {
3826 this._text = '';
3827 this.async(function() {
3828 this._text = text;
3829 }, 100);
3830 },
3831 _onIronAnnounce: function(event) {
3832 if (event.detail && event.detail.text) {
3833 this.announce(event.detail.text);
3834 }
3835 }
3836 });
3837 Polymer.IronA11yAnnouncer.instance = null;
3838 Polymer.IronA11yAnnouncer.requestAvailability = function() {
3839 if (!Polymer.IronA11yAnnouncer.instance) {
3840 Polymer.IronA11yAnnouncer.instance = document.createElement('iron-a11y-ann ouncer');
3841 }
3842 document.body.appendChild(Polymer.IronA11yAnnouncer.instance);
3843 };
3844 })();
3845
3846 Polymer.IronValidatableBehaviorMeta = null;
3847
3848 Polymer.IronValidatableBehavior = {
3849 properties: {
3850 validator: {
3851 type: String
3852 },
3853 invalid: {
3854 notify: true,
3855 reflectToAttribute: true,
3856 type: Boolean,
3857 value: false
3858 },
3859 _validatorMeta: {
3860 type: Object
3861 },
3862 validatorType: {
3863 type: String,
3864 value: 'validator'
3865 },
3866 _validator: {
3867 type: Object,
3868 computed: '__computeValidator(validator)'
3869 }
3870 },
3871 observers: [ '_invalidChanged(invalid)' ],
3872 registered: function() {
3873 Polymer.IronValidatableBehaviorMeta = new Polymer.IronMeta({
3874 type: 'validator'
3875 });
3876 },
3877 _invalidChanged: function() {
3878 if (this.invalid) {
3879 this.setAttribute('aria-invalid', 'true');
3880 } else {
3881 this.removeAttribute('aria-invalid');
3882 }
3883 },
3884 hasValidator: function() {
3885 return this._validator != null;
3886 },
3887 validate: function(value) {
3888 this.invalid = !this._getValidity(value);
3889 return !this.invalid;
3890 },
3891 _getValidity: function(value) {
3892 if (this.hasValidator()) {
3893 return this._validator.validate(value);
3894 }
3895 return true;
3896 },
3897 __computeValidator: function() {
3898 return Polymer.IronValidatableBehaviorMeta && Polymer.IronValidatableBehavio rMeta.byKey(this.validator);
3899 }
3900 };
3901
3902 Polymer({
3903 is: 'iron-input',
3904 "extends": 'input',
3905 behaviors: [ Polymer.IronValidatableBehavior ],
3906 properties: {
3907 bindValue: {
3908 observer: '_bindValueChanged',
3909 type: String
3910 },
3911 preventInvalidInput: {
3912 type: Boolean
3913 },
3914 allowedPattern: {
3915 type: String,
3916 observer: "_allowedPatternChanged"
3917 },
3918 _previousValidInput: {
3919 type: String,
3920 value: ''
3921 },
3922 _patternAlreadyChecked: {
3923 type: Boolean,
3924 value: false
3925 }
3926 },
3927 listeners: {
3928 input: '_onInput',
3929 keypress: '_onKeypress'
3930 },
3931 registered: function() {
3932 if (!this._canDispatchEventOnDisabled()) {
3933 this._origDispatchEvent = this.dispatchEvent;
3934 this.dispatchEvent = this._dispatchEventFirefoxIE;
3935 }
3936 },
3937 created: function() {
3938 Polymer.IronA11yAnnouncer.requestAvailability();
3939 },
3940 _canDispatchEventOnDisabled: function() {
3941 var input = document.createElement('input');
3942 var canDispatch = false;
3943 input.disabled = true;
3944 input.addEventListener('feature-check-dispatch-event', function() {
3945 canDispatch = true;
3946 });
3947 try {
3948 input.dispatchEvent(new Event('feature-check-dispatch-event'));
3949 } catch (e) {}
3950 return canDispatch;
3951 },
3952 _dispatchEventFirefoxIE: function() {
3953 var disabled = this.disabled;
3954 this.disabled = false;
3955 this._origDispatchEvent.apply(this, arguments);
3956 this.disabled = disabled;
3957 },
3958 get _patternRegExp() {
3959 var pattern;
3960 if (this.allowedPattern) {
3961 pattern = new RegExp(this.allowedPattern);
3962 } else {
3963 switch (this.type) {
3964 case 'number':
3965 pattern = /[0-9.,e-]/;
3966 break;
3967 }
3968 }
3969 return pattern;
3970 },
3971 ready: function() {
3972 this.bindValue = this.value;
3973 },
3974 _bindValueChanged: function() {
3975 if (this.value !== this.bindValue) {
3976 this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue == = false) ? '' : this.bindValue;
3977 }
3978 this.fire('bind-value-changed', {
3979 value: this.bindValue
3980 });
3981 },
3982 _allowedPatternChanged: function() {
3983 this.preventInvalidInput = this.allowedPattern ? true : false;
3984 },
3985 _onInput: function() {
3986 if (this.preventInvalidInput && !this._patternAlreadyChecked) {
3987 var valid = this._checkPatternValidity();
3988 if (!valid) {
3989 this._announceInvalidCharacter('Invalid string of characters not entered .');
3990 this.value = this._previousValidInput;
3991 }
3992 }
3993 this.bindValue = this.value;
3994 this._previousValidInput = this.value;
3995 this._patternAlreadyChecked = false;
3996 },
3997 _isPrintable: function(event) {
3998 var anyNonPrintable = event.keyCode == 8 || event.keyCode == 9 || event.keyC ode == 13 || event.keyCode == 27;
3999 var mozNonPrintable = event.keyCode == 19 || event.keyCode == 20 || event.ke yCode == 45 || event.keyCode == 46 || event.keyCode == 144 || event.keyCode == 1 45 || event.keyCode > 32 && event.keyCode < 41 || event.keyCode > 111 && event.k eyCode < 124;
4000 return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable);
4001 },
4002 _onKeypress: function(event) {
4003 if (!this.preventInvalidInput && this.type !== 'number') {
4004 return;
4005 }
4006 var regexp = this._patternRegExp;
4007 if (!regexp) {
4008 return;
4009 }
4010 if (event.metaKey || event.ctrlKey || event.altKey) return;
4011 this._patternAlreadyChecked = true;
4012 var thisChar = String.fromCharCode(event.charCode);
4013 if (this._isPrintable(event) && !regexp.test(thisChar)) {
4014 event.preventDefault();
4015 this._announceInvalidCharacter('Invalid character ' + thisChar + ' not ent ered.');
4016 }
4017 },
4018 _checkPatternValidity: function() {
4019 var regexp = this._patternRegExp;
4020 if (!regexp) {
4021 return true;
4022 }
4023 for (var i = 0; i < this.value.length; i++) {
4024 if (!regexp.test(this.value[i])) {
4025 return false;
4026 }
4027 }
4028 return true;
4029 },
4030 validate: function() {
4031 var valid = this.checkValidity();
4032 if (valid) {
4033 if (this.required && this.value === '') {
4034 valid = false;
4035 } else if (this.hasValidator()) {
4036 valid = Polymer.IronValidatableBehavior.validate.call(this, this.value);
4037 }
4038 }
4039 this.invalid = !valid;
4040 this.fire('iron-input-validate');
4041 return valid;
4042 },
4043 _announceInvalidCharacter: function(message) {
4044 this.fire('iron-announce', {
4045 text: message
4046 });
4047 }
4048 });
4049
4050 Polymer({
4051 is: 'paper-input-container',
4052 properties: {
4053 noLabelFloat: {
4054 type: Boolean,
4055 value: false
4056 },
4057 alwaysFloatLabel: {
4058 type: Boolean,
4059 value: false
4060 },
4061 attrForValue: {
4062 type: String,
4063 value: 'bind-value'
4064 },
4065 autoValidate: {
4066 type: Boolean,
4067 value: false
4068 },
4069 invalid: {
4070 observer: '_invalidChanged',
4071 type: Boolean,
4072 value: false
4073 },
4074 focused: {
4075 readOnly: true,
4076 type: Boolean,
4077 value: false,
4078 notify: true
4079 },
4080 _addons: {
4081 type: Array
4082 },
4083 _inputHasContent: {
4084 type: Boolean,
4085 value: false
4086 },
4087 _inputSelector: {
4088 type: String,
4089 value: 'input,textarea,.paper-input-input'
4090 },
4091 _boundOnFocus: {
4092 type: Function,
4093 value: function() {
4094 return this._onFocus.bind(this);
4095 }
4096 },
4097 _boundOnBlur: {
4098 type: Function,
4099 value: function() {
4100 return this._onBlur.bind(this);
4101 }
4102 },
4103 _boundOnInput: {
4104 type: Function,
4105 value: function() {
4106 return this._onInput.bind(this);
4107 }
4108 },
4109 _boundValueChanged: {
4110 type: Function,
4111 value: function() {
4112 return this._onValueChanged.bind(this);
4113 }
4114 }
4115 },
4116 listeners: {
4117 'addon-attached': '_onAddonAttached',
4118 'iron-input-validate': '_onIronInputValidate'
4119 },
4120 get _valueChangedEvent() {
4121 return this.attrForValue + '-changed';
4122 },
4123 get _propertyForValue() {
4124 return Polymer.CaseMap.dashToCamelCase(this.attrForValue);
4125 },
4126 get _inputElement() {
4127 return Polymer.dom(this).querySelector(this._inputSelector);
4128 },
4129 get _inputElementValue() {
4130 return this._inputElement[this._propertyForValue] || this._inputElement.valu e;
4131 },
4132 ready: function() {
4133 if (!this._addons) {
4134 this._addons = [];
4135 }
4136 this.addEventListener('focus', this._boundOnFocus, true);
4137 this.addEventListener('blur', this._boundOnBlur, true);
4138 },
4139 attached: function() {
4140 if (this.attrForValue) {
4141 this._inputElement.addEventListener(this._valueChangedEvent, this._boundVa lueChanged);
4142 } else {
4143 this.addEventListener('input', this._onInput);
4144 }
4145 if (this._inputElementValue != '') {
4146 this._handleValueAndAutoValidate(this._inputElement);
4147 } else {
4148 this._handleValue(this._inputElement);
4149 }
4150 },
4151 _onAddonAttached: function(event) {
4152 if (!this._addons) {
4153 this._addons = [];
4154 }
4155 var target = event.target;
4156 if (this._addons.indexOf(target) === -1) {
4157 this._addons.push(target);
4158 if (this.isAttached) {
4159 this._handleValue(this._inputElement);
4160 }
4161 }
4162 },
4163 _onFocus: function() {
4164 this._setFocused(true);
4165 },
4166 _onBlur: function() {
4167 this._setFocused(false);
4168 this._handleValueAndAutoValidate(this._inputElement);
4169 },
4170 _onInput: function(event) {
4171 this._handleValueAndAutoValidate(event.target);
4172 },
4173 _onValueChanged: function(event) {
4174 this._handleValueAndAutoValidate(event.target);
4175 },
4176 _handleValue: function(inputElement) {
4177 var value = this._inputElementValue;
4178 if (value || value === 0 || inputElement.type === 'number' && !inputElement. checkValidity()) {
4179 this._inputHasContent = true;
4180 } else {
4181 this._inputHasContent = false;
4182 }
4183 this.updateAddons({
4184 inputElement: inputElement,
4185 value: value,
4186 invalid: this.invalid
4187 });
4188 },
4189 _handleValueAndAutoValidate: function(inputElement) {
4190 if (this.autoValidate) {
4191 var valid;
4192 if (inputElement.validate) {
4193 valid = inputElement.validate(this._inputElementValue);
4194 } else {
4195 valid = inputElement.checkValidity();
4196 }
4197 this.invalid = !valid;
4198 }
4199 this._handleValue(inputElement);
4200 },
4201 _onIronInputValidate: function(event) {
4202 this.invalid = this._inputElement.invalid;
4203 },
4204 _invalidChanged: function() {
4205 if (this._addons) {
4206 this.updateAddons({
4207 invalid: this.invalid
4208 });
4209 }
4210 },
4211 updateAddons: function(state) {
4212 for (var addon, index = 0; addon = this._addons[index]; index++) {
4213 addon.update(state);
4214 }
4215 },
4216 _computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, i nvalid, _inputHasContent) {
4217 var cls = 'input-content';
4218 if (!noLabelFloat) {
4219 var label = this.querySelector('label');
4220 if (alwaysFloatLabel || _inputHasContent) {
4221 cls += ' label-is-floating';
4222 this.$.labelAndInputContainer.style.position = 'static';
4223 if (invalid) {
4224 cls += ' is-invalid';
4225 } else if (focused) {
4226 cls += " label-is-highlighted";
4227 }
4228 } else {
4229 if (label) {
4230 this.$.labelAndInputContainer.style.position = 'relative';
4231 }
4232 }
4233 } else {
4234 if (_inputHasContent) {
4235 cls += ' label-is-hidden';
4236 }
4237 }
4238 return cls;
4239 },
4240 _computeUnderlineClass: function(focused, invalid) {
4241 var cls = 'underline';
4242 if (invalid) {
4243 cls += ' is-invalid';
4244 } else if (focused) {
4245 cls += ' is-highlighted';
4246 }
4247 return cls;
4248 },
4249 _computeAddOnContentClass: function(focused, invalid) {
4250 var cls = 'add-on-content';
4251 if (invalid) {
4252 cls += ' is-invalid';
4253 } else if (focused) {
4254 cls += ' is-highlighted';
4255 }
4256 return cls;
4257 }
4258 });
4259
4260 Polymer.PaperSpinnerBehavior = {
4261 listeners: {
4262 animationend: '__reset',
4263 webkitAnimationEnd: '__reset'
4264 },
4265 properties: {
4266 active: {
4267 type: Boolean,
4268 value: false,
4269 reflectToAttribute: true,
4270 observer: '__activeChanged'
4271 },
4272 alt: {
4273 type: String,
4274 value: 'loading',
4275 observer: '__altChanged'
4276 },
4277 __coolingDown: {
4278 type: Boolean,
4279 value: false
4280 }
4281 },
4282 __computeContainerClasses: function(active, coolingDown) {
4283 return [ active || coolingDown ? 'active' : '', coolingDown ? 'cooldown' : ' ' ].join(' ');
4284 },
4285 __activeChanged: function(active, old) {
4286 this.__setAriaHidden(!active);
4287 this.__coolingDown = !active && old;
4288 },
4289 __altChanged: function(alt) {
4290 if (alt === this.getPropertyInfo('alt').value) {
4291 this.alt = this.getAttribute('aria-label') || alt;
4292 } else {
4293 this.__setAriaHidden(alt === '');
4294 this.setAttribute('aria-label', alt);
4295 }
4296 },
4297 __setAriaHidden: function(hidden) {
4298 var attr = 'aria-hidden';
4299 if (hidden) {
4300 this.setAttribute(attr, 'true');
4301 } else {
4302 this.removeAttribute(attr);
4303 }
4304 },
4305 __reset: function() {
4306 this.active = false;
4307 this.__coolingDown = false;
4308 }
4309 };
4310
4311 Polymer({
4312 is: 'paper-spinner-lite',
4313 behaviors: [ Polymer.PaperSpinnerBehavior ]
4314 });
4315
4316 // Copyright 2016 The Chromium Authors. All rights reserved.
4317 // Use of this source code is governed by a BSD-style license that can be
4318 // found in the LICENSE file.
4319 var CrSearchFieldBehavior = {
4320 properties: {
4321 label: {
4322 type: String,
4323 value: ''
4324 },
4325 clearLabel: {
4326 type: String,
4327 value: ''
4328 },
4329 showingSearch: {
4330 type: Boolean,
4331 value: false,
4332 notify: true,
4333 observer: 'showingSearchChanged_',
4334 reflectToAttribute: true
4335 },
4336 lastValue_: {
4337 type: String,
4338 value: ''
4339 }
4340 },
4341 getSearchInput: function() {},
4342 getValue: function() {
4343 return this.getSearchInput().value;
4344 },
4345 setValue: function(value) {
4346 this.getSearchInput().bindValue = value;
4347 this.onValueChanged_(value);
4348 },
4349 showAndFocus: function() {
4350 this.showingSearch = true;
4351 this.focus_();
4352 },
4353 focus_: function() {
4354 this.getSearchInput().focus();
4355 },
4356 onSearchTermSearch: function() {
4357 this.onValueChanged_(this.getValue());
4358 },
4359 onValueChanged_: function(newValue) {
4360 if (newValue == this.lastValue_) return;
4361 this.fire('search-changed', newValue);
4362 this.lastValue_ = newValue;
4363 },
4364 onSearchTermKeydown: function(e) {
4365 if (e.key == 'Escape') this.showingSearch = false;
4366 },
4367 showingSearchChanged_: function() {
4368 if (this.showingSearch) {
4369 this.focus_();
4370 return;
4371 }
4372 this.setValue('');
4373 this.getSearchInput().blur();
4374 }
4375 };
4376
4377 // Copyright 2016 The Chromium Authors. All rights reserved.
4378 // Use of this source code is governed by a BSD-style license that can be
4379 // found in the LICENSE file.
4380 Polymer({
4381 is: 'cr-toolbar-search-field',
4382 behaviors: [ CrSearchFieldBehavior ],
4383 properties: {
4384 narrow: {
4385 type: Boolean,
4386 reflectToAttribute: true
4387 },
4388 label: String,
4389 clearLabel: String,
4390 spinnerActive: {
4391 type: Boolean,
4392 reflectToAttribute: true
4393 },
4394 hasSearchText_: Boolean
4395 },
4396 listeners: {
4397 tap: 'showSearch_',
4398 'searchInput.bind-value-changed': 'onBindValueChanged_'
4399 },
4400 getSearchInput: function() {
4401 return this.$.searchInput;
4402 },
4403 isSearchFocused: function() {
4404 return this.$.searchTerm.focused;
4405 },
4406 computeIconTabIndex_: function(narrow) {
4407 return narrow ? 0 : -1;
4408 },
4409 isSpinnerShown_: function(spinnerActive, showingSearch) {
4410 return spinnerActive && showingSearch;
4411 },
4412 onInputBlur_: function() {
4413 if (!this.hasSearchText_) this.showingSearch = false;
4414 },
4415 onBindValueChanged_: function() {
4416 var newValue = this.$.searchInput.bindValue;
4417 this.hasSearchText_ = newValue != '';
4418 if (newValue != '') this.showingSearch = true;
4419 },
4420 showSearch_: function(e) {
4421 if (e.target != this.$.clearSearch) this.showingSearch = true;
4422 },
4423 hideSearch_: function(e) {
4424 this.showingSearch = false;
4425 e.stopPropagation();
4426 }
4427 });
4428
4429 // Copyright 2016 The Chromium Authors. All rights reserved.
4430 // Use of this source code is governed by a BSD-style license that can be
4431 // found in the LICENSE file.
4432 Polymer({
4433 is: 'cr-toolbar',
4434 properties: {
4435 pageName: String,
4436 searchPrompt: String,
4437 clearLabel: String,
4438 spinnerActive: Boolean,
4439 showMenu: {
4440 type: Boolean,
4441 reflectToAttribute: true,
4442 value: true
4443 },
4444 narrow_: {
4445 type: Boolean,
4446 reflectToAttribute: true
4447 },
4448 showingSearch_: {
4449 type: Boolean,
4450 reflectToAttribute: true
4451 }
4452 },
4453 getSearchField: function() {
4454 return this.$.search;
4455 },
4456 onMenuTap_: function(e) {
4457 this.fire('cr-menu-tap');
4458 }
4459 });
4460
4461 // Copyright 2015 The Chromium Authors. All rights reserved.
4462 // Use of this source code is governed by a BSD-style license that can be
4463 // found in the LICENSE file.
4464 Polymer({
4465 is: 'history-toolbar',
4466 properties: {
4467 count: {
4468 type: Number,
4469 value: 0,
4470 observer: 'changeToolbarView_'
4471 },
4472 itemsSelected_: {
4473 type: Boolean,
4474 value: false,
4475 reflectToAttribute: true
4476 },
4477 searchTerm: {
4478 type: String,
4479 notify: true
4480 },
4481 spinnerActive: {
4482 type: Boolean,
4483 value: false
4484 },
4485 hasDrawer: {
4486 type: Boolean,
4487 observer: 'hasDrawerChanged_',
4488 reflectToAttribute: true
4489 },
4490 isGroupedMode: {
4491 type: Boolean,
4492 reflectToAttribute: true
4493 },
4494 groupedRange: {
4495 type: Number,
4496 value: 0,
4497 reflectToAttribute: true,
4498 notify: true
4499 },
4500 queryStartTime: String,
4501 queryEndTime: String
4502 },
4503 changeToolbarView_: function() {
4504 this.itemsSelected_ = this.count > 0;
4505 },
4506 setSearchTerm: function(search) {
4507 if (this.searchTerm == search) return;
4508 this.searchTerm = search;
4509 var searchField = this.$['main-toolbar'].getSearchField();
4510 searchField.showAndFocus();
4511 searchField.setValue(search);
4512 },
4513 onSearchChanged_: function(event) {
4514 this.searchTerm = event.detail;
4515 },
4516 onClearSelectionTap_: function() {
4517 this.fire('unselect-all');
4518 },
4519 onDeleteTap_: function() {
4520 this.fire('delete-selected');
4521 },
4522 get searchBar() {
4523 return this.$['main-toolbar'].getSearchField();
4524 },
4525 showSearchField: function() {
4526 this.$['main-toolbar'].getSearchField().showAndFocus();
4527 },
4528 deletingAllowed_: function() {
4529 return loadTimeData.getBoolean('allowDeletingHistory');
4530 },
4531 numberOfItemsSelected_: function(count) {
4532 return count > 0 ? loadTimeData.getStringF('itemsSelected', count) : '';
4533 },
4534 getHistoryInterval_: function(queryStartTime, queryEndTime) {
4535 return loadTimeData.getStringF('historyInterval', queryStartTime, queryEndTi me);
4536 },
4537 hasDrawerChanged_: function() {
4538 this.updateStyles();
4539 }
4540 });
4541
4542 // Copyright 2016 The Chromium Authors. All rights reserved.
4543 // Use of this source code is governed by a BSD-style license that can be
4544 // found in the LICENSE file.
4545 Polymer({
4546 is: 'cr-dialog',
4547 "extends": 'dialog',
4548 cancel: function() {
4549 this.fire('cancel');
4550 HTMLDialogElement.prototype.close.call(this, '');
4551 },
4552 close: function(opt_returnValue) {
4553 HTMLDialogElement.prototype.close.call(this, 'success');
4554 },
4555 getCloseButton: function() {
4556 return this.$.close;
4557 }
4558 });
4559
4560 Polymer.IronFitBehavior = {
4561 properties: {
4562 sizingTarget: {
4563 type: Object,
4564 value: function() {
4565 return this;
4566 }
4567 },
4568 fitInto: {
4569 type: Object,
4570 value: window
4571 },
4572 noOverlap: {
4573 type: Boolean
4574 },
4575 positionTarget: {
4576 type: Element
4577 },
4578 horizontalAlign: {
4579 type: String
4580 },
4581 verticalAlign: {
4582 type: String
4583 },
4584 dynamicAlign: {
4585 type: Boolean
4586 },
4587 horizontalOffset: {
4588 type: Number,
4589 value: 0,
4590 notify: true
4591 },
4592 verticalOffset: {
4593 type: Number,
4594 value: 0,
4595 notify: true
4596 },
4597 autoFitOnAttach: {
4598 type: Boolean,
4599 value: false
4600 },
4601 _fitInfo: {
4602 type: Object
4603 }
4604 },
4605 get _fitWidth() {
4606 var fitWidth;
4607 if (this.fitInto === window) {
4608 fitWidth = this.fitInto.innerWidth;
4609 } else {
4610 fitWidth = this.fitInto.getBoundingClientRect().width;
4611 }
4612 return fitWidth;
4613 },
4614 get _fitHeight() {
4615 var fitHeight;
4616 if (this.fitInto === window) {
4617 fitHeight = this.fitInto.innerHeight;
4618 } else {
4619 fitHeight = this.fitInto.getBoundingClientRect().height;
4620 }
4621 return fitHeight;
4622 },
4623 get _fitLeft() {
4624 var fitLeft;
4625 if (this.fitInto === window) {
4626 fitLeft = 0;
4627 } else {
4628 fitLeft = this.fitInto.getBoundingClientRect().left;
4629 }
4630 return fitLeft;
4631 },
4632 get _fitTop() {
4633 var fitTop;
4634 if (this.fitInto === window) {
4635 fitTop = 0;
4636 } else {
4637 fitTop = this.fitInto.getBoundingClientRect().top;
4638 }
4639 return fitTop;
4640 },
4641 get _defaultPositionTarget() {
4642 var parent = Polymer.dom(this).parentNode;
4643 if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
4644 parent = parent.host;
4645 }
4646 return parent;
4647 },
4648 get _localeHorizontalAlign() {
4649 if (this._isRTL) {
4650 if (this.horizontalAlign === 'right') {
4651 return 'left';
4652 }
4653 if (this.horizontalAlign === 'left') {
4654 return 'right';
4655 }
4656 }
4657 return this.horizontalAlign;
4658 },
4659 attached: function() {
4660 this._isRTL = window.getComputedStyle(this).direction == 'rtl';
4661 this.positionTarget = this.positionTarget || this._defaultPositionTarget;
4662 if (this.autoFitOnAttach) {
4663 if (window.getComputedStyle(this).display === 'none') {
4664 setTimeout(function() {
4665 this.fit();
4666 }.bind(this));
4667 } else {
4668 this.fit();
4669 }
4670 }
4671 },
4672 fit: function() {
4673 this.position();
4674 this.constrain();
4675 this.center();
4676 },
4677 _discoverInfo: function() {
4678 if (this._fitInfo) {
4679 return;
4680 }
4681 var target = window.getComputedStyle(this);
4682 var sizer = window.getComputedStyle(this.sizingTarget);
4683 this._fitInfo = {
4684 inlineStyle: {
4685 top: this.style.top || '',
4686 left: this.style.left || '',
4687 position: this.style.position || ''
4688 },
4689 sizerInlineStyle: {
4690 maxWidth: this.sizingTarget.style.maxWidth || '',
4691 maxHeight: this.sizingTarget.style.maxHeight || '',
4692 boxSizing: this.sizingTarget.style.boxSizing || ''
4693 },
4694 positionedBy: {
4695 vertically: target.top !== 'auto' ? 'top' : target.bottom !== 'auto' ? ' bottom' : null,
4696 horizontally: target.left !== 'auto' ? 'left' : target.right !== 'auto' ? 'right' : null
4697 },
4698 sizedBy: {
4699 height: sizer.maxHeight !== 'none',
4700 width: sizer.maxWidth !== 'none',
4701 minWidth: parseInt(sizer.minWidth, 10) || 0,
4702 minHeight: parseInt(sizer.minHeight, 10) || 0
4703 },
4704 margin: {
4705 top: parseInt(target.marginTop, 10) || 0,
4706 right: parseInt(target.marginRight, 10) || 0,
4707 bottom: parseInt(target.marginBottom, 10) || 0,
4708 left: parseInt(target.marginLeft, 10) || 0
4709 }
4710 };
4711 if (this.verticalOffset) {
4712 this._fitInfo.margin.top = this._fitInfo.margin.bottom = this.verticalOffs et;
4713 this._fitInfo.inlineStyle.marginTop = this.style.marginTop || '';
4714 this._fitInfo.inlineStyle.marginBottom = this.style.marginBottom || '';
4715 this.style.marginTop = this.style.marginBottom = this.verticalOffset + 'px ';
4716 }
4717 if (this.horizontalOffset) {
4718 this._fitInfo.margin.left = this._fitInfo.margin.right = this.horizontalOf fset;
4719 this._fitInfo.inlineStyle.marginLeft = this.style.marginLeft || '';
4720 this._fitInfo.inlineStyle.marginRight = this.style.marginRight || '';
4721 this.style.marginLeft = this.style.marginRight = this.horizontalOffset + ' px';
4722 }
4723 },
4724 resetFit: function() {
4725 var info = this._fitInfo || {};
4726 for (var property in info.sizerInlineStyle) {
4727 this.sizingTarget.style[property] = info.sizerInlineStyle[property];
4728 }
4729 for (var property in info.inlineStyle) {
4730 this.style[property] = info.inlineStyle[property];
4731 }
4732 this._fitInfo = null;
4733 },
4734 refit: function() {
4735 var scrollLeft = this.sizingTarget.scrollLeft;
4736 var scrollTop = this.sizingTarget.scrollTop;
4737 this.resetFit();
4738 this.fit();
4739 this.sizingTarget.scrollLeft = scrollLeft;
4740 this.sizingTarget.scrollTop = scrollTop;
4741 },
4742 position: function() {
4743 if (!this.horizontalAlign && !this.verticalAlign) {
4744 return;
4745 }
4746 this._discoverInfo();
4747 this.style.position = 'fixed';
4748 this.sizingTarget.style.boxSizing = 'border-box';
4749 this.style.left = '0px';
4750 this.style.top = '0px';
4751 var rect = this.getBoundingClientRect();
4752 var positionRect = this.__getNormalizedRect(this.positionTarget);
4753 var fitRect = this.__getNormalizedRect(this.fitInto);
4754 var margin = this._fitInfo.margin;
4755 var size = {
4756 width: rect.width + margin.left + margin.right,
4757 height: rect.height + margin.top + margin.bottom
4758 };
4759 var position = this.__getPosition(this._localeHorizontalAlign, this.vertical Align, size, positionRect, fitRect);
4760 var left = position.left + margin.left;
4761 var top = position.top + margin.top;
4762 var right = Math.min(fitRect.right - margin.right, left + rect.width);
4763 var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height);
4764 var minWidth = this._fitInfo.sizedBy.minWidth;
4765 var minHeight = this._fitInfo.sizedBy.minHeight;
4766 if (left < margin.left) {
4767 left = margin.left;
4768 if (right - left < minWidth) {
4769 left = right - minWidth;
4770 }
4771 }
4772 if (top < margin.top) {
4773 top = margin.top;
4774 if (bottom - top < minHeight) {
4775 top = bottom - minHeight;
4776 }
4777 }
4778 this.sizingTarget.style.maxWidth = right - left + 'px';
4779 this.sizingTarget.style.maxHeight = bottom - top + 'px';
4780 this.style.left = left - rect.left + 'px';
4781 this.style.top = top - rect.top + 'px';
4782 },
4783 constrain: function() {
4784 if (this.horizontalAlign || this.verticalAlign) {
4785 return;
4786 }
4787 this._discoverInfo();
4788 var info = this._fitInfo;
4789 if (!info.positionedBy.vertically) {
4790 this.style.position = 'fixed';
4791 this.style.top = '0px';
4792 }
4793 if (!info.positionedBy.horizontally) {
4794 this.style.position = 'fixed';
4795 this.style.left = '0px';
4796 }
4797 this.sizingTarget.style.boxSizing = 'border-box';
4798 var rect = this.getBoundingClientRect();
4799 if (!info.sizedBy.height) {
4800 this.__sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height');
4801 }
4802 if (!info.sizedBy.width) {
4803 this.__sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right' , 'Width');
4804 }
4805 },
4806 _sizeDimension: function(rect, positionedBy, start, end, extent) {
4807 this.__sizeDimension(rect, positionedBy, start, end, extent);
4808 },
4809 __sizeDimension: function(rect, positionedBy, start, end, extent) {
4810 var info = this._fitInfo;
4811 var fitRect = this.__getNormalizedRect(this.fitInto);
4812 var max = extent === 'Width' ? fitRect.width : fitRect.height;
4813 var flip = positionedBy === end;
4814 var offset = flip ? max - rect[end] : rect[start];
4815 var margin = info.margin[flip ? start : end];
4816 var offsetExtent = 'offset' + extent;
4817 var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent];
4818 this.sizingTarget.style['max' + extent] = max - margin - offset - sizingOffs et + 'px';
4819 },
4820 center: function() {
4821 if (this.horizontalAlign || this.verticalAlign) {
4822 return;
4823 }
4824 this._discoverInfo();
4825 var positionedBy = this._fitInfo.positionedBy;
4826 if (positionedBy.vertically && positionedBy.horizontally) {
4827 return;
4828 }
4829 this.style.position = 'fixed';
4830 if (!positionedBy.vertically) {
4831 this.style.top = '0px';
4832 }
4833 if (!positionedBy.horizontally) {
4834 this.style.left = '0px';
4835 }
4836 var rect = this.getBoundingClientRect();
4837 var fitRect = this.__getNormalizedRect(this.fitInto);
4838 if (!positionedBy.vertically) {
4839 var top = fitRect.top - rect.top + (fitRect.height - rect.height) / 2;
4840 this.style.top = top + 'px';
4841 }
4842 if (!positionedBy.horizontally) {
4843 var left = fitRect.left - rect.left + (fitRect.width - rect.width) / 2;
4844 this.style.left = left + 'px';
4845 }
4846 },
4847 __getNormalizedRect: function(target) {
4848 if (target === document.documentElement || target === window) {
4849 return {
4850 top: 0,
4851 left: 0,
4852 width: window.innerWidth,
4853 height: window.innerHeight,
4854 right: window.innerWidth,
4855 bottom: window.innerHeight
4856 };
4857 }
4858 return target.getBoundingClientRect();
4859 },
4860 __getCroppedArea: function(position, size, fitRect) {
4861 var verticalCrop = Math.min(0, position.top) + Math.min(0, fitRect.bottom - (position.top + size.height));
4862 var horizontalCrop = Math.min(0, position.left) + Math.min(0, fitRect.right - (position.left + size.width));
4863 return Math.abs(verticalCrop) * size.width + Math.abs(horizontalCrop) * size .height;
4864 },
4865 __getPosition: function(hAlign, vAlign, size, positionRect, fitRect) {
4866 var positions = [ {
4867 verticalAlign: 'top',
4868 horizontalAlign: 'left',
4869 top: positionRect.top,
4870 left: positionRect.left
4871 }, {
4872 verticalAlign: 'top',
4873 horizontalAlign: 'right',
4874 top: positionRect.top,
4875 left: positionRect.right - size.width
4876 }, {
4877 verticalAlign: 'bottom',
4878 horizontalAlign: 'left',
4879 top: positionRect.bottom - size.height,
4880 left: positionRect.left
4881 }, {
4882 verticalAlign: 'bottom',
4883 horizontalAlign: 'right',
4884 top: positionRect.bottom - size.height,
4885 left: positionRect.right - size.width
4886 } ];
4887 if (this.noOverlap) {
4888 for (var i = 0, l = positions.length; i < l; i++) {
4889 var copy = {};
4890 for (var key in positions[i]) {
4891 copy[key] = positions[i][key];
4892 }
4893 positions.push(copy);
4894 }
4895 positions[0].top = positions[1].top += positionRect.height;
4896 positions[2].top = positions[3].top -= positionRect.height;
4897 positions[4].left = positions[6].left += positionRect.width;
4898 positions[5].left = positions[7].left -= positionRect.width;
4899 }
4900 vAlign = vAlign === 'auto' ? null : vAlign;
4901 hAlign = hAlign === 'auto' ? null : hAlign;
4902 var position;
4903 for (var i = 0; i < positions.length; i++) {
4904 var pos = positions[i];
4905 if (!this.dynamicAlign && !this.noOverlap && pos.verticalAlign === vAlign && pos.horizontalAlign === hAlign) {
4906 position = pos;
4907 break;
4908 }
4909 var alignOk = (!vAlign || pos.verticalAlign === vAlign) && (!hAlign || pos .horizontalAlign === hAlign);
4910 if (!this.dynamicAlign && !alignOk) {
4911 continue;
4912 }
4913 position = position || pos;
4914 pos.croppedArea = this.__getCroppedArea(pos, size, fitRect);
4915 var diff = pos.croppedArea - position.croppedArea;
4916 if (diff < 0 || diff === 0 && alignOk) {
4917 position = pos;
4918 }
4919 if (position.croppedArea === 0 && alignOk) {
4920 break;
4921 }
4922 }
4923 return position;
4924 }
4925 };
4926
4927 (function() {
4928 'use strict';
4929 Polymer({
4930 is: 'iron-overlay-backdrop',
4931 properties: {
4932 opened: {
4933 reflectToAttribute: true,
4934 type: Boolean,
4935 value: false,
4936 observer: '_openedChanged'
4937 }
4938 },
4939 listeners: {
4940 transitionend: '_onTransitionend'
4941 },
4942 created: function() {
4943 this.__openedRaf = null;
4944 },
4945 attached: function() {
4946 this.opened && this._openedChanged(this.opened);
4947 },
4948 prepare: function() {
4949 if (this.opened && !this.parentNode) {
4950 Polymer.dom(document.body).appendChild(this);
4951 }
4952 },
4953 open: function() {
4954 this.opened = true;
4955 },
4956 close: function() {
4957 this.opened = false;
4958 },
4959 complete: function() {
4960 if (!this.opened && this.parentNode === document.body) {
4961 Polymer.dom(this.parentNode).removeChild(this);
4962 }
4963 },
4964 _onTransitionend: function(event) {
4965 if (event && event.target === this) {
4966 this.complete();
4967 }
4968 },
4969 _openedChanged: function(opened) {
4970 if (opened) {
4971 this.prepare();
4972 } else {
4973 var cs = window.getComputedStyle(this);
4974 if (cs.transitionDuration === '0s' || cs.opacity == 0) {
4975 this.complete();
4976 }
4977 }
4978 if (!this.isAttached) {
4979 return;
4980 }
4981 if (this.__openedRaf) {
4982 window.cancelAnimationFrame(this.__openedRaf);
4983 this.__openedRaf = null;
4984 }
4985 this.scrollTop = this.scrollTop;
4986 this.__openedRaf = window.requestAnimationFrame(function() {
4987 this.__openedRaf = null;
4988 this.toggleClass('opened', this.opened);
4989 }.bind(this));
4990 }
4991 });
4992 })();
4993
4994 Polymer.IronOverlayManagerClass = function() {
4995 this._overlays = [];
4996 this._minimumZ = 101;
4997 this._backdropElement = null;
4998 Polymer.Gestures.add(document, 'tap', this._onCaptureClick.bind(this));
4999 document.addEventListener('focus', this._onCaptureFocus.bind(this), true);
5000 document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true);
5001 };
5002
5003 Polymer.IronOverlayManagerClass.prototype = {
5004 constructor: Polymer.IronOverlayManagerClass,
5005 get backdropElement() {
5006 if (!this._backdropElement) {
5007 this._backdropElement = document.createElement('iron-overlay-backdrop');
5008 }
5009 return this._backdropElement;
5010 },
5011 get deepActiveElement() {
5012 var active = document.activeElement || document.body;
5013 while (active.root && Polymer.dom(active.root).activeElement) {
5014 active = Polymer.dom(active.root).activeElement;
5015 }
5016 return active;
5017 },
5018 _bringOverlayAtIndexToFront: function(i) {
5019 var overlay = this._overlays[i];
5020 if (!overlay) {
5021 return;
5022 }
5023 var lastI = this._overlays.length - 1;
5024 var currentOverlay = this._overlays[lastI];
5025 if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) {
5026 lastI--;
5027 }
5028 if (i >= lastI) {
5029 return;
5030 }
5031 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ);
5032 if (this._getZ(overlay) <= minimumZ) {
5033 this._applyOverlayZ(overlay, minimumZ);
5034 }
5035 while (i < lastI) {
5036 this._overlays[i] = this._overlays[i + 1];
5037 i++;
5038 }
5039 this._overlays[lastI] = overlay;
5040 },
5041 addOrRemoveOverlay: function(overlay) {
5042 if (overlay.opened) {
5043 this.addOverlay(overlay);
5044 } else {
5045 this.removeOverlay(overlay);
5046 }
5047 },
5048 addOverlay: function(overlay) {
5049 var i = this._overlays.indexOf(overlay);
5050 if (i >= 0) {
5051 this._bringOverlayAtIndexToFront(i);
5052 this.trackBackdrop();
5053 return;
5054 }
5055 var insertionIndex = this._overlays.length;
5056 var currentOverlay = this._overlays[insertionIndex - 1];
5057 var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ);
5058 var newZ = this._getZ(overlay);
5059 if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) {
5060 this._applyOverlayZ(currentOverlay, minimumZ);
5061 insertionIndex--;
5062 var previousOverlay = this._overlays[insertionIndex - 1];
5063 minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ);
5064 }
5065 if (newZ <= minimumZ) {
5066 this._applyOverlayZ(overlay, minimumZ);
5067 }
5068 this._overlays.splice(insertionIndex, 0, overlay);
5069 this.trackBackdrop();
5070 },
5071 removeOverlay: function(overlay) {
5072 var i = this._overlays.indexOf(overlay);
5073 if (i === -1) {
5074 return;
5075 }
5076 this._overlays.splice(i, 1);
5077 this.trackBackdrop();
5078 },
5079 currentOverlay: function() {
5080 var i = this._overlays.length - 1;
5081 return this._overlays[i];
5082 },
5083 currentOverlayZ: function() {
5084 return this._getZ(this.currentOverlay());
5085 },
5086 ensureMinimumZ: function(minimumZ) {
5087 this._minimumZ = Math.max(this._minimumZ, minimumZ);
5088 },
5089 focusOverlay: function() {
5090 var current = this.currentOverlay();
5091 if (current) {
5092 current._applyFocus();
5093 }
5094 },
5095 trackBackdrop: function() {
5096 var overlay = this._overlayWithBackdrop();
5097 if (!overlay && !this._backdropElement) {
5098 return;
5099 }
5100 this.backdropElement.style.zIndex = this._getZ(overlay) - 1;
5101 this.backdropElement.opened = !!overlay;
5102 },
5103 getBackdrops: function() {
5104 var backdrops = [];
5105 for (var i = 0; i < this._overlays.length; i++) {
5106 if (this._overlays[i].withBackdrop) {
5107 backdrops.push(this._overlays[i]);
5108 }
5109 }
5110 return backdrops;
5111 },
5112 backdropZ: function() {
5113 return this._getZ(this._overlayWithBackdrop()) - 1;
5114 },
5115 _overlayWithBackdrop: function() {
5116 for (var i = 0; i < this._overlays.length; i++) {
5117 if (this._overlays[i].withBackdrop) {
5118 return this._overlays[i];
5119 }
5120 }
5121 },
5122 _getZ: function(overlay) {
5123 var z = this._minimumZ;
5124 if (overlay) {
5125 var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay).z Index);
5126 if (z1 === z1) {
5127 z = z1;
5128 }
5129 }
5130 return z;
5131 },
5132 _setZ: function(element, z) {
5133 element.style.zIndex = z;
5134 },
5135 _applyOverlayZ: function(overlay, aboveZ) {
5136 this._setZ(overlay, aboveZ + 2);
5137 },
5138 _overlayInPath: function(path) {
5139 path = path || [];
5140 for (var i = 0; i < path.length; i++) {
5141 if (path[i]._manager === this) {
5142 return path[i];
5143 }
5144 }
5145 },
5146 _onCaptureClick: function(event) {
5147 var overlay = this.currentOverlay();
5148 if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) {
5149 overlay._onCaptureClick(event);
5150 }
5151 },
5152 _onCaptureFocus: function(event) {
5153 var overlay = this.currentOverlay();
5154 if (overlay) {
5155 overlay._onCaptureFocus(event);
5156 }
5157 },
5158 _onCaptureKeyDown: function(event) {
5159 var overlay = this.currentOverlay();
5160 if (overlay) {
5161 if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc')) {
5162 overlay._onCaptureEsc(event);
5163 } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 't ab')) {
5164 overlay._onCaptureTab(event);
5165 }
5166 }
5167 },
5168 _shouldBeBehindOverlay: function(overlay1, overlay2) {
5169 return !overlay1.alwaysOnTop && overlay2.alwaysOnTop;
5170 }
5171 };
5172
5173 Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass();
5174
5175 (function() {
5176 'use strict';
5177 Polymer.IronOverlayBehaviorImpl = {
5178 properties: {
5179 opened: {
5180 observer: '_openedChanged',
5181 type: Boolean,
5182 value: false,
5183 notify: true
5184 },
5185 canceled: {
5186 observer: '_canceledChanged',
5187 readOnly: true,
5188 type: Boolean,
5189 value: false
5190 },
5191 withBackdrop: {
5192 observer: '_withBackdropChanged',
5193 type: Boolean
5194 },
5195 noAutoFocus: {
5196 type: Boolean,
5197 value: false
5198 },
5199 noCancelOnEscKey: {
5200 type: Boolean,
5201 value: false
5202 },
5203 noCancelOnOutsideClick: {
5204 type: Boolean,
5205 value: false
5206 },
5207 closingReason: {
5208 type: Object
5209 },
5210 restoreFocusOnClose: {
5211 type: Boolean,
5212 value: false
5213 },
5214 alwaysOnTop: {
5215 type: Boolean
5216 },
5217 _manager: {
5218 type: Object,
5219 value: Polymer.IronOverlayManager
5220 },
5221 _focusedChild: {
5222 type: Object
5223 }
5224 },
5225 listeners: {
5226 'iron-resize': '_onIronResize'
5227 },
5228 get backdropElement() {
5229 return this._manager.backdropElement;
5230 },
5231 get _focusNode() {
5232 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]' ) || this;
5233 },
5234 get _focusableNodes() {
5235 var FOCUSABLE_WITH_DISABLED = [ 'a[href]', 'area[href]', 'iframe', '[tabin dex]', '[contentEditable=true]' ];
5236 var FOCUSABLE_WITHOUT_DISABLED = [ 'input', 'select', 'textarea', 'button' ];
5237 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"])';
5238 var focusables = Polymer.dom(this).querySelectorAll(selector);
5239 if (this.tabIndex >= 0) {
5240 focusables.splice(0, 0, this);
5241 }
5242 return focusables.sort(function(a, b) {
5243 if (a.tabIndex === b.tabIndex) {
5244 return 0;
5245 }
5246 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) {
5247 return 1;
5248 }
5249 return -1;
5250 });
5251 },
5252 ready: function() {
5253 this.__isAnimating = false;
5254 this.__shouldRemoveTabIndex = false;
5255 this.__firstFocusableNode = this.__lastFocusableNode = null;
5256 this.__raf = null;
5257 this.__restoreFocusNode = null;
5258 this._ensureSetup();
5259 },
5260 attached: function() {
5261 if (this.opened) {
5262 this._openedChanged(this.opened);
5263 }
5264 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange);
5265 },
5266 detached: function() {
5267 Polymer.dom(this).unobserveNodes(this._observer);
5268 this._observer = null;
5269 if (this.__raf) {
5270 window.cancelAnimationFrame(this.__raf);
5271 this.__raf = null;
5272 }
5273 this._manager.removeOverlay(this);
5274 },
5275 toggle: function() {
5276 this._setCanceled(false);
5277 this.opened = !this.opened;
5278 },
5279 open: function() {
5280 this._setCanceled(false);
5281 this.opened = true;
5282 },
5283 close: function() {
5284 this._setCanceled(false);
5285 this.opened = false;
5286 },
5287 cancel: function(event) {
5288 var cancelEvent = this.fire('iron-overlay-canceled', event, {
5289 cancelable: true
5290 });
5291 if (cancelEvent.defaultPrevented) {
5292 return;
5293 }
5294 this._setCanceled(true);
5295 this.opened = false;
5296 },
5297 _ensureSetup: function() {
5298 if (this._overlaySetup) {
5299 return;
5300 }
5301 this._overlaySetup = true;
5302 this.style.outline = 'none';
5303 this.style.display = 'none';
5304 },
5305 _openedChanged: function(opened) {
5306 if (opened) {
5307 this.removeAttribute('aria-hidden');
5308 } else {
5309 this.setAttribute('aria-hidden', 'true');
5310 }
5311 if (!this.isAttached) {
5312 return;
5313 }
5314 this.__isAnimating = true;
5315 this.__onNextAnimationFrame(this.__openedChanged);
5316 },
5317 _canceledChanged: function() {
5318 this.closingReason = this.closingReason || {};
5319 this.closingReason.canceled = this.canceled;
5320 },
5321 _withBackdropChanged: function() {
5322 if (this.withBackdrop && !this.hasAttribute('tabindex')) {
5323 this.setAttribute('tabindex', '-1');
5324 this.__shouldRemoveTabIndex = true;
5325 } else if (this.__shouldRemoveTabIndex) {
5326 this.removeAttribute('tabindex');
5327 this.__shouldRemoveTabIndex = false;
5328 }
5329 if (this.opened && this.isAttached) {
5330 this._manager.trackBackdrop();
5331 }
5332 },
5333 _prepareRenderOpened: function() {
5334 this.__restoreFocusNode = this._manager.deepActiveElement;
5335 this._preparePositioning();
5336 this.refit();
5337 this._finishPositioning();
5338 if (this.noAutoFocus && document.activeElement === this._focusNode) {
5339 this._focusNode.blur();
5340 this.__restoreFocusNode.focus();
5341 }
5342 },
5343 _renderOpened: function() {
5344 this._finishRenderOpened();
5345 },
5346 _renderClosed: function() {
5347 this._finishRenderClosed();
5348 },
5349 _finishRenderOpened: function() {
5350 this.notifyResize();
5351 this.__isAnimating = false;
5352 var focusableNodes = this._focusableNodes;
5353 this.__firstFocusableNode = focusableNodes[0];
5354 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1];
5355 this.fire('iron-overlay-opened');
5356 },
5357 _finishRenderClosed: function() {
5358 this.style.display = 'none';
5359 this.style.zIndex = '';
5360 this.notifyResize();
5361 this.__isAnimating = false;
5362 this.fire('iron-overlay-closed', this.closingReason);
5363 },
5364 _preparePositioning: function() {
5365 this.style.transition = this.style.webkitTransition = 'none';
5366 this.style.transform = this.style.webkitTransform = 'none';
5367 this.style.display = '';
5368 },
5369 _finishPositioning: function() {
5370 this.style.display = 'none';
5371 this.scrollTop = this.scrollTop;
5372 this.style.transition = this.style.webkitTransition = '';
5373 this.style.transform = this.style.webkitTransform = '';
5374 this.style.display = '';
5375 this.scrollTop = this.scrollTop;
5376 },
5377 _applyFocus: function() {
5378 if (this.opened) {
5379 if (!this.noAutoFocus) {
5380 this._focusNode.focus();
5381 }
5382 } else {
5383 this._focusNode.blur();
5384 this._focusedChild = null;
5385 if (this.restoreFocusOnClose && this.__restoreFocusNode) {
5386 this.__restoreFocusNode.focus();
5387 }
5388 this.__restoreFocusNode = null;
5389 var currentOverlay = this._manager.currentOverlay();
5390 if (currentOverlay && this !== currentOverlay) {
5391 currentOverlay._applyFocus();
5392 }
5393 }
5394 },
5395 _onCaptureClick: function(event) {
5396 if (!this.noCancelOnOutsideClick) {
5397 this.cancel(event);
5398 }
5399 },
5400 _onCaptureFocus: function(event) {
5401 if (!this.withBackdrop) {
5402 return;
5403 }
5404 var path = Polymer.dom(event).path;
5405 if (path.indexOf(this) === -1) {
5406 event.stopPropagation();
5407 this._applyFocus();
5408 } else {
5409 this._focusedChild = path[0];
5410 }
5411 },
5412 _onCaptureEsc: function(event) {
5413 if (!this.noCancelOnEscKey) {
5414 this.cancel(event);
5415 }
5416 },
5417 _onCaptureTab: function(event) {
5418 if (!this.withBackdrop) {
5419 return;
5420 }
5421 var shift = event.shiftKey;
5422 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable Node;
5423 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo de;
5424 var shouldWrap = false;
5425 if (nodeToCheck === nodeToSet) {
5426 shouldWrap = true;
5427 } else {
5428 var focusedNode = this._manager.deepActiveElement;
5429 shouldWrap = focusedNode === nodeToCheck || focusedNode === this;
5430 }
5431 if (shouldWrap) {
5432 event.preventDefault();
5433 this._focusedChild = nodeToSet;
5434 this._applyFocus();
5435 }
5436 },
5437 _onIronResize: function() {
5438 if (this.opened && !this.__isAnimating) {
5439 this.__onNextAnimationFrame(this.refit);
5440 }
5441 },
5442 _onNodesChange: function() {
5443 if (this.opened && !this.__isAnimating) {
5444 this.notifyResize();
5445 }
5446 },
5447 __openedChanged: function() {
5448 if (this.opened) {
5449 this._prepareRenderOpened();
5450 this._manager.addOverlay(this);
5451 this._applyFocus();
5452 this._renderOpened();
5453 } else {
5454 this._manager.removeOverlay(this);
5455 this._applyFocus();
5456 this._renderClosed();
5457 }
5458 },
5459 __onNextAnimationFrame: function(callback) {
5460 if (this.__raf) {
5461 window.cancelAnimationFrame(this.__raf);
5462 }
5463 var self = this;
5464 this.__raf = window.requestAnimationFrame(function nextAnimationFrame() {
5465 self.__raf = null;
5466 callback.call(self);
5467 });
5468 }
5469 };
5470 Polymer.IronOverlayBehavior = [ Polymer.IronFitBehavior, Polymer.IronResizable Behavior, Polymer.IronOverlayBehaviorImpl ];
5471 })();
5472
5473 Polymer.NeonAnimatableBehavior = {
5474 properties: {
5475 animationConfig: {
5476 type: Object
5477 },
5478 entryAnimation: {
5479 observer: '_entryAnimationChanged',
5480 type: String
5481 },
5482 exitAnimation: {
5483 observer: '_exitAnimationChanged',
5484 type: String
5485 }
5486 },
5487 _entryAnimationChanged: function() {
5488 this.animationConfig = this.animationConfig || {};
5489 this.animationConfig['entry'] = [ {
5490 name: this.entryAnimation,
5491 node: this
5492 } ];
5493 },
5494 _exitAnimationChanged: function() {
5495 this.animationConfig = this.animationConfig || {};
5496 this.animationConfig['exit'] = [ {
5497 name: this.exitAnimation,
5498 node: this
5499 } ];
5500 },
5501 _copyProperties: function(config1, config2) {
5502 for (var property in config2) {
5503 config1[property] = config2[property];
5504 }
5505 },
5506 _cloneConfig: function(config) {
5507 var clone = {
5508 isClone: true
5509 };
5510 this._copyProperties(clone, config);
5511 return clone;
5512 },
5513 _getAnimationConfigRecursive: function(type, map, allConfigs) {
5514 if (!this.animationConfig) {
5515 return;
5516 }
5517 if (this.animationConfig.value && typeof this.animationConfig.value === 'fun ction') {
5518 this._warn(this._logf('playAnimation', "Please put 'animationConfig' insid e of your components 'properties' object instead of outside of it."));
5519 return;
5520 }
5521 var thisConfig;
5522 if (type) {
5523 thisConfig = this.animationConfig[type];
5524 } else {
5525 thisConfig = this.animationConfig;
5526 }
5527 if (!Array.isArray(thisConfig)) {
5528 thisConfig = [ thisConfig ];
5529 }
5530 if (thisConfig) {
5531 for (var config, index = 0; config = thisConfig[index]; index++) {
5532 if (config.animatable) {
5533 config.animatable._getAnimationConfigRecursive(config.type || type, ma p, allConfigs);
5534 } else {
5535 if (config.id) {
5536 var cachedConfig = map[config.id];
5537 if (cachedConfig) {
5538 if (!cachedConfig.isClone) {
5539 map[config.id] = this._cloneConfig(cachedConfig);
5540 cachedConfig = map[config.id];
5541 }
5542 this._copyProperties(cachedConfig, config);
5543 } else {
5544 map[config.id] = config;
5545 }
5546 } else {
5547 allConfigs.push(config);
5548 }
5549 }
5550 }
5551 }
5552 },
5553 getAnimationConfig: function(type) {
5554 var map = {};
5555 var allConfigs = [];
5556 this._getAnimationConfigRecursive(type, map, allConfigs);
5557 for (var key in map) {
5558 allConfigs.push(map[key]);
5559 }
5560 return allConfigs;
5561 }
5562 };
5563
5564 Polymer.NeonAnimationRunnerBehaviorImpl = {
5565 _configureAnimations: function(configs) {
5566 var results = [];
5567 if (configs.length > 0) {
5568 for (var config, index = 0; config = configs[index]; index++) {
5569 var neonAnimation = document.createElement(config.name);
5570 if (neonAnimation.isNeonAnimation) {
5571 var result = null;
5572 try {
5573 result = neonAnimation.configure(config);
5574 if (typeof result.cancel != 'function') {
5575 result = document.timeline.play(result);
5576 }
5577 } catch (e) {
5578 result = null;
5579 console.warn('Couldnt play', '(', config.name, ').', e);
5580 }
5581 if (result) {
5582 results.push({
5583 neonAnimation: neonAnimation,
5584 config: config,
5585 animation: result
5586 });
5587 }
5588 } else {
5589 console.warn(this.is + ':', config.name, 'not found!');
5590 }
5591 }
5592 }
5593 return results;
5594 },
5595 _shouldComplete: function(activeEntries) {
5596 var finished = true;
5597 for (var i = 0; i < activeEntries.length; i++) {
5598 if (activeEntries[i].animation.playState != 'finished') {
5599 finished = false;
5600 break;
5601 }
5602 }
5603 return finished;
5604 },
5605 _complete: function(activeEntries) {
5606 for (var i = 0; i < activeEntries.length; i++) {
5607 activeEntries[i].neonAnimation.complete(activeEntries[i].config);
5608 }
5609 for (var i = 0; i < activeEntries.length; i++) {
5610 activeEntries[i].animation.cancel();
5611 }
5612 },
5613 playAnimation: function(type, cookie) {
5614 var configs = this.getAnimationConfig(type);
5615 if (!configs) {
5616 return;
5617 }
5618 this._active = this._active || {};
5619 if (this._active[type]) {
5620 this._complete(this._active[type]);
5621 delete this._active[type];
5622 }
5623 var activeEntries = this._configureAnimations(configs);
5624 if (activeEntries.length == 0) {
5625 this.fire('neon-animation-finish', cookie, {
5626 bubbles: false
5627 });
5628 return;
5629 }
5630 this._active[type] = activeEntries;
5631 for (var i = 0; i < activeEntries.length; i++) {
5632 activeEntries[i].animation.onfinish = function() {
5633 if (this._shouldComplete(activeEntries)) {
5634 this._complete(activeEntries);
5635 delete this._active[type];
5636 this.fire('neon-animation-finish', cookie, {
5637 bubbles: false
5638 });
5639 }
5640 }.bind(this);
5641 }
5642 },
5643 cancelAnimation: function() {
5644 for (var k in this._animations) {
5645 this._animations[k].cancel();
5646 }
5647 this._animations = {};
5648 }
5649 };
5650
5651 Polymer.NeonAnimationRunnerBehavior = [ Polymer.NeonAnimatableBehavior, Polymer. NeonAnimationRunnerBehaviorImpl ];
5652
5653 Polymer.NeonAnimationBehavior = {
5654 properties: {
5655 animationTiming: {
5656 type: Object,
5657 value: function() {
5658 return {
5659 duration: 500,
5660 easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
5661 fill: 'both'
5662 };
5663 }
5664 }
5665 },
5666 isNeonAnimation: true,
5667 timingFromConfig: function(config) {
5668 if (config.timing) {
5669 for (var property in config.timing) {
5670 this.animationTiming[property] = config.timing[property];
5671 }
5672 }
5673 return this.animationTiming;
5674 },
5675 setPrefixedProperty: function(node, property, value) {
5676 var map = {
5677 transform: [ 'webkitTransform' ],
5678 transformOrigin: [ 'mozTransformOrigin', 'webkitTransformOrigin' ]
5679 };
5680 var prefixes = map[property];
5681 for (var prefix, index = 0; prefix = prefixes[index]; index++) {
5682 node.style[prefix] = value;
5683 }
5684 node.style[property] = value;
5685 },
5686 complete: function() {}
5687 };
5688
5689 Polymer({
5690 is: 'opaque-animation',
5691 behaviors: [ Polymer.NeonAnimationBehavior ],
5692 configure: function(config) {
5693 var node = config.node;
5694 this._effect = new KeyframeEffect(node, [ {
5695 opacity: '1'
5696 }, {
5697 opacity: '1'
5698 } ], this.timingFromConfig(config));
5699 node.style.opacity = '0';
5700 return this._effect;
5701 },
5702 complete: function(config) {
5703 config.node.style.opacity = '';
5704 }
5705 });
5706
5707 (function() {
5708 'use strict';
5709 var LAST_TOUCH_POSITION = {
5710 pageX: 0,
5711 pageY: 0
5712 };
5713 var ROOT_TARGET = null;
5714 var SCROLLABLE_NODES = [];
5715 Polymer.IronDropdownScrollManager = {
5716 get currentLockingElement() {
5717 return this._lockingElements[this._lockingElements.length - 1];
5718 },
5719 elementIsScrollLocked: function(element) {
5720 var currentLockingElement = this.currentLockingElement;
5721 if (currentLockingElement === undefined) return false;
5722 var scrollLocked;
5723 if (this._hasCachedLockedElement(element)) {
5724 return true;
5725 }
5726 if (this._hasCachedUnlockedElement(element)) {
5727 return false;
5728 }
5729 scrollLocked = !!currentLockingElement && currentLockingElement !== elemen t && !this._composedTreeContains(currentLockingElement, element);
5730 if (scrollLocked) {
5731 this._lockedElementCache.push(element);
5732 } else {
5733 this._unlockedElementCache.push(element);
5734 }
5735 return scrollLocked;
5736 },
5737 pushScrollLock: function(element) {
5738 if (this._lockingElements.indexOf(element) >= 0) {
5739 return;
5740 }
5741 if (this._lockingElements.length === 0) {
5742 this._lockScrollInteractions();
5743 }
5744 this._lockingElements.push(element);
5745 this._lockedElementCache = [];
5746 this._unlockedElementCache = [];
5747 },
5748 removeScrollLock: function(element) {
5749 var index = this._lockingElements.indexOf(element);
5750 if (index === -1) {
5751 return;
5752 }
5753 this._lockingElements.splice(index, 1);
5754 this._lockedElementCache = [];
5755 this._unlockedElementCache = [];
5756 if (this._lockingElements.length === 0) {
5757 this._unlockScrollInteractions();
5758 }
5759 },
5760 _lockingElements: [],
5761 _lockedElementCache: null,
5762 _unlockedElementCache: null,
5763 _hasCachedLockedElement: function(element) {
5764 return this._lockedElementCache.indexOf(element) > -1;
5765 },
5766 _hasCachedUnlockedElement: function(element) {
5767 return this._unlockedElementCache.indexOf(element) > -1;
5768 },
5769 _composedTreeContains: function(element, child) {
5770 var contentElements;
5771 var distributedNodes;
5772 var contentIndex;
5773 var nodeIndex;
5774 if (element.contains(child)) {
5775 return true;
5776 }
5777 contentElements = Polymer.dom(element).querySelectorAll('content');
5778 for (contentIndex = 0; contentIndex < contentElements.length; ++contentInd ex) {
5779 distributedNodes = Polymer.dom(contentElements[contentIndex]).getDistrib utedNodes();
5780 for (nodeIndex = 0; nodeIndex < distributedNodes.length; ++nodeIndex) {
5781 if (this._composedTreeContains(distributedNodes[nodeIndex], child)) {
5782 return true;
5783 }
5784 }
5785 }
5786 return false;
5787 },
5788 _scrollInteractionHandler: function(event) {
5789 if (event.cancelable && this._shouldPreventScrolling(event)) {
5790 event.preventDefault();
5791 }
5792 if (event.targetTouches) {
5793 var touch = event.targetTouches[0];
5794 LAST_TOUCH_POSITION.pageX = touch.pageX;
5795 LAST_TOUCH_POSITION.pageY = touch.pageY;
5796 }
5797 },
5798 _lockScrollInteractions: function() {
5799 this._boundScrollHandler = this._boundScrollHandler || this._scrollInterac tionHandler.bind(this);
5800 document.addEventListener('wheel', this._boundScrollHandler, true);
5801 document.addEventListener('mousewheel', this._boundScrollHandler, true);
5802 document.addEventListener('DOMMouseScroll', this._boundScrollHandler, true );
5803 document.addEventListener('touchstart', this._boundScrollHandler, true);
5804 document.addEventListener('touchmove', this._boundScrollHandler, true);
5805 },
5806 _unlockScrollInteractions: function() {
5807 document.removeEventListener('wheel', this._boundScrollHandler, true);
5808 document.removeEventListener('mousewheel', this._boundScrollHandler, true) ;
5809 document.removeEventListener('DOMMouseScroll', this._boundScrollHandler, t rue);
5810 document.removeEventListener('touchstart', this._boundScrollHandler, true) ;
5811 document.removeEventListener('touchmove', this._boundScrollHandler, true);
5812 },
5813 _shouldPreventScrolling: function(event) {
5814 var target = Polymer.dom(event).rootTarget;
5815 if (event.type !== 'touchmove' && ROOT_TARGET !== target) {
5816 ROOT_TARGET = target;
5817 SCROLLABLE_NODES = this._getScrollableNodes(Polymer.dom(event).path);
5818 }
5819 if (!SCROLLABLE_NODES.length) {
5820 return true;
5821 }
5822 if (event.type === 'touchstart') {
5823 return false;
5824 }
5825 var info = this._getScrollInfo(event);
5826 return !this._getScrollingNode(SCROLLABLE_NODES, info.deltaX, info.deltaY) ;
5827 },
5828 _getScrollableNodes: function(nodes) {
5829 var scrollables = [];
5830 var lockingIndex = nodes.indexOf(this.currentLockingElement);
5831 for (var i = 0; i <= lockingIndex; i++) {
5832 var node = nodes[i];
5833 if (node.nodeType === 11) {
5834 continue;
5835 }
5836 var style = node.style;
5837 if (style.overflow !== 'scroll' && style.overflow !== 'auto') {
5838 style = window.getComputedStyle(node);
5839 }
5840 if (style.overflow === 'scroll' || style.overflow === 'auto') {
5841 scrollables.push(node);
5842 }
5843 }
5844 return scrollables;
5845 },
5846 _getScrollingNode: function(nodes, deltaX, deltaY) {
5847 if (!deltaX && !deltaY) {
5848 return;
5849 }
5850 var verticalScroll = Math.abs(deltaY) >= Math.abs(deltaX);
5851 for (var i = 0; i < nodes.length; i++) {
5852 var node = nodes[i];
5853 var canScroll = false;
5854 if (verticalScroll) {
5855 canScroll = deltaY < 0 ? node.scrollTop > 0 : node.scrollTop < node.sc rollHeight - node.clientHeight;
5856 } else {
5857 canScroll = deltaX < 0 ? node.scrollLeft > 0 : node.scrollLeft < node. scrollWidth - node.clientWidth;
5858 }
5859 if (canScroll) {
5860 return node;
5861 }
5862 }
5863 },
5864 _getScrollInfo: function(event) {
5865 var info = {
5866 deltaX: event.deltaX,
5867 deltaY: event.deltaY
5868 };
5869 if ('deltaX' in event) {} else if ('wheelDeltaX' in event) {
5870 info.deltaX = -event.wheelDeltaX;
5871 info.deltaY = -event.wheelDeltaY;
5872 } else if ('axis' in event) {
5873 info.deltaX = event.axis === 1 ? event.detail : 0;
5874 info.deltaY = event.axis === 2 ? event.detail : 0;
5875 } else if (event.targetTouches) {
5876 var touch = event.targetTouches[0];
5877 info.deltaX = LAST_TOUCH_POSITION.pageX - touch.pageX;
5878 info.deltaY = LAST_TOUCH_POSITION.pageY - touch.pageY;
5879 }
5880 return info;
5881 }
5882 };
5883 })();
5884
5885 (function() {
5886 'use strict';
5887 Polymer({
5888 is: 'iron-dropdown',
5889 behaviors: [ Polymer.IronControlState, Polymer.IronA11yKeysBehavior, Polymer .IronOverlayBehavior, Polymer.NeonAnimationRunnerBehavior ],
5890 properties: {
5891 horizontalAlign: {
5892 type: String,
5893 value: 'left',
5894 reflectToAttribute: true
5895 },
5896 verticalAlign: {
5897 type: String,
5898 value: 'top',
5899 reflectToAttribute: true
5900 },
5901 openAnimationConfig: {
5902 type: Object
5903 },
5904 closeAnimationConfig: {
5905 type: Object
5906 },
5907 focusTarget: {
5908 type: Object
5909 },
5910 noAnimations: {
5911 type: Boolean,
5912 value: false
5913 },
5914 allowOutsideScroll: {
5915 type: Boolean,
5916 value: false
5917 },
5918 _boundOnCaptureScroll: {
5919 type: Function,
5920 value: function() {
5921 return this._onCaptureScroll.bind(this);
5922 }
5923 }
5924 },
5925 listeners: {
5926 'neon-animation-finish': '_onNeonAnimationFinish'
5927 },
5928 observers: [ '_updateOverlayPosition(positionTarget, verticalAlign, horizont alAlign, verticalOffset, horizontalOffset)' ],
5929 get containedElement() {
5930 return Polymer.dom(this.$.content).getDistributedNodes()[0];
5931 },
5932 get _focusTarget() {
5933 return this.focusTarget || this.containedElement;
5934 },
5935 ready: function() {
5936 this._scrollTop = 0;
5937 this._scrollLeft = 0;
5938 this._refitOnScrollRAF = null;
5939 },
5940 detached: function() {
5941 this.cancelAnimation();
5942 Polymer.IronDropdownScrollManager.removeScrollLock(this);
5943 },
5944 _openedChanged: function() {
5945 if (this.opened && this.disabled) {
5946 this.cancel();
5947 } else {
5948 this.cancelAnimation();
5949 this.sizingTarget = this.containedElement || this.sizingTarget;
5950 this._updateAnimationConfig();
5951 this._saveScrollPosition();
5952 if (this.opened) {
5953 document.addEventListener('scroll', this._boundOnCaptureScroll);
5954 !this.allowOutsideScroll && Polymer.IronDropdownScrollManager.pushScro llLock(this);
5955 } else {
5956 document.removeEventListener('scroll', this._boundOnCaptureScroll);
5957 Polymer.IronDropdownScrollManager.removeScrollLock(this);
5958 }
5959 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments);
5960 }
5961 },
5962 _renderOpened: function() {
5963 if (!this.noAnimations && this.animationConfig.open) {
5964 this.$.contentWrapper.classList.add('animating');
5965 this.playAnimation('open');
5966 } else {
5967 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments);
5968 }
5969 },
5970 _renderClosed: function() {
5971 if (!this.noAnimations && this.animationConfig.close) {
5972 this.$.contentWrapper.classList.add('animating');
5973 this.playAnimation('close');
5974 } else {
5975 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments);
5976 }
5977 },
5978 _onNeonAnimationFinish: function() {
5979 this.$.contentWrapper.classList.remove('animating');
5980 if (this.opened) {
5981 this._finishRenderOpened();
5982 } else {
5983 this._finishRenderClosed();
5984 }
5985 },
5986 _onCaptureScroll: function() {
5987 if (!this.allowOutsideScroll) {
5988 this._restoreScrollPosition();
5989 } else {
5990 this._refitOnScrollRAF && window.cancelAnimationFrame(this._refitOnScrol lRAF);
5991 this._refitOnScrollRAF = window.requestAnimationFrame(this.refit.bind(th is));
5992 }
5993 },
5994 _saveScrollPosition: function() {
5995 if (document.scrollingElement) {
5996 this._scrollTop = document.scrollingElement.scrollTop;
5997 this._scrollLeft = document.scrollingElement.scrollLeft;
5998 } else {
5999 this._scrollTop = Math.max(document.documentElement.scrollTop, document. body.scrollTop);
6000 this._scrollLeft = Math.max(document.documentElement.scrollLeft, documen t.body.scrollLeft);
6001 }
6002 },
6003 _restoreScrollPosition: function() {
6004 if (document.scrollingElement) {
6005 document.scrollingElement.scrollTop = this._scrollTop;
6006 document.scrollingElement.scrollLeft = this._scrollLeft;
6007 } else {
6008 document.documentElement.scrollTop = this._scrollTop;
6009 document.documentElement.scrollLeft = this._scrollLeft;
6010 document.body.scrollTop = this._scrollTop;
6011 document.body.scrollLeft = this._scrollLeft;
6012 }
6013 },
6014 _updateAnimationConfig: function() {
6015 var animations = (this.openAnimationConfig || []).concat(this.closeAnimati onConfig || []);
6016 for (var i = 0; i < animations.length; i++) {
6017 animations[i].node = this.containedElement;
6018 }
6019 this.animationConfig = {
6020 open: this.openAnimationConfig,
6021 close: this.closeAnimationConfig
6022 };
6023 },
6024 _updateOverlayPosition: function() {
6025 if (this.isAttached) {
6026 this.notifyResize();
6027 }
6028 },
6029 _applyFocus: function() {
6030 var focusTarget = this.focusTarget || this.containedElement;
6031 if (focusTarget && this.opened && !this.noAutoFocus) {
6032 focusTarget.focus();
6033 } else {
6034 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments);
6035 }
6036 }
6037 });
6038 })();
6039
6040 Polymer({
6041 is: 'fade-in-animation',
6042 behaviors: [ Polymer.NeonAnimationBehavior ],
6043 configure: function(config) {
6044 var node = config.node;
6045 this._effect = new KeyframeEffect(node, [ {
6046 opacity: '0'
6047 }, {
6048 opacity: '1'
6049 } ], this.timingFromConfig(config));
6050 return this._effect;
6051 }
6052 });
6053
6054 Polymer({
6055 is: 'fade-out-animation',
6056 behaviors: [ Polymer.NeonAnimationBehavior ],
6057 configure: function(config) {
6058 var node = config.node;
6059 this._effect = new KeyframeEffect(node, [ {
6060 opacity: '1'
6061 }, {
6062 opacity: '0'
6063 } ], this.timingFromConfig(config));
6064 return this._effect;
6065 }
6066 });
6067
6068 Polymer({
6069 is: 'paper-menu-grow-height-animation',
6070 behaviors: [ Polymer.NeonAnimationBehavior ],
6071 configure: function(config) {
6072 var node = config.node;
6073 var rect = node.getBoundingClientRect();
6074 var height = rect.height;
6075 this._effect = new KeyframeEffect(node, [ {
6076 height: height / 2 + 'px'
6077 }, {
6078 height: height + 'px'
6079 } ], this.timingFromConfig(config));
6080 return this._effect;
6081 }
6082 });
6083
6084 Polymer({
6085 is: 'paper-menu-grow-width-animation',
6086 behaviors: [ Polymer.NeonAnimationBehavior ],
6087 configure: function(config) {
6088 var node = config.node;
6089 var rect = node.getBoundingClientRect();
6090 var width = rect.width;
6091 this._effect = new KeyframeEffect(node, [ {
6092 width: width / 2 + 'px'
6093 }, {
6094 width: width + 'px'
6095 } ], this.timingFromConfig(config));
6096 return this._effect;
6097 }
6098 });
6099
6100 Polymer({
6101 is: 'paper-menu-shrink-width-animation',
6102 behaviors: [ Polymer.NeonAnimationBehavior ],
6103 configure: function(config) {
6104 var node = config.node;
6105 var rect = node.getBoundingClientRect();
6106 var width = rect.width;
6107 this._effect = new KeyframeEffect(node, [ {
6108 width: width + 'px'
6109 }, {
6110 width: width - width / 20 + 'px'
6111 } ], this.timingFromConfig(config));
6112 return this._effect;
6113 }
6114 });
6115
6116 Polymer({
6117 is: 'paper-menu-shrink-height-animation',
6118 behaviors: [ Polymer.NeonAnimationBehavior ],
6119 configure: function(config) {
6120 var node = config.node;
6121 var rect = node.getBoundingClientRect();
6122 var height = rect.height;
6123 var top = rect.top;
6124 this.setPrefixedProperty(node, 'transformOrigin', '0 0');
6125 this._effect = new KeyframeEffect(node, [ {
6126 height: height + 'px',
6127 transform: 'translateY(0)'
6128 }, {
6129 height: height / 2 + 'px',
6130 transform: 'translateY(-20px)'
6131 } ], this.timingFromConfig(config));
6132 return this._effect;
6133 }
6134 });
6135
6136 // Copyright 2016 The Chromium Authors. All rights reserved.
6137 // Use of this source code is governed by a BSD-style license that can be
6138 // found in the LICENSE file.
6139 var SLIDE_CUBIC_BEZIER = 'cubic-bezier(0.3, 0.95, 0.5, 1)';
6140
6141 Polymer({
6142 is: 'cr-shared-menu',
6143 behaviors: [ Polymer.IronA11yKeysBehavior ],
6144 properties: {
6145 menuOpen: {
6146 type: Boolean,
6147 observer: 'menuOpenChanged_',
6148 value: false
6149 },
6150 itemData: {
6151 type: Object,
6152 value: null
6153 },
6154 keyEventTarget: {
6155 type: Object,
6156 value: function() {
6157 return this.$.menu;
6158 }
6159 },
6160 openAnimationConfig: {
6161 type: Object,
6162 value: function() {
6163 return [ {
6164 name: 'fade-in-animation',
6165 timing: {
6166 delay: 50,
6167 duration: 200
6168 }
6169 }, {
6170 name: 'paper-menu-grow-width-animation',
6171 timing: {
6172 delay: 50,
6173 duration: 150,
6174 easing: SLIDE_CUBIC_BEZIER
6175 }
6176 }, {
6177 name: 'paper-menu-grow-height-animation',
6178 timing: {
6179 delay: 100,
6180 duration: 275,
6181 easing: SLIDE_CUBIC_BEZIER
6182 }
6183 } ];
6184 }
6185 },
6186 closeAnimationConfig: {
6187 type: Object,
6188 value: function() {
6189 return [ {
6190 name: 'fade-out-animation',
6191 timing: {
6192 duration: 150
6193 }
6194 } ];
6195 }
6196 }
6197 },
6198 keyBindings: {
6199 tab: 'onTabPressed_'
6200 },
6201 listeners: {
6202 'dropdown.iron-overlay-canceled': 'onOverlayCanceled_'
6203 },
6204 lastAnchor_: null,
6205 firstFocus_: null,
6206 lastFocus_: null,
6207 attached: function() {
6208 window.addEventListener('resize', this.closeMenu.bind(this));
6209 },
6210 closeMenu: function() {
6211 if (this.root.activeElement == null) {
6212 this.$.dropdown.restoreFocusOnClose = false;
6213 }
6214 this.menuOpen = false;
6215 },
6216 openMenu: function(anchor, itemData) {
6217 if (this.lastAnchor_ == anchor && this.menuOpen) return;
6218 if (this.menuOpen) this.closeMenu();
6219 this.itemData = itemData;
6220 this.lastAnchor_ = anchor;
6221 this.$.dropdown.restoreFocusOnClose = true;
6222 var focusableChildren = Polymer.dom(this).querySelectorAll('[tabindex]:not([ hidden]),button:not([hidden])');
6223 if (focusableChildren.length > 0) {
6224 this.$.dropdown.focusTarget = focusableChildren[0];
6225 this.firstFocus_ = focusableChildren[0];
6226 this.lastFocus_ = focusableChildren[focusableChildren.length - 1];
6227 }
6228 this.$.dropdown.positionTarget = anchor;
6229 this.menuOpen = true;
6230 },
6231 toggleMenu: function(anchor, itemData) {
6232 if (anchor == this.lastAnchor_ && this.menuOpen) this.closeMenu(); else this .openMenu(anchor, itemData);
6233 },
6234 onTabPressed_: function(e) {
6235 if (!this.firstFocus_ || !this.lastFocus_) return;
6236 var toFocus;
6237 var keyEvent = e.detail.keyboardEvent;
6238 if (keyEvent.shiftKey && keyEvent.target == this.firstFocus_) toFocus = this .lastFocus_; else if (keyEvent.target == this.lastFocus_) toFocus = this.firstFo cus_;
6239 if (!toFocus) return;
6240 e.preventDefault();
6241 toFocus.focus();
6242 },
6243 menuOpenChanged_: function() {
6244 if (!this.menuOpen) {
6245 this.itemData = null;
6246 this.lastAnchor_ = null;
6247 }
6248 },
6249 onOverlayCanceled_: function(e) {
6250 if (e.detail.type == 'tap') this.$.dropdown.restoreFocusOnClose = false;
6251 }
6252 });
6253
6254 Polymer.PaperItemBehaviorImpl = {
6255 hostAttributes: {
6256 role: 'option',
6257 tabindex: '0'
6258 }
6259 };
6260
6261 Polymer.PaperItemBehavior = [ Polymer.IronButtonState, Polymer.IronControlState, Polymer.PaperItemBehaviorImpl ];
6262
6263 Polymer({
6264 is: 'paper-item',
6265 behaviors: [ Polymer.PaperItemBehavior ]
6266 });
6267
6268 Polymer({
6269 is: 'iron-collapse',
6270 behaviors: [ Polymer.IronResizableBehavior ],
6271 properties: {
6272 horizontal: {
6273 type: Boolean,
6274 value: false,
6275 observer: '_horizontalChanged'
6276 },
6277 opened: {
6278 type: Boolean,
6279 value: false,
6280 notify: true,
6281 observer: '_openedChanged'
6282 },
6283 noAnimation: {
6284 type: Boolean
6285 }
6286 },
6287 get dimension() {
6288 return this.horizontal ? 'width' : 'height';
6289 },
6290 get _dimensionMax() {
6291 return this.horizontal ? 'maxWidth' : 'maxHeight';
6292 },
6293 get _dimensionMaxCss() {
6294 return this.horizontal ? 'max-width' : 'max-height';
6295 },
6296 hostAttributes: {
6297 role: 'group',
6298 'aria-hidden': 'true',
6299 'aria-expanded': 'false'
6300 },
6301 listeners: {
6302 transitionend: '_transitionEnd'
6303 },
6304 attached: function() {
6305 this._transitionEnd();
6306 },
6307 toggle: function() {
6308 this.opened = !this.opened;
6309 },
6310 show: function() {
6311 this.opened = true;
6312 },
6313 hide: function() {
6314 this.opened = false;
6315 },
6316 updateSize: function(size, animated) {
6317 var curSize = this.style[this._dimensionMax];
6318 if (curSize === size || size === 'auto' && !curSize) {
6319 return;
6320 }
6321 this._updateTransition(false);
6322 if (animated && !this.noAnimation && this._isDisplayed) {
6323 var startSize = this._calcSize();
6324 if (size === 'auto') {
6325 this.style[this._dimensionMax] = '';
6326 size = this._calcSize();
6327 }
6328 this.style[this._dimensionMax] = startSize;
6329 this.scrollTop = this.scrollTop;
6330 this._updateTransition(true);
6331 }
6332 if (size === 'auto') {
6333 this.style[this._dimensionMax] = '';
6334 } else {
6335 this.style[this._dimensionMax] = size;
6336 }
6337 },
6338 enableTransition: function(enabled) {
6339 Polymer.Base._warn('`enableTransition()` is deprecated, use `noAnimation` in stead.');
6340 this.noAnimation = !enabled;
6341 },
6342 _updateTransition: function(enabled) {
6343 this.style.transitionDuration = enabled && !this.noAnimation ? '' : '0s';
6344 },
6345 _horizontalChanged: function() {
6346 this.style.transitionProperty = this._dimensionMaxCss;
6347 var otherDimension = this._dimensionMax === 'maxWidth' ? 'maxHeight' : 'maxW idth';
6348 this.style[otherDimension] = '';
6349 this.updateSize(this.opened ? 'auto' : '0px', false);
6350 },
6351 _openedChanged: function() {
6352 this.setAttribute('aria-expanded', this.opened);
6353 this.setAttribute('aria-hidden', !this.opened);
6354 this.toggleClass('iron-collapse-closed', false);
6355 this.toggleClass('iron-collapse-opened', false);
6356 this.updateSize(this.opened ? 'auto' : '0px', true);
6357 if (this.opened) {
6358 this.focus();
6359 }
6360 if (this.noAnimation) {
6361 this._transitionEnd();
6362 }
6363 },
6364 _transitionEnd: function() {
6365 if (this.opened) {
6366 this.style[this._dimensionMax] = '';
6367 }
6368 this.toggleClass('iron-collapse-closed', !this.opened);
6369 this.toggleClass('iron-collapse-opened', this.opened);
6370 this._updateTransition(false);
6371 this.notifyResize();
6372 },
6373 get _isDisplayed() {
6374 var rect = this.getBoundingClientRect();
6375 for (var prop in rect) {
6376 if (rect[prop] !== 0) return true;
6377 }
6378 return false;
6379 },
6380 _calcSize: function() {
6381 return this.getBoundingClientRect()[this.dimension] + 'px';
6382 }
6383 });
6384
6385 Polymer.IronFormElementBehavior = {
6386 properties: {
6387 name: {
6388 type: String
6389 },
6390 value: {
6391 notify: true,
6392 type: String
6393 },
6394 required: {
6395 type: Boolean,
6396 value: false
6397 },
6398 _parentForm: {
6399 type: Object
6400 }
6401 },
6402 attached: function() {
6403 this.fire('iron-form-element-register');
6404 },
6405 detached: function() {
6406 if (this._parentForm) {
6407 this._parentForm.fire('iron-form-element-unregister', {
6408 target: this
6409 });
6410 }
6411 }
6412 };
6413
6414 Polymer.IronCheckedElementBehaviorImpl = {
6415 properties: {
6416 checked: {
6417 type: Boolean,
6418 value: false,
6419 reflectToAttribute: true,
6420 notify: true,
6421 observer: '_checkedChanged'
6422 },
6423 toggles: {
6424 type: Boolean,
6425 value: true,
6426 reflectToAttribute: true
6427 },
6428 value: {
6429 type: String,
6430 value: 'on',
6431 observer: '_valueChanged'
6432 }
6433 },
6434 observers: [ '_requiredChanged(required)' ],
6435 created: function() {
6436 this._hasIronCheckedElementBehavior = true;
6437 },
6438 _getValidity: function(_value) {
6439 return this.disabled || !this.required || this.checked;
6440 },
6441 _requiredChanged: function() {
6442 if (this.required) {
6443 this.setAttribute('aria-required', 'true');
6444 } else {
6445 this.removeAttribute('aria-required');
6446 }
6447 },
6448 _checkedChanged: function() {
6449 this.active = this.checked;
6450 this.fire('iron-change');
6451 },
6452 _valueChanged: function() {
6453 if (this.value === undefined || this.value === null) {
6454 this.value = 'on';
6455 }
6456 }
6457 };
6458
6459 Polymer.IronCheckedElementBehavior = [ Polymer.IronFormElementBehavior, Polymer. IronValidatableBehavior, Polymer.IronCheckedElementBehaviorImpl ];
6460
6461 Polymer.PaperCheckedElementBehaviorImpl = {
6462 _checkedChanged: function() {
6463 Polymer.IronCheckedElementBehaviorImpl._checkedChanged.call(this);
6464 if (this.hasRipple()) {
6465 if (this.checked) {
6466 this._ripple.setAttribute('checked', '');
6467 } else {
6468 this._ripple.removeAttribute('checked');
6469 }
6470 }
6471 },
6472 _buttonStateChanged: function() {
6473 Polymer.PaperRippleBehavior._buttonStateChanged.call(this);
6474 if (this.disabled) {
6475 return;
6476 }
6477 if (this.isAttached) {
6478 this.checked = this.active;
6479 }
6480 }
6481 };
6482
6483 Polymer.PaperCheckedElementBehavior = [ Polymer.PaperInkyFocusBehavior, Polymer. IronCheckedElementBehavior, Polymer.PaperCheckedElementBehaviorImpl ];
6484
6485 Polymer({
6486 is: 'paper-checkbox',
6487 behaviors: [ Polymer.PaperCheckedElementBehavior ],
6488 hostAttributes: {
6489 role: 'checkbox',
6490 'aria-checked': false,
6491 tabindex: 0
6492 },
6493 properties: {
6494 ariaActiveAttribute: {
6495 type: String,
6496 value: 'aria-checked'
6497 }
6498 },
6499 _computeCheckboxClass: function(checked, invalid) {
6500 var className = '';
6501 if (checked) {
6502 className += 'checked ';
6503 }
6504 if (invalid) {
6505 className += 'invalid';
6506 }
6507 return className;
6508 },
6509 _computeCheckmarkClass: function(checked) {
6510 return checked ? '' : 'hidden';
6511 },
6512 _createRipple: function() {
6513 this._rippleContainer = this.$.checkboxContainer;
6514 return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this);
6515 }
6516 });
6517
6518 Polymer({
6519 is: 'paper-icon-button-light',
6520 "extends": 'button',
6521 behaviors: [ Polymer.PaperRippleBehavior ],
6522 listeners: {
6523 down: '_rippleDown',
6524 up: '_rippleUp',
6525 focus: '_rippleDown',
6526 blur: '_rippleUp'
6527 },
6528 _rippleDown: function() {
6529 this.getRipple().downAction();
6530 },
6531 _rippleUp: function() {
6532 this.getRipple().upAction();
6533 },
6534 ensureRipple: function(var_args) {
6535 var lastRipple = this._ripple;
6536 Polymer.PaperRippleBehavior.ensureRipple.apply(this, arguments);
6537 if (this._ripple && this._ripple !== lastRipple) {
6538 this._ripple.center = true;
6539 this._ripple.classList.add('circle');
6540 }
6541 }
6542 });
6543
6544 // Copyright 2016 The Chromium Authors. All rights reserved.
6545 // Use of this source code is governed by a BSD-style license that can be
6546 // found in the LICENSE file.
6547 cr.define('cr.icon', function() {
6548 function getSupportedScaleFactors() {
6549 var supportedScaleFactors = [];
6550 if (cr.isMac || cr.isChromeOS || cr.isWindows || cr.isLinux) {
6551 supportedScaleFactors.push(1);
6552 supportedScaleFactors.push(2);
6553 } else {
6554 supportedScaleFactors.push(window.devicePixelRatio);
6555 }
6556 return supportedScaleFactors;
6557 }
6558 function getProfileAvatarIcon(path) {
6559 var chromeThemePath = 'chrome://theme';
6560 var isDefaultAvatar = path.slice(0, chromeThemePath.length) == chromeThemePa th;
6561 return isDefaultAvatar ? imageset(path + '@scalefactorx') : url(path);
6562 }
6563 function imageset(path) {
6564 var supportedScaleFactors = getSupportedScaleFactors();
6565 var replaceStartIndex = path.indexOf('scalefactor');
6566 if (replaceStartIndex < 0) return url(path);
6567 var s = '';
6568 for (var i = 0; i < supportedScaleFactors.length; ++i) {
6569 var scaleFactor = supportedScaleFactors[i];
6570 var pathWithScaleFactor = path.substr(0, replaceStartIndex) + scaleFactor + path.substr(replaceStartIndex + 'scalefactor'.length);
6571 s += url(pathWithScaleFactor) + ' ' + scaleFactor + 'x';
6572 if (i != supportedScaleFactors.length - 1) s += ', ';
6573 }
6574 return '-webkit-image-set(' + s + ')';
6575 }
6576 var FAVICON_URL_REGEX = /\.ico$/i;
6577 function getFaviconImageSet(url, opt_size, opt_type) {
6578 var size = opt_size || 16;
6579 var type = opt_type || 'favicon';
6580 return imageset('chrome://' + type + '/size/' + size + '@scalefactorx/' + (F AVICON_URL_REGEX.test(url) ? 'iconurl/' : '') + url);
6581 }
6582 return {
6583 getSupportedScaleFactors: getSupportedScaleFactors,
6584 getProfileAvatarIcon: getProfileAvatarIcon,
6585 getFaviconImageSet: getFaviconImageSet
6586 };
6587 });
6588
6589 // Copyright 2016 The Chromium Authors. All rights reserved.
6590 // Use of this source code is governed by a BSD-style license that can be
6591 // found in the LICENSE file.
6592 cr.define('md_history', function() {
6593 function BrowserService() {
6594 this.pendingDeleteItems_ = null;
6595 this.pendingDeletePromise_ = null;
6596 }
6597 BrowserService.prototype = {
6598 deleteItems: function(items) {
6599 if (this.pendingDeleteItems_ != null) {
6600 return new Promise(function(resolve, reject) {
6601 reject(items);
6602 });
6603 }
6604 var removalList = items.map(function(item) {
6605 return {
6606 url: item.url,
6607 timestamps: item.allTimestamps
6608 };
6609 });
6610 this.pendingDeleteItems_ = items;
6611 this.pendingDeletePromise_ = new PromiseResolver();
6612 chrome.send('removeVisits', removalList);
6613 return this.pendingDeletePromise_.promise;
6614 },
6615 removeBookmark: function(url) {
6616 chrome.send('removeBookmark', [ url ]);
6617 },
6618 openForeignSessionAllTabs: function(sessionTag) {
6619 chrome.send('openForeignSession', [ sessionTag ]);
6620 },
6621 openForeignSessionTab: function(sessionTag, windowId, tabId, e) {
6622 chrome.send('openForeignSession', [ sessionTag, String(windowId), String(t abId), e.button || 0, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey ]);
6623 },
6624 deleteForeignSession: function(sessionTag) {
6625 chrome.send('deleteForeignSession', [ sessionTag ]);
6626 },
6627 openClearBrowsingData: function() {
6628 chrome.send('clearBrowsingData');
6629 },
6630 resolveDelete_: function(successful) {
6631 if (this.pendingDeleteItems_ == null || this.pendingDeletePromise_ == null ) {
6632 return;
6633 }
6634 if (successful) this.pendingDeletePromise_.resolve(this.pendingDeleteItems _); else this.pendingDeletePromise_.reject(this.pendingDeleteItems_);
6635 this.pendingDeleteItems_ = null;
6636 this.pendingDeletePromise_ = null;
6637 }
6638 };
6639 cr.addSingletonGetter(BrowserService);
6640 return {
6641 BrowserService: BrowserService
6642 };
6643 });
6644
6645 function deleteComplete() {
6646 md_history.BrowserService.getInstance().resolveDelete_(true);
6647 }
6648
6649 function deleteFailed() {
6650 md_history.BrowserService.getInstance().resolveDelete_(false);
6651 }
6652
6653 // Copyright 2016 The Chromium Authors. All rights reserved.
6654 // Use of this source code is governed by a BSD-style license that can be
6655 // found in the LICENSE file.
6656 Polymer({
6657 is: 'history-searched-label',
6658 properties: {
6659 title: String,
6660 searchTerm: String
6661 },
6662 observers: [ 'setSearchedTextToBold_(title, searchTerm)' ],
6663 setSearchedTextToBold_: function() {
6664 var i = 0;
6665 var titleElem = this.$.container;
6666 var titleText = this.title;
6667 if (this.searchTerm == '' || this.searchTerm == null) {
6668 titleElem.textContent = titleText;
6669 return;
6670 }
6671 var re = new RegExp(quoteString(this.searchTerm), 'gim');
6672 var match;
6673 titleElem.textContent = '';
6674 while (match = re.exec(titleText)) {
6675 if (match.index > i) titleElem.appendChild(document.createTextNode(titleTe xt.slice(i, match.index)));
6676 i = re.lastIndex;
6677 var b = document.createElement('b');
6678 b.textContent = titleText.substring(match.index, i);
6679 titleElem.appendChild(b);
6680 }
6681 if (i < titleText.length) titleElem.appendChild(document.createTextNode(titl eText.slice(i)));
6682 }
6683 });
6684
6685 // Copyright 2015 The Chromium Authors. All rights reserved.
6686 // Use of this source code is governed by a BSD-style license that can be
6687 // found in the LICENSE file.
6688 cr.define('md_history', function() {
6689 var HistoryItem = Polymer({
6690 is: 'history-item',
6691 properties: {
6692 item: {
6693 type: Object,
6694 observer: 'showIcon_'
6695 },
6696 searchTerm: {
6697 type: String
6698 },
6699 selected: {
6700 type: Boolean,
6701 notify: true
6702 },
6703 isFirstItem: {
6704 type: Boolean,
6705 reflectToAttribute: true
6706 },
6707 isCardStart: {
6708 type: Boolean,
6709 reflectToAttribute: true
6710 },
6711 isCardEnd: {
6712 type: Boolean,
6713 reflectToAttribute: true
6714 },
6715 embedded: {
6716 type: Boolean,
6717 reflectToAttribute: true
6718 },
6719 hasTimeGap: {
6720 type: Boolean
6721 },
6722 numberOfItems: {
6723 type: Number
6724 },
6725 path: String
6726 },
6727 onCheckboxSelected_: function() {
6728 this.fire('history-checkbox-select', {
6729 element: this,
6730 countAddition: this.$.checkbox.checked ? 1 : -1
6731 });
6732 },
6733 onRemoveBookmarkTap_: function() {
6734 if (!this.item.starred) return;
6735 if (this.$$('#bookmark-star') == this.root.activeElement) this.$['menu-but ton'].focus();
6736 md_history.BrowserService.getInstance().removeBookmark(this.item.url);
6737 this.fire('remove-bookmark-stars', this.item.url);
6738 },
6739 onMenuButtonTap_: function(e) {
6740 this.fire('toggle-menu', {
6741 target: Polymer.dom(e).localTarget,
6742 item: this.item
6743 });
6744 e.stopPropagation();
6745 },
6746 showIcon_: function() {
6747 this.$.icon.style.backgroundImage = cr.icon.getFaviconImageSet(this.item.u rl);
6748 },
6749 selectionNotAllowed_: function() {
6750 return !loadTimeData.getBoolean('allowDeletingHistory');
6751 },
6752 cardTitle_: function(numberOfItems, historyDate, search) {
6753 if (!search) return this.item.dateRelativeDay;
6754 var resultId = numberOfItems == 1 ? 'searchResult' : 'searchResults';
6755 return loadTimeData.getStringF('foundSearchResults', numberOfItems, loadTi meData.getString(resultId), search);
6756 },
6757 cropItemTitle_: function(title) {
6758 return title.length > TITLE_MAX_LENGTH ? title.substr(0, TITLE_MAX_LENGTH) : title;
6759 }
6760 });
6761 HistoryItem.needsTimeGap = function(visits, currentIndex, searchedTerm) {
6762 if (currentIndex >= visits.length - 1 || visits.length == 0) return false;
6763 var currentItem = visits[currentIndex];
6764 var nextItem = visits[currentIndex + 1];
6765 if (searchedTerm) return currentItem.dateShort != nextItem.dateShort;
6766 return currentItem.time - nextItem.time > BROWSING_GAP_TIME && currentItem.d ateRelativeDay == nextItem.dateRelativeDay;
6767 };
6768 return {
6769 HistoryItem: HistoryItem
6770 };
6771 });
6772
6773 // Copyright 2016 The Chromium Authors. All rights reserved.
6774 // Use of this source code is governed by a BSD-style license that can be
6775 // found in the LICENSE file.
6776 var SelectionTreeNode = function(currentPath) {
6777 this.currentPath = currentPath;
6778 this.leaf = false;
6779 this.indexes = [];
6780 this.children = [];
6781 };
6782
6783 SelectionTreeNode.prototype.addChild = function(index, path) {
6784 this.indexes.push(index);
6785 this.children[index] = new SelectionTreeNode(path);
6786 };
6787
6788 var HistoryListBehavior = {
6789 properties: {
6790 selectedPaths: {
6791 type: Array,
6792 value: function() {
6793 return [];
6794 }
6795 }
6796 },
6797 listeners: {
6798 'history-checkbox-select': 'itemSelected_'
6799 },
6800 hasResults: function(historyDataLength) {
6801 return historyDataLength > 0;
6802 },
6803 noResultsMessage: function(searchedTerm, isLoading) {
6804 if (isLoading) return '';
6805 var messageId = searchedTerm !== '' ? 'noSearchResults' : 'noResults';
6806 return loadTimeData.getString(messageId);
6807 },
6808 unselectAllItems: function() {
6809 this.selectedPaths.forEach(function(path) {
6810 this.set(path + '.selected', false);
6811 }.bind(this));
6812 this.selectedPaths = [];
6813 },
6814 deleteSelected: function() {
6815 var toBeRemoved = this.selectedPaths.map(function(path) {
6816 return this.get(path);
6817 }.bind(this));
6818 md_history.BrowserService.getInstance().deleteItems(toBeRemoved).then(functi on() {
6819 this.removeItemsByPath(this.selectedPaths);
6820 this.fire('unselect-all');
6821 }.bind(this));
6822 },
6823 removeItemsByPath: function(paths) {
6824 if (paths.length == 0) return;
6825 this.removeItemsBeneathNode_(this.buildRemovalTree_(paths));
6826 },
6827 buildRemovalTree_: function(paths) {
6828 var rootNode = new SelectionTreeNode(paths[0].split('.')[0]);
6829 paths.forEach(function(path) {
6830 var components = path.split('.');
6831 var node = rootNode;
6832 components.shift();
6833 while (components.length > 1) {
6834 var index = Number(components.shift());
6835 var arrayName = components.shift();
6836 if (!node.children[index]) node.addChild(index, [ node.currentPath, inde x, arrayName ].join('.'));
6837 node = node.children[index];
6838 }
6839 node.leaf = true;
6840 node.indexes.push(Number(components.shift()));
6841 });
6842 return rootNode;
6843 },
6844 removeItemsBeneathNode_: function(node) {
6845 var array = this.get(node.currentPath);
6846 var splices = [];
6847 node.indexes.sort(function(a, b) {
6848 return b - a;
6849 });
6850 node.indexes.forEach(function(index) {
6851 if (node.leaf || this.removeItemsBeneathNode_(node.children[index])) {
6852 var item = array.splice(index, 1);
6853 splices.push({
6854 index: index,
6855 removed: [ item ],
6856 addedCount: 0,
6857 object: array,
6858 type: 'splice'
6859 });
6860 }
6861 }.bind(this));
6862 if (array.length == 0) return true;
6863 this.notifySplices(node.currentPath, splices);
6864 return false;
6865 },
6866 itemSelected_: function(e) {
6867 var item = e.detail.element;
6868 var path = item.path;
6869 if (item.selected) {
6870 this.push('selectedPaths', path);
6871 return;
6872 }
6873 var index = this.selectedPaths.indexOf(path);
6874 if (index != -1) this.splice('selectedPaths', index, 1);
6875 }
6876 };
6877
6878 // Copyright 2016 The Chromium Authors. All rights reserved.
6879 // Use of this source code is governed by a BSD-style license that can be
6880 // found in the LICENSE file.
6881 var HistoryDomain;
6882
6883 var HistoryGroup;
6884
6885 Polymer({
6886 is: 'history-grouped-list',
6887 behaviors: [ HistoryListBehavior ],
6888 properties: {
6889 historyData: {
6890 type: Array
6891 },
6892 groupedHistoryData_: {
6893 type: Array
6894 },
6895 searchedTerm: {
6896 type: String,
6897 value: ''
6898 },
6899 range: {
6900 type: Number
6901 },
6902 queryStartTime: String,
6903 queryEndTime: String
6904 },
6905 observers: [ 'updateGroupedHistoryData_(range, historyData)' ],
6906 createHistoryDomains_: function(visits) {
6907 var domainIndexes = {};
6908 var domains = [];
6909 for (var i = 0, visit; visit = visits[i]; i++) {
6910 var domain = visit.domain;
6911 if (domainIndexes[domain] == undefined) {
6912 domainIndexes[domain] = domains.length;
6913 domains.push({
6914 domain: domain,
6915 visits: [],
6916 expanded: false,
6917 rendered: false
6918 });
6919 }
6920 domains[domainIndexes[domain]].visits.push(visit);
6921 }
6922 var sortByVisits = function(a, b) {
6923 return b.visits.length - a.visits.length;
6924 };
6925 domains.sort(sortByVisits);
6926 return domains;
6927 },
6928 updateGroupedHistoryData_: function() {
6929 if (this.historyData.length == 0) {
6930 this.groupedHistoryData_ = [];
6931 return;
6932 }
6933 if (this.range == HistoryRange.WEEK) {
6934 var days = [];
6935 var currentDayVisits = [ this.historyData[0] ];
6936 var pushCurrentDay = function() {
6937 days.push({
6938 title: this.searchedTerm ? currentDayVisits[0].dateShort : currentDayV isits[0].dateRelativeDay,
6939 domains: this.createHistoryDomains_(currentDayVisits)
6940 });
6941 }.bind(this);
6942 var visitsSameDay = function(a, b) {
6943 if (this.searchedTerm) return a.dateShort == b.dateShort;
6944 return a.dateRelativeDay == b.dateRelativeDay;
6945 }.bind(this);
6946 for (var i = 1; i < this.historyData.length; i++) {
6947 var visit = this.historyData[i];
6948 if (!visitsSameDay(visit, currentDayVisits[0])) {
6949 pushCurrentDay();
6950 currentDayVisits = [];
6951 }
6952 currentDayVisits.push(visit);
6953 }
6954 pushCurrentDay();
6955 this.groupedHistoryData_ = days;
6956 } else if (this.range == HistoryRange.MONTH) {
6957 this.groupedHistoryData_ = [ {
6958 title: this.queryStartTime + ' – ' + this.queryEndTime,
6959 domains: this.createHistoryDomains_(this.historyData)
6960 } ];
6961 }
6962 },
6963 toggleDomainExpanded_: function(e) {
6964 var collapse = e.currentTarget.parentNode.querySelector('iron-collapse');
6965 e.model.set('domain.rendered', true);
6966 setTimeout(function() {
6967 collapse.toggle();
6968 }, 0);
6969 },
6970 needsTimeGap_: function(groupIndex, domainIndex, itemIndex) {
6971 var visits = this.groupedHistoryData_[groupIndex].domains[domainIndex].visit s;
6972 return md_history.HistoryItem.needsTimeGap(visits, itemIndex, this.searchedT erm);
6973 },
6974 pathForItem_: function(groupIndex, domainIndex, itemIndex) {
6975 return [ 'groupedHistoryData_', groupIndex, 'domains', domainIndex, 'visits' , itemIndex ].join('.');
6976 },
6977 getWebsiteIconStyle_: function(domain) {
6978 return 'background-image: ' + cr.icon.getFaviconImageSet(domain.visits[0].ur l);
6979 },
6980 getDropdownIcon_: function(expanded) {
6981 return expanded ? 'cr:expand-less' : 'cr:expand-more';
6982 }
6983 });
6984
6985 Polymer.IronScrollTargetBehavior = {
6986 properties: {
6987 scrollTarget: {
6988 type: HTMLElement,
6989 value: function() {
6990 return this._defaultScrollTarget;
6991 }
6992 }
6993 },
6994 observers: [ '_scrollTargetChanged(scrollTarget, isAttached)' ],
6995 _scrollTargetChanged: function(scrollTarget, isAttached) {
6996 var eventTarget;
6997 if (this._oldScrollTarget) {
6998 eventTarget = this._oldScrollTarget === this._doc ? window : this._oldScro llTarget;
6999 eventTarget.removeEventListener('scroll', this._boundScrollHandler);
7000 this._oldScrollTarget = null;
7001 }
7002 if (!isAttached) {
7003 return;
7004 }
7005 if (scrollTarget === 'document') {
7006 this.scrollTarget = this._doc;
7007 } else if (typeof scrollTarget === 'string') {
7008 this.scrollTarget = this.domHost ? this.domHost.$[scrollTarget] : Polymer. dom(this.ownerDocument).querySelector('#' + scrollTarget);
7009 } else if (this._isValidScrollTarget()) {
7010 eventTarget = scrollTarget === this._doc ? window : scrollTarget;
7011 this._boundScrollHandler = this._boundScrollHandler || this._scrollHandler .bind(this);
7012 this._oldScrollTarget = scrollTarget;
7013 eventTarget.addEventListener('scroll', this._boundScrollHandler);
7014 }
7015 },
7016 _scrollHandler: function scrollHandler() {},
7017 get _defaultScrollTarget() {
7018 return this._doc;
7019 },
7020 get _doc() {
7021 return this.ownerDocument.documentElement;
7022 },
7023 get _scrollTop() {
7024 if (this._isValidScrollTarget()) {
7025 return this.scrollTarget === this._doc ? window.pageYOffset : this.scrollT arget.scrollTop;
7026 }
7027 return 0;
7028 },
7029 get _scrollLeft() {
7030 if (this._isValidScrollTarget()) {
7031 return this.scrollTarget === this._doc ? window.pageXOffset : this.scrollT arget.scrollLeft;
7032 }
7033 return 0;
7034 },
7035 set _scrollTop(top) {
7036 if (this.scrollTarget === this._doc) {
7037 window.scrollTo(window.pageXOffset, top);
7038 } else if (this._isValidScrollTarget()) {
7039 this.scrollTarget.scrollTop = top;
7040 }
7041 },
7042 set _scrollLeft(left) {
7043 if (this.scrollTarget === this._doc) {
7044 window.scrollTo(left, window.pageYOffset);
7045 } else if (this._isValidScrollTarget()) {
7046 this.scrollTarget.scrollLeft = left;
7047 }
7048 },
7049 scroll: function(left, top) {
7050 if (this.scrollTarget === this._doc) {
7051 window.scrollTo(left, top);
7052 } else if (this._isValidScrollTarget()) {
7053 this.scrollTarget.scrollLeft = left;
7054 this.scrollTarget.scrollTop = top;
7055 }
7056 },
7057 get _scrollTargetWidth() {
7058 if (this._isValidScrollTarget()) {
7059 return this.scrollTarget === this._doc ? window.innerWidth : this.scrollTa rget.offsetWidth;
7060 }
7061 return 0;
7062 },
7063 get _scrollTargetHeight() {
7064 if (this._isValidScrollTarget()) {
7065 return this.scrollTarget === this._doc ? window.innerHeight : this.scrollT arget.offsetHeight;
7066 }
7067 return 0;
7068 },
7069 _isValidScrollTarget: function() {
7070 return this.scrollTarget instanceof HTMLElement;
7071 }
7072 };
7073
7074 (function() {
7075 var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/);
7076 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
7077 var DEFAULT_PHYSICAL_COUNT = 3;
7078 var HIDDEN_Y = '-10000px';
7079 var DEFAULT_GRID_SIZE = 200;
7080 var SECRET_TABINDEX = -100;
7081 Polymer({
7082 is: 'iron-list',
7083 properties: {
7084 items: {
7085 type: Array
7086 },
7087 maxPhysicalCount: {
7088 type: Number,
7089 value: 500
7090 },
7091 as: {
7092 type: String,
7093 value: 'item'
7094 },
7095 indexAs: {
7096 type: String,
7097 value: 'index'
7098 },
7099 selectedAs: {
7100 type: String,
7101 value: 'selected'
7102 },
7103 grid: {
7104 type: Boolean,
7105 value: false,
7106 reflectToAttribute: true
7107 },
7108 selectionEnabled: {
7109 type: Boolean,
7110 value: false
7111 },
7112 selectedItem: {
7113 type: Object,
7114 notify: true
7115 },
7116 selectedItems: {
7117 type: Object,
7118 notify: true
7119 },
7120 multiSelection: {
7121 type: Boolean,
7122 value: false
7123 }
7124 },
7125 observers: [ '_itemsChanged(items.*)', '_selectionEnabledChanged(selectionEn abled)', '_multiSelectionChanged(multiSelection)', '_setOverflow(scrollTarget)' ],
7126 behaviors: [ Polymer.Templatizer, Polymer.IronResizableBehavior, Polymer.Iro nA11yKeysBehavior, Polymer.IronScrollTargetBehavior ],
7127 keyBindings: {
7128 up: '_didMoveUp',
7129 down: '_didMoveDown',
7130 enter: '_didEnter'
7131 },
7132 _ratio: .5,
7133 _scrollerPaddingTop: 0,
7134 _scrollPosition: 0,
7135 _physicalSize: 0,
7136 _physicalAverage: 0,
7137 _physicalAverageCount: 0,
7138 _physicalTop: 0,
7139 _virtualCount: 0,
7140 _physicalIndexForKey: null,
7141 _estScrollHeight: 0,
7142 _scrollHeight: 0,
7143 _viewportHeight: 0,
7144 _viewportWidth: 0,
7145 _physicalItems: null,
7146 _physicalSizes: null,
7147 _firstVisibleIndexVal: null,
7148 _lastVisibleIndexVal: null,
7149 _collection: null,
7150 _itemsRendered: false,
7151 _lastPage: null,
7152 _maxPages: 3,
7153 _focusedItem: null,
7154 _focusedIndex: -1,
7155 _offscreenFocusedItem: null,
7156 _focusBackfillItem: null,
7157 _itemsPerRow: 1,
7158 _itemWidth: 0,
7159 _rowHeight: 0,
7160 get _physicalBottom() {
7161 return this._physicalTop + this._physicalSize;
7162 },
7163 get _scrollBottom() {
7164 return this._scrollPosition + this._viewportHeight;
7165 },
7166 get _virtualEnd() {
7167 return this._virtualStart + this._physicalCount - 1;
7168 },
7169 get _hiddenContentSize() {
7170 var size = this.grid ? this._physicalRows * this._rowHeight : this._physic alSize;
7171 return size - this._viewportHeight;
7172 },
7173 get _maxScrollTop() {
7174 return this._estScrollHeight - this._viewportHeight + this._scrollerPaddin gTop;
7175 },
7176 _minVirtualStart: 0,
7177 get _maxVirtualStart() {
7178 return Math.max(0, this._virtualCount - this._physicalCount);
7179 },
7180 _virtualStartVal: 0,
7181 set _virtualStart(val) {
7182 this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._min VirtualStart, val));
7183 },
7184 get _virtualStart() {
7185 return this._virtualStartVal || 0;
7186 },
7187 _physicalStartVal: 0,
7188 set _physicalStart(val) {
7189 this._physicalStartVal = val % this._physicalCount;
7190 if (this._physicalStartVal < 0) {
7191 this._physicalStartVal = this._physicalCount + this._physicalStartVal;
7192 }
7193 this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this ._physicalCount;
7194 },
7195 get _physicalStart() {
7196 return this._physicalStartVal || 0;
7197 },
7198 _physicalCountVal: 0,
7199 set _physicalCount(val) {
7200 this._physicalCountVal = val;
7201 this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this ._physicalCount;
7202 },
7203 get _physicalCount() {
7204 return this._physicalCountVal;
7205 },
7206 _physicalEnd: 0,
7207 get _optPhysicalSize() {
7208 if (this.grid) {
7209 return this._estRowsInView * this._rowHeight * this._maxPages;
7210 }
7211 return this._viewportHeight * this._maxPages;
7212 },
7213 get _optPhysicalCount() {
7214 return this._estRowsInView * this._itemsPerRow * this._maxPages;
7215 },
7216 get _isVisible() {
7217 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this. scrollTarget.offsetHeight);
7218 },
7219 get firstVisibleIndex() {
7220 if (this._firstVisibleIndexVal === null) {
7221 var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddin gTop);
7222 this._firstVisibleIndexVal = this._iterateItems(function(pidx, vidx) {
7223 physicalOffset += this._getPhysicalSizeIncrement(pidx);
7224 if (physicalOffset > this._scrollPosition) {
7225 return this.grid ? vidx - vidx % this._itemsPerRow : vidx;
7226 }
7227 if (this.grid && this._virtualCount - 1 === vidx) {
7228 return vidx - vidx % this._itemsPerRow;
7229 }
7230 }) || 0;
7231 }
7232 return this._firstVisibleIndexVal;
7233 },
7234 get lastVisibleIndex() {
7235 if (this._lastVisibleIndexVal === null) {
7236 if (this.grid) {
7237 var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._i temsPerRow - 1;
7238 this._lastVisibleIndexVal = Math.min(this._virtualCount, lastIndex);
7239 } else {
7240 var physicalOffset = this._physicalTop;
7241 this._iterateItems(function(pidx, vidx) {
7242 if (physicalOffset < this._scrollBottom) {
7243 this._lastVisibleIndexVal = vidx;
7244 } else {
7245 return true;
7246 }
7247 physicalOffset += this._getPhysicalSizeIncrement(pidx);
7248 });
7249 }
7250 }
7251 return this._lastVisibleIndexVal;
7252 },
7253 get _defaultScrollTarget() {
7254 return this;
7255 },
7256 get _virtualRowCount() {
7257 return Math.ceil(this._virtualCount / this._itemsPerRow);
7258 },
7259 get _estRowsInView() {
7260 return Math.ceil(this._viewportHeight / this._rowHeight);
7261 },
7262 get _physicalRows() {
7263 return Math.ceil(this._physicalCount / this._itemsPerRow);
7264 },
7265 ready: function() {
7266 this.addEventListener('focus', this._didFocus.bind(this), true);
7267 },
7268 attached: function() {
7269 this.updateViewportBoundaries();
7270 this._render();
7271 this.listen(this, 'iron-resize', '_resizeHandler');
7272 },
7273 detached: function() {
7274 this._itemsRendered = false;
7275 this.unlisten(this, 'iron-resize', '_resizeHandler');
7276 },
7277 _setOverflow: function(scrollTarget) {
7278 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : '';
7279 this.style.overflow = scrollTarget === this ? 'auto' : '';
7280 },
7281 updateViewportBoundaries: function() {
7282 this._scrollerPaddingTop = this.scrollTarget === this ? 0 : parseInt(windo w.getComputedStyle(this)['padding-top'], 10);
7283 this._viewportHeight = this._scrollTargetHeight;
7284 if (this.grid) {
7285 this._updateGridMetrics();
7286 }
7287 },
7288 _scrollHandler: function() {
7289 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop)) ;
7290 var delta = scrollTop - this._scrollPosition;
7291 var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBotto m;
7292 var ratio = this._ratio;
7293 var recycledTiles = 0;
7294 var hiddenContentSize = this._hiddenContentSize;
7295 var currentRatio = ratio;
7296 var movingUp = [];
7297 this._scrollPosition = scrollTop;
7298 this._firstVisibleIndexVal = null;
7299 this._lastVisibleIndexVal = null;
7300 scrollBottom = this._scrollBottom;
7301 physicalBottom = this._physicalBottom;
7302 if (Math.abs(delta) > this._physicalSize) {
7303 this._physicalTop += delta;
7304 recycledTiles = Math.round(delta / this._physicalAverage);
7305 } else if (delta < 0) {
7306 var topSpace = scrollTop - this._physicalTop;
7307 var virtualStart = this._virtualStart;
7308 recycledTileSet = [];
7309 kth = this._physicalEnd;
7310 currentRatio = topSpace / hiddenContentSize;
7311 while (currentRatio < ratio && recycledTiles < this._physicalCount && vi rtualStart - recycledTiles > 0 && physicalBottom - this._getPhysicalSizeIncremen t(kth) > scrollBottom) {
7312 tileHeight = this._getPhysicalSizeIncrement(kth);
7313 currentRatio += tileHeight / hiddenContentSize;
7314 physicalBottom -= tileHeight;
7315 recycledTileSet.push(kth);
7316 recycledTiles++;
7317 kth = kth === 0 ? this._physicalCount - 1 : kth - 1;
7318 }
7319 movingUp = recycledTileSet;
7320 recycledTiles = -recycledTiles;
7321 } else if (delta > 0) {
7322 var bottomSpace = physicalBottom - scrollBottom;
7323 var virtualEnd = this._virtualEnd;
7324 var lastVirtualItemIndex = this._virtualCount - 1;
7325 recycledTileSet = [];
7326 kth = this._physicalStart;
7327 currentRatio = bottomSpace / hiddenContentSize;
7328 while (currentRatio < ratio && recycledTiles < this._physicalCount && vi rtualEnd + recycledTiles < lastVirtualItemIndex && this._physicalTop + this._get PhysicalSizeIncrement(kth) < scrollTop) {
7329 tileHeight = this._getPhysicalSizeIncrement(kth);
7330 currentRatio += tileHeight / hiddenContentSize;
7331 this._physicalTop += tileHeight;
7332 recycledTileSet.push(kth);
7333 recycledTiles++;
7334 kth = (kth + 1) % this._physicalCount;
7335 }
7336 }
7337 if (recycledTiles === 0) {
7338 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) {
7339 this._increasePoolIfNeeded();
7340 }
7341 } else {
7342 this._virtualStart = this._virtualStart + recycledTiles;
7343 this._physicalStart = this._physicalStart + recycledTiles;
7344 this._update(recycledTileSet, movingUp);
7345 }
7346 },
7347 _update: function(itemSet, movingUp) {
7348 this._manageFocus();
7349 this._assignModels(itemSet);
7350 this._updateMetrics(itemSet);
7351 if (movingUp) {
7352 while (movingUp.length) {
7353 var idx = movingUp.pop();
7354 this._physicalTop -= this._getPhysicalSizeIncrement(idx);
7355 }
7356 }
7357 this._positionItems();
7358 this._updateScrollerSize();
7359 this._increasePoolIfNeeded();
7360 },
7361 _createPool: function(size) {
7362 var physicalItems = new Array(size);
7363 this._ensureTemplatized();
7364 for (var i = 0; i < size; i++) {
7365 var inst = this.stamp(null);
7366 physicalItems[i] = inst.root.querySelector('*');
7367 Polymer.dom(this).appendChild(inst.root);
7368 }
7369 return physicalItems;
7370 },
7371 _increasePoolIfNeeded: function() {
7372 if (this._viewportHeight === 0) {
7373 return false;
7374 }
7375 var isClientHeightFull = this._physicalBottom >= this._scrollBottom && thi s._physicalTop <= this._scrollPosition;
7376 if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) {
7377 return false;
7378 }
7379 var currentPage = Math.floor(this._physicalSize / this._viewportHeight);
7380 if (currentPage === 0) {
7381 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph ysicalCount * .5)));
7382 } else if (this._lastPage !== currentPage && isClientHeightFull) {
7383 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa sePool.bind(this, this._itemsPerRow), 16));
7384 } else {
7385 this._debounceTemplate(this._increasePool.bind(this, this._itemsPerRow)) ;
7386 }
7387 this._lastPage = currentPage;
7388 return true;
7389 },
7390 _increasePool: function(missingItems) {
7391 var nextPhysicalCount = Math.min(this._physicalCount + missingItems, this. _virtualCount - this._virtualStart, Math.max(this.maxPhysicalCount, DEFAULT_PHYS ICAL_COUNT));
7392 var prevPhysicalCount = this._physicalCount;
7393 var delta = nextPhysicalCount - prevPhysicalCount;
7394 if (delta <= 0) {
7395 return;
7396 }
7397 [].push.apply(this._physicalItems, this._createPool(delta));
7398 [].push.apply(this._physicalSizes, new Array(delta));
7399 this._physicalCount = prevPhysicalCount + delta;
7400 if (this._physicalStart > this._physicalEnd && this._isIndexRendered(this. _focusedIndex) && this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd ) {
7401 this._physicalStart = this._physicalStart + delta;
7402 }
7403 this._update();
7404 },
7405 _render: function() {
7406 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0;
7407 if (this.isAttached && !this._itemsRendered && this._isVisible && requires Update) {
7408 this._lastPage = 0;
7409 this._update();
7410 this._itemsRendered = true;
7411 }
7412 },
7413 _ensureTemplatized: function() {
7414 if (!this.ctor) {
7415 var props = {};
7416 props.__key__ = true;
7417 props[this.as] = true;
7418 props[this.indexAs] = true;
7419 props[this.selectedAs] = true;
7420 props.tabIndex = true;
7421 this._instanceProps = props;
7422 this._userTemplate = Polymer.dom(this).querySelector('template');
7423 if (this._userTemplate) {
7424 this.templatize(this._userTemplate);
7425 } else {
7426 console.warn('iron-list requires a template to be provided in light-do m');
7427 }
7428 }
7429 },
7430 _getStampedChildren: function() {
7431 return this._physicalItems;
7432 },
7433 _forwardInstancePath: function(inst, path, value) {
7434 if (path.indexOf(this.as + '.') === 0) {
7435 this.notifyPath('items.' + inst.__key__ + '.' + path.slice(this.as.lengt h + 1), value);
7436 }
7437 },
7438 _forwardParentProp: function(prop, value) {
7439 if (this._physicalItems) {
7440 this._physicalItems.forEach(function(item) {
7441 item._templateInstance[prop] = value;
7442 }, this);
7443 }
7444 },
7445 _forwardParentPath: function(path, value) {
7446 if (this._physicalItems) {
7447 this._physicalItems.forEach(function(item) {
7448 item._templateInstance.notifyPath(path, value, true);
7449 }, this);
7450 }
7451 },
7452 _forwardItemPath: function(path, value) {
7453 if (!this._physicalIndexForKey) {
7454 return;
7455 }
7456 var dot = path.indexOf('.');
7457 var key = path.substring(0, dot < 0 ? path.length : dot);
7458 var idx = this._physicalIndexForKey[key];
7459 var offscreenItem = this._offscreenFocusedItem;
7460 var el = offscreenItem && offscreenItem._templateInstance.__key__ === key ? offscreenItem : this._physicalItems[idx];
7461 if (!el || el._templateInstance.__key__ !== key) {
7462 return;
7463 }
7464 if (dot >= 0) {
7465 path = this.as + '.' + path.substring(dot + 1);
7466 el._templateInstance.notifyPath(path, value, true);
7467 } else {
7468 var currentItem = el._templateInstance[this.as];
7469 if (Array.isArray(this.selectedItems)) {
7470 for (var i = 0; i < this.selectedItems.length; i++) {
7471 if (this.selectedItems[i] === currentItem) {
7472 this.set('selectedItems.' + i, value);
7473 break;
7474 }
7475 }
7476 } else if (this.selectedItem === currentItem) {
7477 this.set('selectedItem', value);
7478 }
7479 el._templateInstance[this.as] = value;
7480 }
7481 },
7482 _itemsChanged: function(change) {
7483 if (change.path === 'items') {
7484 this._virtualStart = 0;
7485 this._physicalTop = 0;
7486 this._virtualCount = this.items ? this.items.length : 0;
7487 this._collection = this.items ? Polymer.Collection.get(this.items) : nul l;
7488 this._physicalIndexForKey = {};
7489 this._firstVisibleIndexVal = null;
7490 this._lastVisibleIndexVal = null;
7491 this._resetScrollPosition(0);
7492 this._removeFocusedItem();
7493 if (!this._physicalItems) {
7494 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi s._virtualCount));
7495 this._physicalItems = this._createPool(this._physicalCount);
7496 this._physicalSizes = new Array(this._physicalCount);
7497 }
7498 this._physicalStart = 0;
7499 } else if (change.path === 'items.splices') {
7500 this._adjustVirtualIndex(change.value.indexSplices);
7501 this._virtualCount = this.items ? this.items.length : 0;
7502 } else {
7503 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change. value);
7504 return;
7505 }
7506 this._itemsRendered = false;
7507 this._debounceTemplate(this._render);
7508 },
7509 _adjustVirtualIndex: function(splices) {
7510 splices.forEach(function(splice) {
7511 splice.removed.forEach(this._removeItem, this);
7512 if (splice.index < this._virtualStart) {
7513 var delta = Math.max(splice.addedCount - splice.removed.length, splice .index - this._virtualStart);
7514 this._virtualStart = this._virtualStart + delta;
7515 if (this._focusedIndex >= 0) {
7516 this._focusedIndex = this._focusedIndex + delta;
7517 }
7518 }
7519 }, this);
7520 },
7521 _removeItem: function(item) {
7522 this.$.selector.deselect(item);
7523 if (this._focusedItem && this._focusedItem._templateInstance[this.as] === item) {
7524 this._removeFocusedItem();
7525 }
7526 },
7527 _iterateItems: function(fn, itemSet) {
7528 var pidx, vidx, rtn, i;
7529 if (arguments.length === 2 && itemSet) {
7530 for (i = 0; i < itemSet.length; i++) {
7531 pidx = itemSet[i];
7532 vidx = this._computeVidx(pidx);
7533 if ((rtn = fn.call(this, pidx, vidx)) != null) {
7534 return rtn;
7535 }
7536 }
7537 } else {
7538 pidx = this._physicalStart;
7539 vidx = this._virtualStart;
7540 for (;pidx < this._physicalCount; pidx++, vidx++) {
7541 if ((rtn = fn.call(this, pidx, vidx)) != null) {
7542 return rtn;
7543 }
7544 }
7545 for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) {
7546 if ((rtn = fn.call(this, pidx, vidx)) != null) {
7547 return rtn;
7548 }
7549 }
7550 }
7551 },
7552 _computeVidx: function(pidx) {
7553 if (pidx >= this._physicalStart) {
7554 return this._virtualStart + (pidx - this._physicalStart);
7555 }
7556 return this._virtualStart + (this._physicalCount - this._physicalStart) + pidx;
7557 },
7558 _assignModels: function(itemSet) {
7559 this._iterateItems(function(pidx, vidx) {
7560 var el = this._physicalItems[pidx];
7561 var inst = el._templateInstance;
7562 var item = this.items && this.items[vidx];
7563 if (item != null) {
7564 inst[this.as] = item;
7565 inst.__key__ = this._collection.getKey(item);
7566 inst[this.selectedAs] = this.$.selector.isSelected(item);
7567 inst[this.indexAs] = vidx;
7568 inst.tabIndex = this._focusedIndex === vidx ? 0 : -1;
7569 this._physicalIndexForKey[inst.__key__] = pidx;
7570 el.removeAttribute('hidden');
7571 } else {
7572 inst.__key__ = null;
7573 el.setAttribute('hidden', '');
7574 }
7575 }, itemSet);
7576 },
7577 _updateMetrics: function(itemSet) {
7578 Polymer.dom.flush();
7579 var newPhysicalSize = 0;
7580 var oldPhysicalSize = 0;
7581 var prevAvgCount = this._physicalAverageCount;
7582 var prevPhysicalAvg = this._physicalAverage;
7583 this._iterateItems(function(pidx, vidx) {
7584 oldPhysicalSize += this._physicalSizes[pidx] || 0;
7585 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
7586 newPhysicalSize += this._physicalSizes[pidx];
7587 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
7588 }, itemSet);
7589 this._viewportHeight = this._scrollTargetHeight;
7590 if (this.grid) {
7591 this._updateGridMetrics();
7592 this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow) * this._rowHeight;
7593 } else {
7594 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalS ize;
7595 }
7596 if (this._physicalAverageCount !== prevAvgCount) {
7597 this._physicalAverage = Math.round((prevPhysicalAvg * prevAvgCount + new PhysicalSize) / this._physicalAverageCount);
7598 }
7599 },
7600 _updateGridMetrics: function() {
7601 this._viewportWidth = this.$.items.offsetWidth;
7602 this._itemWidth = this._physicalCount > 0 ? this._physicalItems[0].getBoun dingClientRect().width : DEFAULT_GRID_SIZE;
7603 this._rowHeight = this._physicalCount > 0 ? this._physicalItems[0].offsetH eight : DEFAULT_GRID_SIZE;
7604 this._itemsPerRow = this._itemWidth ? Math.floor(this._viewportWidth / thi s._itemWidth) : this._itemsPerRow;
7605 },
7606 _positionItems: function() {
7607 this._adjustScrollPosition();
7608 var y = this._physicalTop;
7609 if (this.grid) {
7610 var totalItemWidth = this._itemsPerRow * this._itemWidth;
7611 var rowOffset = (this._viewportWidth - totalItemWidth) / 2;
7612 this._iterateItems(function(pidx, vidx) {
7613 var modulus = vidx % this._itemsPerRow;
7614 var x = Math.floor(modulus * this._itemWidth + rowOffset);
7615 this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]);
7616 if (this._shouldRenderNextRow(vidx)) {
7617 y += this._rowHeight;
7618 }
7619 });
7620 } else {
7621 this._iterateItems(function(pidx, vidx) {
7622 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]);
7623 y += this._physicalSizes[pidx];
7624 });
7625 }
7626 },
7627 _getPhysicalSizeIncrement: function(pidx) {
7628 if (!this.grid) {
7629 return this._physicalSizes[pidx];
7630 }
7631 if (this._computeVidx(pidx) % this._itemsPerRow !== this._itemsPerRow - 1) {
7632 return 0;
7633 }
7634 return this._rowHeight;
7635 },
7636 _shouldRenderNextRow: function(vidx) {
7637 return vidx % this._itemsPerRow === this._itemsPerRow - 1;
7638 },
7639 _adjustScrollPosition: function() {
7640 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : Math.min( this._scrollPosition + this._physicalTop, 0);
7641 if (deltaHeight) {
7642 this._physicalTop = this._physicalTop - deltaHeight;
7643 if (!IOS_TOUCH_SCROLLING && this._physicalTop !== 0) {
7644 this._resetScrollPosition(this._scrollTop - deltaHeight);
7645 }
7646 }
7647 },
7648 _resetScrollPosition: function(pos) {
7649 if (this.scrollTarget) {
7650 this._scrollTop = pos;
7651 this._scrollPosition = this._scrollTop;
7652 }
7653 },
7654 _updateScrollerSize: function(forceUpdate) {
7655 if (this.grid) {
7656 this._estScrollHeight = this._virtualRowCount * this._rowHeight;
7657 } else {
7658 this._estScrollHeight = this._physicalBottom + Math.max(this._virtualCou nt - this._physicalCount - this._virtualStart, 0) * this._physicalAverage;
7659 }
7660 forceUpdate = forceUpdate || this._scrollHeight === 0;
7661 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize;
7662 forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this ._estScrollHeight;
7663 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) {
7664 this.$.items.style.height = this._estScrollHeight + 'px';
7665 this._scrollHeight = this._estScrollHeight;
7666 }
7667 },
7668 scrollToItem: function(item) {
7669 return this.scrollToIndex(this.items.indexOf(item));
7670 },
7671 scrollToIndex: function(idx) {
7672 if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) {
7673 return;
7674 }
7675 Polymer.dom.flush();
7676 idx = Math.min(Math.max(idx, 0), this._virtualCount - 1);
7677 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) {
7678 this._virtualStart = this.grid ? idx - this._itemsPerRow * 2 : idx - 1;
7679 }
7680 this._manageFocus();
7681 this._assignModels();
7682 this._updateMetrics();
7683 var estPhysicalTop = Math.floor(this._virtualStart / this._itemsPerRow) * this._physicalAverage;
7684 this._physicalTop = estPhysicalTop;
7685 var currentTopItem = this._physicalStart;
7686 var currentVirtualItem = this._virtualStart;
7687 var targetOffsetTop = 0;
7688 var hiddenContentSize = this._hiddenContentSize;
7689 while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) {
7690 targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(curre ntTopItem);
7691 currentTopItem = (currentTopItem + 1) % this._physicalCount;
7692 currentVirtualItem++;
7693 }
7694 this._updateScrollerSize(true);
7695 this._positionItems();
7696 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t argetOffsetTop);
7697 this._increasePoolIfNeeded();
7698 this._firstVisibleIndexVal = null;
7699 this._lastVisibleIndexVal = null;
7700 },
7701 _resetAverage: function() {
7702 this._physicalAverage = 0;
7703 this._physicalAverageCount = 0;
7704 },
7705 _resizeHandler: function() {
7706 if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100 ) {
7707 return;
7708 }
7709 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() {
7710 this.updateViewportBoundaries();
7711 this._render();
7712 if (this._itemsRendered && this._physicalItems && this._isVisible) {
7713 this._resetAverage();
7714 this.scrollToIndex(this.firstVisibleIndex);
7715 }
7716 }.bind(this), 1));
7717 },
7718 _getModelFromItem: function(item) {
7719 var key = this._collection.getKey(item);
7720 var pidx = this._physicalIndexForKey[key];
7721 if (pidx != null) {
7722 return this._physicalItems[pidx]._templateInstance;
7723 }
7724 return null;
7725 },
7726 _getNormalizedItem: function(item) {
7727 if (this._collection.getKey(item) === undefined) {
7728 if (typeof item === 'number') {
7729 item = this.items[item];
7730 if (!item) {
7731 throw new RangeError('<item> not found');
7732 }
7733 return item;
7734 }
7735 throw new TypeError('<item> should be a valid item');
7736 }
7737 return item;
7738 },
7739 selectItem: function(item) {
7740 item = this._getNormalizedItem(item);
7741 var model = this._getModelFromItem(item);
7742 if (!this.multiSelection && this.selectedItem) {
7743 this.deselectItem(this.selectedItem);
7744 }
7745 if (model) {
7746 model[this.selectedAs] = true;
7747 }
7748 this.$.selector.select(item);
7749 this.updateSizeForItem(item);
7750 },
7751 deselectItem: function(item) {
7752 item = this._getNormalizedItem(item);
7753 var model = this._getModelFromItem(item);
7754 if (model) {
7755 model[this.selectedAs] = false;
7756 }
7757 this.$.selector.deselect(item);
7758 this.updateSizeForItem(item);
7759 },
7760 toggleSelectionForItem: function(item) {
7761 item = this._getNormalizedItem(item);
7762 if (this.$.selector.isSelected(item)) {
7763 this.deselectItem(item);
7764 } else {
7765 this.selectItem(item);
7766 }
7767 },
7768 clearSelection: function() {
7769 function unselect(item) {
7770 var model = this._getModelFromItem(item);
7771 if (model) {
7772 model[this.selectedAs] = false;
7773 }
7774 }
7775 if (Array.isArray(this.selectedItems)) {
7776 this.selectedItems.forEach(unselect, this);
7777 } else if (this.selectedItem) {
7778 unselect.call(this, this.selectedItem);
7779 }
7780 this.$.selector.clearSelection();
7781 },
7782 _selectionEnabledChanged: function(selectionEnabled) {
7783 var handler = selectionEnabled ? this.listen : this.unlisten;
7784 handler.call(this, this, 'tap', '_selectionHandler');
7785 },
7786 _selectionHandler: function(e) {
7787 var model = this.modelForElement(e.target);
7788 if (!model) {
7789 return;
7790 }
7791 var modelTabIndex, activeElTabIndex;
7792 var target = Polymer.dom(e).path[0];
7793 var activeEl = Polymer.dom(this.domHost ? this.domHost.root : document).ac tiveElement;
7794 var physicalItem = this._physicalItems[this._getPhysicalIndex(model[this.i ndexAs])];
7795 if (target.localName === 'input' || target.localName === 'button' || targe t.localName === 'select') {
7796 return;
7797 }
7798 modelTabIndex = model.tabIndex;
7799 model.tabIndex = SECRET_TABINDEX;
7800 activeElTabIndex = activeEl ? activeEl.tabIndex : -1;
7801 model.tabIndex = modelTabIndex;
7802 if (activeEl && physicalItem.contains(activeEl) && activeElTabIndex !== SE CRET_TABINDEX) {
7803 return;
7804 }
7805 this.toggleSelectionForItem(model[this.as]);
7806 },
7807 _multiSelectionChanged: function(multiSelection) {
7808 this.clearSelection();
7809 this.$.selector.multi = multiSelection;
7810 },
7811 updateSizeForItem: function(item) {
7812 item = this._getNormalizedItem(item);
7813 var key = this._collection.getKey(item);
7814 var pidx = this._physicalIndexForKey[key];
7815 if (pidx != null) {
7816 this._updateMetrics([ pidx ]);
7817 this._positionItems();
7818 }
7819 },
7820 _manageFocus: function() {
7821 var fidx = this._focusedIndex;
7822 if (fidx >= 0 && fidx < this._virtualCount) {
7823 if (this._isIndexRendered(fidx)) {
7824 this._restoreFocusedItem();
7825 } else {
7826 this._createFocusBackfillItem();
7827 }
7828 } else if (this._virtualCount > 0 && this._physicalCount > 0) {
7829 this._focusedIndex = this._virtualStart;
7830 this._focusedItem = this._physicalItems[this._physicalStart];
7831 }
7832 },
7833 _isIndexRendered: function(idx) {
7834 return idx >= this._virtualStart && idx <= this._virtualEnd;
7835 },
7836 _isIndexVisible: function(idx) {
7837 return idx >= this.firstVisibleIndex && idx <= this.lastVisibleIndex;
7838 },
7839 _getPhysicalIndex: function(idx) {
7840 return this._physicalIndexForKey[this._collection.getKey(this._getNormaliz edItem(idx))];
7841 },
7842 _focusPhysicalItem: function(idx) {
7843 if (idx < 0 || idx >= this._virtualCount) {
7844 return;
7845 }
7846 this._restoreFocusedItem();
7847 if (!this._isIndexRendered(idx)) {
7848 this.scrollToIndex(idx);
7849 }
7850 var physicalItem = this._physicalItems[this._getPhysicalIndex(idx)];
7851 var model = physicalItem._templateInstance;
7852 var focusable;
7853 model.tabIndex = SECRET_TABINDEX;
7854 if (physicalItem.tabIndex === SECRET_TABINDEX) {
7855 focusable = physicalItem;
7856 }
7857 if (!focusable) {
7858 focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECR ET_TABINDEX + '"]');
7859 }
7860 model.tabIndex = 0;
7861 this._focusedIndex = idx;
7862 focusable && focusable.focus();
7863 },
7864 _removeFocusedItem: function() {
7865 if (this._offscreenFocusedItem) {
7866 Polymer.dom(this).removeChild(this._offscreenFocusedItem);
7867 }
7868 this._offscreenFocusedItem = null;
7869 this._focusBackfillItem = null;
7870 this._focusedItem = null;
7871 this._focusedIndex = -1;
7872 },
7873 _createFocusBackfillItem: function() {
7874 var pidx, fidx = this._focusedIndex;
7875 if (this._offscreenFocusedItem || fidx < 0) {
7876 return;
7877 }
7878 if (!this._focusBackfillItem) {
7879 var stampedTemplate = this.stamp(null);
7880 this._focusBackfillItem = stampedTemplate.root.querySelector('*');
7881 Polymer.dom(this).appendChild(stampedTemplate.root);
7882 }
7883 pidx = this._getPhysicalIndex(fidx);
7884 if (pidx != null) {
7885 this._offscreenFocusedItem = this._physicalItems[pidx];
7886 this._physicalItems[pidx] = this._focusBackfillItem;
7887 this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem);
7888 }
7889 },
7890 _restoreFocusedItem: function() {
7891 var pidx, fidx = this._focusedIndex;
7892 if (!this._offscreenFocusedItem || this._focusedIndex < 0) {
7893 return;
7894 }
7895 this._assignModels();
7896 pidx = this._getPhysicalIndex(fidx);
7897 if (pidx != null) {
7898 this._focusBackfillItem = this._physicalItems[pidx];
7899 this._physicalItems[pidx] = this._offscreenFocusedItem;
7900 this._offscreenFocusedItem = null;
7901 this.translate3d(0, HIDDEN_Y, 0, this._focusBackfillItem);
7902 }
7903 },
7904 _didFocus: function(e) {
7905 var targetModel = this.modelForElement(e.target);
7906 var focusedModel = this._focusedItem ? this._focusedItem._templateInstance : null;
7907 var hasOffscreenFocusedItem = this._offscreenFocusedItem !== null;
7908 var fidx = this._focusedIndex;
7909 if (!targetModel || !focusedModel) {
7910 return;
7911 }
7912 if (focusedModel === targetModel) {
7913 if (!this._isIndexVisible(fidx)) {
7914 this.scrollToIndex(fidx);
7915 }
7916 } else {
7917 this._restoreFocusedItem();
7918 focusedModel.tabIndex = -1;
7919 targetModel.tabIndex = 0;
7920 fidx = targetModel[this.indexAs];
7921 this._focusedIndex = fidx;
7922 this._focusedItem = this._physicalItems[this._getPhysicalIndex(fidx)];
7923 if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) {
7924 this._update();
7925 }
7926 }
7927 },
7928 _didMoveUp: function() {
7929 this._focusPhysicalItem(this._focusedIndex - 1);
7930 },
7931 _didMoveDown: function(e) {
7932 e.detail.keyboardEvent.preventDefault();
7933 this._focusPhysicalItem(this._focusedIndex + 1);
7934 },
7935 _didEnter: function(e) {
7936 this._focusPhysicalItem(this._focusedIndex);
7937 this._selectionHandler(e.detail.keyboardEvent);
7938 }
7939 });
7940 })();
7941
7942 Polymer({
7943 is: 'iron-scroll-threshold',
7944 properties: {
7945 upperThreshold: {
7946 type: Number,
7947 value: 100
7948 },
7949 lowerThreshold: {
7950 type: Number,
7951 value: 100
7952 },
7953 upperTriggered: {
7954 type: Boolean,
7955 value: false,
7956 notify: true,
7957 readOnly: true
7958 },
7959 lowerTriggered: {
7960 type: Boolean,
7961 value: false,
7962 notify: true,
7963 readOnly: true
7964 },
7965 horizontal: {
7966 type: Boolean,
7967 value: false
7968 }
7969 },
7970 behaviors: [ Polymer.IronScrollTargetBehavior ],
7971 observers: [ '_setOverflow(scrollTarget)', '_initCheck(horizontal, isAttached) ' ],
7972 get _defaultScrollTarget() {
7973 return this;
7974 },
7975 _setOverflow: function(scrollTarget) {
7976 this.style.overflow = scrollTarget === this ? 'auto' : '';
7977 },
7978 _scrollHandler: function() {
7979 var THROTTLE_THRESHOLD = 200;
7980 if (!this.isDebouncerActive('_checkTheshold')) {
7981 this.debounce('_checkTheshold', function() {
7982 this.checkScrollThesholds();
7983 }, THROTTLE_THRESHOLD);
7984 }
7985 },
7986 _initCheck: function(horizontal, isAttached) {
7987 if (isAttached) {
7988 this.debounce('_init', function() {
7989 this.clearTriggers();
7990 this.checkScrollThesholds();
7991 });
7992 }
7993 },
7994 checkScrollThesholds: function() {
7995 if (!this.scrollTarget || this.lowerTriggered && this.upperTriggered) {
7996 return;
7997 }
7998 var upperScrollValue = this.horizontal ? this._scrollLeft : this._scrollTop;
7999 var lowerScrollValue = this.horizontal ? this.scrollTarget.scrollWidth - thi s._scrollTargetWidth - this._scrollLeft : this.scrollTarget.scrollHeight - this. _scrollTargetHeight - this._scrollTop;
8000 if (upperScrollValue <= this.upperThreshold && !this.upperTriggered) {
8001 this._setUpperTriggered(true);
8002 this.fire('upper-threshold');
8003 }
8004 if (lowerScrollValue <= this.lowerThreshold && !this.lowerTriggered) {
8005 this._setLowerTriggered(true);
8006 this.fire('lower-threshold');
8007 }
8008 },
8009 clearTriggers: function() {
8010 this._setUpperTriggered(false);
8011 this._setLowerTriggered(false);
8012 }
8013 });
8014
8015 // Copyright 2015 The Chromium Authors. All rights reserved.
8016 // Use of this source code is governed by a BSD-style license that can be
8017 // found in the LICENSE file.
8018 Polymer({
8019 is: 'history-list',
8020 behaviors: [ HistoryListBehavior ],
8021 properties: {
8022 searchedTerm: {
8023 type: String,
8024 value: ''
8025 },
8026 lastSearchedTerm_: String,
8027 querying: Boolean,
8028 historyData_: Array,
8029 resultLoadingDisabled_: {
8030 type: Boolean,
8031 value: false
8032 }
8033 },
8034 listeners: {
8035 'infinite-list.scroll': 'notifyListScroll_',
8036 'remove-bookmark-stars': 'removeBookmarkStars_'
8037 },
8038 attached: function() {
8039 this.$['infinite-list'].notifyResize();
8040 },
8041 removeBookmarkStars_: function(e) {
8042 var url = e.detail;
8043 if (this.historyData_ === undefined) return;
8044 for (var i = 0; i < this.historyData_.length; i++) {
8045 if (this.historyData_[i].url == url) this.set('historyData_.' + i + '.star red', false);
8046 }
8047 },
8048 disableResultLoading: function() {
8049 this.resultLoadingDisabled_ = true;
8050 },
8051 addNewResults: function(historyResults) {
8052 var results = historyResults.slice();
8053 this.$['scroll-threshold'].clearTriggers();
8054 if (this.lastSearchedTerm_ != this.searchedTerm) {
8055 this.resultLoadingDisabled_ = false;
8056 if (this.historyData_) this.splice('historyData_', 0, this.historyData_.le ngth);
8057 this.fire('unselect-all');
8058 this.lastSearchedTerm_ = this.searchedTerm;
8059 }
8060 if (this.historyData_) {
8061 results.unshift('historyData_');
8062 this.push.apply(this, results);
8063 } else {
8064 this.set('historyData_', results);
8065 }
8066 },
8067 loadMoreData_: function() {
8068 if (this.resultLoadingDisabled_ || this.querying) return;
8069 this.fire('load-more-history');
8070 },
8071 needsTimeGap_: function(item, index, length) {
8072 return md_history.HistoryItem.needsTimeGap(this.historyData_, index, this.se archedTerm);
8073 },
8074 isCardStart_: function(item, i, length) {
8075 if (length == 0 || i > length - 1) return false;
8076 return i == 0 || this.historyData_[i].dateRelativeDay != this.historyData_[i - 1].dateRelativeDay;
8077 },
8078 isCardEnd_: function(item, i, length) {
8079 if (length == 0 || i > length - 1) return false;
8080 return i == length - 1 || this.historyData_[i].dateRelativeDay != this.histo ryData_[i + 1].dateRelativeDay;
8081 },
8082 isFirstItem_: function(index) {
8083 return index == 0;
8084 },
8085 notifyListScroll_: function() {
8086 this.fire('history-list-scrolled');
8087 },
8088 pathForItem_: function(index) {
8089 return 'historyData_.' + index;
8090 }
8091 });
8092
8093 // Copyright 2016 The Chromium Authors. All rights reserved.
8094 // Use of this source code is governed by a BSD-style license that can be
8095 // found in the LICENSE file.
8096 Polymer({
8097 is: 'history-list-container',
8098 properties: {
8099 selectedPage_: String,
8100 grouped: Boolean,
8101 queryState: Object,
8102 queryResult: Object
8103 },
8104 observers: [ 'groupedRangeChanged_(queryState.range)' ],
8105 listeners: {
8106 'history-list-scrolled': 'closeMenu_',
8107 'load-more-history': 'loadMoreHistory_',
8108 'toggle-menu': 'toggleMenu_'
8109 },
8110 historyResult: function(info, results) {
8111 this.initializeResults_(info, results);
8112 this.closeMenu_();
8113 if (this.selectedPage_ == 'grouped-list') {
8114 this.$$('#grouped-list').historyData = results;
8115 return;
8116 }
8117 var list = this.$['infinite-list'];
8118 list.addNewResults(results);
8119 if (info.finished) list.disableResultLoading();
8120 },
8121 queryHistory: function(incremental) {
8122 var queryState = this.queryState;
8123 var noResults = !this.queryResult || this.queryResult.results == null;
8124 if (queryState.queryingDisabled || !this.queryState.searchTerm && noResults) {
8125 return;
8126 }
8127 if (!incremental && this.$.dialog.open) this.$.dialog.close();
8128 this.set('queryState.querying', true);
8129 this.set('queryState.incremental', incremental);
8130 var lastVisitTime = 0;
8131 if (incremental) {
8132 var lastVisit = this.queryResult.results.slice(-1)[0];
8133 lastVisitTime = lastVisit ? lastVisit.time : 0;
8134 }
8135 var maxResults = queryState.range == HistoryRange.ALL_TIME ? RESULTS_PER_PAG E : 0;
8136 chrome.send('queryHistory', [ queryState.searchTerm, queryState.groupedOffse t, queryState.range, lastVisitTime, maxResults ]);
8137 },
8138 unselectAllItems: function(count) {
8139 this.getSelectedList_().unselectAllItems(count);
8140 },
8141 deleteSelectedWithPrompt: function() {
8142 if (!loadTimeData.getBoolean('allowDeletingHistory')) return;
8143 this.$.dialog.showModal();
8144 },
8145 groupedRangeChanged_: function(range) {
8146 this.selectedPage_ = this.queryState.range == HistoryRange.ALL_TIME ? 'infin ite-list' : 'grouped-list';
8147 this.queryHistory(false);
8148 },
8149 loadMoreHistory_: function() {
8150 this.queryHistory(true);
8151 },
8152 initializeResults_: function(info, results) {
8153 if (results.length == 0) return;
8154 var currentDate = results[0].dateRelativeDay;
8155 for (var i = 0; i < results.length; i++) {
8156 results[i].selected = false;
8157 results[i].readableTimestamp = info.term == '' ? results[i].dateTimeOfDay : results[i].dateShort;
8158 if (results[i].dateRelativeDay != currentDate) {
8159 currentDate = results[i].dateRelativeDay;
8160 }
8161 }
8162 },
8163 onDialogConfirmTap_: function() {
8164 this.getSelectedList_().deleteSelected();
8165 this.$.dialog.close();
8166 },
8167 onDialogCancelTap_: function() {
8168 this.$.dialog.close();
8169 },
8170 closeMenu_: function() {
8171 this.$.sharedMenu.closeMenu();
8172 },
8173 toggleMenu_: function(e) {
8174 var target = e.detail.target;
8175 this.$.sharedMenu.toggleMenu(target, e.detail.item);
8176 },
8177 onMoreFromSiteTap_: function() {
8178 var menu = this.$.sharedMenu;
8179 this.fire('search-domain', {
8180 domain: menu.itemData.domain
8181 });
8182 menu.closeMenu();
8183 },
8184 onRemoveFromHistoryTap_: function() {
8185 var menu = this.$.sharedMenu;
8186 md_history.BrowserService.getInstance().deleteItems([ menu.itemData ]).then( function(items) {
8187 this.getSelectedList_().removeItemsByPath(items[0].path);
8188 this.fire('unselect-all');
8189 }.bind(this));
8190 menu.closeMenu();
8191 },
8192 getSelectedList_: function() {
8193 return this.$.content.selectedItem;
8194 }
8195 });
8196
8197 // Copyright 2016 The Chromium Authors. All rights reserved.
8198 // Use of this source code is governed by a BSD-style license that can be
8199 // found in the LICENSE file.
8200 Polymer({
8201 is: 'history-synced-device-card',
8202 properties: {
8203 device: String,
8204 lastUpdateTime: String,
8205 tabs: {
8206 type: Array,
8207 value: function() {
8208 return [];
8209 },
8210 observer: 'updateIcons_'
8211 },
8212 separatorIndexes: Array,
8213 cardOpen_: {
8214 type: Boolean,
8215 value: true
8216 },
8217 searchTerm: String,
8218 sessionTag: String
8219 },
8220 openTab_: function(e) {
8221 var tab = e.model.tab;
8222 md_history.BrowserService.getInstance().openForeignSessionTab(this.sessionTa g, tab.windowId, tab.sessionId, e);
8223 e.preventDefault();
8224 },
8225 toggleTabCard: function() {
8226 this.$.collapse.toggle();
8227 this.$['dropdown-indicator'].icon = this.$.collapse.opened ? 'cr:expand-less ' : 'cr:expand-more';
8228 },
8229 updateIcons_: function() {
8230 this.async(function() {
8231 var icons = Polymer.dom(this.root).querySelectorAll('.website-icon');
8232 for (var i = 0; i < this.tabs.length; i++) {
8233 icons[i].style.backgroundImage = cr.icon.getFaviconImageSet(this.tabs[i] .url);
8234 }
8235 });
8236 },
8237 isWindowSeparatorIndex_: function(index, separatorIndexes) {
8238 return this.separatorIndexes.indexOf(index) != -1;
8239 },
8240 getCollapseTitle_: function(cardOpen) {
8241 return cardOpen ? loadTimeData.getString('collapseSessionButton') : loadTime Data.getString('expandSessionButton');
8242 },
8243 onMenuButtonTap_: function(e) {
8244 this.fire('toggle-menu', {
8245 target: Polymer.dom(e).localTarget,
8246 tag: this.sessionTag
8247 });
8248 e.stopPropagation();
8249 }
8250 });
8251
8252 // Copyright 2016 The Chromium Authors. All rights reserved.
8253 // Use of this source code is governed by a BSD-style license that can be
8254 // found in the LICENSE file.
8255 var ForeignDeviceInternal;
8256
8257 Polymer({
8258 is: 'history-synced-device-manager',
8259 properties: {
8260 sessionList: {
8261 type: Array,
8262 observer: 'updateSyncedDevices'
8263 },
8264 searchTerm: {
8265 type: String,
8266 observer: 'searchTermChanged'
8267 },
8268 syncedDevices_: {
8269 type: Array,
8270 value: function() {
8271 return [];
8272 }
8273 },
8274 signInState_: {
8275 type: Boolean,
8276 value: loadTimeData.getBoolean('isUserSignedIn')
8277 },
8278 guestSession_: {
8279 type: Boolean,
8280 value: loadTimeData.getBoolean('isGuestSession')
8281 },
8282 fetchingSyncedTabs_: {
8283 type: Boolean,
8284 value: false
8285 }
8286 },
8287 listeners: {
8288 'toggle-menu': 'onToggleMenu_'
8289 },
8290 attached: function() {
8291 chrome.send('otherDevicesInitialized');
8292 },
8293 createInternalDevice_: function(session) {
8294 var tabs = [];
8295 var separatorIndexes = [];
8296 for (var i = 0; i < session.windows.length; i++) {
8297 var windowId = session.windows[i].sessionId;
8298 var newTabs = session.windows[i].tabs;
8299 if (newTabs.length == 0) continue;
8300 newTabs.forEach(function(tab) {
8301 tab.windowId = windowId;
8302 });
8303 var windowAdded = false;
8304 if (!this.searchTerm) {
8305 tabs = tabs.concat(newTabs);
8306 windowAdded = true;
8307 } else {
8308 var searchText = this.searchTerm.toLowerCase();
8309 for (var j = 0; j < newTabs.length; j++) {
8310 var tab = newTabs[j];
8311 if (tab.title.toLowerCase().indexOf(searchText) != -1) {
8312 tabs.push(tab);
8313 windowAdded = true;
8314 }
8315 }
8316 }
8317 if (windowAdded && i != session.windows.length - 1) separatorIndexes.push( tabs.length - 1);
8318 }
8319 return {
8320 device: session.name,
8321 lastUpdateTime: '– ' + session.modifiedTime,
8322 separatorIndexes: separatorIndexes,
8323 timestamp: session.timestamp,
8324 tabs: tabs,
8325 tag: session.tag
8326 };
8327 },
8328 onSignInTap_: function() {
8329 chrome.send('SyncSetupShowSetupUI');
8330 chrome.send('SyncSetupStartSignIn', [ false ]);
8331 },
8332 onToggleMenu_: function(e) {
8333 this.$.menu.toggleMenu(e.detail.target, e.detail.tag);
8334 },
8335 onOpenAllTap_: function() {
8336 md_history.BrowserService.getInstance().openForeignSessionAllTabs(this.$.men u.itemData);
8337 this.$.menu.closeMenu();
8338 },
8339 onDeleteSessionTap_: function() {
8340 md_history.BrowserService.getInstance().deleteForeignSession(this.$.menu.ite mData);
8341 this.$.menu.closeMenu();
8342 },
8343 clearDisplayedSyncedDevices_: function() {
8344 this.syncedDevices_ = [];
8345 },
8346 showNoSyncedMessage: function(signInState, syncedDevicesLength, guestSession) {
8347 if (guestSession) return true;
8348 return signInState && syncedDevicesLength == 0;
8349 },
8350 showSignInGuide: function(signInState, guestSession) {
8351 return !signInState && !guestSession;
8352 },
8353 noSyncedTabsMessage: function(fetchingSyncedTabs) {
8354 return loadTimeData.getString(fetchingSyncedTabs ? 'loading' : 'noSyncedResu lts');
8355 },
8356 updateSyncedDevices: function(sessionList) {
8357 this.fetchingSyncedTabs_ = false;
8358 if (!sessionList) return;
8359 var updateCount = Math.min(sessionList.length, this.syncedDevices_.length);
8360 for (var i = 0; i < updateCount; i++) {
8361 var oldDevice = this.syncedDevices_[i];
8362 if (oldDevice.tag != sessionList[i].tag || oldDevice.timestamp != sessionL ist[i].timestamp) {
8363 this.splice('syncedDevices_', i, 1, this.createInternalDevice_(sessionLi st[i]));
8364 }
8365 }
8366 for (var i = updateCount; i < sessionList.length; i++) {
8367 this.push('syncedDevices_', this.createInternalDevice_(sessionList[i]));
8368 }
8369 },
8370 updateSignInState: function(isUserSignedIn) {
8371 if (this.signInState_ == isUserSignedIn) return;
8372 this.signInState_ = isUserSignedIn;
8373 if (!isUserSignedIn) {
8374 this.clearDisplayedSyncedDevices_();
8375 return;
8376 }
8377 this.fetchingSyncedTabs_ = true;
8378 },
8379 searchTermChanged: function(searchTerm) {
8380 this.clearDisplayedSyncedDevices_();
8381 this.updateSyncedDevices(this.sessionList);
8382 }
8383 });
8384
8385 Polymer({
8386 is: 'iron-selector',
8387 behaviors: [ Polymer.IronMultiSelectableBehavior ]
8388 });
8389
8390 // Copyright 2016 The Chromium Authors. All rights reserved.
8391 // Use of this source code is governed by a BSD-style license that can be
8392 // found in the LICENSE file.
8393 Polymer({
8394 is: 'history-side-bar',
8395 properties: {
8396 selectedPage: {
8397 type: String,
8398 notify: true
8399 },
8400 route: Object,
8401 showFooter: Boolean,
8402 drawer: {
8403 type: Boolean,
8404 reflectToAttribute: true
8405 }
8406 },
8407 onSelectorActivate_: function() {
8408 this.fire('history-close-drawer');
8409 },
8410 onClearBrowsingDataTap_: function(e) {
8411 md_history.BrowserService.getInstance().openClearBrowsingData();
8412 e.preventDefault();
8413 },
8414 getQueryString_: function(route) {
8415 return window.location.search;
8416 }
8417 });
8418
8419 // Copyright 2016 The Chromium Authors. All rights reserved.
8420 // Use of this source code is governed by a BSD-style license that can be
8421 // found in the LICENSE file.
8422 Polymer({
8423 is: 'history-app',
8424 properties: {
8425 showSidebarFooter: Boolean,
8426 selectedPage_: {
8427 type: String,
8428 value: 'history',
8429 observer: 'unselectAll'
8430 },
8431 grouped_: {
8432 type: Boolean,
8433 reflectToAttribute: true
8434 },
8435 queryState_: {
8436 type: Object,
8437 value: function() {
8438 return {
8439 incremental: false,
8440 querying: true,
8441 queryingDisabled: false,
8442 _range: HistoryRange.ALL_TIME,
8443 searchTerm: '',
8444 groupedOffset: 0,
8445 set range(val) {
8446 this._range = Number(val);
8447 },
8448 get range() {
8449 return this._range;
8450 }
8451 };
8452 }
8453 },
8454 queryResult_: {
8455 type: Object,
8456 value: function() {
8457 return {
8458 info: null,
8459 results: null,
8460 sessionList: null
8461 };
8462 }
8463 },
8464 routeData_: Object,
8465 queryParams_: Object,
8466 hasDrawer_: Boolean
8467 },
8468 observers: [ 'routeDataChanged_(routeData_.page)', 'selectedPageChanged_(selec tedPage_)', 'searchTermChanged_(queryState_.searchTerm)', 'searchQueryParamChang ed_(queryParams_.q)' ],
8469 listeners: {
8470 'cr-menu-tap': 'onMenuTap_',
8471 'history-checkbox-select': 'checkboxSelected',
8472 'unselect-all': 'unselectAll',
8473 'delete-selected': 'deleteSelected',
8474 'search-domain': 'searchDomain_',
8475 'history-close-drawer': 'closeDrawer_'
8476 },
8477 ready: function() {
8478 this.grouped_ = loadTimeData.getBoolean('groupByDomain');
8479 cr.ui.decorate('command', cr.ui.Command);
8480 document.addEventListener('canExecute', this.onCanExecute_.bind(this));
8481 document.addEventListener('command', this.onCommand_.bind(this));
8482 if (window.location.hash) {
8483 window.location.href = window.location.href.split('#')[0] + '?' + window.l ocation.hash.substr(1);
8484 }
8485 },
8486 onMenuTap_: function() {
8487 var drawer = this.$$('#drawer');
8488 if (drawer) drawer.toggle();
8489 },
8490 checkboxSelected: function(e) {
8491 var toolbar = this.$.toolbar;
8492 toolbar.count += e.detail.countAddition;
8493 },
8494 unselectAll: function() {
8495 var listContainer = this.$['history'];
8496 var toolbar = this.$.toolbar;
8497 listContainer.unselectAllItems(toolbar.count);
8498 toolbar.count = 0;
8499 },
8500 deleteSelected: function() {
8501 this.$.history.deleteSelectedWithPrompt();
8502 },
8503 historyResult: function(info, results) {
8504 this.set('queryState_.querying', false);
8505 this.set('queryResult_.info', info);
8506 this.set('queryResult_.results', results);
8507 var listContainer = this.$['history'];
8508 listContainer.historyResult(info, results);
8509 },
8510 searchDomain_: function(e) {
8511 this.$.toolbar.setSearchTerm(e.detail.domain);
8512 },
8513 onCanExecute_: function(e) {
8514 e = e;
8515 switch (e.command.id) {
8516 case 'find-command':
8517 e.canExecute = true;
8518 break;
8519
8520 case 'slash-command':
8521 e.canExecute = !this.$.toolbar.searchBar.isSearchFocused();
8522 break;
8523
8524 case 'delete-command':
8525 e.canExecute = this.$.toolbar.count > 0;
8526 break;
8527 }
8528 },
8529 searchTermChanged_: function(searchTerm) {
8530 this.set('queryParams_.q', searchTerm || null);
8531 this.$['history'].queryHistory(false);
8532 },
8533 searchQueryParamChanged_: function(searchQuery) {
8534 this.$.toolbar.setSearchTerm(searchQuery || '');
8535 },
8536 onCommand_: function(e) {
8537 if (e.command.id == 'find-command' || e.command.id == 'slash-command') this. $.toolbar.showSearchField();
8538 if (e.command.id == 'delete-command') this.deleteSelected();
8539 },
8540 setForeignSessions: function(sessionList, isTabSyncEnabled) {
8541 if (!isTabSyncEnabled) return;
8542 this.set('queryResult_.sessionList', sessionList);
8543 },
8544 updateSignInState: function(isUserSignedIn) {
8545 var syncedDeviceManagerElem = this.$$('history-synced-device-manager');
8546 if (syncedDeviceManagerElem) syncedDeviceManagerElem.updateSignInState(isUse rSignedIn);
8547 },
8548 syncedTabsSelected_: function(selectedPage) {
8549 return selectedPage == 'syncedTabs';
8550 },
8551 shouldShowSpinner_: function(querying, incremental, searchTerm) {
8552 return querying && !incremental && searchTerm != '';
8553 },
8554 routeDataChanged_: function(page) {
8555 this.selectedPage_ = page;
8556 },
8557 selectedPageChanged_: function(selectedPage) {
8558 this.set('routeData_.page', selectedPage);
8559 },
8560 getSelectedPage_: function(selectedPage, items) {
8561 return selectedPage;
8562 },
8563 closeDrawer_: function() {
8564 var drawer = this.$$('#drawer');
8565 if (drawer) drawer.close();
8566 }
8567 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698