1<!-- 2@license 3Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 4This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7Code distributed by Google as part of the polymer project is also 8subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9--> 10 11<link rel="import" href="../polymer/polymer.html"> 12 13<script> 14 /** 15 * `IronResizableBehavior` is a behavior that can be used in Polymer elements to 16 * coordinate the flow of resize events between "resizers" (elements that control the 17 * size or hidden state of their children) and "resizables" (elements that need to be 18 * notified when they are resized or un-hidden by their parents in order to take 19 * action on their new measurements). 20 * 21 * Elements that perform measurement should add the `IronResizableBehavior` behavior to 22 * their element definition and listen for the `iron-resize` event on themselves. 23 * This event will be fired when they become showing after having been hidden, 24 * when they are resized explicitly by another resizable, or when the window has been 25 * resized. 26 * 27 * Note, the `iron-resize` event is non-bubbling. 28 * 29 * @polymerBehavior Polymer.IronResizableBehavior 30 * @demo demo/index.html 31 **/ 32 Polymer.IronResizableBehavior = { 33 properties: { 34 /** 35 * The closest ancestor element that implements `IronResizableBehavior`. 36 */ 37 _parentResizable: { 38 type: Object, 39 observer: '_parentResizableChanged' 40 }, 41 42 /** 43 * True if this element is currently notifying its descendant elements of 44 * resize. 45 */ 46 _notifyingDescendant: { 47 type: Boolean, 48 value: false 49 } 50 }, 51 52 listeners: { 53 'iron-request-resize-notifications': '_onIronRequestResizeNotifications' 54 }, 55 56 created: function() { 57 // We don't really need property effects on these, and also we want them 58 // to be created before the `_parentResizable` observer fires: 59 this._interestedResizables = []; 60 this._boundNotifyResize = this.notifyResize.bind(this); 61 }, 62 63 attached: function() { 64 this.fire('iron-request-resize-notifications', null, { 65 node: this, 66 bubbles: true, 67 cancelable: true 68 }); 69 70 if (!this._parentResizable) { 71 window.addEventListener('resize', this._boundNotifyResize); 72 this.notifyResize(); 73 } 74 }, 75 76 detached: function() { 77 if (this._parentResizable) { 78 this._parentResizable.stopResizeNotificationsFor(this); 79 } else { 80 window.removeEventListener('resize', this._boundNotifyResize); 81 } 82 83 this._parentResizable = null; 84 }, 85 86 /** 87 * Can be called to manually notify a resizable and its descendant 88 * resizables of a resize change. 89 */ 90 notifyResize: function() { 91 if (!this.isAttached) { 92 return; 93 } 94 95 this._interestedResizables.forEach(function(resizable) { 96 if (this.resizerShouldNotify(resizable)) { 97 this._notifyDescendant(resizable); 98 } 99 }, this); 100 101 this._fireResize(); 102 }, 103 104 /** 105 * Used to assign the closest resizable ancestor to this resizable 106 * if the ancestor detects a request for notifications. 107 */ 108 assignParentResizable: function(parentResizable) { 109 this._parentResizable = parentResizable; 110 }, 111 112 /** 113 * Used to remove a resizable descendant from the list of descendants 114 * that should be notified of a resize change. 115 */ 116 stopResizeNotificationsFor: function(target) { 117 var index = this._interestedResizables.indexOf(target); 118 119 if (index > -1) { 120 this._interestedResizables.splice(index, 1); 121 this.unlisten(target, 'iron-resize', '_onDescendantIronResize'); 122 } 123 }, 124 125 /** 126 * This method can be overridden to filter nested elements that should or 127 * should not be notified by the current element. Return true if an element 128 * should be notified, or false if it should not be notified. 129 * 130 * @param {HTMLElement} element A candidate descendant element that 131 * implements `IronResizableBehavior`. 132 * @return {boolean} True if the `element` should be notified of resize. 133 */ 134 resizerShouldNotify: function(element) { return true; }, 135 136 _onDescendantIronResize: function(event) { 137 if (this._notifyingDescendant) { 138 event.stopPropagation(); 139 return; 140 } 141 142 // NOTE(cdata): In ShadowDOM, event retargeting makes echoing of the 143 // otherwise non-bubbling event "just work." We do it manually here for 144 // the case where Polymer is not using shadow roots for whatever reason: 145 if (!Polymer.Settings.useShadow) { 146 this._fireResize(); 147 } 148 }, 149 150 _fireResize: function() { 151 this.fire('iron-resize', null, { 152 node: this, 153 bubbles: false 154 }); 155 }, 156 157 _onIronRequestResizeNotifications: function(event) { 158 var target = event.path ? event.path[0] : event.target; 159 160 if (target === this) { 161 return; 162 } 163 164 if (this._interestedResizables.indexOf(target) === -1) { 165 this._interestedResizables.push(target); 166 this.listen(target, 'iron-resize', '_onDescendantIronResize'); 167 } 168 169 target.assignParentResizable(this); 170 this._notifyDescendant(target); 171 172 event.stopPropagation(); 173 }, 174 175 _parentResizableChanged: function(parentResizable) { 176 if (parentResizable) { 177 window.removeEventListener('resize', this._boundNotifyResize); 178 } 179 }, 180 181 _notifyDescendant: function(descendant) { 182 // NOTE(cdata): In IE10, attached is fired on children first, so it's 183 // important not to notify them if the parent is not attached yet (or 184 // else they will get redundantly notified when the parent attaches). 185 if (!this.isAttached) { 186 return; 187 } 188 189 this._notifyingDescendant = true; 190 descendant.notifyResize(); 191 this._notifyingDescendant = false; 192 } 193 }; 194</script> 195 196