| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 Polymer = {dom: 'shadow'}; | 5 Polymer = {dom: 'shadow'}; |
| 6 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 6 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 7 // Use of this source code is governed by a BSD-style license that can be | 7 // Use of this source code is governed by a BSD-style license that can be |
| 8 // found in the LICENSE file. | 8 // found in the LICENSE file. |
| 9 | 9 |
| 10 /** | 10 /** |
| (...skipping 2335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2346 removeAttribute: function(attr) { | 2346 removeAttribute: function(attr) { |
| 2347 if (attr.toLowerCase() == 'disabled') | 2347 if (attr.toLowerCase() == 'disabled') |
| 2348 this.disabled = false; | 2348 this.disabled = false; |
| 2349 else | 2349 else |
| 2350 HTMLAnchorElement.prototype.removeAttribute.apply(this, arguments); | 2350 HTMLAnchorElement.prototype.removeAttribute.apply(this, arguments); |
| 2351 }, | 2351 }, |
| 2352 }, | 2352 }, |
| 2353 | 2353 |
| 2354 extends: 'a', | 2354 extends: 'a', |
| 2355 }); | 2355 }); |
| 2356 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2357 // Use of this source code is governed by a BSD-style license that can be | |
| 2358 // found in the LICENSE file. | |
| 2359 | |
| 2360 /** @typedef {{img: HTMLImageElement, url: string}} */ | |
| 2361 var LoadIconRequest; | |
| 2362 | |
| 2363 cr.define('downloads', function() { | |
| 2364 /** | |
| 2365 * @param {number} maxAllowed The maximum number of simultaneous downloads | |
| 2366 * allowed. | |
| 2367 * @constructor | |
| 2368 */ | |
| 2369 function ThrottledIconLoader(maxAllowed) { | |
| 2370 assert(maxAllowed > 0); | |
| 2371 | |
| 2372 /** @private {number} */ | |
| 2373 this.maxAllowed_ = maxAllowed; | |
| 2374 | |
| 2375 /** @private {!Array<!LoadIconRequest>} */ | |
| 2376 this.requests_ = []; | |
| 2377 } | |
| 2378 | |
| 2379 ThrottledIconLoader.prototype = { | |
| 2380 /** @private {number} */ | |
| 2381 loading_: 0, | |
| 2382 | |
| 2383 /** | |
| 2384 * Load the provided |url| into |img.src| after appending ?scale=. | |
| 2385 * @param {!HTMLImageElement} img An <img> to show the loaded image in. | |
| 2386 * @param {string} url A remote image URL to load. | |
| 2387 */ | |
| 2388 loadScaledIcon: function(img, url) { | |
| 2389 var scaledUrl = url + '?scale=' + window.devicePixelRatio + 'x'; | |
| 2390 if (img.src == scaledUrl) | |
| 2391 return; | |
| 2392 | |
| 2393 this.requests_.push({img: img, url: scaledUrl}); | |
| 2394 this.loadNextIcon_(); | |
| 2395 }, | |
| 2396 | |
| 2397 /** @private */ | |
| 2398 loadNextIcon_: function() { | |
| 2399 if (this.loading_ > this.maxAllowed_ || !this.requests_.length) | |
| 2400 return; | |
| 2401 | |
| 2402 var request = this.requests_.shift(); | |
| 2403 var img = request.img; | |
| 2404 | |
| 2405 img.onabort = img.onerror = img.onload = function() { | |
| 2406 this.loading_--; | |
| 2407 this.loadNextIcon_(); | |
| 2408 }.bind(this); | |
| 2409 | |
| 2410 this.loading_++; | |
| 2411 img.src = request.url; | |
| 2412 }, | |
| 2413 }; | |
| 2414 | |
| 2415 return {ThrottledIconLoader: ThrottledIconLoader}; | |
| 2416 }); | |
| 2417 // Copyright 2014 Google Inc. All rights reserved. | 2356 // Copyright 2014 Google Inc. All rights reserved. |
| 2418 // | 2357 // |
| 2419 // Licensed under the Apache License, Version 2.0 (the "License"); | 2358 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 2420 // you may not use this file except in compliance with the License. | 2359 // you may not use this file except in compliance with the License. |
| 2421 // You may obtain a copy of the License at | 2360 // You may obtain a copy of the License at |
| 2422 // | 2361 // |
| 2423 // http://www.apache.org/licenses/LICENSE-2.0 | 2362 // http://www.apache.org/licenses/LICENSE-2.0 |
| 2424 // | 2363 // |
| 2425 // Unless required by applicable law or agreed to in writing, software | 2364 // Unless required by applicable law or agreed to in writing, software |
| 2426 // distributed under the License is distributed on an "AS IS" BASIS, | 2365 // distributed under the License is distributed on an "AS IS" BASIS, |
| (...skipping 573 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3000 debouncer.complete(); | 2939 debouncer.complete(); |
| 3001 } | 2940 } |
| 3002 }, | 2941 }, |
| 3003 cancelDebouncer: function (jobName) { | 2942 cancelDebouncer: function (jobName) { |
| 3004 var debouncer = this._debouncers[jobName]; | 2943 var debouncer = this._debouncers[jobName]; |
| 3005 if (debouncer) { | 2944 if (debouncer) { |
| 3006 debouncer.stop(); | 2945 debouncer.stop(); |
| 3007 } | 2946 } |
| 3008 } | 2947 } |
| 3009 }); | 2948 }); |
| 3010 Polymer.version = '1.1.4'; | 2949 Polymer.version = '1.1.5'; |
| 3011 Polymer.Base._addFeature({ | 2950 Polymer.Base._addFeature({ |
| 3012 _registerFeatures: function () { | 2951 _registerFeatures: function () { |
| 3013 this._prepIs(); | 2952 this._prepIs(); |
| 3014 this._prepAttributes(); | 2953 this._prepAttributes(); |
| 3015 this._prepBehaviors(); | 2954 this._prepBehaviors(); |
| 3016 this._prepConstructor(); | 2955 this._prepConstructor(); |
| 3017 }, | 2956 }, |
| 3018 _prepBehavior: function (b) { | 2957 _prepBehavior: function (b) { |
| 3019 this._addHostAttributes(b.hostAttributes); | 2958 this._addHostAttributes(b.hostAttributes); |
| 3020 }, | 2959 }, |
| (...skipping 1856 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4877 name = key; | 4816 name = key; |
| 4878 } else { | 4817 } else { |
| 4879 name = key.split('.'); | 4818 name = key.split('.'); |
| 4880 node = this.$[name[0]]; | 4819 node = this.$[name[0]]; |
| 4881 name = name[1]; | 4820 name = name[1]; |
| 4882 } | 4821 } |
| 4883 this.listen(node, name, listeners[key]); | 4822 this.listen(node, name, listeners[key]); |
| 4884 } | 4823 } |
| 4885 }, | 4824 }, |
| 4886 listen: function (node, eventName, methodName) { | 4825 listen: function (node, eventName, methodName) { |
| 4887 this._listen(node, eventName, this._createEventHandler(node, eventName, methodNa
me)); | 4826 var handler = this._recallEventHandler(this, eventName, node, methodName); |
| 4827 if (!handler) { |
| 4828 handler = this._createEventHandler(node, eventName, methodName); |
| 4829 } |
| 4830 if (handler._listening) { |
| 4831 return; |
| 4832 } |
| 4833 this._listen(node, eventName, handler); |
| 4834 handler._listening = true; |
| 4888 }, | 4835 }, |
| 4889 _boundListenerKey: function (eventName, methodName) { | 4836 _boundListenerKey: function (eventName, methodName) { |
| 4890 return eventName + ':' + methodName; | 4837 return eventName + ':' + methodName; |
| 4891 }, | 4838 }, |
| 4892 _recordEventHandler: function (host, eventName, target, methodName, handler) { | 4839 _recordEventHandler: function (host, eventName, target, methodName, handler) { |
| 4893 var hbl = host.__boundListeners; | 4840 var hbl = host.__boundListeners; |
| 4894 if (!hbl) { | 4841 if (!hbl) { |
| 4895 hbl = host.__boundListeners = new WeakMap(); | 4842 hbl = host.__boundListeners = new WeakMap(); |
| 4896 } | 4843 } |
| 4897 var bl = hbl.get(target); | 4844 var bl = hbl.get(target); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 4916 }, | 4863 }, |
| 4917 _createEventHandler: function (node, eventName, methodName) { | 4864 _createEventHandler: function (node, eventName, methodName) { |
| 4918 var host = this; | 4865 var host = this; |
| 4919 var handler = function (e) { | 4866 var handler = function (e) { |
| 4920 if (host[methodName]) { | 4867 if (host[methodName]) { |
| 4921 host[methodName](e, e.detail); | 4868 host[methodName](e, e.detail); |
| 4922 } else { | 4869 } else { |
| 4923 host._warn(host._logf('_createEventHandler', 'listener method `' + methodName +
'` not defined')); | 4870 host._warn(host._logf('_createEventHandler', 'listener method `' + methodName +
'` not defined')); |
| 4924 } | 4871 } |
| 4925 }; | 4872 }; |
| 4873 handler._listening = false; |
| 4926 this._recordEventHandler(host, eventName, node, methodName, handler); | 4874 this._recordEventHandler(host, eventName, node, methodName, handler); |
| 4927 return handler; | 4875 return handler; |
| 4928 }, | 4876 }, |
| 4929 unlisten: function (node, eventName, methodName) { | 4877 unlisten: function (node, eventName, methodName) { |
| 4930 var handler = this._recallEventHandler(this, eventName, node, methodName); | 4878 var handler = this._recallEventHandler(this, eventName, node, methodName); |
| 4931 if (handler) { | 4879 if (handler) { |
| 4932 this._unlisten(node, eventName, handler); | 4880 this._unlisten(node, eventName, handler); |
| 4881 handler._listening = false; |
| 4933 } | 4882 } |
| 4934 }, | 4883 }, |
| 4935 _listen: function (node, eventName, handler) { | 4884 _listen: function (node, eventName, handler) { |
| 4936 node.addEventListener(eventName, handler); | 4885 node.addEventListener(eventName, handler); |
| 4937 }, | 4886 }, |
| 4938 _unlisten: function (node, eventName, handler) { | 4887 _unlisten: function (node, eventName, handler) { |
| 4939 node.removeEventListener(eventName, handler); | 4888 node.removeEventListener(eventName, handler); |
| 4940 } | 4889 } |
| 4941 }); | 4890 }); |
| 4942 (function () { | 4891 (function () { |
| (...skipping 827 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5770 return l; | 5719 return l; |
| 5771 }, | 5720 }, |
| 5772 create: function (tag, props) { | 5721 create: function (tag, props) { |
| 5773 var elt = document.createElement(tag); | 5722 var elt = document.createElement(tag); |
| 5774 if (props) { | 5723 if (props) { |
| 5775 for (var n in props) { | 5724 for (var n in props) { |
| 5776 elt[n] = props[n]; | 5725 elt[n] = props[n]; |
| 5777 } | 5726 } |
| 5778 } | 5727 } |
| 5779 return elt; | 5728 return elt; |
| 5729 }, |
| 5730 isLightDescendant: function (node) { |
| 5731 return this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(n
ode).getOwnerRoot(); |
| 5732 }, |
| 5733 isLocalDescendant: function (node) { |
| 5734 return this.root === Polymer.dom(node).getOwnerRoot(); |
| 5780 } | 5735 } |
| 5781 }); | 5736 }); |
| 5782 Polymer.Bind = { | 5737 Polymer.Bind = { |
| 5783 prepareModel: function (model) { | 5738 prepareModel: function (model) { |
| 5784 model._propertyEffects = {}; | 5739 model._propertyEffects = {}; |
| 5785 model._bindListeners = []; | 5740 model._bindListeners = []; |
| 5786 Polymer.Base.mixin(model, this._modelApi); | 5741 Polymer.Base.mixin(model, this._modelApi); |
| 5787 }, | 5742 }, |
| 5788 _modelApi: { | 5743 _modelApi: { |
| 5789 _notifyChange: function (property) { | 5744 _notifyChange: function (property) { |
| (...skipping 781 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6571 return ret; | 6526 return ret; |
| 6572 }, | 6527 }, |
| 6573 unshift: function (path) { | 6528 unshift: function (path) { |
| 6574 var array = this.get(path); | 6529 var array = this.get(path); |
| 6575 var args = Array.prototype.slice.call(arguments, 1); | 6530 var args = Array.prototype.slice.call(arguments, 1); |
| 6576 var ret = array.unshift.apply(array, args); | 6531 var ret = array.unshift.apply(array, args); |
| 6577 if (args.length) { | 6532 if (args.length) { |
| 6578 this._notifySplice(array, path, 0, args.length, []); | 6533 this._notifySplice(array, path, 0, args.length, []); |
| 6579 } | 6534 } |
| 6580 return ret; | 6535 return ret; |
| 6536 }, |
| 6537 prepareModelNotifyPath: function (model) { |
| 6538 this.mixin(model, { |
| 6539 fire: Polymer.Base.fire, |
| 6540 notifyPath: Polymer.Base.notifyPath, |
| 6541 _EVENT_CHANGED: Polymer.Base._EVENT_CHANGED, |
| 6542 _notifyPath: Polymer.Base._notifyPath, |
| 6543 _pathEffector: Polymer.Base._pathEffector, |
| 6544 _annotationPathEffect: Polymer.Base._annotationPathEffect, |
| 6545 _complexObserverPathEffect: Polymer.Base._complexObserverPathEffect, |
| 6546 _annotatedComputationPathEffect: Polymer.Base._annotatedComputationPathEffect, |
| 6547 _computePathEffect: Polymer.Base._computePathEffect, |
| 6548 _modelForPath: Polymer.Base._modelForPath, |
| 6549 _pathMatchesEffect: Polymer.Base._pathMatchesEffect, |
| 6550 _notifyBoundPaths: Polymer.Base._notifyBoundPaths |
| 6551 }); |
| 6581 } | 6552 } |
| 6582 }); | 6553 }); |
| 6583 }()); | 6554 }()); |
| 6584 Polymer.Base._addFeature({ | 6555 Polymer.Base._addFeature({ |
| 6585 resolveUrl: function (url) { | 6556 resolveUrl: function (url) { |
| 6586 var module = Polymer.DomModule.import(this.is); | 6557 var module = Polymer.DomModule.import(this.is); |
| 6587 var root = ''; | 6558 var root = ''; |
| 6588 if (module) { | 6559 if (module) { |
| 6589 var assetPath = module.getAttribute('assetpath') || ''; | 6560 var assetPath = module.getAttribute('assetpath') || ''; |
| 6590 root = Polymer.ResolveUrl.resolveUrl(assetPath, module.ownerDocument.baseURI); | 6561 root = Polymer.ResolveUrl.resolveUrl(assetPath, module.ownerDocument.baseURI); |
| (...skipping 1186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7777 } | 7748 } |
| 7778 }); | 7749 }); |
| 7779 } | 7750 } |
| 7780 }); | 7751 }); |
| 7781 }()); | 7752 }()); |
| 7782 Polymer.Templatizer = { | 7753 Polymer.Templatizer = { |
| 7783 properties: { __hideTemplateChildren__: { observer: '_showHideChildren' } }, | 7754 properties: { __hideTemplateChildren__: { observer: '_showHideChildren' } }, |
| 7784 _instanceProps: Polymer.nob, | 7755 _instanceProps: Polymer.nob, |
| 7785 _parentPropPrefix: '_parent_', | 7756 _parentPropPrefix: '_parent_', |
| 7786 templatize: function (template) { | 7757 templatize: function (template) { |
| 7758 this._templatized = template; |
| 7787 if (!template._content) { | 7759 if (!template._content) { |
| 7788 template._content = template.content; | 7760 template._content = template.content; |
| 7789 } | 7761 } |
| 7790 if (template._content._ctor) { | 7762 if (template._content._ctor) { |
| 7791 this.ctor = template._content._ctor; | 7763 this.ctor = template._content._ctor; |
| 7792 this._prepParentProperties(this.ctor.prototype, template); | 7764 this._prepParentProperties(this.ctor.prototype, template); |
| 7793 return; | 7765 return; |
| 7794 } | 7766 } |
| 7795 var archetype = Object.create(Polymer.Base); | 7767 var archetype = Object.create(Polymer.Base); |
| 7796 this._customPrepAnnotations(archetype, template); | 7768 this._customPrepAnnotations(archetype, template); |
| 7769 this._prepParentProperties(archetype, template); |
| 7797 archetype._prepEffects(); | 7770 archetype._prepEffects(); |
| 7798 this._customPrepEffects(archetype); | 7771 this._customPrepEffects(archetype); |
| 7799 archetype._prepBehaviors(); | 7772 archetype._prepBehaviors(); |
| 7800 archetype._prepBindings(); | 7773 archetype._prepBindings(); |
| 7801 this._prepParentProperties(archetype, template); | |
| 7802 archetype._notifyPath = this._notifyPathImpl; | 7774 archetype._notifyPath = this._notifyPathImpl; |
| 7803 archetype._scopeElementClass = this._scopeElementClassImpl; | 7775 archetype._scopeElementClass = this._scopeElementClassImpl; |
| 7804 archetype.listen = this._listenImpl; | 7776 archetype.listen = this._listenImpl; |
| 7805 archetype._showHideChildren = this._showHideChildrenImpl; | 7777 archetype._showHideChildren = this._showHideChildrenImpl; |
| 7806 var _constructor = this._constructorImpl; | 7778 var _constructor = this._constructorImpl; |
| 7807 var ctor = function TemplateInstance(model, host) { | 7779 var ctor = function TemplateInstance(model, host) { |
| 7808 _constructor.call(this, model, host); | 7780 _constructor.call(this, model, host); |
| 7809 }; | 7781 }; |
| 7810 ctor.prototype = archetype; | 7782 ctor.prototype = archetype; |
| 7811 archetype.constructor = ctor; | 7783 archetype.constructor = ctor; |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7874 if (this._forwardParentProp && parentProps) { | 7846 if (this._forwardParentProp && parentProps) { |
| 7875 var proto = archetype._parentPropProto; | 7847 var proto = archetype._parentPropProto; |
| 7876 var prop; | 7848 var prop; |
| 7877 if (!proto) { | 7849 if (!proto) { |
| 7878 for (prop in this._instanceProps) { | 7850 for (prop in this._instanceProps) { |
| 7879 delete parentProps[prop]; | 7851 delete parentProps[prop]; |
| 7880 } | 7852 } |
| 7881 proto = archetype._parentPropProto = Object.create(null); | 7853 proto = archetype._parentPropProto = Object.create(null); |
| 7882 if (template != this) { | 7854 if (template != this) { |
| 7883 Polymer.Bind.prepareModel(proto); | 7855 Polymer.Bind.prepareModel(proto); |
| 7856 Polymer.Base.prepareModelNotifyPath(proto); |
| 7884 } | 7857 } |
| 7885 for (prop in parentProps) { | 7858 for (prop in parentProps) { |
| 7886 var parentProp = this._parentPropPrefix + prop; | 7859 var parentProp = this._parentPropPrefix + prop; |
| 7887 var effects = [ | 7860 var effects = [ |
| 7888 { | 7861 { |
| 7889 kind: 'function', | 7862 kind: 'function', |
| 7890 effect: this._createForwardPropEffector(prop) | 7863 effect: this._createForwardPropEffector(prop) |
| 7891 }, | 7864 }, |
| 7892 { kind: 'notify' } | 7865 { kind: 'notify' } |
| 7893 ]; | 7866 ]; |
| 7894 Polymer.Bind._createAccessors(proto, parentProp, effects); | 7867 Polymer.Bind._createAccessors(proto, parentProp, effects); |
| 7895 } | 7868 } |
| 7896 } | 7869 } |
| 7897 if (template != this) { | 7870 if (template != this) { |
| 7898 Polymer.Bind.prepareInstance(template); | 7871 Polymer.Bind.prepareInstance(template); |
| 7899 template._forwardParentProp = this._forwardParentProp.bind(this); | 7872 template._forwardParentProp = this._forwardParentProp.bind(this); |
| 7900 } | 7873 } |
| 7901 this._extendTemplate(template, proto); | 7874 this._extendTemplate(template, proto); |
| 7875 template._pathEffector = this._pathEffectorImpl.bind(this); |
| 7902 } | 7876 } |
| 7903 }, | 7877 }, |
| 7904 _createForwardPropEffector: function (prop) { | 7878 _createForwardPropEffector: function (prop) { |
| 7905 return function (source, value) { | 7879 return function (source, value) { |
| 7906 this._forwardParentProp(prop, value); | 7880 this._forwardParentProp(prop, value); |
| 7907 }; | 7881 }; |
| 7908 }, | 7882 }, |
| 7909 _createHostPropEffector: function (prop) { | 7883 _createHostPropEffector: function (prop) { |
| 7910 var prefix = this._parentPropPrefix; | 7884 var prefix = this._parentPropPrefix; |
| 7911 return function (source, value) { | 7885 return function (source, value) { |
| 7912 this.dataHost[prefix + prop] = value; | 7886 this.dataHost._templatized[prefix + prop] = value; |
| 7913 }; | 7887 }; |
| 7914 }, | 7888 }, |
| 7915 _createInstancePropEffector: function (prop) { | 7889 _createInstancePropEffector: function (prop) { |
| 7916 return function (source, value, old, fromAbove) { | 7890 return function (source, value, old, fromAbove) { |
| 7917 if (!fromAbove) { | 7891 if (!fromAbove) { |
| 7918 this.dataHost._forwardInstanceProp(this, prop, value); | 7892 this.dataHost._forwardInstanceProp(this, prop, value); |
| 7919 } | 7893 } |
| 7920 }; | 7894 }; |
| 7921 }, | 7895 }, |
| 7922 _extendTemplate: function (template, proto) { | 7896 _extendTemplate: function (template, proto) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 7934 _forwardInstancePath: function (inst, path, value) { | 7908 _forwardInstancePath: function (inst, path, value) { |
| 7935 }, | 7909 }, |
| 7936 _forwardInstanceProp: function (inst, prop, value) { | 7910 _forwardInstanceProp: function (inst, prop, value) { |
| 7937 }, | 7911 }, |
| 7938 _notifyPathImpl: function (path, value) { | 7912 _notifyPathImpl: function (path, value) { |
| 7939 var dataHost = this.dataHost; | 7913 var dataHost = this.dataHost; |
| 7940 var dot = path.indexOf('.'); | 7914 var dot = path.indexOf('.'); |
| 7941 var root = dot < 0 ? path : path.slice(0, dot); | 7915 var root = dot < 0 ? path : path.slice(0, dot); |
| 7942 dataHost._forwardInstancePath.call(dataHost, this, path, value); | 7916 dataHost._forwardInstancePath.call(dataHost, this, path, value); |
| 7943 if (root in dataHost._parentProps) { | 7917 if (root in dataHost._parentProps) { |
| 7944 dataHost.notifyPath(dataHost._parentPropPrefix + path, value); | 7918 dataHost._templatized.notifyPath(dataHost._parentPropPrefix + path, value); |
| 7945 } | 7919 } |
| 7946 }, | 7920 }, |
| 7947 _pathEffector: function (path, value, fromAbove) { | 7921 _pathEffectorImpl: function (path, value, fromAbove) { |
| 7948 if (this._forwardParentPath) { | 7922 if (this._forwardParentPath) { |
| 7949 if (path.indexOf(this._parentPropPrefix) === 0) { | 7923 if (path.indexOf(this._parentPropPrefix) === 0) { |
| 7950 this._forwardParentPath(path.substring(8), value); | 7924 var subPath = path.substring(this._parentPropPrefix.length); |
| 7925 this._forwardParentPath(subPath, value); |
| 7951 } | 7926 } |
| 7952 } | 7927 } |
| 7953 Polymer.Base._pathEffector.apply(this, arguments); | 7928 Polymer.Base._pathEffector.call(this._templatized, path, value, fromAbove); |
| 7954 }, | 7929 }, |
| 7955 _constructorImpl: function (model, host) { | 7930 _constructorImpl: function (model, host) { |
| 7956 this._rootDataHost = host._getRootDataHost(); | 7931 this._rootDataHost = host._getRootDataHost(); |
| 7957 this._setupConfigure(model); | 7932 this._setupConfigure(model); |
| 7958 this._pushHost(host); | 7933 this._pushHost(host); |
| 7959 this.root = this.instanceTemplate(this._template); | 7934 this.root = this.instanceTemplate(this._template); |
| 7960 this.root.__noContent = !this._notes._hasContent; | 7935 this.root.__noContent = !this._notes._hasContent; |
| 7961 this.root.__styleScoped = true; | 7936 this.root.__styleScoped = true; |
| 7962 this._popHost(); | 7937 this._popHost(); |
| 7963 this._marshalAnnotatedNodes(); | 7938 this._marshalAnnotatedNodes(); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 7986 }, | 7961 }, |
| 7987 _scopeElementClassImpl: function (node, value) { | 7962 _scopeElementClassImpl: function (node, value) { |
| 7988 var host = this._rootDataHost; | 7963 var host = this._rootDataHost; |
| 7989 if (host) { | 7964 if (host) { |
| 7990 return host._scopeElementClass(node, value); | 7965 return host._scopeElementClass(node, value); |
| 7991 } | 7966 } |
| 7992 }, | 7967 }, |
| 7993 stamp: function (model) { | 7968 stamp: function (model) { |
| 7994 model = model || {}; | 7969 model = model || {}; |
| 7995 if (this._parentProps) { | 7970 if (this._parentProps) { |
| 7971 var templatized = this._templatized; |
| 7996 for (var prop in this._parentProps) { | 7972 for (var prop in this._parentProps) { |
| 7997 model[prop] = this[this._parentPropPrefix + prop]; | 7973 model[prop] = templatized[this._parentPropPrefix + prop]; |
| 7998 } | 7974 } |
| 7999 } | 7975 } |
| 8000 return new this.ctor(model, this); | 7976 return new this.ctor(model, this); |
| 8001 }, | 7977 }, |
| 8002 modelForElement: function (el) { | 7978 modelForElement: function (el) { |
| 8003 var model; | 7979 var model; |
| 8004 while (el) { | 7980 while (el) { |
| 8005 if (model = el._templateInstance) { | 7981 if (model = el._templateInstance) { |
| 8006 if (model.dataHost != this) { | 7982 if (model.dataHost != this) { |
| 8007 el = model.dataHost; | 7983 el = model.dataHost; |
| (...skipping 587 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8595 select: function (item) { | 8571 select: function (item) { |
| 8596 var icol = Polymer.Collection.get(this.items); | 8572 var icol = Polymer.Collection.get(this.items); |
| 8597 var key = icol.getKey(item); | 8573 var key = icol.getKey(item); |
| 8598 if (this.multi) { | 8574 if (this.multi) { |
| 8599 if (this.isSelected(item)) { | 8575 if (this.isSelected(item)) { |
| 8600 if (this.toggle) { | 8576 if (this.toggle) { |
| 8601 this.deselect(item); | 8577 this.deselect(item); |
| 8602 } | 8578 } |
| 8603 } else { | 8579 } else { |
| 8604 this.push('selected', item); | 8580 this.push('selected', item); |
| 8605 skey = this._selectedColl.getKey(item); | 8581 var skey = this._selectedColl.getKey(item); |
| 8606 this.linkPaths('selected.' + skey, 'items.' + key); | 8582 this.linkPaths('selected.' + skey, 'items.' + key); |
| 8607 } | 8583 } |
| 8608 } else { | 8584 } else { |
| 8609 if (this.toggle && item == this.selected) { | 8585 if (this.toggle && item == this.selected) { |
| 8610 this.deselect(); | 8586 this.deselect(); |
| 8611 } else { | 8587 } else { |
| 8612 this.selected = item; | 8588 this.selected = item; |
| 8613 this.selectedItem = item; | 8589 this.selectedItem = item; |
| 8614 this.linkPaths('selected', 'items.' + key); | 8590 this.linkPaths('selected', 'items.' + key); |
| 8615 this.linkPaths('selectedItem', 'items.' + key); | 8591 this.linkPaths('selectedItem', 'items.' + key); |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8764 this._prepBehaviors(); | 8740 this._prepBehaviors(); |
| 8765 this._prepConfigure(); | 8741 this._prepConfigure(); |
| 8766 this._prepBindings(); | 8742 this._prepBindings(); |
| 8767 Polymer.Base._initFeatures.call(this); | 8743 Polymer.Base._initFeatures.call(this); |
| 8768 this._children = Array.prototype.slice.call(this.root.childNodes); | 8744 this._children = Array.prototype.slice.call(this.root.childNodes); |
| 8769 } | 8745 } |
| 8770 this._insertChildren(); | 8746 this._insertChildren(); |
| 8771 this.fire('dom-change'); | 8747 this.fire('dom-change'); |
| 8772 } | 8748 } |
| 8773 }); | 8749 }); |
| 8750 /** |
| 8751 * `IronResizableBehavior` is a behavior that can be used in Polymer elements
to |
| 8752 * coordinate the flow of resize events between "resizers" (elements that cont
rol the |
| 8753 * size or hidden state of their children) and "resizables" (elements that nee
d to be |
| 8754 * notified when they are resized or un-hidden by their parents in order to ta
ke |
| 8755 * action on their new measurements). |
| 8756 * Elements that perform measurement should add the `IronResizableBehavior` be
havior to |
| 8757 * their element definition and listen for the `iron-resize` event on themselv
es. |
| 8758 * This event will be fired when they become showing after having been hidden, |
| 8759 * when they are resized explicitly by another resizable, or when the window h
as been |
| 8760 * resized. |
| 8761 * Note, the `iron-resize` event is non-bubbling. |
| 8762 * |
| 8763 * @polymerBehavior Polymer.IronResizableBehavior |
| 8764 * @demo demo/index.html |
| 8765 **/ |
| 8766 Polymer.IronResizableBehavior = { |
| 8767 properties: { |
| 8768 /** |
| 8769 * The closest ancestor element that implements `IronResizableBehavior`. |
| 8770 */ |
| 8771 _parentResizable: { |
| 8772 type: Object, |
| 8773 observer: '_parentResizableChanged' |
| 8774 }, |
| 8775 |
| 8776 /** |
| 8777 * True if this element is currently notifying its descedant elements of |
| 8778 * resize. |
| 8779 */ |
| 8780 _notifyingDescendant: { |
| 8781 type: Boolean, |
| 8782 value: false |
| 8783 } |
| 8784 }, |
| 8785 |
| 8786 listeners: { |
| 8787 'iron-request-resize-notifications': '_onIronRequestResizeNotifications' |
| 8788 }, |
| 8789 |
| 8790 created: function() { |
| 8791 // We don't really need property effects on these, and also we want them |
| 8792 // to be created before the `_parentResizable` observer fires: |
| 8793 this._interestedResizables = []; |
| 8794 this._boundNotifyResize = this.notifyResize.bind(this); |
| 8795 }, |
| 8796 |
| 8797 attached: function() { |
| 8798 this.fire('iron-request-resize-notifications', null, { |
| 8799 node: this, |
| 8800 bubbles: true, |
| 8801 cancelable: true |
| 8802 }); |
| 8803 |
| 8804 if (!this._parentResizable) { |
| 8805 window.addEventListener('resize', this._boundNotifyResize); |
| 8806 this.notifyResize(); |
| 8807 } |
| 8808 }, |
| 8809 |
| 8810 detached: function() { |
| 8811 if (this._parentResizable) { |
| 8812 this._parentResizable.stopResizeNotificationsFor(this); |
| 8813 } else { |
| 8814 window.removeEventListener('resize', this._boundNotifyResize); |
| 8815 } |
| 8816 |
| 8817 this._parentResizable = null; |
| 8818 }, |
| 8819 |
| 8820 /** |
| 8821 * Can be called to manually notify a resizable and its descendant |
| 8822 * resizables of a resize change. |
| 8823 */ |
| 8824 notifyResize: function() { |
| 8825 if (!this.isAttached) { |
| 8826 return; |
| 8827 } |
| 8828 |
| 8829 this._interestedResizables.forEach(function(resizable) { |
| 8830 if (this.resizerShouldNotify(resizable)) { |
| 8831 this._notifyDescendant(resizable); |
| 8832 } |
| 8833 }, this); |
| 8834 |
| 8835 this._fireResize(); |
| 8836 }, |
| 8837 |
| 8838 /** |
| 8839 * Used to assign the closest resizable ancestor to this resizable |
| 8840 * if the ancestor detects a request for notifications. |
| 8841 */ |
| 8842 assignParentResizable: function(parentResizable) { |
| 8843 this._parentResizable = parentResizable; |
| 8844 }, |
| 8845 |
| 8846 /** |
| 8847 * Used to remove a resizable descendant from the list of descendants |
| 8848 * that should be notified of a resize change. |
| 8849 */ |
| 8850 stopResizeNotificationsFor: function(target) { |
| 8851 var index = this._interestedResizables.indexOf(target); |
| 8852 |
| 8853 if (index > -1) { |
| 8854 this._interestedResizables.splice(index, 1); |
| 8855 this.unlisten(target, 'iron-resize', '_onDescendantIronResize'); |
| 8856 } |
| 8857 }, |
| 8858 |
| 8859 /** |
| 8860 * This method can be overridden to filter nested elements that should or |
| 8861 * should not be notified by the current element. Return true if an element |
| 8862 * should be notified, or false if it should not be notified. |
| 8863 * |
| 8864 * @param {HTMLElement} element A candidate descendant element that |
| 8865 * implements `IronResizableBehavior`. |
| 8866 * @return {boolean} True if the `element` should be notified of resize. |
| 8867 */ |
| 8868 resizerShouldNotify: function(element) { return true; }, |
| 8869 |
| 8870 _onDescendantIronResize: function(event) { |
| 8871 if (this._notifyingDescendant) { |
| 8872 event.stopPropagation(); |
| 8873 return; |
| 8874 } |
| 8875 |
| 8876 // NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the |
| 8877 // otherwise non-bubbling event "just work." We do it manually here for |
| 8878 // the case where Polymer is not using shadow roots for whatever reason: |
| 8879 if (!Polymer.Settings.useShadow) { |
| 8880 this._fireResize(); |
| 8881 } |
| 8882 }, |
| 8883 |
| 8884 _fireResize: function() { |
| 8885 this.fire('iron-resize', null, { |
| 8886 node: this, |
| 8887 bubbles: false |
| 8888 }); |
| 8889 }, |
| 8890 |
| 8891 _onIronRequestResizeNotifications: function(event) { |
| 8892 var target = event.path ? event.path[0] : event.target; |
| 8893 |
| 8894 if (target === this) { |
| 8895 return; |
| 8896 } |
| 8897 |
| 8898 if (this._interestedResizables.indexOf(target) === -1) { |
| 8899 this._interestedResizables.push(target); |
| 8900 this.listen(target, 'iron-resize', '_onDescendantIronResize'); |
| 8901 } |
| 8902 |
| 8903 target.assignParentResizable(this); |
| 8904 this._notifyDescendant(target); |
| 8905 |
| 8906 event.stopPropagation(); |
| 8907 }, |
| 8908 |
| 8909 _parentResizableChanged: function(parentResizable) { |
| 8910 if (parentResizable) { |
| 8911 window.removeEventListener('resize', this._boundNotifyResize); |
| 8912 } |
| 8913 }, |
| 8914 |
| 8915 _notifyDescendant: function(descendant) { |
| 8916 // NOTE(cdata): In IE10, attached is fired on children first, so it's |
| 8917 // important not to notify them if the parent is not attached yet (or |
| 8918 // else they will get redundantly notified when the parent attaches). |
| 8919 if (!this.isAttached) { |
| 8920 return; |
| 8921 } |
| 8922 |
| 8923 this._notifyingDescendant = true; |
| 8924 descendant.notifyResize(); |
| 8925 this._notifyingDescendant = false; |
| 8926 } |
| 8927 }; |
| 8928 (function() { |
| 8929 |
| 8930 var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/); |
| 8931 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; |
| 8932 var DEFAULT_PHYSICAL_COUNT = 20; |
| 8933 var MAX_PHYSICAL_COUNT = 500; |
| 8934 |
| 8935 Polymer({ |
| 8936 |
| 8937 is: 'iron-list', |
| 8938 |
| 8939 properties: { |
| 8940 |
| 8941 /** |
| 8942 * An array containing items determining how many instances of the templat
e |
| 8943 * to stamp and that that each template instance should bind to. |
| 8944 */ |
| 8945 items: { |
| 8946 type: Array |
| 8947 }, |
| 8948 |
| 8949 /** |
| 8950 * The name of the variable to add to the binding scope for the array |
| 8951 * element associated with a given template instance. |
| 8952 */ |
| 8953 as: { |
| 8954 type: String, |
| 8955 value: 'item' |
| 8956 }, |
| 8957 |
| 8958 /** |
| 8959 * The name of the variable to add to the binding scope with the index |
| 8960 * for the row. If `sort` is provided, the index will reflect the |
| 8961 * sorted order (rather than the original array order). |
| 8962 */ |
| 8963 indexAs: { |
| 8964 type: String, |
| 8965 value: 'index' |
| 8966 }, |
| 8967 |
| 8968 /** |
| 8969 * The name of the variable to add to the binding scope to indicate |
| 8970 * if the row is selected. |
| 8971 */ |
| 8972 selectedAs: { |
| 8973 type: String, |
| 8974 value: 'selected' |
| 8975 }, |
| 8976 |
| 8977 /** |
| 8978 * When true, tapping a row will select the item, placing its data model |
| 8979 * in the set of selected items retrievable via the selection property. |
| 8980 * |
| 8981 * Note that tapping focusable elements within the list item will not |
| 8982 * result in selection, since they are presumed to have their * own action
. |
| 8983 */ |
| 8984 selectionEnabled: { |
| 8985 type: Boolean, |
| 8986 value: false |
| 8987 }, |
| 8988 |
| 8989 /** |
| 8990 * When `multiSelection` is false, this is the currently selected item, or
`null` |
| 8991 * if no item is selected. |
| 8992 */ |
| 8993 selectedItem: { |
| 8994 type: Object, |
| 8995 notify: true |
| 8996 }, |
| 8997 |
| 8998 /** |
| 8999 * When `multiSelection` is true, this is an array that contains the selec
ted items. |
| 9000 */ |
| 9001 selectedItems: { |
| 9002 type: Object, |
| 9003 notify: true |
| 9004 }, |
| 9005 |
| 9006 /** |
| 9007 * When `true`, multiple items may be selected at once (in this case, |
| 9008 * `selected` is an array of currently selected items). When `false`, |
| 9009 * only one item may be selected at a time. |
| 9010 */ |
| 9011 multiSelection: { |
| 9012 type: Boolean, |
| 9013 value: false |
| 9014 } |
| 9015 }, |
| 9016 |
| 9017 observers: [ |
| 9018 '_itemsChanged(items.*)', |
| 9019 '_selectionEnabledChanged(selectionEnabled)', |
| 9020 '_multiSelectionChanged(multiSelection)' |
| 9021 ], |
| 9022 |
| 9023 behaviors: [ |
| 9024 Polymer.Templatizer, |
| 9025 Polymer.IronResizableBehavior |
| 9026 ], |
| 9027 |
| 9028 listeners: { |
| 9029 'iron-resize': '_resizeHandler' |
| 9030 }, |
| 9031 |
| 9032 /** |
| 9033 * The ratio of hidden tiles that should remain in the scroll direction. |
| 9034 * Recommended value ~0.5, so it will distribute tiles evely in both directi
ons. |
| 9035 */ |
| 9036 _ratio: 0.5, |
| 9037 |
| 9038 /** |
| 9039 * The element that controls the scroll |
| 9040 * @type {?Element} |
| 9041 */ |
| 9042 _scroller: null, |
| 9043 |
| 9044 /** |
| 9045 * The padding-top value of the `scroller` element |
| 9046 */ |
| 9047 _scrollerPaddingTop: 0, |
| 9048 |
| 9049 /** |
| 9050 * This value is the same as `scrollTop`. |
| 9051 */ |
| 9052 _scrollPosition: 0, |
| 9053 |
| 9054 /** |
| 9055 * The number of tiles in the DOM. |
| 9056 */ |
| 9057 _physicalCount: 0, |
| 9058 |
| 9059 /** |
| 9060 * The k-th tile that is at the top of the scrolling list. |
| 9061 */ |
| 9062 _physicalStart: 0, |
| 9063 |
| 9064 /** |
| 9065 * The k-th tile that is at the bottom of the scrolling list. |
| 9066 */ |
| 9067 _physicalEnd: 0, |
| 9068 |
| 9069 /** |
| 9070 * The sum of the heights of all the tiles in the DOM. |
| 9071 */ |
| 9072 _physicalSize: 0, |
| 9073 |
| 9074 /** |
| 9075 * The average `offsetHeight` of the tiles observed till now. |
| 9076 */ |
| 9077 _physicalAverage: 0, |
| 9078 |
| 9079 /** |
| 9080 * The number of tiles which `offsetHeight` > 0 observed until now. |
| 9081 */ |
| 9082 _physicalAverageCount: 0, |
| 9083 |
| 9084 /** |
| 9085 * The Y position of the item rendered in the `_physicalStart` |
| 9086 * tile relative to the scrolling list. |
| 9087 */ |
| 9088 _physicalTop: 0, |
| 9089 |
| 9090 /** |
| 9091 * The number of items in the list. |
| 9092 */ |
| 9093 _virtualCount: 0, |
| 9094 |
| 9095 /** |
| 9096 * The n-th item rendered in the `_physicalStart` tile. |
| 9097 */ |
| 9098 _virtualStartVal: 0, |
| 9099 |
| 9100 /** |
| 9101 * A map between an item key and its physical item index |
| 9102 */ |
| 9103 _physicalIndexForKey: null, |
| 9104 |
| 9105 /** |
| 9106 * The estimated scroll height based on `_physicalAverage` |
| 9107 */ |
| 9108 _estScrollHeight: 0, |
| 9109 |
| 9110 /** |
| 9111 * The scroll height of the dom node |
| 9112 */ |
| 9113 _scrollHeight: 0, |
| 9114 |
| 9115 /** |
| 9116 * The size of the viewport |
| 9117 */ |
| 9118 _viewportSize: 0, |
| 9119 |
| 9120 /** |
| 9121 * An array of DOM nodes that are currently in the tree |
| 9122 * @type {?Array<!TemplatizerNode>} |
| 9123 */ |
| 9124 _physicalItems: null, |
| 9125 |
| 9126 /** |
| 9127 * An array of heights for each item in `_physicalItems` |
| 9128 * @type {?Array<number>} |
| 9129 */ |
| 9130 _physicalSizes: null, |
| 9131 |
| 9132 /** |
| 9133 * A cached value for the visible index. |
| 9134 * See `firstVisibleIndex` |
| 9135 * @type {?number} |
| 9136 */ |
| 9137 _firstVisibleIndexVal: null, |
| 9138 |
| 9139 /** |
| 9140 * A Polymer collection for the items. |
| 9141 * @type {?Polymer.Collection} |
| 9142 */ |
| 9143 _collection: null, |
| 9144 |
| 9145 /** |
| 9146 * True if the current item list was rendered for the first time |
| 9147 * after attached. |
| 9148 */ |
| 9149 _itemsRendered: false, |
| 9150 |
| 9151 /** |
| 9152 * The bottom of the physical content. |
| 9153 */ |
| 9154 get _physicalBottom() { |
| 9155 return this._physicalTop + this._physicalSize; |
| 9156 }, |
| 9157 |
| 9158 /** |
| 9159 * The n-th item rendered in the last physical item. |
| 9160 */ |
| 9161 get _virtualEnd() { |
| 9162 return this._virtualStartVal + this._physicalCount - 1; |
| 9163 }, |
| 9164 |
| 9165 /** |
| 9166 * The lowest n-th value for an item such that it can be rendered in `_physi
calStart`. |
| 9167 */ |
| 9168 _minVirtualStart: 0, |
| 9169 |
| 9170 /** |
| 9171 * The largest n-th value for an item such that it can be rendered in `_phys
icalStart`. |
| 9172 */ |
| 9173 get _maxVirtualStart() { |
| 9174 return this._virtualCount < this._physicalCount ? |
| 9175 this._virtualCount : this._virtualCount - this._physicalCount; |
| 9176 }, |
| 9177 |
| 9178 /** |
| 9179 * The height of the physical content that isn't on the screen. |
| 9180 */ |
| 9181 get _hiddenContentSize() { |
| 9182 return this._physicalSize - this._viewportSize; |
| 9183 }, |
| 9184 |
| 9185 /** |
| 9186 * The maximum scroll top value. |
| 9187 */ |
| 9188 get _maxScrollTop() { |
| 9189 return this._estScrollHeight - this._viewportSize; |
| 9190 }, |
| 9191 |
| 9192 /** |
| 9193 * Sets the n-th item rendered in `_physicalStart` |
| 9194 */ |
| 9195 set _virtualStart(val) { |
| 9196 // clamp the value so that _minVirtualStart <= val <= _maxVirtualStart |
| 9197 this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._min
VirtualStart, val)); |
| 9198 this._physicalStart = this._virtualStartVal % this._physicalCount; |
| 9199 this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this
._physicalCount; |
| 9200 }, |
| 9201 |
| 9202 /** |
| 9203 * Gets the n-th item rendered in `_physicalStart` |
| 9204 */ |
| 9205 get _virtualStart() { |
| 9206 return this._virtualStartVal; |
| 9207 }, |
| 9208 |
| 9209 /** |
| 9210 * An optimal physical size such that we will have enough physical items |
| 9211 * to fill up the viewport and recycle when the user scrolls. |
| 9212 * |
| 9213 * This default value assumes that we will at least have the equivalent |
| 9214 * to a viewport of physical items above and below the user's viewport. |
| 9215 */ |
| 9216 get _optPhysicalSize() { |
| 9217 return this._viewportSize * 3; |
| 9218 }, |
| 9219 |
| 9220 /** |
| 9221 * True if the current list is visible. |
| 9222 */ |
| 9223 get _isVisible() { |
| 9224 return this._scroller && Boolean(this._scroller.offsetWidth || this._scrol
ler.offsetHeight); |
| 9225 }, |
| 9226 |
| 9227 /** |
| 9228 * Gets the first visible item in the viewport. |
| 9229 * |
| 9230 * @type {number} |
| 9231 */ |
| 9232 get firstVisibleIndex() { |
| 9233 var physicalOffset; |
| 9234 |
| 9235 if (this._firstVisibleIndexVal === null) { |
| 9236 physicalOffset = this._physicalTop; |
| 9237 |
| 9238 this._firstVisibleIndexVal = this._iterateItems( |
| 9239 function(pidx, vidx) { |
| 9240 physicalOffset += this._physicalSizes[pidx]; |
| 9241 |
| 9242 if (physicalOffset > this._scrollPosition) { |
| 9243 return vidx; |
| 9244 } |
| 9245 }) || 0; |
| 9246 } |
| 9247 |
| 9248 return this._firstVisibleIndexVal; |
| 9249 }, |
| 9250 |
| 9251 ready: function() { |
| 9252 if (IOS_TOUCH_SCROLLING) { |
| 9253 this._scrollListener = function() { |
| 9254 requestAnimationFrame(this._scrollHandler.bind(this)); |
| 9255 }.bind(this); |
| 9256 } else { |
| 9257 this._scrollListener = this._scrollHandler.bind(this); |
| 9258 } |
| 9259 }, |
| 9260 |
| 9261 /** |
| 9262 * When the element has been attached to the DOM tree. |
| 9263 */ |
| 9264 attached: function() { |
| 9265 // delegate to the parent's scroller |
| 9266 // e.g. paper-scroll-header-panel |
| 9267 var el = Polymer.dom(this); |
| 9268 |
| 9269 var parentNode = /** @type {?{scroller: ?Element}} */ (el.parentNode); |
| 9270 if (parentNode && parentNode.scroller) { |
| 9271 this._scroller = parentNode.scroller; |
| 9272 } else { |
| 9273 this._scroller = this; |
| 9274 this.classList.add('has-scroller'); |
| 9275 } |
| 9276 |
| 9277 if (IOS_TOUCH_SCROLLING) { |
| 9278 this._scroller.style.webkitOverflowScrolling = 'touch'; |
| 9279 } |
| 9280 |
| 9281 this._scroller.addEventListener('scroll', this._scrollListener); |
| 9282 |
| 9283 this.updateViewportBoundaries(); |
| 9284 this._render(); |
| 9285 }, |
| 9286 |
| 9287 /** |
| 9288 * When the element has been removed from the DOM tree. |
| 9289 */ |
| 9290 detached: function() { |
| 9291 this._itemsRendered = false; |
| 9292 if (this._scroller) { |
| 9293 this._scroller.removeEventListener('scroll', this._scrollListener); |
| 9294 } |
| 9295 }, |
| 9296 |
| 9297 /** |
| 9298 * Invoke this method if you dynamically update the viewport's |
| 9299 * size or CSS padding. |
| 9300 * |
| 9301 * @method updateViewportBoundaries |
| 9302 */ |
| 9303 updateViewportBoundaries: function() { |
| 9304 var scrollerStyle = window.getComputedStyle(this._scroller); |
| 9305 this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10); |
| 9306 this._viewportSize = this._scroller.offsetHeight; |
| 9307 }, |
| 9308 |
| 9309 /** |
| 9310 * Update the models, the position of the |
| 9311 * items in the viewport and recycle tiles as needed. |
| 9312 */ |
| 9313 _refresh: function() { |
| 9314 var SCROLL_DIRECTION_UP = -1; |
| 9315 var SCROLL_DIRECTION_DOWN = 1; |
| 9316 var SCROLL_DIRECTION_NONE = 0; |
| 9317 |
| 9318 // clamp the `scrollTop` value |
| 9319 // IE 10|11 scrollTop may go above `_maxScrollTop` |
| 9320 // iOS `scrollTop` may go below 0 and above `_maxScrollTop` |
| 9321 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.sc
rollTop)); |
| 9322 |
| 9323 var tileHeight, kth, recycledTileSet; |
| 9324 var ratio = this._ratio; |
| 9325 var delta = scrollTop - this._scrollPosition; |
| 9326 var direction = SCROLL_DIRECTION_NONE; |
| 9327 var recycledTiles = 0; |
| 9328 var hiddenContentSize = this._hiddenContentSize; |
| 9329 var currentRatio = ratio; |
| 9330 var movingUp = []; |
| 9331 |
| 9332 // track the last `scrollTop` |
| 9333 this._scrollPosition = scrollTop; |
| 9334 |
| 9335 // clear cached visible index |
| 9336 this._firstVisibleIndexVal = null; |
| 9337 |
| 9338 // random access |
| 9339 if (Math.abs(delta) > this._physicalSize) { |
| 9340 this._physicalTop += delta; |
| 9341 direction = SCROLL_DIRECTION_NONE; |
| 9342 recycledTiles = Math.round(delta / this._physicalAverage); |
| 9343 } |
| 9344 // scroll up |
| 9345 else if (delta < 0) { |
| 9346 var topSpace = scrollTop - this._physicalTop; |
| 9347 var virtualStart = this._virtualStart; |
| 9348 |
| 9349 direction = SCROLL_DIRECTION_UP; |
| 9350 recycledTileSet = []; |
| 9351 |
| 9352 kth = this._physicalEnd; |
| 9353 currentRatio = topSpace / hiddenContentSize; |
| 9354 |
| 9355 // move tiles from bottom to top |
| 9356 while ( |
| 9357 // approximate `currentRatio` to `ratio` |
| 9358 currentRatio < ratio && |
| 9359 // recycle less physical items than the total |
| 9360 recycledTiles < this._physicalCount && |
| 9361 // ensure that these recycled tiles are needed |
| 9362 virtualStart - recycledTiles > 0 |
| 9363 ) { |
| 9364 |
| 9365 tileHeight = this._physicalSizes[kth] || this._physicalAverage; |
| 9366 currentRatio += tileHeight / hiddenContentSize; |
| 9367 |
| 9368 recycledTileSet.push(kth); |
| 9369 recycledTiles++; |
| 9370 kth = (kth === 0) ? this._physicalCount - 1 : kth - 1; |
| 9371 } |
| 9372 |
| 9373 movingUp = recycledTileSet; |
| 9374 recycledTiles = -recycledTiles; |
| 9375 |
| 9376 } |
| 9377 // scroll down |
| 9378 else if (delta > 0) { |
| 9379 var bottomSpace = this._physicalBottom - (scrollTop + this._viewportSize
); |
| 9380 var virtualEnd = this._virtualEnd; |
| 9381 var lastVirtualItemIndex = this._virtualCount-1; |
| 9382 |
| 9383 direction = SCROLL_DIRECTION_DOWN; |
| 9384 recycledTileSet = []; |
| 9385 |
| 9386 kth = this._physicalStart; |
| 9387 currentRatio = bottomSpace / hiddenContentSize; |
| 9388 |
| 9389 // move tiles from top to bottom |
| 9390 while ( |
| 9391 // approximate `currentRatio` to `ratio` |
| 9392 currentRatio < ratio && |
| 9393 // recycle less physical items than the total |
| 9394 recycledTiles < this._physicalCount && |
| 9395 // ensure that these recycled tiles are needed |
| 9396 virtualEnd + recycledTiles < lastVirtualItemIndex |
| 9397 ) { |
| 9398 |
| 9399 tileHeight = this._physicalSizes[kth] || this._physicalAverage; |
| 9400 currentRatio += tileHeight / hiddenContentSize; |
| 9401 |
| 9402 this._physicalTop += tileHeight; |
| 9403 recycledTileSet.push(kth); |
| 9404 recycledTiles++; |
| 9405 kth = (kth + 1) % this._physicalCount; |
| 9406 } |
| 9407 } |
| 9408 |
| 9409 if (recycledTiles !== 0) { |
| 9410 this._virtualStart = this._virtualStart + recycledTiles; |
| 9411 this._update(recycledTileSet, movingUp); |
| 9412 } |
| 9413 }, |
| 9414 |
| 9415 /** |
| 9416 * Update the list of items, starting from the `_virtualStartVal` item. |
| 9417 * @param {!Array<number>=} itemSet |
| 9418 * @param {!Array<number>=} movingUp |
| 9419 */ |
| 9420 _update: function(itemSet, movingUp) { |
| 9421 // update models |
| 9422 this._assignModels(itemSet); |
| 9423 |
| 9424 // measure heights |
| 9425 this._updateMetrics(itemSet); |
| 9426 |
| 9427 // adjust offset after measuring |
| 9428 if (movingUp) { |
| 9429 while (movingUp.length) { |
| 9430 this._physicalTop -= this._physicalSizes[movingUp.pop()]; |
| 9431 } |
| 9432 } |
| 9433 // update the position of the items |
| 9434 this._positionItems(); |
| 9435 |
| 9436 // set the scroller size |
| 9437 this._updateScrollerSize(); |
| 9438 |
| 9439 // increase the pool of physical items if needed |
| 9440 if (this._increasePoolIfNeeded()) { |
| 9441 // set models to the new items |
| 9442 this.async(this._update); |
| 9443 } |
| 9444 }, |
| 9445 |
| 9446 /** |
| 9447 * Creates a pool of DOM elements and attaches them to the local dom. |
| 9448 */ |
| 9449 _createPool: function(size) { |
| 9450 var physicalItems = new Array(size); |
| 9451 |
| 9452 this._ensureTemplatized(); |
| 9453 |
| 9454 for (var i = 0; i < size; i++) { |
| 9455 var inst = this.stamp(null); |
| 9456 |
| 9457 // First element child is item; Safari doesn't support children[0] |
| 9458 // on a doc fragment |
| 9459 physicalItems[i] = inst.root.querySelector('*'); |
| 9460 Polymer.dom(this).appendChild(inst.root); |
| 9461 } |
| 9462 |
| 9463 return physicalItems; |
| 9464 }, |
| 9465 |
| 9466 /** |
| 9467 * Increases the pool size. That is, the physical items in the DOM. |
| 9468 * This function will allocate additional physical items |
| 9469 * (limited by `MAX_PHYSICAL_COUNT`) if the content size is shorter than |
| 9470 * `_optPhysicalSize` |
| 9471 * |
| 9472 * @return boolean |
| 9473 */ |
| 9474 _increasePoolIfNeeded: function() { |
| 9475 if (this._physicalSize >= this._optPhysicalSize || this._physicalAverage =
== 0) { |
| 9476 return false; |
| 9477 } |
| 9478 |
| 9479 // the estimated number of physical items that we will need to reach |
| 9480 // the cap established by `_optPhysicalSize`. |
| 9481 var missingItems = Math.round( |
| 9482 (this._optPhysicalSize - this._physicalSize) * 1.2 / this._physicalAve
rage |
| 9483 ); |
| 9484 |
| 9485 // limit the size |
| 9486 var nextPhysicalCount = Math.min( |
| 9487 this._physicalCount + missingItems, |
| 9488 this._virtualCount, |
| 9489 MAX_PHYSICAL_COUNT |
| 9490 ); |
| 9491 |
| 9492 var prevPhysicalCount = this._physicalCount; |
| 9493 var delta = nextPhysicalCount - prevPhysicalCount; |
| 9494 |
| 9495 if (delta <= 0) { |
| 9496 return false; |
| 9497 } |
| 9498 |
| 9499 var newPhysicalItems = this._createPool(delta); |
| 9500 var emptyArray = new Array(delta); |
| 9501 |
| 9502 [].push.apply(this._physicalItems, newPhysicalItems); |
| 9503 [].push.apply(this._physicalSizes, emptyArray); |
| 9504 |
| 9505 this._physicalCount = prevPhysicalCount + delta; |
| 9506 |
| 9507 return true; |
| 9508 }, |
| 9509 |
| 9510 /** |
| 9511 * Render a new list of items. This method does exactly the same as `update`
, |
| 9512 * but it also ensures that only one `update` cycle is created. |
| 9513 */ |
| 9514 _render: function() { |
| 9515 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; |
| 9516 |
| 9517 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { |
| 9518 this._update(); |
| 9519 this._itemsRendered = true; |
| 9520 } |
| 9521 }, |
| 9522 |
| 9523 /** |
| 9524 * Templetizes the user template. |
| 9525 */ |
| 9526 _ensureTemplatized: function() { |
| 9527 if (!this.ctor) { |
| 9528 // Template instance props that should be excluded from forwarding |
| 9529 var props = {}; |
| 9530 |
| 9531 props.__key__ = true; |
| 9532 props[this.as] = true; |
| 9533 props[this.indexAs] = true; |
| 9534 props[this.selectedAs] = true; |
| 9535 |
| 9536 this._instanceProps = props; |
| 9537 this._userTemplate = Polymer.dom(this).querySelector('template'); |
| 9538 |
| 9539 if (this._userTemplate) { |
| 9540 this.templatize(this._userTemplate); |
| 9541 } else { |
| 9542 console.warn('iron-list requires a template to be provided in light-do
m'); |
| 9543 } |
| 9544 } |
| 9545 }, |
| 9546 |
| 9547 /** |
| 9548 * Implements extension point from Templatizer mixin. |
| 9549 */ |
| 9550 _getStampedChildren: function() { |
| 9551 return this._physicalItems; |
| 9552 }, |
| 9553 |
| 9554 /** |
| 9555 * Implements extension point from Templatizer |
| 9556 * Called as a side effect of a template instance path change, responsible |
| 9557 * for notifying items.<key-for-instance>.<path> change up to host. |
| 9558 */ |
| 9559 _forwardInstancePath: function(inst, path, value) { |
| 9560 if (path.indexOf(this.as + '.') === 0) { |
| 9561 this.notifyPath('items.' + inst.__key__ + '.' + |
| 9562 path.slice(this.as.length + 1), value); |
| 9563 } |
| 9564 }, |
| 9565 |
| 9566 /** |
| 9567 * Implements extension point from Templatizer mixin |
| 9568 * Called as side-effect of a host property change, responsible for |
| 9569 * notifying parent path change on each row. |
| 9570 */ |
| 9571 _forwardParentProp: function(prop, value) { |
| 9572 if (this._physicalItems) { |
| 9573 this._physicalItems.forEach(function(item) { |
| 9574 item._templateInstance[prop] = value; |
| 9575 }, this); |
| 9576 } |
| 9577 }, |
| 9578 |
| 9579 /** |
| 9580 * Implements extension point from Templatizer |
| 9581 * Called as side-effect of a host path change, responsible for |
| 9582 * notifying parent.<path> path change on each row. |
| 9583 */ |
| 9584 _forwardParentPath: function(path, value) { |
| 9585 if (this._physicalItems) { |
| 9586 this._physicalItems.forEach(function(item) { |
| 9587 item._templateInstance.notifyPath(path, value, true); |
| 9588 }, this); |
| 9589 } |
| 9590 }, |
| 9591 |
| 9592 /** |
| 9593 * Called as a side effect of a host items.<key>.<path> path change, |
| 9594 * responsible for notifying item.<path> changes to row for key. |
| 9595 */ |
| 9596 _forwardItemPath: function(path, value) { |
| 9597 if (this._physicalIndexForKey) { |
| 9598 var dot = path.indexOf('.'); |
| 9599 var key = path.substring(0, dot < 0 ? path.length : dot); |
| 9600 var idx = this._physicalIndexForKey[key]; |
| 9601 var row = this._physicalItems[idx]; |
| 9602 if (row) { |
| 9603 var inst = row._templateInstance; |
| 9604 if (dot >= 0) { |
| 9605 path = this.as + '.' + path.substring(dot+1); |
| 9606 inst.notifyPath(path, value, true); |
| 9607 } else { |
| 9608 inst[this.as] = value; |
| 9609 } |
| 9610 } |
| 9611 } |
| 9612 }, |
| 9613 |
| 9614 /** |
| 9615 * Called when the items have changed. That is, ressignments |
| 9616 * to `items`, splices or updates to a single item. |
| 9617 */ |
| 9618 _itemsChanged: function(change) { |
| 9619 if (change.path === 'items') { |
| 9620 // render the new set |
| 9621 this._itemsRendered = false; |
| 9622 |
| 9623 // update the whole set |
| 9624 this._virtualStartVal = 0; |
| 9625 this._physicalTop = 0; |
| 9626 this._virtualCount = this.items ? this.items.length : 0; |
| 9627 this._collection = this.items ? Polymer.Collection.get(this.items) : nul
l; |
| 9628 this._physicalIndexForKey = {}; |
| 9629 |
| 9630 // scroll to the top |
| 9631 this._resetScrollPosition(0); |
| 9632 |
| 9633 // create the initial physical items |
| 9634 if (!this._physicalItems) { |
| 9635 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi
s._virtualCount)); |
| 9636 this._physicalItems = this._createPool(this._physicalCount); |
| 9637 this._physicalSizes = new Array(this._physicalCount); |
| 9638 } |
| 9639 |
| 9640 this.debounce('refresh', this._render); |
| 9641 |
| 9642 } else if (change.path === 'items.splices') { |
| 9643 // render the new set |
| 9644 this._itemsRendered = false; |
| 9645 |
| 9646 this._adjustVirtualIndex(change.value.indexSplices); |
| 9647 this._virtualCount = this.items ? this.items.length : 0; |
| 9648 |
| 9649 this.debounce('refresh', this._render); |
| 9650 |
| 9651 } else { |
| 9652 // update a single item |
| 9653 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); |
| 9654 } |
| 9655 }, |
| 9656 |
| 9657 /** |
| 9658 * @param {!Array<!PolymerSplice>} splices |
| 9659 */ |
| 9660 _adjustVirtualIndex: function(splices) { |
| 9661 var i, splice, idx; |
| 9662 |
| 9663 for (i = 0; i < splices.length; i++) { |
| 9664 splice = splices[i]; |
| 9665 |
| 9666 // deselect removed items |
| 9667 splice.removed.forEach(this.$.selector.deselect, this.$.selector); |
| 9668 |
| 9669 idx = splice.index; |
| 9670 // We only need to care about changes happening above the current positi
on |
| 9671 if (idx >= this._virtualStartVal) { |
| 9672 break; |
| 9673 } |
| 9674 |
| 9675 this._virtualStart = this._virtualStart + |
| 9676 Math.max(splice.addedCount - splice.removed.length, idx - this._virt
ualStartVal); |
| 9677 } |
| 9678 }, |
| 9679 |
| 9680 _scrollHandler: function() { |
| 9681 this._refresh(); |
| 9682 }, |
| 9683 |
| 9684 /** |
| 9685 * Executes a provided function per every physical index in `itemSet` |
| 9686 * `itemSet` default value is equivalent to the entire set of physical index
es. |
| 9687 * |
| 9688 * @param {!function(number, number)} fn |
| 9689 * @param {!Array<number>=} itemSet |
| 9690 */ |
| 9691 _iterateItems: function(fn, itemSet) { |
| 9692 var pidx, vidx, rtn, i; |
| 9693 |
| 9694 if (arguments.length === 2 && itemSet) { |
| 9695 for (i = 0; i < itemSet.length; i++) { |
| 9696 pidx = itemSet[i]; |
| 9697 if (pidx >= this._physicalStart) { |
| 9698 vidx = this._virtualStartVal + (pidx - this._physicalStart); |
| 9699 } else { |
| 9700 vidx = this._virtualStartVal + (this._physicalCount - this._physical
Start) + pidx; |
| 9701 } |
| 9702 if ((rtn = fn.call(this, pidx, vidx)) != null) { |
| 9703 return rtn; |
| 9704 } |
| 9705 } |
| 9706 } else { |
| 9707 pidx = this._physicalStart; |
| 9708 vidx = this._virtualStartVal; |
| 9709 |
| 9710 for (; pidx < this._physicalCount; pidx++, vidx++) { |
| 9711 if ((rtn = fn.call(this, pidx, vidx)) != null) { |
| 9712 return rtn; |
| 9713 } |
| 9714 } |
| 9715 |
| 9716 pidx = 0; |
| 9717 |
| 9718 for (; pidx < this._physicalStart; pidx++, vidx++) { |
| 9719 if ((rtn = fn.call(this, pidx, vidx)) != null) { |
| 9720 return rtn; |
| 9721 } |
| 9722 } |
| 9723 } |
| 9724 }, |
| 9725 |
| 9726 /** |
| 9727 * Assigns the data models to a given set of items. |
| 9728 * @param {!Array<number>=} itemSet |
| 9729 */ |
| 9730 _assignModels: function(itemSet) { |
| 9731 this._iterateItems(function(pidx, vidx) { |
| 9732 var el = this._physicalItems[pidx]; |
| 9733 var inst = el._templateInstance; |
| 9734 var item = this.items && this.items[vidx]; |
| 9735 |
| 9736 if (item) { |
| 9737 inst[this.as] = item; |
| 9738 inst.__key__ = this._collection.getKey(item); |
| 9739 inst[this.selectedAs] = |
| 9740 /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(it
em); |
| 9741 inst[this.indexAs] = vidx; |
| 9742 el.removeAttribute('hidden'); |
| 9743 this._physicalIndexForKey[inst.__key__] = pidx; |
| 9744 } else { |
| 9745 inst.__key__ = null; |
| 9746 el.setAttribute('hidden', ''); |
| 9747 } |
| 9748 |
| 9749 }, itemSet); |
| 9750 }, |
| 9751 |
| 9752 /** |
| 9753 * Updates the height for a given set of items. |
| 9754 * |
| 9755 * @param {!Array<number>=} itemSet |
| 9756 */ |
| 9757 _updateMetrics: function(itemSet) { |
| 9758 var newPhysicalSize = 0; |
| 9759 var oldPhysicalSize = 0; |
| 9760 var prevAvgCount = this._physicalAverageCount; |
| 9761 var prevPhysicalAvg = this._physicalAverage; |
| 9762 // Make sure we distributed all the physical items |
| 9763 // so we can measure them |
| 9764 Polymer.dom.flush(); |
| 9765 |
| 9766 this._iterateItems(function(pidx, vidx) { |
| 9767 oldPhysicalSize += this._physicalSizes[pidx] || 0; |
| 9768 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; |
| 9769 newPhysicalSize += this._physicalSizes[pidx]; |
| 9770 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; |
| 9771 }, itemSet); |
| 9772 |
| 9773 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSiz
e; |
| 9774 this._viewportSize = this._scroller.offsetHeight; |
| 9775 |
| 9776 // update the average if we measured something |
| 9777 if (this._physicalAverageCount !== prevAvgCount) { |
| 9778 this._physicalAverage = Math.round( |
| 9779 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / |
| 9780 this._physicalAverageCount); |
| 9781 } |
| 9782 }, |
| 9783 |
| 9784 /** |
| 9785 * Updates the position of the physical items. |
| 9786 */ |
| 9787 _positionItems: function() { |
| 9788 this._adjustScrollPosition(); |
| 9789 |
| 9790 var y = this._physicalTop; |
| 9791 |
| 9792 this._iterateItems(function(pidx) { |
| 9793 |
| 9794 this.transform('translate3d(0, ' + y + 'px, 0)', this._physicalItems[pid
x]); |
| 9795 y += this._physicalSizes[pidx]; |
| 9796 |
| 9797 }); |
| 9798 }, |
| 9799 |
| 9800 /** |
| 9801 * Adjusts the scroll position when it was overestimated. |
| 9802 */ |
| 9803 _adjustScrollPosition: function() { |
| 9804 var deltaHeight = this._virtualStartVal === 0 ? this._physicalTop : |
| 9805 Math.min(this._scrollPosition + this._physicalTop, 0); |
| 9806 |
| 9807 if (deltaHeight) { |
| 9808 this._physicalTop = this._physicalTop - deltaHeight; |
| 9809 |
| 9810 // juking scroll position during interial scrolling on iOS is no bueno |
| 9811 if (!IOS_TOUCH_SCROLLING) { |
| 9812 this._resetScrollPosition(this._scroller.scrollTop - deltaHeight); |
| 9813 } |
| 9814 } |
| 9815 }, |
| 9816 |
| 9817 /** |
| 9818 * Sets the position of the scroll. |
| 9819 */ |
| 9820 _resetScrollPosition: function(pos) { |
| 9821 if (this._scroller) { |
| 9822 this._scroller.scrollTop = pos; |
| 9823 this._scrollPosition = this._scroller.scrollTop; |
| 9824 } |
| 9825 }, |
| 9826 |
| 9827 /** |
| 9828 * Sets the scroll height, that's the height of the content, |
| 9829 * |
| 9830 * @param {boolean=} forceUpdate If true, updates the height no matter what. |
| 9831 */ |
| 9832 _updateScrollerSize: function(forceUpdate) { |
| 9833 this._estScrollHeight = (this._physicalBottom + |
| 9834 Math.max(this._virtualCount - this._physicalCount - this._virtualStart
Val, 0) * this._physicalAverage); |
| 9835 |
| 9836 forceUpdate = forceUpdate || this._scrollHeight === 0; |
| 9837 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; |
| 9838 |
| 9839 // amortize height adjustment, so it won't trigger repaints very often |
| 9840 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { |
| 9841 this.$.items.style.height = this._estScrollHeight + 'px'; |
| 9842 this._scrollHeight = this._estScrollHeight; |
| 9843 } |
| 9844 }, |
| 9845 |
| 9846 /** |
| 9847 * Scroll to a specific item in the virtual list regardless |
| 9848 * of the physical items in the DOM tree. |
| 9849 * |
| 9850 * @method scrollToIndex |
| 9851 * @param {number} idx The index of the item |
| 9852 */ |
| 9853 scrollToIndex: function(idx) { |
| 9854 if (typeof idx !== 'number') { |
| 9855 return; |
| 9856 } |
| 9857 |
| 9858 var firstVisible = this.firstVisibleIndex; |
| 9859 |
| 9860 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); |
| 9861 |
| 9862 // start at the previous virtual item |
| 9863 // so we have a item above the first visible item |
| 9864 this._virtualStart = idx - 1; |
| 9865 |
| 9866 // assign new models |
| 9867 this._assignModels(); |
| 9868 |
| 9869 // measure the new sizes |
| 9870 this._updateMetrics(); |
| 9871 |
| 9872 // estimate new physical offset |
| 9873 this._physicalTop = this._virtualStart * this._physicalAverage; |
| 9874 |
| 9875 var currentTopItem = this._physicalStart; |
| 9876 var currentVirtualItem = this._virtualStart; |
| 9877 var targetOffsetTop = 0; |
| 9878 var hiddenContentSize = this._hiddenContentSize; |
| 9879 |
| 9880 // scroll to the item as much as we can |
| 9881 while (currentVirtualItem !== idx && targetOffsetTop < hiddenContentSize)
{ |
| 9882 targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; |
| 9883 currentTopItem = (currentTopItem + 1) % this._physicalCount; |
| 9884 currentVirtualItem++; |
| 9885 } |
| 9886 |
| 9887 // update the scroller size |
| 9888 this._updateScrollerSize(true); |
| 9889 |
| 9890 // update the position of the items |
| 9891 this._positionItems(); |
| 9892 |
| 9893 // set the new scroll position |
| 9894 this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1); |
| 9895 |
| 9896 // increase the pool of physical items if needed |
| 9897 if (this._increasePoolIfNeeded()) { |
| 9898 // set models to the new items |
| 9899 this.async(this._update); |
| 9900 } |
| 9901 |
| 9902 // clear cached visible index |
| 9903 this._firstVisibleIndexVal = null; |
| 9904 }, |
| 9905 |
| 9906 /** |
| 9907 * Reset the physical average and the average count. |
| 9908 */ |
| 9909 _resetAverage: function() { |
| 9910 this._physicalAverage = 0; |
| 9911 this._physicalAverageCount = 0; |
| 9912 }, |
| 9913 |
| 9914 /** |
| 9915 * A handler for the `iron-resize` event triggered by `IronResizableBehavior
` |
| 9916 * when the element is resized. |
| 9917 */ |
| 9918 _resizeHandler: function() { |
| 9919 this.debounce('resize', function() { |
| 9920 this._render(); |
| 9921 if (this._itemsRendered && this._physicalItems && this._isVisible) { |
| 9922 this._resetAverage(); |
| 9923 this.updateViewportBoundaries(); |
| 9924 this.scrollToIndex(this.firstVisibleIndex); |
| 9925 } |
| 9926 }); |
| 9927 }, |
| 9928 |
| 9929 _getModelFromItem: function(item) { |
| 9930 var key = this._collection.getKey(item); |
| 9931 var pidx = this._physicalIndexForKey[key]; |
| 9932 |
| 9933 if (pidx !== undefined) { |
| 9934 return this._physicalItems[pidx]._templateInstance; |
| 9935 } |
| 9936 return null; |
| 9937 }, |
| 9938 |
| 9939 /** |
| 9940 * Gets a valid item instance from its index or the object value. |
| 9941 * |
| 9942 * @param {(Object|number)} item The item object or its index |
| 9943 */ |
| 9944 _getNormalizedItem: function(item) { |
| 9945 if (typeof item === 'number') { |
| 9946 item = this.items[item]; |
| 9947 if (!item) { |
| 9948 throw new RangeError('<item> not found'); |
| 9949 } |
| 9950 } else if (this._collection.getKey(item) === undefined) { |
| 9951 throw new TypeError('<item> should be a valid item'); |
| 9952 } |
| 9953 return item; |
| 9954 }, |
| 9955 |
| 9956 /** |
| 9957 * Select the list item at the given index. |
| 9958 * |
| 9959 * @method selectItem |
| 9960 * @param {(Object|number)} item The item object or its index |
| 9961 */ |
| 9962 selectItem: function(item) { |
| 9963 item = this._getNormalizedItem(item); |
| 9964 var model = this._getModelFromItem(item); |
| 9965 |
| 9966 if (!this.multiSelection && this.selectedItem) { |
| 9967 this.deselectItem(this.selectedItem); |
| 9968 } |
| 9969 if (model) { |
| 9970 model[this.selectedAs] = true; |
| 9971 } |
| 9972 this.$.selector.select(item); |
| 9973 }, |
| 9974 |
| 9975 /** |
| 9976 * Deselects the given item list if it is already selected. |
| 9977 * |
| 9978 |
| 9979 * @method deselect |
| 9980 * @param {(Object|number)} item The item object or its index |
| 9981 */ |
| 9982 deselectItem: function(item) { |
| 9983 item = this._getNormalizedItem(item); |
| 9984 var model = this._getModelFromItem(item); |
| 9985 |
| 9986 if (model) { |
| 9987 model[this.selectedAs] = false; |
| 9988 } |
| 9989 this.$.selector.deselect(item); |
| 9990 }, |
| 9991 |
| 9992 /** |
| 9993 * Select or deselect a given item depending on whether the item |
| 9994 * has already been selected. |
| 9995 * |
| 9996 * @method toggleSelectionForItem |
| 9997 * @param {(Object|number)} item The item object or its index |
| 9998 */ |
| 9999 toggleSelectionForItem: function(item) { |
| 10000 item = this._getNormalizedItem(item); |
| 10001 if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item
)) { |
| 10002 this.deselectItem(item); |
| 10003 } else { |
| 10004 this.selectItem(item); |
| 10005 } |
| 10006 }, |
| 10007 |
| 10008 /** |
| 10009 * Clears the current selection state of the list. |
| 10010 * |
| 10011 * @method clearSelection |
| 10012 */ |
| 10013 clearSelection: function() { |
| 10014 function unselect(item) { |
| 10015 var model = this._getModelFromItem(item); |
| 10016 if (model) { |
| 10017 model[this.selectedAs] = false; |
| 10018 } |
| 10019 } |
| 10020 |
| 10021 if (Array.isArray(this.selectedItems)) { |
| 10022 this.selectedItems.forEach(unselect, this); |
| 10023 } else if (this.selectedItem) { |
| 10024 unselect.call(this, this.selectedItem); |
| 10025 } |
| 10026 |
| 10027 /** @type {!ArraySelectorElement} */ (this.$.selector).clearSelection(); |
| 10028 }, |
| 10029 |
| 10030 /** |
| 10031 * Add an event listener to `tap` if `selectionEnabled` is true, |
| 10032 * it will remove the listener otherwise. |
| 10033 */ |
| 10034 _selectionEnabledChanged: function(selectionEnabled) { |
| 10035 if (selectionEnabled) { |
| 10036 this.listen(this, 'tap', '_selectionHandler'); |
| 10037 this.listen(this, 'keypress', '_selectionHandler'); |
| 10038 } else { |
| 10039 this.unlisten(this, 'tap', '_selectionHandler'); |
| 10040 this.unlisten(this, 'keypress', '_selectionHandler'); |
| 10041 } |
| 10042 }, |
| 10043 |
| 10044 /** |
| 10045 * Select an item from an event object. |
| 10046 */ |
| 10047 _selectionHandler: function(e) { |
| 10048 if (e.type !== 'keypress' || e.keyCode === 13) { |
| 10049 var model = this.modelForElement(e.target); |
| 10050 if (model) { |
| 10051 this.toggleSelectionForItem(model[this.as]); |
| 10052 } |
| 10053 } |
| 10054 }, |
| 10055 |
| 10056 _multiSelectionChanged: function(multiSelection) { |
| 10057 this.clearSelection(); |
| 10058 this.$.selector.multi = multiSelection; |
| 10059 }, |
| 10060 |
| 10061 /** |
| 10062 * Updates the size of an item. |
| 10063 * |
| 10064 * @method updateSizeForItem |
| 10065 * @param {(Object|number)} item The item object or its index |
| 10066 */ |
| 10067 updateSizeForItem: function(item) { |
| 10068 item = this._getNormalizedItem(item); |
| 10069 var key = this._collection.getKey(item); |
| 10070 var pidx = this._physicalIndexForKey[key]; |
| 10071 |
| 10072 if (pidx !== undefined) { |
| 10073 this._updateMetrics([pidx]); |
| 10074 this._positionItems(); |
| 10075 } |
| 10076 } |
| 10077 }); |
| 10078 |
| 10079 })(); |
| 8774 (function() { | 10080 (function() { |
| 8775 | 10081 |
| 8776 'use strict'; | 10082 'use strict'; |
| 8777 | 10083 |
| 8778 var SHADOW_WHEN_SCROLLING = 1; | 10084 var SHADOW_WHEN_SCROLLING = 1; |
| 8779 var SHADOW_ALWAYS = 2; | 10085 var SHADOW_ALWAYS = 2; |
| 8780 | 10086 |
| 8781 | 10087 |
| 8782 var MODE_CONFIGS = { | 10088 var MODE_CONFIGS = { |
| 8783 | 10089 |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9011 }, | 10317 }, |
| 9012 | 10318 |
| 9013 _getScrollerForMode: function(mode) { | 10319 _getScrollerForMode: function(mode) { |
| 9014 return MODE_CONFIGS.outerScroll[mode] ? | 10320 return MODE_CONFIGS.outerScroll[mode] ? |
| 9015 this : this.$.mainContainer; | 10321 this : this.$.mainContainer; |
| 9016 } | 10322 } |
| 9017 | 10323 |
| 9018 }); | 10324 }); |
| 9019 | 10325 |
| 9020 })(); | 10326 })(); |
| 10327 (function() { |
| 10328 |
| 10329 // monostate data |
| 10330 var metaDatas = {}; |
| 10331 var metaArrays = {}; |
| 10332 |
| 10333 Polymer.IronMeta = Polymer({ |
| 10334 |
| 10335 is: 'iron-meta', |
| 10336 |
| 10337 properties: { |
| 10338 |
| 10339 /** |
| 10340 * The type of meta-data. All meta-data of the same type is stored |
| 10341 * together. |
| 10342 */ |
| 10343 type: { |
| 10344 type: String, |
| 10345 value: 'default', |
| 10346 observer: '_typeChanged' |
| 10347 }, |
| 10348 |
| 10349 /** |
| 10350 * The key used to store `value` under the `type` namespace. |
| 10351 */ |
| 10352 key: { |
| 10353 type: String, |
| 10354 observer: '_keyChanged' |
| 10355 }, |
| 10356 |
| 10357 /** |
| 10358 * The meta-data to store or retrieve. |
| 10359 */ |
| 10360 value: { |
| 10361 type: Object, |
| 10362 notify: true, |
| 10363 observer: '_valueChanged' |
| 10364 }, |
| 10365 |
| 10366 /** |
| 10367 * If true, `value` is set to the iron-meta instance itself. |
| 10368 */ |
| 10369 self: { |
| 10370 type: Boolean, |
| 10371 observer: '_selfChanged' |
| 10372 }, |
| 10373 |
| 10374 /** |
| 10375 * Array of all meta-data values for the given type. |
| 10376 */ |
| 10377 list: { |
| 10378 type: Array, |
| 10379 notify: true |
| 10380 } |
| 10381 |
| 10382 }, |
| 10383 |
| 10384 /** |
| 10385 * Only runs if someone invokes the factory/constructor directly |
| 10386 * e.g. `new Polymer.IronMeta()` |
| 10387 */ |
| 10388 factoryImpl: function(config) { |
| 10389 if (config) { |
| 10390 for (var n in config) { |
| 10391 switch(n) { |
| 10392 case 'type': |
| 10393 case 'key': |
| 10394 case 'value': |
| 10395 this[n] = config[n]; |
| 10396 break; |
| 10397 } |
| 10398 } |
| 10399 } |
| 10400 }, |
| 10401 |
| 10402 created: function() { |
| 10403 // TODO(sjmiles): good for debugging? |
| 10404 this._metaDatas = metaDatas; |
| 10405 this._metaArrays = metaArrays; |
| 10406 }, |
| 10407 |
| 10408 _keyChanged: function(key, old) { |
| 10409 this._resetRegistration(old); |
| 10410 }, |
| 10411 |
| 10412 _valueChanged: function(value) { |
| 10413 this._resetRegistration(this.key); |
| 10414 }, |
| 10415 |
| 10416 _selfChanged: function(self) { |
| 10417 if (self) { |
| 10418 this.value = this; |
| 10419 } |
| 10420 }, |
| 10421 |
| 10422 _typeChanged: function(type) { |
| 10423 this._unregisterKey(this.key); |
| 10424 if (!metaDatas[type]) { |
| 10425 metaDatas[type] = {}; |
| 10426 } |
| 10427 this._metaData = metaDatas[type]; |
| 10428 if (!metaArrays[type]) { |
| 10429 metaArrays[type] = []; |
| 10430 } |
| 10431 this.list = metaArrays[type]; |
| 10432 this._registerKeyValue(this.key, this.value); |
| 10433 }, |
| 10434 |
| 10435 /** |
| 10436 * Retrieves meta data value by key. |
| 10437 * |
| 10438 * @method byKey |
| 10439 * @param {string} key The key of the meta-data to be returned. |
| 10440 * @return {*} |
| 10441 */ |
| 10442 byKey: function(key) { |
| 10443 return this._metaData && this._metaData[key]; |
| 10444 }, |
| 10445 |
| 10446 _resetRegistration: function(oldKey) { |
| 10447 this._unregisterKey(oldKey); |
| 10448 this._registerKeyValue(this.key, this.value); |
| 10449 }, |
| 10450 |
| 10451 _unregisterKey: function(key) { |
| 10452 this._unregister(key, this._metaData, this.list); |
| 10453 }, |
| 10454 |
| 10455 _registerKeyValue: function(key, value) { |
| 10456 this._register(key, value, this._metaData, this.list); |
| 10457 }, |
| 10458 |
| 10459 _register: function(key, value, data, list) { |
| 10460 if (key && data && value !== undefined) { |
| 10461 data[key] = value; |
| 10462 list.push(value); |
| 10463 } |
| 10464 }, |
| 10465 |
| 10466 _unregister: function(key, data, list) { |
| 10467 if (key && data) { |
| 10468 if (key in data) { |
| 10469 var value = data[key]; |
| 10470 delete data[key]; |
| 10471 this.arrayDelete(list, value); |
| 10472 } |
| 10473 } |
| 10474 } |
| 10475 |
| 10476 }); |
| 10477 |
| 10478 /** |
| 10479 `iron-meta-query` can be used to access infomation stored in `iron-meta`. |
| 10480 |
| 10481 Examples: |
| 10482 |
| 10483 If I create an instance like this: |
| 10484 |
| 10485 <iron-meta key="info" value="foo/bar"></iron-meta> |
| 10486 |
| 10487 Note that value="foo/bar" is the metadata I've defined. I could define more |
| 10488 attributes or use child nodes to define additional metadata. |
| 10489 |
| 10490 Now I can access that element (and it's metadata) from any `iron-meta-query`
instance: |
| 10491 |
| 10492 var value = new Polymer.IronMetaQuery({key: 'info'}).value; |
| 10493 |
| 10494 @group Polymer Iron Elements |
| 10495 @element iron-meta-query |
| 10496 */ |
| 10497 Polymer.IronMetaQuery = Polymer({ |
| 10498 |
| 10499 is: 'iron-meta-query', |
| 10500 |
| 10501 properties: { |
| 10502 |
| 10503 /** |
| 10504 * The type of meta-data. All meta-data of the same type is stored |
| 10505 * together. |
| 10506 */ |
| 10507 type: { |
| 10508 type: String, |
| 10509 value: 'default', |
| 10510 observer: '_typeChanged' |
| 10511 }, |
| 10512 |
| 10513 /** |
| 10514 * Specifies a key to use for retrieving `value` from the `type` |
| 10515 * namespace. |
| 10516 */ |
| 10517 key: { |
| 10518 type: String, |
| 10519 observer: '_keyChanged' |
| 10520 }, |
| 10521 |
| 10522 /** |
| 10523 * The meta-data to store or retrieve. |
| 10524 */ |
| 10525 value: { |
| 10526 type: Object, |
| 10527 notify: true, |
| 10528 readOnly: true |
| 10529 }, |
| 10530 |
| 10531 /** |
| 10532 * Array of all meta-data values for the given type. |
| 10533 */ |
| 10534 list: { |
| 10535 type: Array, |
| 10536 notify: true |
| 10537 } |
| 10538 |
| 10539 }, |
| 10540 |
| 10541 /** |
| 10542 * Actually a factory method, not a true constructor. Only runs if |
| 10543 * someone invokes it directly (via `new Polymer.IronMeta()`); |
| 10544 */ |
| 10545 factoryImpl: function(config) { |
| 10546 if (config) { |
| 10547 for (var n in config) { |
| 10548 switch(n) { |
| 10549 case 'type': |
| 10550 case 'key': |
| 10551 this[n] = config[n]; |
| 10552 break; |
| 10553 } |
| 10554 } |
| 10555 } |
| 10556 }, |
| 10557 |
| 10558 created: function() { |
| 10559 // TODO(sjmiles): good for debugging? |
| 10560 this._metaDatas = metaDatas; |
| 10561 this._metaArrays = metaArrays; |
| 10562 }, |
| 10563 |
| 10564 _keyChanged: function(key) { |
| 10565 this._setValue(this._metaData && this._metaData[key]); |
| 10566 }, |
| 10567 |
| 10568 _typeChanged: function(type) { |
| 10569 this._metaData = metaDatas[type]; |
| 10570 this.list = metaArrays[type]; |
| 10571 if (this.key) { |
| 10572 this._keyChanged(this.key); |
| 10573 } |
| 10574 }, |
| 10575 |
| 10576 /** |
| 10577 * Retrieves meta data value by key. |
| 10578 * @param {string} key The key of the meta-data to be returned. |
| 10579 * @return {*} |
| 10580 */ |
| 10581 byKey: function(key) { |
| 10582 return this._metaData && this._metaData[key]; |
| 10583 } |
| 10584 |
| 10585 }); |
| 10586 |
| 10587 })(); |
| 10588 Polymer({ |
| 10589 |
| 10590 is: 'iron-icon', |
| 10591 |
| 10592 properties: { |
| 10593 |
| 10594 /** |
| 10595 * The name of the icon to use. The name should be of the form: |
| 10596 * `iconset_name:icon_name`. |
| 10597 */ |
| 10598 icon: { |
| 10599 type: String, |
| 10600 observer: '_iconChanged' |
| 10601 }, |
| 10602 |
| 10603 /** |
| 10604 * The name of the theme to used, if one is specified by the |
| 10605 * iconset. |
| 10606 */ |
| 10607 theme: { |
| 10608 type: String, |
| 10609 observer: '_updateIcon' |
| 10610 }, |
| 10611 |
| 10612 /** |
| 10613 * If using iron-icon without an iconset, you can set the src to be |
| 10614 * the URL of an individual icon image file. Note that this will take |
| 10615 * precedence over a given icon attribute. |
| 10616 */ |
| 10617 src: { |
| 10618 type: String, |
| 10619 observer: '_srcChanged' |
| 10620 }, |
| 10621 |
| 10622 /** |
| 10623 * @type {!Polymer.IronMeta} |
| 10624 */ |
| 10625 _meta: { |
| 10626 value: Polymer.Base.create('iron-meta', {type: 'iconset'}) |
| 10627 } |
| 10628 |
| 10629 }, |
| 10630 |
| 10631 _DEFAULT_ICONSET: 'icons', |
| 10632 |
| 10633 _iconChanged: function(icon) { |
| 10634 var parts = (icon || '').split(':'); |
| 10635 this._iconName = parts.pop(); |
| 10636 this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; |
| 10637 this._updateIcon(); |
| 10638 }, |
| 10639 |
| 10640 _srcChanged: function(src) { |
| 10641 this._updateIcon(); |
| 10642 }, |
| 10643 |
| 10644 _usesIconset: function() { |
| 10645 return this.icon || !this.src; |
| 10646 }, |
| 10647 |
| 10648 /** @suppress {visibility} */ |
| 10649 _updateIcon: function() { |
| 10650 if (this._usesIconset()) { |
| 10651 if (this._iconsetName) { |
| 10652 this._iconset = /** @type {?Polymer.Iconset} */ ( |
| 10653 this._meta.byKey(this._iconsetName)); |
| 10654 if (this._iconset) { |
| 10655 this._iconset.applyIcon(this, this._iconName, this.theme); |
| 10656 this.unlisten(window, 'iron-iconset-added', '_updateIcon'); |
| 10657 } else { |
| 10658 this.listen(window, 'iron-iconset-added', '_updateIcon'); |
| 10659 } |
| 10660 } |
| 10661 } else { |
| 10662 if (!this._img) { |
| 10663 this._img = document.createElement('img'); |
| 10664 this._img.style.width = '100%'; |
| 10665 this._img.style.height = '100%'; |
| 10666 this._img.draggable = false; |
| 10667 } |
| 10668 this._img.src = this.src; |
| 10669 Polymer.dom(this.root).appendChild(this._img); |
| 10670 } |
| 10671 } |
| 10672 |
| 10673 }); |
| 10674 /** |
| 10675 * The `iron-iconset-svg` element allows users to define their own icon sets |
| 10676 * that contain svg icons. The svg icon elements should be children of the |
| 10677 * `iron-iconset-svg` element. Multiple icons should be given distinct id's. |
| 10678 * |
| 10679 * Using svg elements to create icons has a few advantages over traditional |
| 10680 * bitmap graphics like jpg or png. Icons that use svg are vector based so the
y |
| 10681 * are resolution independent and should look good on any device. They are |
| 10682 * stylable via css. Icons can be themed, colorized, and even animated. |
| 10683 * |
| 10684 * Example: |
| 10685 * |
| 10686 * <iron-iconset-svg name="my-svg-icons" size="24"> |
| 10687 * <svg> |
| 10688 * <defs> |
| 10689 * <g id="shape"> |
| 10690 * <rect x="50" y="50" width="50" height="50" /> |
| 10691 * <circle cx="50" cy="50" r="50" /> |
| 10692 * </g> |
| 10693 * </defs> |
| 10694 * </svg> |
| 10695 * </iron-iconset-svg> |
| 10696 * |
| 10697 * This will automatically register the icon set "my-svg-icons" to the iconset |
| 10698 * database. To use these icons from within another element, make a |
| 10699 * `iron-iconset` element and call the `byId` method |
| 10700 * to retrieve a given iconset. To apply a particular icon inside an |
| 10701 * element use the `applyIcon` method. For example: |
| 10702 * |
| 10703 * iconset.applyIcon(iconNode, 'car'); |
| 10704 * |
| 10705 * @element iron-iconset-svg |
| 10706 * @demo demo/index.html |
| 10707 */ |
| 10708 Polymer({ |
| 10709 |
| 10710 is: 'iron-iconset-svg', |
| 10711 |
| 10712 properties: { |
| 10713 |
| 10714 /** |
| 10715 * The name of the iconset. |
| 10716 * |
| 10717 * @attribute name |
| 10718 * @type string |
| 10719 */ |
| 10720 name: { |
| 10721 type: String, |
| 10722 observer: '_nameChanged' |
| 10723 }, |
| 10724 |
| 10725 /** |
| 10726 * The size of an individual icon. Note that icons must be square. |
| 10727 * |
| 10728 * @attribute iconSize |
| 10729 * @type number |
| 10730 * @default 24 |
| 10731 */ |
| 10732 size: { |
| 10733 type: Number, |
| 10734 value: 24 |
| 10735 } |
| 10736 |
| 10737 }, |
| 10738 |
| 10739 /** |
| 10740 * Construct an array of all icon names in this iconset. |
| 10741 * |
| 10742 * @return {!Array} Array of icon names. |
| 10743 */ |
| 10744 getIconNames: function() { |
| 10745 this._icons = this._createIconMap(); |
| 10746 return Object.keys(this._icons).map(function(n) { |
| 10747 return this.name + ':' + n; |
| 10748 }, this); |
| 10749 }, |
| 10750 |
| 10751 /** |
| 10752 * Applies an icon to the given element. |
| 10753 * |
| 10754 * An svg icon is prepended to the element's shadowRoot if it exists, |
| 10755 * otherwise to the element itself. |
| 10756 * |
| 10757 * @method applyIcon |
| 10758 * @param {Element} element Element to which the icon is applied. |
| 10759 * @param {string} iconName Name of the icon to apply. |
| 10760 * @return {Element} The svg element which renders the icon. |
| 10761 */ |
| 10762 applyIcon: function(element, iconName) { |
| 10763 // insert svg element into shadow root, if it exists |
| 10764 element = element.root || element; |
| 10765 // Remove old svg element |
| 10766 this.removeIcon(element); |
| 10767 // install new svg element |
| 10768 var svg = this._cloneIcon(iconName); |
| 10769 if (svg) { |
| 10770 var pde = Polymer.dom(element); |
| 10771 pde.insertBefore(svg, pde.childNodes[0]); |
| 10772 return element._svgIcon = svg; |
| 10773 } |
| 10774 return null; |
| 10775 }, |
| 10776 |
| 10777 /** |
| 10778 * Remove an icon from the given element by undoing the changes effected |
| 10779 * by `applyIcon`. |
| 10780 * |
| 10781 * @param {Element} element The element from which the icon is removed. |
| 10782 */ |
| 10783 removeIcon: function(element) { |
| 10784 // Remove old svg element |
| 10785 if (element._svgIcon) { |
| 10786 Polymer.dom(element).removeChild(element._svgIcon); |
| 10787 element._svgIcon = null; |
| 10788 } |
| 10789 }, |
| 10790 |
| 10791 /** |
| 10792 * |
| 10793 * When name is changed, register iconset metadata |
| 10794 * |
| 10795 */ |
| 10796 _nameChanged: function() { |
| 10797 new Polymer.IronMeta({type: 'iconset', key: this.name, value: this}); |
| 10798 this.async(function() { |
| 10799 this.fire('iron-iconset-added', this, {node: window}); |
| 10800 }); |
| 10801 }, |
| 10802 |
| 10803 /** |
| 10804 * Create a map of child SVG elements by id. |
| 10805 * |
| 10806 * @return {!Object} Map of id's to SVG elements. |
| 10807 */ |
| 10808 _createIconMap: function() { |
| 10809 // Objects chained to Object.prototype (`{}`) have members. Specifically, |
| 10810 // on FF there is a `watch` method that confuses the icon map, so we |
| 10811 // need to use a null-based object here. |
| 10812 var icons = Object.create(null); |
| 10813 Polymer.dom(this).querySelectorAll('[id]') |
| 10814 .forEach(function(icon) { |
| 10815 icons[icon.id] = icon; |
| 10816 }); |
| 10817 return icons; |
| 10818 }, |
| 10819 |
| 10820 /** |
| 10821 * Produce installable clone of the SVG element matching `id` in this |
| 10822 * iconset, or `undefined` if there is no matching element. |
| 10823 * |
| 10824 * @return {Element} Returns an installable clone of the SVG element |
| 10825 * matching `id`. |
| 10826 */ |
| 10827 _cloneIcon: function(id) { |
| 10828 // create the icon map on-demand, since the iconset itself has no discrete |
| 10829 // signal to know when it's children are fully parsed |
| 10830 this._icons = this._icons || this._createIconMap(); |
| 10831 return this._prepareSvgClone(this._icons[id], this.size); |
| 10832 }, |
| 10833 |
| 10834 /** |
| 10835 * @param {Element} sourceSvg |
| 10836 * @param {number} size |
| 10837 * @return {Element} |
| 10838 */ |
| 10839 _prepareSvgClone: function(sourceSvg, size) { |
| 10840 if (sourceSvg) { |
| 10841 var content = sourceSvg.cloneNode(true), |
| 10842 svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), |
| 10843 viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + s
ize; |
| 10844 svg.setAttribute('viewBox', viewBox); |
| 10845 svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); |
| 10846 // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/
370136 |
| 10847 // TODO(sjmiles): inline style may not be ideal, but avoids requiring a
shadow-root |
| 10848 svg.style.cssText = 'pointer-events: none; display: block; width: 100%;
height: 100%;'; |
| 10849 svg.appendChild(content).removeAttribute('id'); |
| 10850 return svg; |
| 10851 } |
| 10852 return null; |
| 10853 } |
| 10854 |
| 10855 }); |
| 9021 Polymer({ | 10856 Polymer({ |
| 9022 is: 'paper-material', | 10857 is: 'paper-material', |
| 9023 | 10858 |
| 9024 properties: { | 10859 properties: { |
| 9025 | 10860 |
| 9026 /** | 10861 /** |
| 9027 * The z-depth of this element, from 0-5. Setting to 0 will remove the | 10862 * The z-depth of this element, from 0-5. Setting to 0 will remove the |
| 9028 * shadow, and each increasing number greater than 0 will be "deeper" | 10863 * shadow, and each increasing number greater than 0 will be "deeper" |
| 9029 * than the last. | 10864 * than the last. |
| 9030 * | 10865 * |
| (...skipping 800 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9831 /** | 11666 /** |
| 9832 * If true, the ripple will remain in the "down" state until `holdDown` | 11667 * If true, the ripple will remain in the "down" state until `holdDown` |
| 9833 * is set to false again. | 11668 * is set to false again. |
| 9834 */ | 11669 */ |
| 9835 holdDown: { | 11670 holdDown: { |
| 9836 type: Boolean, | 11671 type: Boolean, |
| 9837 value: false, | 11672 value: false, |
| 9838 observer: '_holdDownChanged' | 11673 observer: '_holdDownChanged' |
| 9839 }, | 11674 }, |
| 9840 | 11675 |
| 11676 /** |
| 11677 * If true, the ripple will not generate a ripple effect |
| 11678 * via pointer interaction. |
| 11679 * Calling ripple's imperative api like `simulatedRipple` will |
| 11680 * still generate the ripple effect. |
| 11681 */ |
| 11682 noink: { |
| 11683 type: Boolean, |
| 11684 value: false |
| 11685 }, |
| 11686 |
| 9841 _animating: { | 11687 _animating: { |
| 9842 type: Boolean | 11688 type: Boolean |
| 9843 }, | 11689 }, |
| 9844 | 11690 |
| 9845 _boundAnimate: { | 11691 _boundAnimate: { |
| 9846 type: Function, | 11692 type: Function, |
| 9847 value: function() { | 11693 value: function() { |
| 9848 return this.animate.bind(this); | 11694 return this.animate.bind(this); |
| 9849 } | 11695 } |
| 9850 } | 11696 } |
| 9851 }, | 11697 }, |
| 9852 | 11698 |
| 11699 observers: [ |
| 11700 '_noinkChanged(noink, isAttached)' |
| 11701 ], |
| 11702 |
| 9853 get target () { | 11703 get target () { |
| 9854 var ownerRoot = Polymer.dom(this).getOwnerRoot(); | 11704 var ownerRoot = Polymer.dom(this).getOwnerRoot(); |
| 9855 var target; | 11705 var target; |
| 9856 | 11706 |
| 9857 if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE | 11707 if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE |
| 9858 target = ownerRoot.host; | 11708 target = ownerRoot.host; |
| 9859 } else { | 11709 } else { |
| 9860 target = this.parentNode; | 11710 target = this.parentNode; |
| 9861 } | 11711 } |
| 9862 | 11712 |
| 9863 return target; | 11713 return target; |
| 9864 }, | 11714 }, |
| 9865 | 11715 |
| 9866 keyBindings: { | 11716 keyBindings: { |
| 9867 'enter:keydown': '_onEnterKeydown', | 11717 'enter:keydown': '_onEnterKeydown', |
| 9868 'space:keydown': '_onSpaceKeydown', | 11718 'space:keydown': '_onSpaceKeydown', |
| 9869 'space:keyup': '_onSpaceKeyup' | 11719 'space:keyup': '_onSpaceKeyup' |
| 9870 }, | 11720 }, |
| 9871 | 11721 |
| 9872 attached: function() { | 11722 attached: function() { |
| 9873 this.listen(this.target, 'up', 'upAction'); | 11723 this.listen(this.target, 'up', 'uiUpAction'); |
| 9874 this.listen(this.target, 'down', 'downAction'); | 11724 this.listen(this.target, 'down', 'uiDownAction'); |
| 9875 | 11725 }, |
| 9876 if (!this.target.hasAttribute('noink')) { | 11726 |
| 9877 this.keyEventTarget = this.target; | 11727 detached: function() { |
| 9878 } | 11728 this.unlisten(this.target, 'up', 'uiUpAction'); |
| 11729 this.unlisten(this.target, 'down', 'uiDownAction'); |
| 9879 }, | 11730 }, |
| 9880 | 11731 |
| 9881 get shouldKeepAnimating () { | 11732 get shouldKeepAnimating () { |
| 9882 for (var index = 0; index < this.ripples.length; ++index) { | 11733 for (var index = 0; index < this.ripples.length; ++index) { |
| 9883 if (!this.ripples[index].isAnimationComplete) { | 11734 if (!this.ripples[index].isAnimationComplete) { |
| 9884 return true; | 11735 return true; |
| 9885 } | 11736 } |
| 9886 } | 11737 } |
| 9887 | 11738 |
| 9888 return false; | 11739 return false; |
| 9889 }, | 11740 }, |
| 9890 | 11741 |
| 9891 simulatedRipple: function() { | 11742 simulatedRipple: function() { |
| 9892 this.downAction(null); | 11743 this.downAction(null); |
| 9893 | 11744 |
| 9894 // Please see polymer/polymer#1305 | 11745 // Please see polymer/polymer#1305 |
| 9895 this.async(function() { | 11746 this.async(function() { |
| 9896 this.upAction(); | 11747 this.upAction(); |
| 9897 }, 1); | 11748 }, 1); |
| 9898 }, | 11749 }, |
| 9899 | 11750 |
| 9900 /** @param {Event=} event */ | 11751 /** |
| 11752 * Provokes a ripple down effect via a UI event, |
| 11753 * respecting the `noink` property. |
| 11754 * @param {Event=} event |
| 11755 */ |
| 11756 uiDownAction: function(event) { |
| 11757 if (!this.noink) { |
| 11758 this.downAction(event); |
| 11759 } |
| 11760 }, |
| 11761 |
| 11762 /** |
| 11763 * Provokes a ripple down effect via a UI event, |
| 11764 * *not* respecting the `noink` property. |
| 11765 * @param {Event=} event |
| 11766 */ |
| 9901 downAction: function(event) { | 11767 downAction: function(event) { |
| 9902 if (this.holdDown && this.ripples.length > 0) { | 11768 if (this.holdDown && this.ripples.length > 0) { |
| 9903 return; | 11769 return; |
| 9904 } | 11770 } |
| 9905 | 11771 |
| 9906 var ripple = this.addRipple(); | 11772 var ripple = this.addRipple(); |
| 9907 | 11773 |
| 9908 ripple.downAction(event); | 11774 ripple.downAction(event); |
| 9909 | 11775 |
| 9910 if (!this._animating) { | 11776 if (!this._animating) { |
| 9911 this.animate(); | 11777 this.animate(); |
| 9912 } | 11778 } |
| 9913 }, | 11779 }, |
| 9914 | 11780 |
| 9915 /** @param {Event=} event */ | 11781 /** |
| 11782 * Provokes a ripple up effect via a UI event, |
| 11783 * respecting the `noink` property. |
| 11784 * @param {Event=} event |
| 11785 */ |
| 11786 uiUpAction: function(event) { |
| 11787 if (!this.noink) { |
| 11788 this.upAction(event); |
| 11789 } |
| 11790 }, |
| 11791 |
| 11792 /** |
| 11793 * Provokes a ripple up effect via a UI event, |
| 11794 * *not* respecting the `noink` property. |
| 11795 * @param {Event=} event |
| 11796 */ |
| 9916 upAction: function(event) { | 11797 upAction: function(event) { |
| 9917 if (this.holdDown) { | 11798 if (this.holdDown) { |
| 9918 return; | 11799 return; |
| 9919 } | 11800 } |
| 9920 | 11801 |
| 9921 this.ripples.forEach(function(ripple) { | 11802 this.ripples.forEach(function(ripple) { |
| 9922 ripple.upAction(event); | 11803 ripple.upAction(event); |
| 9923 }); | 11804 }); |
| 9924 | 11805 |
| 9925 this.animate(); | 11806 this.animate(); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9978 } | 11859 } |
| 9979 | 11860 |
| 9980 if (!this.shouldKeepAnimating && this.ripples.length === 0) { | 11861 if (!this.shouldKeepAnimating && this.ripples.length === 0) { |
| 9981 this.onAnimationComplete(); | 11862 this.onAnimationComplete(); |
| 9982 } else { | 11863 } else { |
| 9983 window.requestAnimationFrame(this._boundAnimate); | 11864 window.requestAnimationFrame(this._boundAnimate); |
| 9984 } | 11865 } |
| 9985 }, | 11866 }, |
| 9986 | 11867 |
| 9987 _onEnterKeydown: function() { | 11868 _onEnterKeydown: function() { |
| 9988 this.downAction(); | 11869 this.uiDownAction(); |
| 9989 this.async(this.upAction, 1); | 11870 this.async(this.uiUpAction, 1); |
| 9990 }, | 11871 }, |
| 9991 | 11872 |
| 9992 _onSpaceKeydown: function() { | 11873 _onSpaceKeydown: function() { |
| 9993 this.downAction(); | 11874 this.uiDownAction(); |
| 9994 }, | 11875 }, |
| 9995 | 11876 |
| 9996 _onSpaceKeyup: function() { | 11877 _onSpaceKeyup: function() { |
| 9997 this.upAction(); | 11878 this.uiUpAction(); |
| 9998 }, | 11879 }, |
| 9999 | 11880 |
| 10000 _holdDownChanged: function(holdDown) { | 11881 // note: holdDown does not respect noink since it can be a focus based |
| 10001 if (holdDown) { | 11882 // effect. |
| 11883 _holdDownChanged: function(newVal, oldVal) { |
| 11884 if (oldVal === undefined) { |
| 11885 return; |
| 11886 } |
| 11887 if (newVal) { |
| 10002 this.downAction(); | 11888 this.downAction(); |
| 10003 } else { | 11889 } else { |
| 10004 this.upAction(); | 11890 this.upAction(); |
| 10005 } | 11891 } |
| 11892 }, |
| 11893 |
| 11894 _noinkChanged: function(noink, attached) { |
| 11895 if (attached) { |
| 11896 this.keyEventTarget = noink ? this : this.target; |
| 11897 } |
| 10006 } | 11898 } |
| 10007 }); | 11899 }); |
| 10008 })(); | 11900 })(); |
| 10009 /** | 11901 /** |
| 10010 * @demo demo/index.html | 11902 * @demo demo/index.html |
| 10011 * @polymerBehavior | 11903 * @polymerBehavior |
| 10012 */ | 11904 */ |
| 10013 Polymer.IronControlState = { | 11905 Polymer.IronControlState = { |
| 10014 | 11906 |
| 10015 properties: { | 11907 properties: { |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10201 | 12093 |
| 10202 // to emulate native checkbox, (de-)activations from a user interaction fire | 12094 // to emulate native checkbox, (de-)activations from a user interaction fire |
| 10203 // 'change' events | 12095 // 'change' events |
| 10204 _userActivate: function(active) { | 12096 _userActivate: function(active) { |
| 10205 if (this.active !== active) { | 12097 if (this.active !== active) { |
| 10206 this.active = active; | 12098 this.active = active; |
| 10207 this.fire('change'); | 12099 this.fire('change'); |
| 10208 } | 12100 } |
| 10209 }, | 12101 }, |
| 10210 | 12102 |
| 10211 _eventSourceIsPrimaryInput: function(event) { | |
| 10212 event = event.detail.sourceEvent || event; | |
| 10213 | |
| 10214 // Always true for non-mouse events.... | |
| 10215 if (!this._mouseEventRe.test(event.type)) { | |
| 10216 return true; | |
| 10217 } | |
| 10218 | |
| 10219 // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons | |
| 10220 if ('buttons' in event) { | |
| 10221 return event.buttons === 1; | |
| 10222 } | |
| 10223 | |
| 10224 // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which | |
| 10225 if (typeof event.which === 'number') { | |
| 10226 return event.which < 2; | |
| 10227 } | |
| 10228 | |
| 10229 // http://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button | |
| 10230 return event.button < 1; | |
| 10231 }, | |
| 10232 | |
| 10233 _downHandler: function(event) { | 12103 _downHandler: function(event) { |
| 10234 if (!this._eventSourceIsPrimaryInput(event)) { | |
| 10235 return; | |
| 10236 } | |
| 10237 | |
| 10238 this._setPointerDown(true); | 12104 this._setPointerDown(true); |
| 10239 this._setPressed(true); | 12105 this._setPressed(true); |
| 10240 this._setReceivedFocusFromKeyboard(false); | 12106 this._setReceivedFocusFromKeyboard(false); |
| 10241 }, | 12107 }, |
| 10242 | 12108 |
| 10243 _upHandler: function() { | 12109 _upHandler: function() { |
| 10244 this._setPointerDown(false); | 12110 this._setPointerDown(false); |
| 10245 this._setPressed(false); | 12111 this._setPressed(false); |
| 10246 }, | 12112 }, |
| 10247 | 12113 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10305 } | 12171 } |
| 10306 } | 12172 } |
| 10307 | 12173 |
| 10308 }; | 12174 }; |
| 10309 | 12175 |
| 10310 /** @polymerBehavior */ | 12176 /** @polymerBehavior */ |
| 10311 Polymer.IronButtonState = [ | 12177 Polymer.IronButtonState = [ |
| 10312 Polymer.IronA11yKeysBehavior, | 12178 Polymer.IronA11yKeysBehavior, |
| 10313 Polymer.IronButtonStateImpl | 12179 Polymer.IronButtonStateImpl |
| 10314 ]; | 12180 ]; |
| 10315 /** @polymerBehavior */ | 12181 /** |
| 12182 * `Polymer.PaperRippleBehavior` dynamically implements a ripple |
| 12183 * when the element has focus via pointer or keyboard. |
| 12184 * |
| 12185 * NOTE: This behavior is intended to be used in conjunction with and after |
| 12186 * `Polymer.IronButtonState` and `Polymer.IronControlState`. |
| 12187 * |
| 12188 * @polymerBehavior Polymer.PaperRippleBehavior |
| 12189 */ |
| 12190 Polymer.PaperRippleBehavior = { |
| 12191 |
| 12192 properties: { |
| 12193 /** |
| 12194 * If true, the element will not produce a ripple effect when interacted |
| 12195 * with via the pointer. |
| 12196 */ |
| 12197 noink: { |
| 12198 type: Boolean, |
| 12199 observer: '_noinkChanged' |
| 12200 } |
| 12201 }, |
| 12202 |
| 12203 /** |
| 12204 * Ensures a `<paper-ripple>` element is available when the element is |
| 12205 * focused. |
| 12206 */ |
| 12207 _buttonStateChanged: function() { |
| 12208 if (this.focused) { |
| 12209 this.ensureRipple(); |
| 12210 } |
| 12211 }, |
| 12212 |
| 12213 /** |
| 12214 * In addition to the functionality provided in `IronButtonState`, ensures |
| 12215 * a ripple effect is created when the element is in a `pressed` state. |
| 12216 */ |
| 12217 _downHandler: function(event) { |
| 12218 Polymer.IronButtonStateImpl._downHandler.call(this, event); |
| 12219 if (this.pressed) { |
| 12220 this.ensureRipple(event); |
| 12221 } |
| 12222 }, |
| 12223 |
| 12224 /** |
| 12225 * Ensures this element contains a ripple effect. For startup efficiency |
| 12226 * the ripple effect is dynamically on demand when needed. |
| 12227 * @param {!Event=} opt_triggeringEvent (optional) event that triggered the |
| 12228 * ripple. |
| 12229 */ |
| 12230 ensureRipple: function(opt_triggeringEvent) { |
| 12231 if (!this.hasRipple()) { |
| 12232 this._ripple = this._createRipple(); |
| 12233 this._ripple.noink = this.noink; |
| 12234 var rippleContainer = this._rippleContainer || this.root; |
| 12235 if (rippleContainer) { |
| 12236 Polymer.dom(rippleContainer).appendChild(this._ripple); |
| 12237 } |
| 12238 var domContainer = rippleContainer === this.shadyRoot ? this : |
| 12239 rippleContainer; |
| 12240 if (opt_triggeringEvent && |
| 12241 domContainer.contains(opt_triggeringEvent.target)) { |
| 12242 this._ripple.uiDownAction(opt_triggeringEvent); |
| 12243 } |
| 12244 } |
| 12245 }, |
| 12246 |
| 12247 /** |
| 12248 * Returns the `<paper-ripple>` element used by this element to create |
| 12249 * ripple effects. The element's ripple is created on demand, when |
| 12250 * necessary, and calling this method will force the |
| 12251 * ripple to be created. |
| 12252 */ |
| 12253 getRipple: function() { |
| 12254 this.ensureRipple(); |
| 12255 return this._ripple; |
| 12256 }, |
| 12257 |
| 12258 /** |
| 12259 * Returns true if this element currently contains a ripple effect. |
| 12260 * @return {boolean} |
| 12261 */ |
| 12262 hasRipple: function() { |
| 12263 return Boolean(this._ripple); |
| 12264 }, |
| 12265 |
| 12266 /** |
| 12267 * Create the element's ripple effect via creating a `<paper-ripple>`. |
| 12268 * Override this method to customize the ripple element. |
| 12269 * @return {element} Returns a `<paper-ripple>` element. |
| 12270 */ |
| 12271 _createRipple: function() { |
| 12272 return document.createElement('paper-ripple'); |
| 12273 }, |
| 12274 |
| 12275 _noinkChanged: function(noink) { |
| 12276 if (this.hasRipple()) { |
| 12277 this._ripple.noink = noink; |
| 12278 } |
| 12279 } |
| 12280 |
| 12281 }; |
| 12282 /** @polymerBehavior Polymer.PaperButtonBehavior */ |
| 10316 Polymer.PaperButtonBehaviorImpl = { | 12283 Polymer.PaperButtonBehaviorImpl = { |
| 10317 | 12284 |
| 10318 properties: { | 12285 properties: { |
| 10319 | 12286 |
| 10320 _elevation: { | 12287 /** |
| 10321 type: Number | 12288 * The z-depth of this element, from 0-5. Setting to 0 will remove the |
| 12289 * shadow, and each increasing number greater than 0 will be "deeper" |
| 12290 * than the last. |
| 12291 * |
| 12292 * @attribute elevation |
| 12293 * @type number |
| 12294 * @default 1 |
| 12295 */ |
| 12296 elevation: { |
| 12297 type: Number, |
| 12298 reflectToAttribute: true, |
| 12299 readOnly: true |
| 10322 } | 12300 } |
| 10323 | 12301 |
| 10324 }, | 12302 }, |
| 10325 | 12303 |
| 10326 observers: [ | 12304 observers: [ |
| 10327 '_calculateElevation(focused, disabled, active, pressed, receivedFocusFrom
Keyboard)' | 12305 '_calculateElevation(focused, disabled, active, pressed, receivedFocusFrom
Keyboard)', |
| 12306 '_computeKeyboardClass(receivedFocusFromKeyboard)' |
| 10328 ], | 12307 ], |
| 10329 | 12308 |
| 10330 hostAttributes: { | 12309 hostAttributes: { |
| 10331 role: 'button', | 12310 role: 'button', |
| 10332 tabindex: '0' | 12311 tabindex: '0', |
| 12312 animated: true |
| 10333 }, | 12313 }, |
| 10334 | 12314 |
| 10335 _calculateElevation: function() { | 12315 _calculateElevation: function() { |
| 10336 var e = 1; | 12316 var e = 1; |
| 10337 if (this.disabled) { | 12317 if (this.disabled) { |
| 10338 e = 0; | 12318 e = 0; |
| 10339 } else if (this.active || this.pressed) { | 12319 } else if (this.active || this.pressed) { |
| 10340 e = 4; | 12320 e = 4; |
| 10341 } else if (this.receivedFocusFromKeyboard) { | 12321 } else if (this.receivedFocusFromKeyboard) { |
| 10342 e = 3; | 12322 e = 3; |
| 10343 } | 12323 } |
| 10344 this._elevation = e; | 12324 this._setElevation(e); |
| 12325 }, |
| 12326 |
| 12327 _computeKeyboardClass: function(receivedFocusFromKeyboard) { |
| 12328 this.classList.toggle('keyboard-focus', receivedFocusFromKeyboard); |
| 12329 }, |
| 12330 |
| 12331 /** |
| 12332 * In addition to `IronButtonState` behavior, when space key goes down, |
| 12333 * create a ripple down effect. |
| 12334 */ |
| 12335 _spaceKeyDownHandler: function(event) { |
| 12336 Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event); |
| 12337 if (this.hasRipple()) { |
| 12338 this._ripple.uiDownAction(); |
| 12339 } |
| 12340 }, |
| 12341 |
| 12342 /** |
| 12343 * In addition to `IronButtonState` behavior, when space key goes up, |
| 12344 * create a ripple up effect. |
| 12345 */ |
| 12346 _spaceKeyUpHandler: function(event) { |
| 12347 Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event); |
| 12348 if (this.hasRipple()) { |
| 12349 this._ripple.uiUpAction(); |
| 12350 } |
| 10345 } | 12351 } |
| 12352 |
| 10346 }; | 12353 }; |
| 10347 | 12354 |
| 10348 /** @polymerBehavior */ | 12355 /** @polymerBehavior */ |
| 10349 Polymer.PaperButtonBehavior = [ | 12356 Polymer.PaperButtonBehavior = [ |
| 10350 Polymer.IronButtonState, | 12357 Polymer.IronButtonState, |
| 10351 Polymer.IronControlState, | 12358 Polymer.IronControlState, |
| 12359 Polymer.PaperRippleBehavior, |
| 10352 Polymer.PaperButtonBehaviorImpl | 12360 Polymer.PaperButtonBehaviorImpl |
| 10353 ]; | 12361 ]; |
| 10354 Polymer({ | 12362 Polymer({ |
| 10355 is: 'paper-button', | 12363 is: 'paper-button', |
| 10356 | 12364 |
| 10357 behaviors: [ | 12365 behaviors: [ |
| 10358 Polymer.PaperButtonBehavior | 12366 Polymer.PaperButtonBehavior |
| 10359 ], | 12367 ], |
| 10360 | 12368 |
| 10361 properties: { | 12369 properties: { |
| 10362 /** | 12370 /** |
| 10363 * If true, the button should be styled with a shadow. | 12371 * If true, the button should be styled with a shadow. |
| 10364 */ | 12372 */ |
| 10365 raised: { | 12373 raised: { |
| 10366 type: Boolean, | 12374 type: Boolean, |
| 10367 reflectToAttribute: true, | 12375 reflectToAttribute: true, |
| 10368 value: false, | 12376 value: false, |
| 10369 observer: '_calculateElevation' | 12377 observer: '_calculateElevation' |
| 10370 } | 12378 } |
| 10371 }, | 12379 }, |
| 10372 | 12380 |
| 10373 _calculateElevation: function() { | 12381 _calculateElevation: function() { |
| 10374 if (!this.raised) { | 12382 if (!this.raised) { |
| 10375 this._elevation = 0; | 12383 this.elevation = 0; |
| 10376 } else { | 12384 } else { |
| 10377 Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this); | 12385 Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this); |
| 10378 } | 12386 } |
| 10379 }, | |
| 10380 | |
| 10381 _computeContentClass: function(receivedFocusFromKeyboard) { | |
| 10382 var className = 'content '; | |
| 10383 if (receivedFocusFromKeyboard) { | |
| 10384 className += ' keyboard-focus'; | |
| 10385 } | |
| 10386 return className; | |
| 10387 } | 12387 } |
| 10388 }); | 12388 }); |
| 10389 /** | 12389 /** |
| 10390 * `iron-range-behavior` provides the behavior for something with a minimum to m
aximum range. | 12390 * `iron-range-behavior` provides the behavior for something with a minimum to m
aximum range. |
| 10391 * | 12391 * |
| 10392 * @demo demo/index.html | 12392 * @demo demo/index.html |
| 10393 * @polymerBehavior | 12393 * @polymerBehavior |
| 10394 */ | 12394 */ |
| 10395 Polymer.IronRangeBehavior = { | 12395 Polymer.IronRangeBehavior = { |
| 10396 | 12396 |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10580 | 12580 |
| 10581 }); | 12581 }); |
| 10582 // Copyright 2015 The Chromium Authors. All rights reserved. | 12582 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 10583 // Use of this source code is governed by a BSD-style license that can be | 12583 // Use of this source code is governed by a BSD-style license that can be |
| 10584 // found in the LICENSE file. | 12584 // found in the LICENSE file. |
| 10585 | 12585 |
| 10586 cr.define('downloads', function() { | 12586 cr.define('downloads', function() { |
| 10587 var Item = Polymer({ | 12587 var Item = Polymer({ |
| 10588 is: 'downloads-item', | 12588 is: 'downloads-item', |
| 10589 | 12589 |
| 10590 /** | |
| 10591 * @param {!downloads.ThrottledIconLoader} iconLoader | |
| 10592 */ | |
| 10593 factoryImpl: function(iconLoader) { | |
| 10594 /** @private {!downloads.ThrottledIconLoader} */ | |
| 10595 this.iconLoader_ = iconLoader; | |
| 10596 }, | |
| 10597 | |
| 10598 properties: { | 12590 properties: { |
| 10599 data: { | 12591 data: { |
| 10600 type: Object, | 12592 type: Object, |
| 10601 }, | 12593 }, |
| 10602 | 12594 |
| 10603 hideDate: { | 12595 hideDate: { |
| 10604 type: Boolean, | 12596 type: Boolean, |
| 10605 value: true, | 12597 value: true, |
| 10606 }, | 12598 }, |
| 10607 | 12599 |
| 10608 readyPromise: { | |
| 10609 type: Object, | |
| 10610 value: function() { | |
| 10611 return new Promise(function(resolve, reject) { | |
| 10612 this.resolveReadyPromise_ = resolve; | |
| 10613 }.bind(this)); | |
| 10614 }, | |
| 10615 }, | |
| 10616 | |
| 10617 completelyOnDisk_: { | 12600 completelyOnDisk_: { |
| 10618 computed: 'computeCompletelyOnDisk_(' + | 12601 computed: 'computeCompletelyOnDisk_(' + |
| 10619 'data.state, data.file_externally_removed)', | 12602 'data.state, data.file_externally_removed)', |
| 10620 type: Boolean, | 12603 type: Boolean, |
| 10621 value: true, | 12604 value: true, |
| 10622 }, | 12605 }, |
| 10623 | 12606 |
| 10624 controlledBy_: { | 12607 controlledBy_: { |
| 10625 computed: 'computeControlledBy_(data.by_ext_id, data.by_ext_name)', | 12608 computed: 'computeControlledBy_(data.by_ext_id, data.by_ext_name)', |
| 10626 type: String, | 12609 type: String, |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10679 computed: 'computeIsMalware_(isDangerous_, data.danger_type)', | 12662 computed: 'computeIsMalware_(isDangerous_, data.danger_type)', |
| 10680 type: Boolean, | 12663 type: Boolean, |
| 10681 value: false, | 12664 value: false, |
| 10682 }, | 12665 }, |
| 10683 }, | 12666 }, |
| 10684 | 12667 |
| 10685 observers: [ | 12668 observers: [ |
| 10686 // TODO(dbeam): this gets called way more when I observe data.by_ext_id | 12669 // TODO(dbeam): this gets called way more when I observe data.by_ext_id |
| 10687 // and data.by_ext_name directly. Why? | 12670 // and data.by_ext_name directly. Why? |
| 10688 'observeControlledBy_(controlledBy_)', | 12671 'observeControlledBy_(controlledBy_)', |
| 12672 'observeIsDangerous_(isDangerous_, data.file_path)', |
| 10689 ], | 12673 ], |
| 10690 | 12674 |
| 10691 ready: function() { | 12675 ready: function() { |
| 10692 this.content = this.$.content; | 12676 this.content = this.$.content; |
| 10693 this.resolveReadyPromise_(); | |
| 10694 }, | |
| 10695 | |
| 10696 /** @param {!downloads.Data} data */ | |
| 10697 update: function(data) { | |
| 10698 this.data = data; | |
| 10699 | |
| 10700 if (!this.isDangerous_) { | |
| 10701 var icon = 'chrome://fileicon/' + encodeURIComponent(data.file_path); | |
| 10702 this.iconLoader_.loadScaledIcon(this.$['file-icon'], icon); | |
| 10703 } | |
| 10704 }, | 12677 }, |
| 10705 | 12678 |
| 10706 /** @private */ | 12679 /** @private */ |
| 10707 computeClass_: function() { | 12680 computeClass_: function() { |
| 10708 var classes = []; | 12681 var classes = []; |
| 10709 | 12682 |
| 10710 if (this.isActive_) | 12683 if (this.isActive_) |
| 10711 classes.push('is-active'); | 12684 classes.push('is-active'); |
| 10712 | 12685 |
| 10713 if (this.isDangerous_) | 12686 if (this.isDangerous_) |
| (...skipping 15 matching lines...) Expand all Loading... |
| 10729 computeControlledBy_: function() { | 12702 computeControlledBy_: function() { |
| 10730 if (!this.data.by_ext_id || !this.data.by_ext_name) | 12703 if (!this.data.by_ext_id || !this.data.by_ext_name) |
| 10731 return ''; | 12704 return ''; |
| 10732 | 12705 |
| 10733 var url = 'chrome://extensions#' + this.data.by_ext_id; | 12706 var url = 'chrome://extensions#' + this.data.by_ext_id; |
| 10734 var name = this.data.by_ext_name; | 12707 var name = this.data.by_ext_name; |
| 10735 return loadTimeData.getStringF('controlledByUrl', url, name); | 12708 return loadTimeData.getStringF('controlledByUrl', url, name); |
| 10736 }, | 12709 }, |
| 10737 | 12710 |
| 10738 /** @private */ | 12711 /** @private */ |
| 12712 computeDangerIcon_: function() { |
| 12713 if (!this.isDangerous_) |
| 12714 return ''; |
| 12715 |
| 12716 switch (this.data.danger_type) { |
| 12717 case downloads.DangerType.DANGEROUS_CONTENT: |
| 12718 case downloads.DangerType.DANGEROUS_HOST: |
| 12719 case downloads.DangerType.DANGEROUS_URL: |
| 12720 case downloads.DangerType.POTENTIALLY_UNWANTED: |
| 12721 case downloads.DangerType.UNCOMMON_CONTENT: |
| 12722 return 'remove-circle'; |
| 12723 default: |
| 12724 return 'warning'; |
| 12725 } |
| 12726 }, |
| 12727 |
| 12728 /** @private */ |
| 10739 computeDate_: function() { | 12729 computeDate_: function() { |
| 10740 if (this.hideDate) | 12730 if (this.hideDate) |
| 10741 return ''; | 12731 return ''; |
| 10742 return assert(this.data.since_string || this.data.date_string); | 12732 return assert(this.data.since_string || this.data.date_string); |
| 10743 }, | 12733 }, |
| 10744 | 12734 |
| 10745 /** @private */ | 12735 /** @private */ |
| 10746 computeDescription_: function() { | 12736 computeDescription_: function() { |
| 10747 var data = this.data; | 12737 var data = this.data; |
| 10748 | 12738 |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10837 isIndeterminate_: function() { | 12827 isIndeterminate_: function() { |
| 10838 return this.data.percent == -1; | 12828 return this.data.percent == -1; |
| 10839 }, | 12829 }, |
| 10840 | 12830 |
| 10841 /** @private */ | 12831 /** @private */ |
| 10842 observeControlledBy_: function() { | 12832 observeControlledBy_: function() { |
| 10843 this.$['controlled-by'].innerHTML = this.controlledBy_; | 12833 this.$['controlled-by'].innerHTML = this.controlledBy_; |
| 10844 }, | 12834 }, |
| 10845 | 12835 |
| 10846 /** @private */ | 12836 /** @private */ |
| 12837 observeIsDangerous_: function() { |
| 12838 if (this.data && !this.isDangerous_) { |
| 12839 var filePath = encodeURIComponent(this.data.file_path); |
| 12840 this.$['file-icon'].src = 'chrome://fileicon/' + filePath; |
| 12841 } |
| 12842 }, |
| 12843 |
| 12844 /** @private */ |
| 10847 onCancelTap_: function() { | 12845 onCancelTap_: function() { |
| 10848 downloads.ActionService.getInstance().cancel(this.data.id); | 12846 downloads.ActionService.getInstance().cancel(this.data.id); |
| 10849 }, | 12847 }, |
| 10850 | 12848 |
| 10851 /** @private */ | 12849 /** @private */ |
| 10852 onDiscardDangerousTap_: function() { | 12850 onDiscardDangerousTap_: function() { |
| 10853 downloads.ActionService.getInstance().discardDangerous(this.data.id); | 12851 downloads.ActionService.getInstance().discardDangerous(this.data.id); |
| 10854 }, | 12852 }, |
| 10855 | 12853 |
| 10856 /** | 12854 /** |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10897 }, | 12895 }, |
| 10898 | 12896 |
| 10899 /** @private */ | 12897 /** @private */ |
| 10900 onShowTap_: function() { | 12898 onShowTap_: function() { |
| 10901 downloads.ActionService.getInstance().show(this.data.id); | 12899 downloads.ActionService.getInstance().show(this.data.id); |
| 10902 }, | 12900 }, |
| 10903 }); | 12901 }); |
| 10904 | 12902 |
| 10905 return {Item: Item}; | 12903 return {Item: Item}; |
| 10906 }); | 12904 }); |
| 10907 (function() { | |
| 10908 | |
| 10909 // monostate data | |
| 10910 var metaDatas = {}; | |
| 10911 var metaArrays = {}; | |
| 10912 | |
| 10913 Polymer.IronMeta = Polymer({ | |
| 10914 | |
| 10915 is: 'iron-meta', | |
| 10916 | |
| 10917 properties: { | |
| 10918 | |
| 10919 /** | |
| 10920 * The type of meta-data. All meta-data of the same type is stored | |
| 10921 * together. | |
| 10922 */ | |
| 10923 type: { | |
| 10924 type: String, | |
| 10925 value: 'default', | |
| 10926 observer: '_typeChanged' | |
| 10927 }, | |
| 10928 | |
| 10929 /** | |
| 10930 * The key used to store `value` under the `type` namespace. | |
| 10931 */ | |
| 10932 key: { | |
| 10933 type: String, | |
| 10934 observer: '_keyChanged' | |
| 10935 }, | |
| 10936 | |
| 10937 /** | |
| 10938 * The meta-data to store or retrieve. | |
| 10939 */ | |
| 10940 value: { | |
| 10941 type: Object, | |
| 10942 notify: true, | |
| 10943 observer: '_valueChanged' | |
| 10944 }, | |
| 10945 | |
| 10946 /** | |
| 10947 * If true, `value` is set to the iron-meta instance itself. | |
| 10948 */ | |
| 10949 self: { | |
| 10950 type: Boolean, | |
| 10951 observer: '_selfChanged' | |
| 10952 }, | |
| 10953 | |
| 10954 /** | |
| 10955 * Array of all meta-data values for the given type. | |
| 10956 */ | |
| 10957 list: { | |
| 10958 type: Array, | |
| 10959 notify: true | |
| 10960 } | |
| 10961 | |
| 10962 }, | |
| 10963 | |
| 10964 /** | |
| 10965 * Only runs if someone invokes the factory/constructor directly | |
| 10966 * e.g. `new Polymer.IronMeta()` | |
| 10967 */ | |
| 10968 factoryImpl: function(config) { | |
| 10969 if (config) { | |
| 10970 for (var n in config) { | |
| 10971 switch(n) { | |
| 10972 case 'type': | |
| 10973 case 'key': | |
| 10974 case 'value': | |
| 10975 this[n] = config[n]; | |
| 10976 break; | |
| 10977 } | |
| 10978 } | |
| 10979 } | |
| 10980 }, | |
| 10981 | |
| 10982 created: function() { | |
| 10983 // TODO(sjmiles): good for debugging? | |
| 10984 this._metaDatas = metaDatas; | |
| 10985 this._metaArrays = metaArrays; | |
| 10986 }, | |
| 10987 | |
| 10988 _keyChanged: function(key, old) { | |
| 10989 this._resetRegistration(old); | |
| 10990 }, | |
| 10991 | |
| 10992 _valueChanged: function(value) { | |
| 10993 this._resetRegistration(this.key); | |
| 10994 }, | |
| 10995 | |
| 10996 _selfChanged: function(self) { | |
| 10997 if (self) { | |
| 10998 this.value = this; | |
| 10999 } | |
| 11000 }, | |
| 11001 | |
| 11002 _typeChanged: function(type) { | |
| 11003 this._unregisterKey(this.key); | |
| 11004 if (!metaDatas[type]) { | |
| 11005 metaDatas[type] = {}; | |
| 11006 } | |
| 11007 this._metaData = metaDatas[type]; | |
| 11008 if (!metaArrays[type]) { | |
| 11009 metaArrays[type] = []; | |
| 11010 } | |
| 11011 this.list = metaArrays[type]; | |
| 11012 this._registerKeyValue(this.key, this.value); | |
| 11013 }, | |
| 11014 | |
| 11015 /** | |
| 11016 * Retrieves meta data value by key. | |
| 11017 * | |
| 11018 * @method byKey | |
| 11019 * @param {string} key The key of the meta-data to be returned. | |
| 11020 * @return {*} | |
| 11021 */ | |
| 11022 byKey: function(key) { | |
| 11023 return this._metaData && this._metaData[key]; | |
| 11024 }, | |
| 11025 | |
| 11026 _resetRegistration: function(oldKey) { | |
| 11027 this._unregisterKey(oldKey); | |
| 11028 this._registerKeyValue(this.key, this.value); | |
| 11029 }, | |
| 11030 | |
| 11031 _unregisterKey: function(key) { | |
| 11032 this._unregister(key, this._metaData, this.list); | |
| 11033 }, | |
| 11034 | |
| 11035 _registerKeyValue: function(key, value) { | |
| 11036 this._register(key, value, this._metaData, this.list); | |
| 11037 }, | |
| 11038 | |
| 11039 _register: function(key, value, data, list) { | |
| 11040 if (key && data && value !== undefined) { | |
| 11041 data[key] = value; | |
| 11042 list.push(value); | |
| 11043 } | |
| 11044 }, | |
| 11045 | |
| 11046 _unregister: function(key, data, list) { | |
| 11047 if (key && data) { | |
| 11048 if (key in data) { | |
| 11049 var value = data[key]; | |
| 11050 delete data[key]; | |
| 11051 this.arrayDelete(list, value); | |
| 11052 } | |
| 11053 } | |
| 11054 } | |
| 11055 | |
| 11056 }); | |
| 11057 | |
| 11058 /** | |
| 11059 `iron-meta-query` can be used to access infomation stored in `iron-meta`. | |
| 11060 | |
| 11061 Examples: | |
| 11062 | |
| 11063 If I create an instance like this: | |
| 11064 | |
| 11065 <iron-meta key="info" value="foo/bar"></iron-meta> | |
| 11066 | |
| 11067 Note that value="foo/bar" is the metadata I've defined. I could define more | |
| 11068 attributes or use child nodes to define additional metadata. | |
| 11069 | |
| 11070 Now I can access that element (and it's metadata) from any `iron-meta-query`
instance: | |
| 11071 | |
| 11072 var value = new Polymer.IronMetaQuery({key: 'info'}).value; | |
| 11073 | |
| 11074 @group Polymer Iron Elements | |
| 11075 @element iron-meta-query | |
| 11076 */ | |
| 11077 Polymer.IronMetaQuery = Polymer({ | |
| 11078 | |
| 11079 is: 'iron-meta-query', | |
| 11080 | |
| 11081 properties: { | |
| 11082 | |
| 11083 /** | |
| 11084 * The type of meta-data. All meta-data of the same type is stored | |
| 11085 * together. | |
| 11086 */ | |
| 11087 type: { | |
| 11088 type: String, | |
| 11089 value: 'default', | |
| 11090 observer: '_typeChanged' | |
| 11091 }, | |
| 11092 | |
| 11093 /** | |
| 11094 * Specifies a key to use for retrieving `value` from the `type` | |
| 11095 * namespace. | |
| 11096 */ | |
| 11097 key: { | |
| 11098 type: String, | |
| 11099 observer: '_keyChanged' | |
| 11100 }, | |
| 11101 | |
| 11102 /** | |
| 11103 * The meta-data to store or retrieve. | |
| 11104 */ | |
| 11105 value: { | |
| 11106 type: Object, | |
| 11107 notify: true, | |
| 11108 readOnly: true | |
| 11109 }, | |
| 11110 | |
| 11111 /** | |
| 11112 * Array of all meta-data values for the given type. | |
| 11113 */ | |
| 11114 list: { | |
| 11115 type: Array, | |
| 11116 notify: true | |
| 11117 } | |
| 11118 | |
| 11119 }, | |
| 11120 | |
| 11121 /** | |
| 11122 * Actually a factory method, not a true constructor. Only runs if | |
| 11123 * someone invokes it directly (via `new Polymer.IronMeta()`); | |
| 11124 */ | |
| 11125 factoryImpl: function(config) { | |
| 11126 if (config) { | |
| 11127 for (var n in config) { | |
| 11128 switch(n) { | |
| 11129 case 'type': | |
| 11130 case 'key': | |
| 11131 this[n] = config[n]; | |
| 11132 break; | |
| 11133 } | |
| 11134 } | |
| 11135 } | |
| 11136 }, | |
| 11137 | |
| 11138 created: function() { | |
| 11139 // TODO(sjmiles): good for debugging? | |
| 11140 this._metaDatas = metaDatas; | |
| 11141 this._metaArrays = metaArrays; | |
| 11142 }, | |
| 11143 | |
| 11144 _keyChanged: function(key) { | |
| 11145 this._setValue(this._metaData && this._metaData[key]); | |
| 11146 }, | |
| 11147 | |
| 11148 _typeChanged: function(type) { | |
| 11149 this._metaData = metaDatas[type]; | |
| 11150 this.list = metaArrays[type]; | |
| 11151 if (this.key) { | |
| 11152 this._keyChanged(this.key); | |
| 11153 } | |
| 11154 }, | |
| 11155 | |
| 11156 /** | |
| 11157 * Retrieves meta data value by key. | |
| 11158 * @param {string} key The key of the meta-data to be returned. | |
| 11159 * @return {*} | |
| 11160 */ | |
| 11161 byKey: function(key) { | |
| 11162 return this._metaData && this._metaData[key]; | |
| 11163 } | |
| 11164 | |
| 11165 }); | |
| 11166 | |
| 11167 })(); | |
| 11168 Polymer({ | |
| 11169 | |
| 11170 is: 'iron-icon', | |
| 11171 | |
| 11172 properties: { | |
| 11173 | |
| 11174 /** | |
| 11175 * The name of the icon to use. The name should be of the form: | |
| 11176 * `iconset_name:icon_name`. | |
| 11177 */ | |
| 11178 icon: { | |
| 11179 type: String, | |
| 11180 observer: '_iconChanged' | |
| 11181 }, | |
| 11182 | |
| 11183 /** | |
| 11184 * The name of the theme to used, if one is specified by the | |
| 11185 * iconset. | |
| 11186 */ | |
| 11187 theme: { | |
| 11188 type: String, | |
| 11189 observer: '_updateIcon' | |
| 11190 }, | |
| 11191 | |
| 11192 /** | |
| 11193 * If using iron-icon without an iconset, you can set the src to be | |
| 11194 * the URL of an individual icon image file. Note that this will take | |
| 11195 * precedence over a given icon attribute. | |
| 11196 */ | |
| 11197 src: { | |
| 11198 type: String, | |
| 11199 observer: '_srcChanged' | |
| 11200 }, | |
| 11201 | |
| 11202 /** | |
| 11203 * @type {!Polymer.IronMeta} | |
| 11204 */ | |
| 11205 _meta: { | |
| 11206 value: Polymer.Base.create('iron-meta', {type: 'iconset'}) | |
| 11207 } | |
| 11208 | |
| 11209 }, | |
| 11210 | |
| 11211 _DEFAULT_ICONSET: 'icons', | |
| 11212 | |
| 11213 _iconChanged: function(icon) { | |
| 11214 var parts = (icon || '').split(':'); | |
| 11215 this._iconName = parts.pop(); | |
| 11216 this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; | |
| 11217 this._updateIcon(); | |
| 11218 }, | |
| 11219 | |
| 11220 _srcChanged: function(src) { | |
| 11221 this._updateIcon(); | |
| 11222 }, | |
| 11223 | |
| 11224 _usesIconset: function() { | |
| 11225 return this.icon || !this.src; | |
| 11226 }, | |
| 11227 | |
| 11228 /** @suppress {visibility} */ | |
| 11229 _updateIcon: function() { | |
| 11230 if (this._usesIconset()) { | |
| 11231 if (this._iconsetName) { | |
| 11232 this._iconset = /** @type {?Polymer.Iconset} */ ( | |
| 11233 this._meta.byKey(this._iconsetName)); | |
| 11234 if (this._iconset) { | |
| 11235 this._iconset.applyIcon(this, this._iconName, this.theme); | |
| 11236 this.unlisten(window, 'iron-iconset-added', '_updateIcon'); | |
| 11237 } else { | |
| 11238 this.listen(window, 'iron-iconset-added', '_updateIcon'); | |
| 11239 } | |
| 11240 } | |
| 11241 } else { | |
| 11242 if (!this._img) { | |
| 11243 this._img = document.createElement('img'); | |
| 11244 this._img.style.width = '100%'; | |
| 11245 this._img.style.height = '100%'; | |
| 11246 this._img.draggable = false; | |
| 11247 } | |
| 11248 this._img.src = this.src; | |
| 11249 Polymer.dom(this.root).appendChild(this._img); | |
| 11250 } | |
| 11251 } | |
| 11252 | |
| 11253 }); | |
| 11254 /** | |
| 11255 * The `iron-iconset-svg` element allows users to define their own icon sets | |
| 11256 * that contain svg icons. The svg icon elements should be children of the | |
| 11257 * `iron-iconset-svg` element. Multiple icons should be given distinct id's. | |
| 11258 * | |
| 11259 * Using svg elements to create icons has a few advantages over traditional | |
| 11260 * bitmap graphics like jpg or png. Icons that use svg are vector based so the
y | |
| 11261 * are resolution independent and should look good on any device. They are | |
| 11262 * stylable via css. Icons can be themed, colorized, and even animated. | |
| 11263 * | |
| 11264 * Example: | |
| 11265 * | |
| 11266 * <iron-iconset-svg name="my-svg-icons" size="24"> | |
| 11267 * <svg> | |
| 11268 * <defs> | |
| 11269 * <g id="shape"> | |
| 11270 * <rect x="50" y="50" width="50" height="50" /> | |
| 11271 * <circle cx="50" cy="50" r="50" /> | |
| 11272 * </g> | |
| 11273 * </defs> | |
| 11274 * </svg> | |
| 11275 * </iron-iconset-svg> | |
| 11276 * | |
| 11277 * This will automatically register the icon set "my-svg-icons" to the iconset | |
| 11278 * database. To use these icons from within another element, make a | |
| 11279 * `iron-iconset` element and call the `byId` method | |
| 11280 * to retrieve a given iconset. To apply a particular icon inside an | |
| 11281 * element use the `applyIcon` method. For example: | |
| 11282 * | |
| 11283 * iconset.applyIcon(iconNode, 'car'); | |
| 11284 * | |
| 11285 * @element iron-iconset-svg | |
| 11286 * @demo demo/index.html | |
| 11287 */ | |
| 11288 Polymer({ | |
| 11289 | |
| 11290 is: 'iron-iconset-svg', | |
| 11291 | |
| 11292 properties: { | |
| 11293 | |
| 11294 /** | |
| 11295 * The name of the iconset. | |
| 11296 * | |
| 11297 * @attribute name | |
| 11298 * @type string | |
| 11299 */ | |
| 11300 name: { | |
| 11301 type: String, | |
| 11302 observer: '_nameChanged' | |
| 11303 }, | |
| 11304 | |
| 11305 /** | |
| 11306 * The size of an individual icon. Note that icons must be square. | |
| 11307 * | |
| 11308 * @attribute iconSize | |
| 11309 * @type number | |
| 11310 * @default 24 | |
| 11311 */ | |
| 11312 size: { | |
| 11313 type: Number, | |
| 11314 value: 24 | |
| 11315 } | |
| 11316 | |
| 11317 }, | |
| 11318 | |
| 11319 /** | |
| 11320 * Construct an array of all icon names in this iconset. | |
| 11321 * | |
| 11322 * @return {!Array} Array of icon names. | |
| 11323 */ | |
| 11324 getIconNames: function() { | |
| 11325 this._icons = this._createIconMap(); | |
| 11326 return Object.keys(this._icons).map(function(n) { | |
| 11327 return this.name + ':' + n; | |
| 11328 }, this); | |
| 11329 }, | |
| 11330 | |
| 11331 /** | |
| 11332 * Applies an icon to the given element. | |
| 11333 * | |
| 11334 * An svg icon is prepended to the element's shadowRoot if it exists, | |
| 11335 * otherwise to the element itself. | |
| 11336 * | |
| 11337 * @method applyIcon | |
| 11338 * @param {Element} element Element to which the icon is applied. | |
| 11339 * @param {string} iconName Name of the icon to apply. | |
| 11340 * @return {Element} The svg element which renders the icon. | |
| 11341 */ | |
| 11342 applyIcon: function(element, iconName) { | |
| 11343 // insert svg element into shadow root, if it exists | |
| 11344 element = element.root || element; | |
| 11345 // Remove old svg element | |
| 11346 this.removeIcon(element); | |
| 11347 // install new svg element | |
| 11348 var svg = this._cloneIcon(iconName); | |
| 11349 if (svg) { | |
| 11350 var pde = Polymer.dom(element); | |
| 11351 pde.insertBefore(svg, pde.childNodes[0]); | |
| 11352 return element._svgIcon = svg; | |
| 11353 } | |
| 11354 return null; | |
| 11355 }, | |
| 11356 | |
| 11357 /** | |
| 11358 * Remove an icon from the given element by undoing the changes effected | |
| 11359 * by `applyIcon`. | |
| 11360 * | |
| 11361 * @param {Element} element The element from which the icon is removed. | |
| 11362 */ | |
| 11363 removeIcon: function(element) { | |
| 11364 // Remove old svg element | |
| 11365 if (element._svgIcon) { | |
| 11366 Polymer.dom(element).removeChild(element._svgIcon); | |
| 11367 element._svgIcon = null; | |
| 11368 } | |
| 11369 }, | |
| 11370 | |
| 11371 /** | |
| 11372 * | |
| 11373 * When name is changed, register iconset metadata | |
| 11374 * | |
| 11375 */ | |
| 11376 _nameChanged: function() { | |
| 11377 new Polymer.IronMeta({type: 'iconset', key: this.name, value: this}); | |
| 11378 this.async(function() { | |
| 11379 this.fire('iron-iconset-added', this, {node: window}); | |
| 11380 }); | |
| 11381 }, | |
| 11382 | |
| 11383 /** | |
| 11384 * Create a map of child SVG elements by id. | |
| 11385 * | |
| 11386 * @return {!Object} Map of id's to SVG elements. | |
| 11387 */ | |
| 11388 _createIconMap: function() { | |
| 11389 // Objects chained to Object.prototype (`{}`) have members. Specifically, | |
| 11390 // on FF there is a `watch` method that confuses the icon map, so we | |
| 11391 // need to use a null-based object here. | |
| 11392 var icons = Object.create(null); | |
| 11393 Polymer.dom(this).querySelectorAll('[id]') | |
| 11394 .forEach(function(icon) { | |
| 11395 icons[icon.id] = icon; | |
| 11396 }); | |
| 11397 return icons; | |
| 11398 }, | |
| 11399 | |
| 11400 /** | |
| 11401 * Produce installable clone of the SVG element matching `id` in this | |
| 11402 * iconset, or `undefined` if there is no matching element. | |
| 11403 * | |
| 11404 * @return {Element} Returns an installable clone of the SVG element | |
| 11405 * matching `id`. | |
| 11406 */ | |
| 11407 _cloneIcon: function(id) { | |
| 11408 // create the icon map on-demand, since the iconset itself has no discrete | |
| 11409 // signal to know when it's children are fully parsed | |
| 11410 this._icons = this._icons || this._createIconMap(); | |
| 11411 return this._prepareSvgClone(this._icons[id], this.size); | |
| 11412 }, | |
| 11413 | |
| 11414 /** | |
| 11415 * @param {Element} sourceSvg | |
| 11416 * @param {number} size | |
| 11417 * @return {Element} | |
| 11418 */ | |
| 11419 _prepareSvgClone: function(sourceSvg, size) { | |
| 11420 if (sourceSvg) { | |
| 11421 var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | |
| 11422 svg.setAttribute('viewBox', ['0', '0', size, size].join(' ')); | |
| 11423 svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); | |
| 11424 // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/
370136 | |
| 11425 // TODO(sjmiles): inline style may not be ideal, but avoids requiring a
shadow-root | |
| 11426 svg.style.cssText = 'pointer-events: none; display: block; width: 100%;
height: 100%;'; | |
| 11427 svg.appendChild(sourceSvg.cloneNode(true)).removeAttribute('id'); | |
| 11428 return svg; | |
| 11429 } | |
| 11430 return null; | |
| 11431 } | |
| 11432 | |
| 11433 }); | |
| 11434 Polymer({ | 12905 Polymer({ |
| 11435 is: 'paper-item', | 12906 is: 'paper-item', |
| 11436 | 12907 |
| 11437 hostAttributes: { | 12908 hostAttributes: { |
| 11438 role: 'listitem', | 12909 role: 'listitem', |
| 11439 tabindex: '0' | 12910 tabindex: '0' |
| 11440 }, | 12911 }, |
| 11441 | 12912 |
| 11442 behaviors: [ | 12913 behaviors: [ |
| 11443 Polymer.IronControlState, | 12914 Polymer.IronControlState, |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 11635 value: null | 13106 value: null |
| 11636 }, | 13107 }, |
| 11637 | 13108 |
| 11638 /** | 13109 /** |
| 11639 * The set of excluded elements where the key is the `localName` | 13110 * The set of excluded elements where the key is the `localName` |
| 11640 * of the element that will be ignored from the item list. | 13111 * of the element that will be ignored from the item list. |
| 11641 * | 13112 * |
| 11642 * @type {object} | 13113 * @type {object} |
| 11643 * @default {template: 1} | 13114 * @default {template: 1} |
| 11644 */ | 13115 */ |
| 11645 excludedLocalNames: { | 13116 _excludedLocalNames: { |
| 11646 type: Object, | 13117 type: Object, |
| 11647 value: function() { | 13118 value: function() { |
| 11648 return { | 13119 return { |
| 11649 'template': 1 | 13120 'template': 1 |
| 11650 }; | 13121 }; |
| 11651 } | 13122 } |
| 11652 } | 13123 } |
| 11653 }, | 13124 }, |
| 11654 | 13125 |
| 11655 observers: [ | 13126 observers: [ |
| 11656 '_updateSelected(attrForSelected, selected)' | 13127 '_updateSelected(attrForSelected, selected)' |
| 11657 ], | 13128 ], |
| 11658 | 13129 |
| 11659 created: function() { | 13130 created: function() { |
| 11660 this._bindFilterItem = this._filterItem.bind(this); | 13131 this._bindFilterItem = this._filterItem.bind(this); |
| 11661 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); | 13132 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); |
| 13133 // TODO(cdata): When polymer/polymer#2535 lands, we do not need to do this |
| 13134 // book keeping anymore: |
| 13135 this.__listeningForActivate = false; |
| 11662 }, | 13136 }, |
| 11663 | 13137 |
| 11664 attached: function() { | 13138 attached: function() { |
| 11665 this._observer = this._observeItems(this); | 13139 this._observer = this._observeItems(this); |
| 11666 this._contentObserver = this._observeContent(this); | 13140 this._contentObserver = this._observeContent(this); |
| 11667 if (!this.selectedItem && this.selected) { | 13141 if (!this.selectedItem && this.selected) { |
| 11668 this._updateSelected(this.attrForSelected,this.selected) | 13142 this._updateSelected(this.attrForSelected,this.selected) |
| 11669 } | 13143 } |
| 13144 this._addListener(this.activateEvent); |
| 11670 }, | 13145 }, |
| 11671 | 13146 |
| 11672 detached: function() { | 13147 detached: function() { |
| 11673 if (this._observer) { | 13148 if (this._observer) { |
| 11674 this._observer.disconnect(); | 13149 this._observer.disconnect(); |
| 11675 } | 13150 } |
| 11676 if (this._contentObserver) { | 13151 if (this._contentObserver) { |
| 11677 this._contentObserver.disconnect(); | 13152 this._contentObserver.disconnect(); |
| 11678 } | 13153 } |
| 11679 this._removeListener(this.activateEvent); | 13154 this._removeListener(this.activateEvent); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 11726 * Selects the next item. | 13201 * Selects the next item. |
| 11727 * | 13202 * |
| 11728 * @method selectNext | 13203 * @method selectNext |
| 11729 */ | 13204 */ |
| 11730 selectNext: function() { | 13205 selectNext: function() { |
| 11731 var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.l
ength; | 13206 var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.l
ength; |
| 11732 this.selected = this._indexToValue(index); | 13207 this.selected = this._indexToValue(index); |
| 11733 }, | 13208 }, |
| 11734 | 13209 |
| 11735 _addListener: function(eventName) { | 13210 _addListener: function(eventName) { |
| 13211 if (!this.isAttached || this.__listeningForActivate) { |
| 13212 return; |
| 13213 } |
| 13214 |
| 13215 this.__listeningForActivate = true; |
| 11736 this.listen(this, eventName, '_activateHandler'); | 13216 this.listen(this, eventName, '_activateHandler'); |
| 11737 }, | 13217 }, |
| 11738 | 13218 |
| 11739 _removeListener: function(eventName) { | 13219 _removeListener: function(eventName) { |
| 11740 this.unlisten(this, eventName, '_activateHandler'); | 13220 this.unlisten(this, eventName, '_activateHandler'); |
| 13221 this.__listeningForActivate = false; |
| 11741 }, | 13222 }, |
| 11742 | 13223 |
| 11743 _activateEventChanged: function(eventName, old) { | 13224 _activateEventChanged: function(eventName, old) { |
| 11744 this._removeListener(old); | 13225 this._removeListener(old); |
| 11745 this._addListener(eventName); | 13226 this._addListener(eventName); |
| 11746 }, | 13227 }, |
| 11747 | 13228 |
| 11748 _updateSelected: function() { | 13229 _updateSelected: function() { |
| 11749 this._selectSelected(this.selected); | 13230 this._selectSelected(this.selected); |
| 11750 }, | 13231 }, |
| 11751 | 13232 |
| 11752 _selectSelected: function(selected) { | 13233 _selectSelected: function(selected) { |
| 11753 this._selection.select(this._valueToItem(this.selected)); | 13234 this._selection.select(this._valueToItem(this.selected)); |
| 11754 }, | 13235 }, |
| 11755 | 13236 |
| 11756 _filterItem: function(node) { | 13237 _filterItem: function(node) { |
| 11757 return !this.excludedLocalNames[node.localName]; | 13238 return !this._excludedLocalNames[node.localName]; |
| 11758 }, | 13239 }, |
| 11759 | 13240 |
| 11760 _valueToItem: function(value) { | 13241 _valueToItem: function(value) { |
| 11761 return (value == null) ? null : this.items[this._valueToIndex(value)]; | 13242 return (value == null) ? null : this.items[this._valueToIndex(value)]; |
| 11762 }, | 13243 }, |
| 11763 | 13244 |
| 11764 _valueToIndex: function(value) { | 13245 _valueToIndex: function(value) { |
| 11765 if (this.attrForSelected) { | 13246 if (this.attrForSelected) { |
| 11766 for (var i = 0, item; item = this.items[i]; i++) { | 13247 for (var i = 0, item; item = this.items[i]; i++) { |
| 11767 if (this._valueForItem(item) == value) { | 13248 if (this._valueForItem(item) == value) { |
| (...skipping 490 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 12258 is: 'paper-menu', | 13739 is: 'paper-menu', |
| 12259 | 13740 |
| 12260 behaviors: [ | 13741 behaviors: [ |
| 12261 Polymer.IronMenuBehavior | 13742 Polymer.IronMenuBehavior |
| 12262 ] | 13743 ] |
| 12263 | 13744 |
| 12264 }); | 13745 }); |
| 12265 | 13746 |
| 12266 })(); | 13747 })(); |
| 12267 /** | 13748 /** |
| 12268 * `IronResizableBehavior` is a behavior that can be used in Polymer elements
to | |
| 12269 * coordinate the flow of resize events between "resizers" (elements that cont
rol the | |
| 12270 * size or hidden state of their children) and "resizables" (elements that nee
d to be | |
| 12271 * notified when they are resized or un-hidden by their parents in order to ta
ke | |
| 12272 * action on their new measurements). | |
| 12273 * Elements that perform measurement should add the `IronResizableBehavior` be
havior to | |
| 12274 * their element definition and listen for the `iron-resize` event on themselv
es. | |
| 12275 * This event will be fired when they become showing after having been hidden, | |
| 12276 * when they are resized explicitly by another resizable, or when the window h
as been | |
| 12277 * resized. | |
| 12278 * Note, the `iron-resize` event is non-bubbling. | |
| 12279 * | |
| 12280 * @polymerBehavior Polymer.IronResizableBehavior | |
| 12281 * @demo demo/index.html | |
| 12282 **/ | |
| 12283 Polymer.IronResizableBehavior = { | |
| 12284 properties: { | |
| 12285 /** | |
| 12286 * The closest ancestor element that implements `IronResizableBehavior`. | |
| 12287 */ | |
| 12288 _parentResizable: { | |
| 12289 type: Object, | |
| 12290 observer: '_parentResizableChanged' | |
| 12291 }, | |
| 12292 | |
| 12293 /** | |
| 12294 * True if this element is currently notifying its descedant elements of | |
| 12295 * resize. | |
| 12296 */ | |
| 12297 _notifyingDescendant: { | |
| 12298 type: Boolean, | |
| 12299 value: false | |
| 12300 } | |
| 12301 }, | |
| 12302 | |
| 12303 listeners: { | |
| 12304 'iron-request-resize-notifications': '_onIronRequestResizeNotifications' | |
| 12305 }, | |
| 12306 | |
| 12307 created: function() { | |
| 12308 // We don't really need property effects on these, and also we want them | |
| 12309 // to be created before the `_parentResizable` observer fires: | |
| 12310 this._interestedResizables = []; | |
| 12311 this._boundNotifyResize = this.notifyResize.bind(this); | |
| 12312 }, | |
| 12313 | |
| 12314 attached: function() { | |
| 12315 this.fire('iron-request-resize-notifications', null, { | |
| 12316 node: this, | |
| 12317 bubbles: true, | |
| 12318 cancelable: true | |
| 12319 }); | |
| 12320 | |
| 12321 if (!this._parentResizable) { | |
| 12322 window.addEventListener('resize', this._boundNotifyResize); | |
| 12323 this.notifyResize(); | |
| 12324 } | |
| 12325 }, | |
| 12326 | |
| 12327 detached: function() { | |
| 12328 if (this._parentResizable) { | |
| 12329 this._parentResizable.stopResizeNotificationsFor(this); | |
| 12330 } else { | |
| 12331 window.removeEventListener('resize', this._boundNotifyResize); | |
| 12332 } | |
| 12333 | |
| 12334 this._parentResizable = null; | |
| 12335 }, | |
| 12336 | |
| 12337 /** | |
| 12338 * Can be called to manually notify a resizable and its descendant | |
| 12339 * resizables of a resize change. | |
| 12340 */ | |
| 12341 notifyResize: function() { | |
| 12342 if (!this.isAttached) { | |
| 12343 return; | |
| 12344 } | |
| 12345 | |
| 12346 this._interestedResizables.forEach(function(resizable) { | |
| 12347 if (this.resizerShouldNotify(resizable)) { | |
| 12348 this._notifyDescendant(resizable); | |
| 12349 } | |
| 12350 }, this); | |
| 12351 | |
| 12352 this._fireResize(); | |
| 12353 }, | |
| 12354 | |
| 12355 /** | |
| 12356 * Used to assign the closest resizable ancestor to this resizable | |
| 12357 * if the ancestor detects a request for notifications. | |
| 12358 */ | |
| 12359 assignParentResizable: function(parentResizable) { | |
| 12360 this._parentResizable = parentResizable; | |
| 12361 }, | |
| 12362 | |
| 12363 /** | |
| 12364 * Used to remove a resizable descendant from the list of descendants | |
| 12365 * that should be notified of a resize change. | |
| 12366 */ | |
| 12367 stopResizeNotificationsFor: function(target) { | |
| 12368 var index = this._interestedResizables.indexOf(target); | |
| 12369 | |
| 12370 if (index > -1) { | |
| 12371 this._interestedResizables.splice(index, 1); | |
| 12372 this.unlisten(target, 'iron-resize', '_onDescendantIronResize'); | |
| 12373 } | |
| 12374 }, | |
| 12375 | |
| 12376 /** | |
| 12377 * This method can be overridden to filter nested elements that should or | |
| 12378 * should not be notified by the current element. Return true if an element | |
| 12379 * should be notified, or false if it should not be notified. | |
| 12380 * | |
| 12381 * @param {HTMLElement} element A candidate descendant element that | |
| 12382 * implements `IronResizableBehavior`. | |
| 12383 * @return {boolean} True if the `element` should be notified of resize. | |
| 12384 */ | |
| 12385 resizerShouldNotify: function(element) { return true; }, | |
| 12386 | |
| 12387 _onDescendantIronResize: function(event) { | |
| 12388 if (this._notifyingDescendant) { | |
| 12389 event.stopPropagation(); | |
| 12390 return; | |
| 12391 } | |
| 12392 | |
| 12393 // NOTE(cdata): In ShadowDOM, event retargetting makes echoing of the | |
| 12394 // otherwise non-bubbling event "just work." We do it manually here for | |
| 12395 // the case where Polymer is not using shadow roots for whatever reason: | |
| 12396 if (!Polymer.Settings.useShadow) { | |
| 12397 this._fireResize(); | |
| 12398 } | |
| 12399 }, | |
| 12400 | |
| 12401 _fireResize: function() { | |
| 12402 this.fire('iron-resize', null, { | |
| 12403 node: this, | |
| 12404 bubbles: false | |
| 12405 }); | |
| 12406 }, | |
| 12407 | |
| 12408 _onIronRequestResizeNotifications: function(event) { | |
| 12409 var target = event.path ? event.path[0] : event.target; | |
| 12410 | |
| 12411 if (target === this) { | |
| 12412 return; | |
| 12413 } | |
| 12414 | |
| 12415 if (this._interestedResizables.indexOf(target) === -1) { | |
| 12416 this._interestedResizables.push(target); | |
| 12417 this.listen(target, 'iron-resize', '_onDescendantIronResize'); | |
| 12418 } | |
| 12419 | |
| 12420 target.assignParentResizable(this); | |
| 12421 this._notifyDescendant(target); | |
| 12422 | |
| 12423 event.stopPropagation(); | |
| 12424 }, | |
| 12425 | |
| 12426 _parentResizableChanged: function(parentResizable) { | |
| 12427 if (parentResizable) { | |
| 12428 window.removeEventListener('resize', this._boundNotifyResize); | |
| 12429 } | |
| 12430 }, | |
| 12431 | |
| 12432 _notifyDescendant: function(descendant) { | |
| 12433 // NOTE(cdata): In IE10, attached is fired on children first, so it's | |
| 12434 // important not to notify them if the parent is not attached yet (or | |
| 12435 // else they will get redundantly notified when the parent attaches). | |
| 12436 if (!this.isAttached) { | |
| 12437 return; | |
| 12438 } | |
| 12439 | |
| 12440 this._notifyingDescendant = true; | |
| 12441 descendant.notifyResize(); | |
| 12442 this._notifyingDescendant = false; | |
| 12443 } | |
| 12444 }; | |
| 12445 /** | |
| 12446 Polymer.IronFitBehavior fits an element in another element using `max-height` an
d `max-width`, and | 13749 Polymer.IronFitBehavior fits an element in another element using `max-height` an
d `max-width`, and |
| 12447 optionally centers it in the window or another element. | 13750 optionally centers it in the window or another element. |
| 12448 | 13751 |
| 12449 The element will only be sized and/or positioned if it has not already been size
d and/or positioned | 13752 The element will only be sized and/or positioned if it has not already been size
d and/or positioned |
| 12450 by CSS. | 13753 by CSS. |
| 12451 | 13754 |
| 12452 CSS properties | Action | 13755 CSS properties | Action |
| 12453 -----------------------------|------------------------------------------- | 13756 -----------------------------|------------------------------------------- |
| 12454 `position` set | Element is not centered horizontally or verticall
y | 13757 `position` set | Element is not centered horizontally or verticall
y |
| 12455 `top` or `bottom` set | Element is not vertically centered | 13758 `top` or `bottom` set | Element is not vertically centered |
| (...skipping 394 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 12850 on top of other content. It includes an optional backdrop, and can be used to im
plement a variety | 14153 on top of other content. It includes an optional backdrop, and can be used to im
plement a variety |
| 12851 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. | 14154 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. |
| 12852 | 14155 |
| 12853 ### Closing and canceling | 14156 ### Closing and canceling |
| 12854 | 14157 |
| 12855 A dialog may be hidden by closing or canceling. The difference between close and
cancel is user | 14158 A dialog may be hidden by closing or canceling. The difference between close and
cancel is user |
| 12856 intent. Closing generally implies that the user acknowledged the content on the
overlay. By default, | 14159 intent. Closing generally implies that the user acknowledged the content on the
overlay. By default, |
| 12857 it will cancel whenever the user taps outside it or presses the escape key. This
behavior is | 14160 it will cancel whenever the user taps outside it or presses the escape key. This
behavior is |
| 12858 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click
` properties. | 14161 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click
` properties. |
| 12859 `close()` should be called explicitly by the implementer when the user interacts
with a control | 14162 `close()` should be called explicitly by the implementer when the user interacts
with a control |
| 12860 in the overlay element. | 14163 in the overlay element. When the dialog is canceled, the overlay fires an 'iron-
overlay-canceled' |
| 14164 event. Call `preventDefault` on this event to prevent the overlay from closing. |
| 12861 | 14165 |
| 12862 ### Positioning | 14166 ### Positioning |
| 12863 | 14167 |
| 12864 By default the element is sized and positioned to fit and centered inside the wi
ndow. You can | 14168 By default the element is sized and positioned to fit and centered inside the wi
ndow. You can |
| 12865 position and size it manually using CSS. See `Polymer.IronFitBehavior`. | 14169 position and size it manually using CSS. See `Polymer.IronFitBehavior`. |
| 12866 | 14170 |
| 12867 ### Backdrop | 14171 ### Backdrop |
| 12868 | 14172 |
| 12869 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The
backdrop is | 14173 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The
backdrop is |
| 12870 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page
for styling | 14174 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page
for styling |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 13021 */ | 14325 */ |
| 13022 close: function() { | 14326 close: function() { |
| 13023 this.opened = false; | 14327 this.opened = false; |
| 13024 this._setCanceled(false); | 14328 this._setCanceled(false); |
| 13025 }, | 14329 }, |
| 13026 | 14330 |
| 13027 /** | 14331 /** |
| 13028 * Cancels the overlay. | 14332 * Cancels the overlay. |
| 13029 */ | 14333 */ |
| 13030 cancel: function() { | 14334 cancel: function() { |
| 14335 var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelabl
e: true}); |
| 14336 if (cancelEvent.defaultPrevented) { |
| 14337 return; |
| 14338 } |
| 14339 |
| 13031 this.opened = false; | 14340 this.opened = false; |
| 13032 this._setCanceled(true); | 14341 this._setCanceled(true); |
| 13033 }, | 14342 }, |
| 13034 | 14343 |
| 13035 _ensureSetup: function() { | 14344 _ensureSetup: function() { |
| 13036 if (this._overlaySetup) { | 14345 if (this._overlaySetup) { |
| 13037 return; | 14346 return; |
| 13038 } | 14347 } |
| 13039 this._overlaySetup = true; | 14348 this._overlaySetup = true; |
| 13040 this.style.outline = 'none'; | 14349 this.style.outline = 'none'; |
| (...skipping 1528 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 14569 }); | 15878 }); |
| 14570 | 15879 |
| 14571 PaperMenuButton.ANIMATION_CUBIC_BEZIER = 'cubic-bezier(.3,.95,.5,1)'; | 15880 PaperMenuButton.ANIMATION_CUBIC_BEZIER = 'cubic-bezier(.3,.95,.5,1)'; |
| 14572 PaperMenuButton.MAX_ANIMATION_TIME_MS = 400; | 15881 PaperMenuButton.MAX_ANIMATION_TIME_MS = 400; |
| 14573 | 15882 |
| 14574 Polymer.PaperMenuButton = PaperMenuButton; | 15883 Polymer.PaperMenuButton = PaperMenuButton; |
| 14575 })(); | 15884 })(); |
| 14576 /** | 15885 /** |
| 14577 * `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has k
eyboard focus. | 15886 * `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has k
eyboard focus. |
| 14578 * | 15887 * |
| 14579 * @polymerBehavior Polymer.PaperInkyFocusBehavior | 15888 * @polymerBehavior Polymer.PaperInkyFocusBehaviorImpl |
| 14580 */ | 15889 */ |
| 14581 Polymer.PaperInkyFocusBehaviorImpl = { | 15890 Polymer.PaperInkyFocusBehaviorImpl = { |
| 14582 | 15891 |
| 14583 observers: [ | 15892 observers: [ |
| 14584 '_focusedChanged(receivedFocusFromKeyboard)' | 15893 '_focusedChanged(receivedFocusFromKeyboard)' |
| 14585 ], | 15894 ], |
| 14586 | 15895 |
| 14587 _focusedChanged: function(receivedFocusFromKeyboard) { | 15896 _focusedChanged: function(receivedFocusFromKeyboard) { |
| 14588 if (!this.$.ink) { | 15897 if (receivedFocusFromKeyboard) { |
| 14589 return; | 15898 this.ensureRipple(); |
| 14590 } | 15899 } |
| 15900 if (this.hasRipple()) { |
| 15901 this._ripple.holdDown = receivedFocusFromKeyboard; |
| 15902 } |
| 15903 }, |
| 14591 | 15904 |
| 14592 this.$.ink.holdDown = receivedFocusFromKeyboard; | 15905 _createRipple: function() { |
| 15906 var ripple = Polymer.PaperRippleBehavior._createRipple(); |
| 15907 ripple.id = 'ink'; |
| 15908 ripple.setAttribute('center', ''); |
| 15909 ripple.classList.add('circle'); |
| 15910 return ripple; |
| 14593 } | 15911 } |
| 14594 | 15912 |
| 14595 }; | 15913 }; |
| 14596 | 15914 |
| 14597 /** @polymerBehavior Polymer.PaperInkyFocusBehavior */ | 15915 /** @polymerBehavior Polymer.PaperInkyFocusBehavior */ |
| 14598 Polymer.PaperInkyFocusBehavior = [ | 15916 Polymer.PaperInkyFocusBehavior = [ |
| 14599 Polymer.IronButtonState, | 15917 Polymer.IronButtonState, |
| 14600 Polymer.IronControlState, | 15918 Polymer.IronControlState, |
| 15919 Polymer.PaperRippleBehavior, |
| 14601 Polymer.PaperInkyFocusBehaviorImpl | 15920 Polymer.PaperInkyFocusBehaviorImpl |
| 14602 ]; | 15921 ]; |
| 14603 Polymer({ | 15922 Polymer({ |
| 14604 is: 'paper-icon-button', | 15923 is: 'paper-icon-button', |
| 14605 | 15924 |
| 14606 hostAttributes: { | 15925 hostAttributes: { |
| 14607 role: 'button', | 15926 role: 'button', |
| 14608 tabindex: '0' | 15927 tabindex: '0' |
| 14609 }, | |
| 14610 | |
| 14611 behaviors: [ | |
| 14612 Polymer.PaperInkyFocusBehavior | |
| 14613 ], | |
| 14614 | |
| 14615 properties: { | |
| 14616 /** | |
| 14617 * The URL of an image for the icon. If the src property is specified, | |
| 14618 * the icon property should not be. | |
| 14619 */ | |
| 14620 src: { | |
| 14621 type: String | |
| 14622 }, | 15928 }, |
| 14623 | 15929 |
| 14624 /** | 15930 behaviors: [ |
| 14625 * Specifies the icon name or index in the set of icons available in | 15931 Polymer.PaperInkyFocusBehavior |
| 14626 * the icon's icon set. If the icon property is specified, | 15932 ], |
| 14627 * the src property should not be. | 15933 |
| 14628 */ | 15934 properties: { |
| 14629 icon: { | 15935 /** |
| 14630 type: String | 15936 * The URL of an image for the icon. If the src property is specified, |
| 15937 * the icon property should not be. |
| 15938 */ |
| 15939 src: { |
| 15940 type: String |
| 15941 }, |
| 15942 |
| 15943 /** |
| 15944 * Specifies the icon name or index in the set of icons available in |
| 15945 * the icon's icon set. If the icon property is specified, |
| 15946 * the src property should not be. |
| 15947 */ |
| 15948 icon: { |
| 15949 type: String |
| 15950 }, |
| 15951 |
| 15952 /** |
| 15953 * Specifies the alternate text for the button, for accessibility. |
| 15954 */ |
| 15955 alt: { |
| 15956 type: String, |
| 15957 observer: "_altChanged" |
| 15958 } |
| 14631 }, | 15959 }, |
| 14632 | 15960 |
| 14633 /** | 15961 _altChanged: function(newValue, oldValue) { |
| 14634 * Specifies the alternate text for the button, for accessibility. | 15962 var label = this.getAttribute('aria-label'); |
| 14635 */ | 15963 |
| 14636 alt: { | 15964 // Don't stomp over a user-set aria-label. |
| 14637 type: String, | 15965 if (!label || oldValue == label) { |
| 14638 observer: "_altChanged" | 15966 this.setAttribute('aria-label', newValue); |
| 15967 } |
| 14639 } | 15968 } |
| 14640 }, | 15969 }); |
| 14641 | |
| 14642 _altChanged: function(newValue, oldValue) { | |
| 14643 var label = this.getAttribute('aria-label'); | |
| 14644 | |
| 14645 // Don't stomp over a user-set aria-label. | |
| 14646 if (!label || oldValue == label) { | |
| 14647 this.setAttribute('aria-label', newValue); | |
| 14648 } | |
| 14649 } | |
| 14650 }); | |
| 14651 /** | 15970 /** |
| 14652 * Use `Polymer.IronValidatableBehavior` to implement an element that validate
s user input. | 15971 * Use `Polymer.IronValidatableBehavior` to implement an element that validate
s user input. |
| 14653 * | 15972 * |
| 14654 * ### Accessibility | 15973 * ### Accessibility |
| 14655 * | 15974 * |
| 14656 * Changing the `invalid` property, either manually or by calling `validate()`
will update the | 15975 * Changing the `invalid` property, either manually or by calling `validate()`
will update the |
| 14657 * `aria-invalid` attribute. | 15976 * `aria-invalid` attribute. |
| 14658 * | 15977 * |
| 14659 * @demo demo/index.html | 15978 * @demo demo/index.html |
| 14660 * @polymerBehavior | 15979 * @polymerBehavior |
| (...skipping 777 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 15438 | 16757 |
| 15439 cr.define('downloads', function() { | 16758 cr.define('downloads', function() { |
| 15440 var Manager = Polymer({ | 16759 var Manager = Polymer({ |
| 15441 is: 'downloads-manager', | 16760 is: 'downloads-manager', |
| 15442 | 16761 |
| 15443 properties: { | 16762 properties: { |
| 15444 hasDownloads_: { | 16763 hasDownloads_: { |
| 15445 type: Boolean, | 16764 type: Boolean, |
| 15446 value: false, | 16765 value: false, |
| 15447 }, | 16766 }, |
| 16767 |
| 16768 items_: { |
| 16769 type: Array, |
| 16770 }, |
| 15448 }, | 16771 }, |
| 15449 | 16772 |
| 15450 /** | 16773 /** |
| 15451 * @return {number} A guess at how many items could be visible at once. | |
| 15452 * @private | |
| 15453 */ | |
| 15454 guesstimateNumberOfVisibleItems_: function() { | |
| 15455 var toolbarHeight = this.$.toolbar.offsetHeight; | |
| 15456 return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1; | |
| 15457 }, | |
| 15458 | |
| 15459 /** | |
| 15460 * @param {Event} e | 16774 * @param {Event} e |
| 15461 * @private | 16775 * @private |
| 15462 */ | 16776 */ |
| 15463 onCanExecute_: function(e) { | 16777 onCanExecute_: function(e) { |
| 15464 e = /** @type {cr.ui.CanExecuteEvent} */(e); | 16778 e = /** @type {cr.ui.CanExecuteEvent} */(e); |
| 15465 switch (e.command.id) { | 16779 switch (e.command.id) { |
| 15466 case 'undo-command': | 16780 case 'undo-command': |
| 15467 e.canExecute = this.$.toolbar.canUndo(); | 16781 e.canExecute = this.$.toolbar.canUndo(); |
| 15468 break; | 16782 break; |
| 15469 case 'clear-all-command': | 16783 case 'clear-all-command': |
| (...skipping 16 matching lines...) Expand all Loading... |
| 15486 /** @private */ | 16800 /** @private */ |
| 15487 onLoad_: function() { | 16801 onLoad_: function() { |
| 15488 cr.ui.decorate('command', cr.ui.Command); | 16802 cr.ui.decorate('command', cr.ui.Command); |
| 15489 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); | 16803 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); |
| 15490 document.addEventListener('command', this.onCommand_.bind(this)); | 16804 document.addEventListener('command', this.onCommand_.bind(this)); |
| 15491 | 16805 |
| 15492 // Shows all downloads. | 16806 // Shows all downloads. |
| 15493 downloads.ActionService.getInstance().search(''); | 16807 downloads.ActionService.getInstance().search(''); |
| 15494 }, | 16808 }, |
| 15495 | 16809 |
| 15496 /** @private */ | |
| 15497 rebuildFocusGrid_: function() { | |
| 15498 var activeElement = this.shadowRoot.activeElement; | |
| 15499 | |
| 15500 var activeItem; | |
| 15501 if (activeElement && activeElement.tagName == 'downloads-item') | |
| 15502 activeItem = activeElement; | |
| 15503 | |
| 15504 var activeControl = activeItem && activeItem.shadowRoot.activeElement; | |
| 15505 | |
| 15506 /** @private {!cr.ui.FocusGrid} */ | |
| 15507 this.focusGrid_ = this.focusGrid_ || new cr.ui.FocusGrid; | |
| 15508 this.focusGrid_.destroy(); | |
| 15509 | |
| 15510 var boundary = this.$['downloads-list']; | |
| 15511 | |
| 15512 this.items_.forEach(function(item) { | |
| 15513 var focusRow = new downloads.FocusRow(item.content, boundary); | |
| 15514 this.focusGrid_.addRow(focusRow); | |
| 15515 | |
| 15516 if (item == activeItem && !cr.ui.FocusRow.isFocusable(activeControl)) | |
| 15517 focusRow.getEquivalentElement(activeControl).focus(); | |
| 15518 }, this); | |
| 15519 | |
| 15520 this.focusGrid_.ensureRowActive(); | |
| 15521 }, | |
| 15522 | |
| 15523 /** | 16810 /** |
| 15524 * @return {number} The number of downloads shown on the page. | 16811 * @return {number} The number of downloads shown on the page. |
| 15525 * @private | 16812 * @private |
| 15526 */ | 16813 */ |
| 15527 size_: function() { | 16814 size_: function() { |
| 15528 return this.items_.length; | 16815 return this.items_.length; |
| 15529 }, | 16816 }, |
| 15530 | 16817 |
| 15531 /** | 16818 /** |
| 15532 * Called when all items need to be updated. | 16819 * Called when all items need to be updated. |
| 15533 * @param {!Array<!downloads.Data>} list A list of new download data. | 16820 * @param {!Array<!downloads.Data>} list A list of new download data. |
| 15534 * @private | 16821 * @private |
| 15535 */ | 16822 */ |
| 15536 updateAll_: function(list) { | 16823 updateAll_: function(list) { |
| 15537 var oldIdMap = this.idMap_ || {}; | 16824 /** @private {!Object<number>} */ |
| 16825 this.idToIndex_ = {}; |
| 15538 | 16826 |
| 15539 /** @private {!Object<!downloads.Item>} */ | 16827 var items = []; |
| 15540 this.idMap_ = {}; | |
| 15541 | |
| 15542 /** @private {!Array<!downloads.Item>} */ | |
| 15543 this.items_ = []; | |
| 15544 | |
| 15545 if (!this.iconLoader_) { | |
| 15546 var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1); | |
| 15547 /** @private {downloads.ThrottledIconLoader} */ | |
| 15548 this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate); | |
| 15549 } | |
| 15550 | 16828 |
| 15551 for (var i = 0; i < list.length; ++i) { | 16829 for (var i = 0; i < list.length; ++i) { |
| 15552 var data = list[i]; | 16830 var data = list[i]; |
| 15553 var id = data.id; | 16831 var id = data.id; |
| 15554 | 16832 |
| 15555 // Re-use old items when possible (saves work, preserves focus). | 16833 this.idToIndex_[id] = i; |
| 15556 var item = oldIdMap[id] || new downloads.Item(this.iconLoader_); | |
| 15557 | 16834 |
| 15558 this.idMap_[id] = item; // Associated by ID for fast lookup. | 16835 var prev = list[i - 1]; |
| 15559 this.items_.push(item); // Add to sorted list for order. | |
| 15560 | 16836 |
| 15561 // Render |item| but don't actually add to the DOM yet. |this.items_| | 16837 items.push({ |
| 15562 // must be fully created to be able to find the right spot to insert. | 16838 index: i, |
| 15563 item.update(data); | 16839 item: data, |
| 15564 | 16840 hideDate: !!prev && prev.date_string == data.date_string, |
| 15565 // Collapse redundant dates. | 16841 }); |
| 15566 var prev = list[i - 1]; | |
| 15567 item.hideDate = !!prev && prev.date_string == data.date_string; | |
| 15568 | |
| 15569 delete oldIdMap[id]; | |
| 15570 } | 16842 } |
| 15571 | 16843 |
| 15572 // Remove stale, previously rendered items from the DOM. | 16844 // TODO(dbeam): this is a huge hack. Let's figure out a better way. |
| 15573 for (var id in oldIdMap) { | 16845 var resetScrollPosition = this.$['downloads-list']._resetScrollPosition; |
| 15574 if (oldIdMap[id].parentNode) | 16846 this.$['downloads-list']._resetScrollPosition = function() {}; |
| 15575 oldIdMap[id].parentNode.removeChild(oldIdMap[id]); | 16847 this.items_ = items; |
| 15576 delete oldIdMap[id]; | 16848 this.$['downloads-list']._resetScrollPosition = resetScrollPosition; |
| 15577 } | |
| 15578 | |
| 15579 for (var i = 0; i < this.items_.length; ++i) { | |
| 15580 var item = this.items_[i]; | |
| 15581 if (item.parentNode) // Already in the DOM; skip. | |
| 15582 continue; | |
| 15583 | |
| 15584 var before = null; | |
| 15585 // Find the next rendered item after this one, and insert before it. | |
| 15586 for (var j = i + 1; !before && j < this.items_.length; ++j) { | |
| 15587 if (this.items_[j].parentNode) | |
| 15588 before = this.items_[j]; | |
| 15589 } | |
| 15590 // If |before| is null, |item| will just get added at the end. | |
| 15591 this.$['downloads-list'].insertBefore(item, before); | |
| 15592 } | |
| 15593 | 16849 |
| 15594 var hasDownloads = this.size_() > 0; | 16850 var hasDownloads = this.size_() > 0; |
| 15595 if (!hasDownloads) { | 16851 if (!hasDownloads) { |
| 15596 var isSearching = downloads.ActionService.getInstance().isSearching(); | 16852 var isSearching = downloads.ActionService.getInstance().isSearching(); |
| 15597 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; | 16853 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; |
| 15598 this.$['no-downloads'].querySelector('span').textContent = | 16854 this.$['no-downloads'].querySelector('span').textContent = |
| 15599 loadTimeData.getString(messageToShow); | 16855 loadTimeData.getString(messageToShow); |
| 15600 } | 16856 } |
| 15601 this.hasDownloads_ = hasDownloads; | 16857 this.hasDownloads_ = hasDownloads; |
| 15602 | 16858 |
| 15603 if (loadTimeData.getBoolean('allowDeletingHistory')) | 16859 if (loadTimeData.getBoolean('allowDeletingHistory')) |
| 15604 this.$.toolbar.downloadsShowing = this.hasDownloads_; | 16860 this.$.toolbar.downloadsShowing = this.hasDownloads_; |
| 15605 | 16861 |
| 15606 this.$.panel.classList.remove('loading'); | 16862 this.$.panel.classList.remove('loading'); |
| 15607 | |
| 15608 var allReady = this.items_.map(function(i) { return i.readyPromise; }); | |
| 15609 Promise.all(allReady).then(this.rebuildFocusGrid_.bind(this)); | |
| 15610 }, | 16863 }, |
| 15611 | 16864 |
| 15612 /** | 16865 /** |
| 15613 * @param {!downloads.Data} data | 16866 * @param {!downloads.Data} data |
| 15614 * @private | 16867 * @private |
| 15615 */ | 16868 */ |
| 15616 updateItem_: function(data) { | 16869 updateItem_: function(data) { |
| 15617 var item = this.idMap_[data.id]; | 16870 var index = this.idToIndex_[data.id]; |
| 15618 | 16871 this.set('items_.' + index + '.item', data); |
| 15619 var activeControl = this.shadowRoot.activeElement == item ? | 16872 this.$['downloads-list'].updateSizeForItem(index); |
| 15620 item.shadowRoot.activeElement : null; | |
| 15621 | |
| 15622 item.update(data); | |
| 15623 | |
| 15624 this.async(function() { | |
| 15625 if (activeControl && !cr.ui.FocusRow.isFocusable(activeControl)) { | |
| 15626 var focusRow = this.focusGrid_.getRowForRoot(item.content); | |
| 15627 focusRow.getEquivalentElement(activeControl).focus(); | |
| 15628 } | |
| 15629 }.bind(this)); | |
| 15630 }, | 16873 }, |
| 15631 }); | 16874 }); |
| 15632 | 16875 |
| 15633 Manager.size = function() { | 16876 Manager.size = function() { |
| 15634 return document.querySelector('downloads-manager').size_(); | 16877 return document.querySelector('downloads-manager').size_(); |
| 15635 }; | 16878 }; |
| 15636 | 16879 |
| 15637 Manager.updateAll = function(list) { | 16880 Manager.updateAll = function(list) { |
| 15638 document.querySelector('downloads-manager').updateAll_(list); | 16881 document.querySelector('downloads-manager').updateAll_(list); |
| 15639 }; | 16882 }; |
| 15640 | 16883 |
| 15641 Manager.updateItem = function(item) { | 16884 Manager.updateItem = function(item) { |
| 15642 document.querySelector('downloads-manager').updateItem_(item); | 16885 document.querySelector('downloads-manager').updateItem_(item); |
| 15643 }; | 16886 }; |
| 15644 | 16887 |
| 15645 Manager.onLoad = function() { | 16888 Manager.onLoad = function() { |
| 15646 document.querySelector('downloads-manager').onLoad_(); | 16889 document.querySelector('downloads-manager').onLoad_(); |
| 15647 }; | 16890 }; |
| 15648 | 16891 |
| 15649 return {Manager: Manager}; | 16892 return {Manager: Manager}; |
| 15650 }); | 16893 }); |
| 15651 | 16894 |
| 15652 window.addEventListener('load', downloads.Manager.onLoad); | 16895 window.addEventListener('load', downloads.Manager.onLoad); |
| OLD | NEW |