| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 function PromiseResolver() { | 4 function PromiseResolver() { |
| 5 this.resolve_; | 5 this.resolve_; |
| 6 this.reject_; | 6 this.reject_; |
| 7 this.promise_ = new Promise(function(resolve, reject) { | 7 this.promise_ = new Promise(function(resolve, reject) { |
| 8 this.resolve_ = resolve; | 8 this.resolve_ = resolve; |
| 9 this.reject_ = reject; | 9 this.reject_ = reject; |
| 10 }.bind(this)); | 10 }.bind(this)); |
| (...skipping 3845 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3856 if (cl.contains('expand')) { | 3856 if (cl.contains('expand')) { |
| 3857 cl.remove('expand'); | 3857 cl.remove('expand'); |
| 3858 cl.add('contract'); | 3858 cl.add('contract'); |
| 3859 this._positionBar(this._pos.width, this._pos.left); | 3859 this._positionBar(this._pos.width, this._pos.left); |
| 3860 } else if (cl.contains('contract')) { | 3860 } else if (cl.contains('contract')) { |
| 3861 cl.remove('contract'); | 3861 cl.remove('contract'); |
| 3862 } | 3862 } |
| 3863 } | 3863 } |
| 3864 }); | 3864 }); |
| 3865 | 3865 |
| 3866 Polymer.NeonAnimatableBehavior = { |
| 3867 properties: { |
| 3868 animationConfig: { |
| 3869 type: Object |
| 3870 }, |
| 3871 entryAnimation: { |
| 3872 observer: '_entryAnimationChanged', |
| 3873 type: String |
| 3874 }, |
| 3875 exitAnimation: { |
| 3876 observer: '_exitAnimationChanged', |
| 3877 type: String |
| 3878 } |
| 3879 }, |
| 3880 _entryAnimationChanged: function() { |
| 3881 this.animationConfig = this.animationConfig || {}; |
| 3882 this.animationConfig['entry'] = [ { |
| 3883 name: this.entryAnimation, |
| 3884 node: this |
| 3885 } ]; |
| 3886 }, |
| 3887 _exitAnimationChanged: function() { |
| 3888 this.animationConfig = this.animationConfig || {}; |
| 3889 this.animationConfig['exit'] = [ { |
| 3890 name: this.exitAnimation, |
| 3891 node: this |
| 3892 } ]; |
| 3893 }, |
| 3894 _copyProperties: function(config1, config2) { |
| 3895 for (var property in config2) { |
| 3896 config1[property] = config2[property]; |
| 3897 } |
| 3898 }, |
| 3899 _cloneConfig: function(config) { |
| 3900 var clone = { |
| 3901 isClone: true |
| 3902 }; |
| 3903 this._copyProperties(clone, config); |
| 3904 return clone; |
| 3905 }, |
| 3906 _getAnimationConfigRecursive: function(type, map, allConfigs) { |
| 3907 if (!this.animationConfig) { |
| 3908 return; |
| 3909 } |
| 3910 if (this.animationConfig.value && typeof this.animationConfig.value === 'fun
ction') { |
| 3911 this._warn(this._logf('playAnimation', "Please put 'animationConfig' insid
e of your components 'properties' object instead of outside of it.")); |
| 3912 return; |
| 3913 } |
| 3914 var thisConfig; |
| 3915 if (type) { |
| 3916 thisConfig = this.animationConfig[type]; |
| 3917 } else { |
| 3918 thisConfig = this.animationConfig; |
| 3919 } |
| 3920 if (!Array.isArray(thisConfig)) { |
| 3921 thisConfig = [ thisConfig ]; |
| 3922 } |
| 3923 if (thisConfig) { |
| 3924 for (var config, index = 0; config = thisConfig[index]; index++) { |
| 3925 if (config.animatable) { |
| 3926 config.animatable._getAnimationConfigRecursive(config.type || type, ma
p, allConfigs); |
| 3927 } else { |
| 3928 if (config.id) { |
| 3929 var cachedConfig = map[config.id]; |
| 3930 if (cachedConfig) { |
| 3931 if (!cachedConfig.isClone) { |
| 3932 map[config.id] = this._cloneConfig(cachedConfig); |
| 3933 cachedConfig = map[config.id]; |
| 3934 } |
| 3935 this._copyProperties(cachedConfig, config); |
| 3936 } else { |
| 3937 map[config.id] = config; |
| 3938 } |
| 3939 } else { |
| 3940 allConfigs.push(config); |
| 3941 } |
| 3942 } |
| 3943 } |
| 3944 } |
| 3945 }, |
| 3946 getAnimationConfig: function(type) { |
| 3947 var map = {}; |
| 3948 var allConfigs = []; |
| 3949 this._getAnimationConfigRecursive(type, map, allConfigs); |
| 3950 for (var key in map) { |
| 3951 allConfigs.push(map[key]); |
| 3952 } |
| 3953 return allConfigs; |
| 3954 } |
| 3955 }; |
| 3956 |
| 3957 Polymer.NeonAnimationRunnerBehaviorImpl = { |
| 3958 _configureAnimations: function(configs) { |
| 3959 var results = []; |
| 3960 if (configs.length > 0) { |
| 3961 for (var config, index = 0; config = configs[index]; index++) { |
| 3962 var neonAnimation = document.createElement(config.name); |
| 3963 if (neonAnimation.isNeonAnimation) { |
| 3964 var result = null; |
| 3965 try { |
| 3966 result = neonAnimation.configure(config); |
| 3967 if (typeof result.cancel != 'function') { |
| 3968 result = document.timeline.play(result); |
| 3969 } |
| 3970 } catch (e) { |
| 3971 result = null; |
| 3972 console.warn('Couldnt play', '(', config.name, ').', e); |
| 3973 } |
| 3974 if (result) { |
| 3975 results.push({ |
| 3976 neonAnimation: neonAnimation, |
| 3977 config: config, |
| 3978 animation: result |
| 3979 }); |
| 3980 } |
| 3981 } else { |
| 3982 console.warn(this.is + ':', config.name, 'not found!'); |
| 3983 } |
| 3984 } |
| 3985 } |
| 3986 return results; |
| 3987 }, |
| 3988 _shouldComplete: function(activeEntries) { |
| 3989 var finished = true; |
| 3990 for (var i = 0; i < activeEntries.length; i++) { |
| 3991 if (activeEntries[i].animation.playState != 'finished') { |
| 3992 finished = false; |
| 3993 break; |
| 3994 } |
| 3995 } |
| 3996 return finished; |
| 3997 }, |
| 3998 _complete: function(activeEntries) { |
| 3999 for (var i = 0; i < activeEntries.length; i++) { |
| 4000 activeEntries[i].neonAnimation.complete(activeEntries[i].config); |
| 4001 } |
| 4002 for (var i = 0; i < activeEntries.length; i++) { |
| 4003 activeEntries[i].animation.cancel(); |
| 4004 } |
| 4005 }, |
| 4006 playAnimation: function(type, cookie) { |
| 4007 var configs = this.getAnimationConfig(type); |
| 4008 if (!configs) { |
| 4009 return; |
| 4010 } |
| 4011 this._active = this._active || {}; |
| 4012 if (this._active[type]) { |
| 4013 this._complete(this._active[type]); |
| 4014 delete this._active[type]; |
| 4015 } |
| 4016 var activeEntries = this._configureAnimations(configs); |
| 4017 if (activeEntries.length == 0) { |
| 4018 this.fire('neon-animation-finish', cookie, { |
| 4019 bubbles: false |
| 4020 }); |
| 4021 return; |
| 4022 } |
| 4023 this._active[type] = activeEntries; |
| 4024 for (var i = 0; i < activeEntries.length; i++) { |
| 4025 activeEntries[i].animation.onfinish = function() { |
| 4026 if (this._shouldComplete(activeEntries)) { |
| 4027 this._complete(activeEntries); |
| 4028 delete this._active[type]; |
| 4029 this.fire('neon-animation-finish', cookie, { |
| 4030 bubbles: false |
| 4031 }); |
| 4032 } |
| 4033 }.bind(this); |
| 4034 } |
| 4035 }, |
| 4036 cancelAnimation: function() { |
| 4037 for (var k in this._animations) { |
| 4038 this._animations[k].cancel(); |
| 4039 } |
| 4040 this._animations = {}; |
| 4041 } |
| 4042 }; |
| 4043 |
| 4044 Polymer.NeonAnimationRunnerBehavior = [ Polymer.NeonAnimatableBehavior, Polymer.
NeonAnimationRunnerBehaviorImpl ]; |
| 4045 |
| 4046 Polymer.NeonAnimationBehavior = { |
| 4047 properties: { |
| 4048 animationTiming: { |
| 4049 type: Object, |
| 4050 value: function() { |
| 4051 return { |
| 4052 duration: 500, |
| 4053 easing: 'cubic-bezier(0.4, 0, 0.2, 1)', |
| 4054 fill: 'both' |
| 4055 }; |
| 4056 } |
| 4057 } |
| 4058 }, |
| 4059 isNeonAnimation: true, |
| 4060 timingFromConfig: function(config) { |
| 4061 if (config.timing) { |
| 4062 for (var property in config.timing) { |
| 4063 this.animationTiming[property] = config.timing[property]; |
| 4064 } |
| 4065 } |
| 4066 return this.animationTiming; |
| 4067 }, |
| 4068 setPrefixedProperty: function(node, property, value) { |
| 4069 var map = { |
| 4070 transform: [ 'webkitTransform' ], |
| 4071 transformOrigin: [ 'mozTransformOrigin', 'webkitTransformOrigin' ] |
| 4072 }; |
| 4073 var prefixes = map[property]; |
| 4074 for (var prefix, index = 0; prefix = prefixes[index]; index++) { |
| 4075 node.style[prefix] = value; |
| 4076 } |
| 4077 node.style[property] = value; |
| 4078 }, |
| 4079 complete: function() {} |
| 4080 }; |
| 4081 |
| 4082 Polymer({ |
| 4083 is: 'fade-in-animation', |
| 4084 behaviors: [ Polymer.NeonAnimationBehavior ], |
| 4085 configure: function(config) { |
| 4086 var node = config.node; |
| 4087 this._effect = new KeyframeEffect(node, [ { |
| 4088 opacity: '0' |
| 4089 }, { |
| 4090 opacity: '1' |
| 4091 } ], this.timingFromConfig(config)); |
| 4092 return this._effect; |
| 4093 } |
| 4094 }); |
| 4095 |
| 4096 Polymer({ |
| 4097 is: 'fade-out-animation', |
| 4098 behaviors: [ Polymer.NeonAnimationBehavior ], |
| 4099 configure: function(config) { |
| 4100 var node = config.node; |
| 4101 this._effect = new KeyframeEffect(node, [ { |
| 4102 opacity: '1' |
| 4103 }, { |
| 4104 opacity: '0' |
| 4105 } ], this.timingFromConfig(config)); |
| 4106 return this._effect; |
| 4107 } |
| 4108 }); |
| 4109 |
| 4110 Polymer({ |
| 4111 is: 'paper-tooltip', |
| 4112 hostAttributes: { |
| 4113 role: 'tooltip', |
| 4114 tabindex: -1 |
| 4115 }, |
| 4116 behaviors: [ Polymer.NeonAnimationRunnerBehavior ], |
| 4117 properties: { |
| 4118 "for": { |
| 4119 type: String, |
| 4120 observer: '_forChanged' |
| 4121 }, |
| 4122 manualMode: { |
| 4123 type: Boolean, |
| 4124 value: false |
| 4125 }, |
| 4126 position: { |
| 4127 type: String, |
| 4128 value: 'bottom' |
| 4129 }, |
| 4130 fitToVisibleBounds: { |
| 4131 type: Boolean, |
| 4132 value: false |
| 4133 }, |
| 4134 offset: { |
| 4135 type: Number, |
| 4136 value: 14 |
| 4137 }, |
| 4138 marginTop: { |
| 4139 type: Number, |
| 4140 value: 14 |
| 4141 }, |
| 4142 animationDelay: { |
| 4143 type: Number, |
| 4144 value: 500 |
| 4145 }, |
| 4146 animationConfig: { |
| 4147 type: Object, |
| 4148 value: function() { |
| 4149 return { |
| 4150 entry: [ { |
| 4151 name: 'fade-in-animation', |
| 4152 node: this, |
| 4153 timing: { |
| 4154 delay: 0 |
| 4155 } |
| 4156 } ], |
| 4157 exit: [ { |
| 4158 name: 'fade-out-animation', |
| 4159 node: this |
| 4160 } ] |
| 4161 }; |
| 4162 } |
| 4163 }, |
| 4164 _showing: { |
| 4165 type: Boolean, |
| 4166 value: false |
| 4167 } |
| 4168 }, |
| 4169 listeners: { |
| 4170 'neon-animation-finish': '_onAnimationFinish' |
| 4171 }, |
| 4172 get target() { |
| 4173 var parentNode = Polymer.dom(this).parentNode; |
| 4174 var ownerRoot = Polymer.dom(this).getOwnerRoot(); |
| 4175 var target; |
| 4176 if (this.for) { |
| 4177 target = Polymer.dom(ownerRoot).querySelector('#' + this.for); |
| 4178 } else { |
| 4179 target = parentNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE ? ownerRoot.ho
st : parentNode; |
| 4180 } |
| 4181 return target; |
| 4182 }, |
| 4183 attached: function() { |
| 4184 this._target = this.target; |
| 4185 if (this.manualMode) return; |
| 4186 this.listen(this._target, 'mouseenter', 'show'); |
| 4187 this.listen(this._target, 'focus', 'show'); |
| 4188 this.listen(this._target, 'mouseleave', 'hide'); |
| 4189 this.listen(this._target, 'blur', 'hide'); |
| 4190 this.listen(this._target, 'tap', 'hide'); |
| 4191 this.listen(this, 'mouseenter', 'hide'); |
| 4192 }, |
| 4193 detached: function() { |
| 4194 if (this._target && !this.manualMode) { |
| 4195 this.unlisten(this._target, 'mouseenter', 'show'); |
| 4196 this.unlisten(this._target, 'focus', 'show'); |
| 4197 this.unlisten(this._target, 'mouseleave', 'hide'); |
| 4198 this.unlisten(this._target, 'blur', 'hide'); |
| 4199 this.unlisten(this._target, 'tap', 'hide'); |
| 4200 this.unlisten(this, 'mouseenter', 'hide'); |
| 4201 } |
| 4202 }, |
| 4203 show: function() { |
| 4204 if (this._showing) return; |
| 4205 if (Polymer.dom(this).textContent.trim() === '') return; |
| 4206 this.cancelAnimation(); |
| 4207 this._showing = true; |
| 4208 this.toggleClass('hidden', false, this.$.tooltip); |
| 4209 this.updatePosition(); |
| 4210 this.animationConfig.entry[0].timing.delay = this.animationDelay; |
| 4211 this._animationPlaying = true; |
| 4212 this.playAnimation('entry'); |
| 4213 }, |
| 4214 hide: function() { |
| 4215 if (!this._showing) { |
| 4216 return; |
| 4217 } |
| 4218 if (this._animationPlaying) { |
| 4219 this.cancelAnimation(); |
| 4220 this._showing = false; |
| 4221 this._onAnimationFinish(); |
| 4222 return; |
| 4223 } |
| 4224 this._showing = false; |
| 4225 this._animationPlaying = true; |
| 4226 this.playAnimation('exit'); |
| 4227 }, |
| 4228 _forChanged: function() { |
| 4229 this._target = this.target; |
| 4230 }, |
| 4231 updatePosition: function() { |
| 4232 if (!this._target || !this.offsetParent) return; |
| 4233 var offset = this.offset; |
| 4234 if (this.marginTop != 14 && this.offset == 14) offset = this.marginTop; |
| 4235 var parentRect = this.offsetParent.getBoundingClientRect(); |
| 4236 var targetRect = this._target.getBoundingClientRect(); |
| 4237 var thisRect = this.getBoundingClientRect(); |
| 4238 var horizontalCenterOffset = (targetRect.width - thisRect.width) / 2; |
| 4239 var verticalCenterOffset = (targetRect.height - thisRect.height) / 2; |
| 4240 var targetLeft = targetRect.left - parentRect.left; |
| 4241 var targetTop = targetRect.top - parentRect.top; |
| 4242 var tooltipLeft, tooltipTop; |
| 4243 switch (this.position) { |
| 4244 case 'top': |
| 4245 tooltipLeft = targetLeft + horizontalCenterOffset; |
| 4246 tooltipTop = targetTop - thisRect.height - offset; |
| 4247 break; |
| 4248 |
| 4249 case 'bottom': |
| 4250 tooltipLeft = targetLeft + horizontalCenterOffset; |
| 4251 tooltipTop = targetTop + targetRect.height + offset; |
| 4252 break; |
| 4253 |
| 4254 case 'left': |
| 4255 tooltipLeft = targetLeft - thisRect.width - offset; |
| 4256 tooltipTop = targetTop + verticalCenterOffset; |
| 4257 break; |
| 4258 |
| 4259 case 'right': |
| 4260 tooltipLeft = targetLeft + targetRect.width + offset; |
| 4261 tooltipTop = targetTop + verticalCenterOffset; |
| 4262 break; |
| 4263 } |
| 4264 if (this.fitToVisibleBounds) { |
| 4265 if (tooltipLeft + thisRect.width > window.innerWidth) { |
| 4266 this.style.right = '0px'; |
| 4267 this.style.left = 'auto'; |
| 4268 } else { |
| 4269 this.style.left = Math.max(0, tooltipLeft) + 'px'; |
| 4270 this.style.right = 'auto'; |
| 4271 } |
| 4272 if (tooltipTop + thisRect.height > window.innerHeight) { |
| 4273 this.style.bottom = '0px'; |
| 4274 this.style.top = 'auto'; |
| 4275 } else { |
| 4276 this.style.top = Math.max(0, tooltipTop) + 'px'; |
| 4277 this.style.bottom = 'auto'; |
| 4278 } |
| 4279 } else { |
| 4280 this.style.left = tooltipLeft + 'px'; |
| 4281 this.style.top = tooltipTop + 'px'; |
| 4282 } |
| 4283 }, |
| 4284 _onAnimationFinish: function() { |
| 4285 this._animationPlaying = false; |
| 4286 if (!this._showing) { |
| 4287 this.toggleClass('hidden', true, this.$.tooltip); |
| 4288 } |
| 4289 } |
| 4290 }); |
| 4291 |
| 3866 (function() { | 4292 (function() { |
| 3867 'use strict'; | 4293 'use strict'; |
| 3868 Polymer.IronA11yAnnouncer = Polymer({ | 4294 Polymer.IronA11yAnnouncer = Polymer({ |
| 3869 is: 'iron-a11y-announcer', | 4295 is: 'iron-a11y-announcer', |
| 3870 properties: { | 4296 properties: { |
| 3871 mode: { | 4297 mode: { |
| 3872 type: String, | 4298 type: String, |
| 3873 value: 'polite' | 4299 value: 'polite' |
| 3874 }, | 4300 }, |
| 3875 _text: { | 4301 _text: { |
| (...skipping 614 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4490 // Copyright 2016 The Chromium Authors. All rights reserved. | 4916 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 4491 // Use of this source code is governed by a BSD-style license that can be | 4917 // Use of this source code is governed by a BSD-style license that can be |
| 4492 // found in the LICENSE file. | 4918 // found in the LICENSE file. |
| 4493 Polymer({ | 4919 Polymer({ |
| 4494 is: 'cr-toolbar', | 4920 is: 'cr-toolbar', |
| 4495 properties: { | 4921 properties: { |
| 4496 pageName: String, | 4922 pageName: String, |
| 4497 searchPrompt: String, | 4923 searchPrompt: String, |
| 4498 clearLabel: String, | 4924 clearLabel: String, |
| 4499 menuLabel: String, | 4925 menuLabel: String, |
| 4926 menuPromo: String, |
| 4500 spinnerActive: Boolean, | 4927 spinnerActive: Boolean, |
| 4501 showMenu: { | 4928 showMenu: Boolean, |
| 4502 type: Boolean, | 4929 showMenuPromo: Boolean, |
| 4503 value: false | |
| 4504 }, | |
| 4505 narrow_: { | 4930 narrow_: { |
| 4506 type: Boolean, | 4931 type: Boolean, |
| 4507 reflectToAttribute: true | 4932 reflectToAttribute: true |
| 4508 }, | 4933 }, |
| 4509 showingSearch_: { | 4934 showingSearch_: { |
| 4510 type: Boolean, | 4935 type: Boolean, |
| 4511 reflectToAttribute: true | 4936 reflectToAttribute: true |
| 4937 }, |
| 4938 showingMenuPromo_: { |
| 4939 type: Boolean, |
| 4940 computed: 'computeShowingMenuPromo_(showMenuPromo, narrow_)', |
| 4941 observer: 'showingMenuPromoChanged_' |
| 4512 } | 4942 } |
| 4513 }, | 4943 }, |
| 4514 getSearchField: function() { | 4944 getSearchField: function() { |
| 4515 return this.$.search; | 4945 return this.$.search; |
| 4516 }, | 4946 }, |
| 4517 onMenuTap_: function(e) { | 4947 computeShowingMenuPromo_: function(showMenuPromo, narrow) { |
| 4948 return showMenuPromo && narrow; |
| 4949 }, |
| 4950 onMenuTap_: function() { |
| 4518 this.fire('cr-menu-tap'); | 4951 this.fire('cr-menu-tap'); |
| 4952 this.onMenuPromoCloseTap_(); |
| 4953 }, |
| 4954 onMenuPromoCloseTap_: function() { |
| 4955 this.fire('cr-menu-promo-shown'); |
| 4956 }, |
| 4957 showingMenuPromoChanged_: function() { |
| 4958 Polymer.RenderStatus.afterNextRender(this, function() { |
| 4959 if (this.showingMenuPromo_) this.$$('paper-tooltip').show(); |
| 4960 }.bind(this)); |
| 4961 }, |
| 4962 titleIfNotShowingMenuPromo_: function(title, showingMenuPromo) { |
| 4963 return showingMenuPromo ? '' : title; |
| 4519 } | 4964 } |
| 4520 }); | 4965 }); |
| 4521 | 4966 |
| 4967 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 4968 // Use of this source code is governed by a BSD-style license that can be |
| 4969 // found in the LICENSE file. |
| 4970 cr.define('md_history', function() { |
| 4971 function BrowserService() { |
| 4972 this.pendingDeleteItems_ = null; |
| 4973 this.pendingDeletePromise_ = null; |
| 4974 } |
| 4975 BrowserService.prototype = { |
| 4976 deleteItems: function(items) { |
| 4977 if (this.pendingDeleteItems_ != null) { |
| 4978 return new Promise(function(resolve, reject) { |
| 4979 reject(items); |
| 4980 }); |
| 4981 } |
| 4982 var removalList = items.map(function(item) { |
| 4983 return { |
| 4984 url: item.url, |
| 4985 timestamps: item.allTimestamps |
| 4986 }; |
| 4987 }); |
| 4988 this.pendingDeleteItems_ = items; |
| 4989 this.pendingDeletePromise_ = new PromiseResolver(); |
| 4990 chrome.send('removeVisits', removalList); |
| 4991 return this.pendingDeletePromise_.promise; |
| 4992 }, |
| 4993 removeBookmark: function(url) { |
| 4994 chrome.send('removeBookmark', [ url ]); |
| 4995 }, |
| 4996 openForeignSessionAllTabs: function(sessionTag) { |
| 4997 chrome.send('openForeignSession', [ sessionTag ]); |
| 4998 }, |
| 4999 openForeignSessionTab: function(sessionTag, windowId, tabId, e) { |
| 5000 chrome.send('openForeignSession', [ sessionTag, String(windowId), String(t
abId), e.button || 0, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey ]); |
| 5001 }, |
| 5002 deleteForeignSession: function(sessionTag) { |
| 5003 chrome.send('deleteForeignSession', [ sessionTag ]); |
| 5004 }, |
| 5005 openClearBrowsingData: function() { |
| 5006 chrome.send('clearBrowsingData'); |
| 5007 }, |
| 5008 recordHistogram: function(histogram, value, max) { |
| 5009 chrome.send('metricsHandler:recordInHistogram', [ histogram, value, max ])
; |
| 5010 }, |
| 5011 recordAction: function(action) { |
| 5012 if (action.indexOf('_') == -1) action = 'HistoryPage_' + action; |
| 5013 chrome.send('metricsHandler:recordAction', [ action ]); |
| 5014 }, |
| 5015 resolveDelete_: function(successful) { |
| 5016 if (this.pendingDeleteItems_ == null || this.pendingDeletePromise_ == null
) { |
| 5017 return; |
| 5018 } |
| 5019 if (successful) this.pendingDeletePromise_.resolve(this.pendingDeleteItems
_); else this.pendingDeletePromise_.reject(this.pendingDeleteItems_); |
| 5020 this.pendingDeleteItems_ = null; |
| 5021 this.pendingDeletePromise_ = null; |
| 5022 }, |
| 5023 menuPromoShown: function() { |
| 5024 chrome.send('menuPromoShown'); |
| 5025 } |
| 5026 }; |
| 5027 cr.addSingletonGetter(BrowserService); |
| 5028 return { |
| 5029 BrowserService: BrowserService |
| 5030 }; |
| 5031 }); |
| 5032 |
| 5033 function deleteComplete() { |
| 5034 md_history.BrowserService.getInstance().resolveDelete_(true); |
| 5035 } |
| 5036 |
| 5037 function deleteFailed() { |
| 5038 md_history.BrowserService.getInstance().resolveDelete_(false); |
| 5039 } |
| 5040 |
| 4522 // Copyright 2015 The Chromium Authors. All rights reserved. | 5041 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 4523 // Use of this source code is governed by a BSD-style license that can be | 5042 // Use of this source code is governed by a BSD-style license that can be |
| 4524 // found in the LICENSE file. | 5043 // found in the LICENSE file. |
| 4525 Polymer({ | 5044 Polymer({ |
| 4526 is: 'history-toolbar', | 5045 is: 'history-toolbar', |
| 4527 properties: { | 5046 properties: { |
| 4528 count: { | 5047 count: { |
| 4529 type: Number, | 5048 type: Number, |
| 4530 value: 0, | 5049 value: 0, |
| 4531 observer: 'changeToolbarView_' | 5050 observer: 'changeToolbarView_' |
| (...skipping 20 matching lines...) Expand all Loading... |
| 4552 type: Boolean, | 5071 type: Boolean, |
| 4553 reflectToAttribute: true | 5072 reflectToAttribute: true |
| 4554 }, | 5073 }, |
| 4555 groupedRange: { | 5074 groupedRange: { |
| 4556 type: Number, | 5075 type: Number, |
| 4557 value: 0, | 5076 value: 0, |
| 4558 reflectToAttribute: true, | 5077 reflectToAttribute: true, |
| 4559 notify: true | 5078 notify: true |
| 4560 }, | 5079 }, |
| 4561 queryStartTime: String, | 5080 queryStartTime: String, |
| 4562 queryEndTime: String | 5081 queryEndTime: String, |
| 5082 showMenuPromo_: { |
| 5083 type: Boolean, |
| 5084 value: function() { |
| 5085 return loadTimeData.getBoolean('showMenuPromo'); |
| 5086 } |
| 5087 } |
| 4563 }, | 5088 }, |
| 4564 changeToolbarView_: function() { | 5089 changeToolbarView_: function() { |
| 4565 this.itemsSelected_ = this.count > 0; | 5090 this.itemsSelected_ = this.count > 0; |
| 4566 }, | 5091 }, |
| 4567 setSearchTerm: function(search) { | 5092 setSearchTerm: function(search) { |
| 4568 if (this.searchTerm == search) return; | 5093 if (this.searchTerm == search) return; |
| 4569 this.searchTerm = search; | 5094 this.searchTerm = search; |
| 4570 var searchField = this.$['main-toolbar'].getSearchField(); | 5095 var searchField = this.$['main-toolbar'].getSearchField(); |
| 4571 searchField.showAndFocus(); | 5096 searchField.showAndFocus(); |
| 4572 searchField.setValue(search); | 5097 searchField.setValue(search); |
| 4573 }, | 5098 }, |
| 5099 onMenuPromoShown_: function() { |
| 5100 this.showMenuPromo_ = false; |
| 5101 md_history.BrowserService.getInstance().menuPromoShown(); |
| 5102 }, |
| 4574 onSearchChanged_: function(event) { | 5103 onSearchChanged_: function(event) { |
| 4575 this.searchTerm = event.detail; | 5104 this.searchTerm = event.detail; |
| 4576 }, | 5105 }, |
| 4577 onClearSelectionTap_: function() { | 5106 onClearSelectionTap_: function() { |
| 4578 this.fire('unselect-all'); | 5107 this.fire('unselect-all'); |
| 4579 }, | 5108 }, |
| 4580 onDeleteTap_: function() { | 5109 onDeleteTap_: function() { |
| 4581 this.fire('delete-selected'); | 5110 this.fire('delete-selected'); |
| 4582 }, | 5111 }, |
| 4583 get searchBar() { | 5112 get searchBar() { |
| (...skipping 945 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5529 var self = this; | 6058 var self = this; |
| 5530 this.__raf = window.requestAnimationFrame(function nextAnimationFrame() { | 6059 this.__raf = window.requestAnimationFrame(function nextAnimationFrame() { |
| 5531 self.__raf = null; | 6060 self.__raf = null; |
| 5532 callback.call(self); | 6061 callback.call(self); |
| 5533 }); | 6062 }); |
| 5534 } | 6063 } |
| 5535 }; | 6064 }; |
| 5536 Polymer.IronOverlayBehavior = [ Polymer.IronFitBehavior, Polymer.IronResizable
Behavior, Polymer.IronOverlayBehaviorImpl ]; | 6065 Polymer.IronOverlayBehavior = [ Polymer.IronFitBehavior, Polymer.IronResizable
Behavior, Polymer.IronOverlayBehaviorImpl ]; |
| 5537 })(); | 6066 })(); |
| 5538 | 6067 |
| 5539 Polymer.NeonAnimatableBehavior = { | |
| 5540 properties: { | |
| 5541 animationConfig: { | |
| 5542 type: Object | |
| 5543 }, | |
| 5544 entryAnimation: { | |
| 5545 observer: '_entryAnimationChanged', | |
| 5546 type: String | |
| 5547 }, | |
| 5548 exitAnimation: { | |
| 5549 observer: '_exitAnimationChanged', | |
| 5550 type: String | |
| 5551 } | |
| 5552 }, | |
| 5553 _entryAnimationChanged: function() { | |
| 5554 this.animationConfig = this.animationConfig || {}; | |
| 5555 this.animationConfig['entry'] = [ { | |
| 5556 name: this.entryAnimation, | |
| 5557 node: this | |
| 5558 } ]; | |
| 5559 }, | |
| 5560 _exitAnimationChanged: function() { | |
| 5561 this.animationConfig = this.animationConfig || {}; | |
| 5562 this.animationConfig['exit'] = [ { | |
| 5563 name: this.exitAnimation, | |
| 5564 node: this | |
| 5565 } ]; | |
| 5566 }, | |
| 5567 _copyProperties: function(config1, config2) { | |
| 5568 for (var property in config2) { | |
| 5569 config1[property] = config2[property]; | |
| 5570 } | |
| 5571 }, | |
| 5572 _cloneConfig: function(config) { | |
| 5573 var clone = { | |
| 5574 isClone: true | |
| 5575 }; | |
| 5576 this._copyProperties(clone, config); | |
| 5577 return clone; | |
| 5578 }, | |
| 5579 _getAnimationConfigRecursive: function(type, map, allConfigs) { | |
| 5580 if (!this.animationConfig) { | |
| 5581 return; | |
| 5582 } | |
| 5583 if (this.animationConfig.value && typeof this.animationConfig.value === 'fun
ction') { | |
| 5584 this._warn(this._logf('playAnimation', "Please put 'animationConfig' insid
e of your components 'properties' object instead of outside of it.")); | |
| 5585 return; | |
| 5586 } | |
| 5587 var thisConfig; | |
| 5588 if (type) { | |
| 5589 thisConfig = this.animationConfig[type]; | |
| 5590 } else { | |
| 5591 thisConfig = this.animationConfig; | |
| 5592 } | |
| 5593 if (!Array.isArray(thisConfig)) { | |
| 5594 thisConfig = [ thisConfig ]; | |
| 5595 } | |
| 5596 if (thisConfig) { | |
| 5597 for (var config, index = 0; config = thisConfig[index]; index++) { | |
| 5598 if (config.animatable) { | |
| 5599 config.animatable._getAnimationConfigRecursive(config.type || type, ma
p, allConfigs); | |
| 5600 } else { | |
| 5601 if (config.id) { | |
| 5602 var cachedConfig = map[config.id]; | |
| 5603 if (cachedConfig) { | |
| 5604 if (!cachedConfig.isClone) { | |
| 5605 map[config.id] = this._cloneConfig(cachedConfig); | |
| 5606 cachedConfig = map[config.id]; | |
| 5607 } | |
| 5608 this._copyProperties(cachedConfig, config); | |
| 5609 } else { | |
| 5610 map[config.id] = config; | |
| 5611 } | |
| 5612 } else { | |
| 5613 allConfigs.push(config); | |
| 5614 } | |
| 5615 } | |
| 5616 } | |
| 5617 } | |
| 5618 }, | |
| 5619 getAnimationConfig: function(type) { | |
| 5620 var map = {}; | |
| 5621 var allConfigs = []; | |
| 5622 this._getAnimationConfigRecursive(type, map, allConfigs); | |
| 5623 for (var key in map) { | |
| 5624 allConfigs.push(map[key]); | |
| 5625 } | |
| 5626 return allConfigs; | |
| 5627 } | |
| 5628 }; | |
| 5629 | |
| 5630 Polymer.NeonAnimationRunnerBehaviorImpl = { | |
| 5631 _configureAnimations: function(configs) { | |
| 5632 var results = []; | |
| 5633 if (configs.length > 0) { | |
| 5634 for (var config, index = 0; config = configs[index]; index++) { | |
| 5635 var neonAnimation = document.createElement(config.name); | |
| 5636 if (neonAnimation.isNeonAnimation) { | |
| 5637 var result = null; | |
| 5638 try { | |
| 5639 result = neonAnimation.configure(config); | |
| 5640 if (typeof result.cancel != 'function') { | |
| 5641 result = document.timeline.play(result); | |
| 5642 } | |
| 5643 } catch (e) { | |
| 5644 result = null; | |
| 5645 console.warn('Couldnt play', '(', config.name, ').', e); | |
| 5646 } | |
| 5647 if (result) { | |
| 5648 results.push({ | |
| 5649 neonAnimation: neonAnimation, | |
| 5650 config: config, | |
| 5651 animation: result | |
| 5652 }); | |
| 5653 } | |
| 5654 } else { | |
| 5655 console.warn(this.is + ':', config.name, 'not found!'); | |
| 5656 } | |
| 5657 } | |
| 5658 } | |
| 5659 return results; | |
| 5660 }, | |
| 5661 _shouldComplete: function(activeEntries) { | |
| 5662 var finished = true; | |
| 5663 for (var i = 0; i < activeEntries.length; i++) { | |
| 5664 if (activeEntries[i].animation.playState != 'finished') { | |
| 5665 finished = false; | |
| 5666 break; | |
| 5667 } | |
| 5668 } | |
| 5669 return finished; | |
| 5670 }, | |
| 5671 _complete: function(activeEntries) { | |
| 5672 for (var i = 0; i < activeEntries.length; i++) { | |
| 5673 activeEntries[i].neonAnimation.complete(activeEntries[i].config); | |
| 5674 } | |
| 5675 for (var i = 0; i < activeEntries.length; i++) { | |
| 5676 activeEntries[i].animation.cancel(); | |
| 5677 } | |
| 5678 }, | |
| 5679 playAnimation: function(type, cookie) { | |
| 5680 var configs = this.getAnimationConfig(type); | |
| 5681 if (!configs) { | |
| 5682 return; | |
| 5683 } | |
| 5684 this._active = this._active || {}; | |
| 5685 if (this._active[type]) { | |
| 5686 this._complete(this._active[type]); | |
| 5687 delete this._active[type]; | |
| 5688 } | |
| 5689 var activeEntries = this._configureAnimations(configs); | |
| 5690 if (activeEntries.length == 0) { | |
| 5691 this.fire('neon-animation-finish', cookie, { | |
| 5692 bubbles: false | |
| 5693 }); | |
| 5694 return; | |
| 5695 } | |
| 5696 this._active[type] = activeEntries; | |
| 5697 for (var i = 0; i < activeEntries.length; i++) { | |
| 5698 activeEntries[i].animation.onfinish = function() { | |
| 5699 if (this._shouldComplete(activeEntries)) { | |
| 5700 this._complete(activeEntries); | |
| 5701 delete this._active[type]; | |
| 5702 this.fire('neon-animation-finish', cookie, { | |
| 5703 bubbles: false | |
| 5704 }); | |
| 5705 } | |
| 5706 }.bind(this); | |
| 5707 } | |
| 5708 }, | |
| 5709 cancelAnimation: function() { | |
| 5710 for (var k in this._animations) { | |
| 5711 this._animations[k].cancel(); | |
| 5712 } | |
| 5713 this._animations = {}; | |
| 5714 } | |
| 5715 }; | |
| 5716 | |
| 5717 Polymer.NeonAnimationRunnerBehavior = [ Polymer.NeonAnimatableBehavior, Polymer.
NeonAnimationRunnerBehaviorImpl ]; | |
| 5718 | |
| 5719 Polymer.NeonAnimationBehavior = { | |
| 5720 properties: { | |
| 5721 animationTiming: { | |
| 5722 type: Object, | |
| 5723 value: function() { | |
| 5724 return { | |
| 5725 duration: 500, | |
| 5726 easing: 'cubic-bezier(0.4, 0, 0.2, 1)', | |
| 5727 fill: 'both' | |
| 5728 }; | |
| 5729 } | |
| 5730 } | |
| 5731 }, | |
| 5732 isNeonAnimation: true, | |
| 5733 timingFromConfig: function(config) { | |
| 5734 if (config.timing) { | |
| 5735 for (var property in config.timing) { | |
| 5736 this.animationTiming[property] = config.timing[property]; | |
| 5737 } | |
| 5738 } | |
| 5739 return this.animationTiming; | |
| 5740 }, | |
| 5741 setPrefixedProperty: function(node, property, value) { | |
| 5742 var map = { | |
| 5743 transform: [ 'webkitTransform' ], | |
| 5744 transformOrigin: [ 'mozTransformOrigin', 'webkitTransformOrigin' ] | |
| 5745 }; | |
| 5746 var prefixes = map[property]; | |
| 5747 for (var prefix, index = 0; prefix = prefixes[index]; index++) { | |
| 5748 node.style[prefix] = value; | |
| 5749 } | |
| 5750 node.style[property] = value; | |
| 5751 }, | |
| 5752 complete: function() {} | |
| 5753 }; | |
| 5754 | |
| 5755 Polymer({ | 6068 Polymer({ |
| 5756 is: 'opaque-animation', | 6069 is: 'opaque-animation', |
| 5757 behaviors: [ Polymer.NeonAnimationBehavior ], | 6070 behaviors: [ Polymer.NeonAnimationBehavior ], |
| 5758 configure: function(config) { | 6071 configure: function(config) { |
| 5759 var node = config.node; | 6072 var node = config.node; |
| 5760 this._effect = new KeyframeEffect(node, [ { | 6073 this._effect = new KeyframeEffect(node, [ { |
| 5761 opacity: '1' | 6074 opacity: '1' |
| 5762 }, { | 6075 }, { |
| 5763 opacity: '1' | 6076 opacity: '1' |
| 5764 } ], this.timingFromConfig(config)); | 6077 } ], this.timingFromConfig(config)); |
| (...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6097 if (focusTarget && this.opened && !this.noAutoFocus) { | 6410 if (focusTarget && this.opened && !this.noAutoFocus) { |
| 6098 focusTarget.focus(); | 6411 focusTarget.focus(); |
| 6099 } else { | 6412 } else { |
| 6100 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); | 6413 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); |
| 6101 } | 6414 } |
| 6102 } | 6415 } |
| 6103 }); | 6416 }); |
| 6104 })(); | 6417 })(); |
| 6105 | 6418 |
| 6106 Polymer({ | 6419 Polymer({ |
| 6107 is: 'fade-in-animation', | |
| 6108 behaviors: [ Polymer.NeonAnimationBehavior ], | |
| 6109 configure: function(config) { | |
| 6110 var node = config.node; | |
| 6111 this._effect = new KeyframeEffect(node, [ { | |
| 6112 opacity: '0' | |
| 6113 }, { | |
| 6114 opacity: '1' | |
| 6115 } ], this.timingFromConfig(config)); | |
| 6116 return this._effect; | |
| 6117 } | |
| 6118 }); | |
| 6119 | |
| 6120 Polymer({ | |
| 6121 is: 'fade-out-animation', | |
| 6122 behaviors: [ Polymer.NeonAnimationBehavior ], | |
| 6123 configure: function(config) { | |
| 6124 var node = config.node; | |
| 6125 this._effect = new KeyframeEffect(node, [ { | |
| 6126 opacity: '1' | |
| 6127 }, { | |
| 6128 opacity: '0' | |
| 6129 } ], this.timingFromConfig(config)); | |
| 6130 return this._effect; | |
| 6131 } | |
| 6132 }); | |
| 6133 | |
| 6134 Polymer({ | |
| 6135 is: 'paper-menu-grow-height-animation', | 6420 is: 'paper-menu-grow-height-animation', |
| 6136 behaviors: [ Polymer.NeonAnimationBehavior ], | 6421 behaviors: [ Polymer.NeonAnimationBehavior ], |
| 6137 configure: function(config) { | 6422 configure: function(config) { |
| 6138 var node = config.node; | 6423 var node = config.node; |
| 6139 var rect = node.getBoundingClientRect(); | 6424 var rect = node.getBoundingClientRect(); |
| 6140 var height = rect.height; | 6425 var height = rect.height; |
| 6141 this._effect = new KeyframeEffect(node, [ { | 6426 this._effect = new KeyframeEffect(node, [ { |
| 6142 height: height / 2 + 'px' | 6427 height: height / 2 + 'px' |
| 6143 }, { | 6428 }, { |
| 6144 height: height + 'px' | 6429 height: height + 'px' |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6325 } | 6610 } |
| 6326 }; | 6611 }; |
| 6327 | 6612 |
| 6328 Polymer.PaperItemBehavior = [ Polymer.IronButtonState, Polymer.IronControlState,
Polymer.PaperItemBehaviorImpl ]; | 6613 Polymer.PaperItemBehavior = [ Polymer.IronButtonState, Polymer.IronControlState,
Polymer.PaperItemBehaviorImpl ]; |
| 6329 | 6614 |
| 6330 Polymer({ | 6615 Polymer({ |
| 6331 is: 'paper-item', | 6616 is: 'paper-item', |
| 6332 behaviors: [ Polymer.PaperItemBehavior ] | 6617 behaviors: [ Polymer.PaperItemBehavior ] |
| 6333 }); | 6618 }); |
| 6334 | 6619 |
| 6335 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 6336 // Use of this source code is governed by a BSD-style license that can be | |
| 6337 // found in the LICENSE file. | |
| 6338 cr.define('md_history', function() { | |
| 6339 function BrowserService() { | |
| 6340 this.pendingDeleteItems_ = null; | |
| 6341 this.pendingDeletePromise_ = null; | |
| 6342 } | |
| 6343 BrowserService.prototype = { | |
| 6344 deleteItems: function(items) { | |
| 6345 if (this.pendingDeleteItems_ != null) { | |
| 6346 return new Promise(function(resolve, reject) { | |
| 6347 reject(items); | |
| 6348 }); | |
| 6349 } | |
| 6350 var removalList = items.map(function(item) { | |
| 6351 return { | |
| 6352 url: item.url, | |
| 6353 timestamps: item.allTimestamps | |
| 6354 }; | |
| 6355 }); | |
| 6356 this.pendingDeleteItems_ = items; | |
| 6357 this.pendingDeletePromise_ = new PromiseResolver(); | |
| 6358 chrome.send('removeVisits', removalList); | |
| 6359 return this.pendingDeletePromise_.promise; | |
| 6360 }, | |
| 6361 removeBookmark: function(url) { | |
| 6362 chrome.send('removeBookmark', [ url ]); | |
| 6363 }, | |
| 6364 openForeignSessionAllTabs: function(sessionTag) { | |
| 6365 chrome.send('openForeignSession', [ sessionTag ]); | |
| 6366 }, | |
| 6367 openForeignSessionTab: function(sessionTag, windowId, tabId, e) { | |
| 6368 chrome.send('openForeignSession', [ sessionTag, String(windowId), String(t
abId), e.button || 0, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey ]); | |
| 6369 }, | |
| 6370 deleteForeignSession: function(sessionTag) { | |
| 6371 chrome.send('deleteForeignSession', [ sessionTag ]); | |
| 6372 }, | |
| 6373 openClearBrowsingData: function() { | |
| 6374 chrome.send('clearBrowsingData'); | |
| 6375 }, | |
| 6376 recordHistogram: function(histogram, value, max) { | |
| 6377 chrome.send('metricsHandler:recordInHistogram', [ histogram, value, max ])
; | |
| 6378 }, | |
| 6379 recordAction: function(action) { | |
| 6380 if (action.indexOf('_') == -1) action = 'HistoryPage_' + action; | |
| 6381 chrome.send('metricsHandler:recordAction', [ action ]); | |
| 6382 }, | |
| 6383 resolveDelete_: function(successful) { | |
| 6384 if (this.pendingDeleteItems_ == null || this.pendingDeletePromise_ == null
) { | |
| 6385 return; | |
| 6386 } | |
| 6387 if (successful) this.pendingDeletePromise_.resolve(this.pendingDeleteItems
_); else this.pendingDeletePromise_.reject(this.pendingDeleteItems_); | |
| 6388 this.pendingDeleteItems_ = null; | |
| 6389 this.pendingDeletePromise_ = null; | |
| 6390 } | |
| 6391 }; | |
| 6392 cr.addSingletonGetter(BrowserService); | |
| 6393 return { | |
| 6394 BrowserService: BrowserService | |
| 6395 }; | |
| 6396 }); | |
| 6397 | |
| 6398 function deleteComplete() { | |
| 6399 md_history.BrowserService.getInstance().resolveDelete_(true); | |
| 6400 } | |
| 6401 | |
| 6402 function deleteFailed() { | |
| 6403 md_history.BrowserService.getInstance().resolveDelete_(false); | |
| 6404 } | |
| 6405 | |
| 6406 Polymer({ | 6620 Polymer({ |
| 6407 is: 'iron-collapse', | 6621 is: 'iron-collapse', |
| 6408 behaviors: [ Polymer.IronResizableBehavior ], | 6622 behaviors: [ Polymer.IronResizableBehavior ], |
| 6409 properties: { | 6623 properties: { |
| 6410 horizontal: { | 6624 horizontal: { |
| 6411 type: Boolean, | 6625 type: Boolean, |
| 6412 value: false, | 6626 value: false, |
| 6413 observer: '_horizontalChanged' | 6627 observer: '_horizontalChanged' |
| 6414 }, | 6628 }, |
| 6415 opened: { | 6629 opened: { |
| (...skipping 2370 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8786 | 9000 |
| 8787 case HistoryRange.MONTH: | 9001 case HistoryRange.MONTH: |
| 8788 histogramValue = HistoryPageViewHistogram.GROUPED_MONTH; | 9002 histogramValue = HistoryPageViewHistogram.GROUPED_MONTH; |
| 8789 break; | 9003 break; |
| 8790 } | 9004 } |
| 8791 break; | 9005 break; |
| 8792 } | 9006 } |
| 8793 md_history.BrowserService.getInstance().recordHistogram('History.HistoryPage
View', histogramValue, HistoryPageViewHistogram.END); | 9007 md_history.BrowserService.getInstance().recordHistogram('History.HistoryPage
View', histogramValue, HistoryPageViewHistogram.END); |
| 8794 } | 9008 } |
| 8795 }); | 9009 }); |
| OLD | NEW |