Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
michael@0 | 1 | <?xml version="1.0"?> |
michael@0 | 2 | <!-- This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | - License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> |
michael@0 | 5 | |
michael@0 | 6 | <bindings id="browserPanelUIBindings" |
michael@0 | 7 | xmlns="http://www.mozilla.org/xbl" |
michael@0 | 8 | xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" |
michael@0 | 9 | xmlns:xbl="http://www.mozilla.org/xbl"> |
michael@0 | 10 | |
michael@0 | 11 | <binding id="panelmultiview"> |
michael@0 | 12 | <resources> |
michael@0 | 13 | <stylesheet src="chrome://browser/content/customizableui/panelUI.css"/> |
michael@0 | 14 | </resources> |
michael@0 | 15 | <content> |
michael@0 | 16 | <xul:box anonid="viewContainer" class="panel-viewcontainer" xbl:inherits="panelopen,viewtype,transitioning"> |
michael@0 | 17 | <xul:stack anonid="viewStack" xbl:inherits="viewtype" viewtype="main" class="panel-viewstack"> |
michael@0 | 18 | <xul:vbox anonid="mainViewContainer" class="panel-mainview"/> |
michael@0 | 19 | |
michael@0 | 20 | <!-- Used to capture click events over the PanelUI-mainView if we're in |
michael@0 | 21 | subview mode. That way, any click on the PanelUI-mainView causes us |
michael@0 | 22 | to revert to the mainView mode, whereupon PanelUI-click-capture then |
michael@0 | 23 | allows click events to go through it. --> |
michael@0 | 24 | <xul:vbox anonid="clickCapturer" class="panel-clickcapturer"/> |
michael@0 | 25 | |
michael@0 | 26 | <!-- We manually set display: none (via a CSS attribute selector) on the |
michael@0 | 27 | subviews that are not being displayed. We're using this over a deck |
michael@0 | 28 | because a deck assumes the size of its largest child, regardless of |
michael@0 | 29 | whether or not it is shown. That's not good for our case, since we |
michael@0 | 30 | want to allow each subview to be uniquely sized. --> |
michael@0 | 31 | <xul:vbox anonid="subViews" class="panel-subviews" xbl:inherits="panelopen"> |
michael@0 | 32 | <children includes="panelview"/> |
michael@0 | 33 | </xul:vbox> |
michael@0 | 34 | </xul:stack> |
michael@0 | 35 | </xul:box> |
michael@0 | 36 | </content> |
michael@0 | 37 | <implementation implements="nsIDOMEventListener"> |
michael@0 | 38 | <field name="_clickCapturer" readonly="true"> |
michael@0 | 39 | document.getAnonymousElementByAttribute(this, "anonid", "clickCapturer"); |
michael@0 | 40 | </field> |
michael@0 | 41 | <field name="_viewContainer" readonly="true"> |
michael@0 | 42 | document.getAnonymousElementByAttribute(this, "anonid", "viewContainer"); |
michael@0 | 43 | </field> |
michael@0 | 44 | <field name="_mainViewContainer" readonly="true"> |
michael@0 | 45 | document.getAnonymousElementByAttribute(this, "anonid", "mainViewContainer"); |
michael@0 | 46 | </field> |
michael@0 | 47 | <field name="_subViews" readonly="true"> |
michael@0 | 48 | document.getAnonymousElementByAttribute(this, "anonid", "subViews"); |
michael@0 | 49 | </field> |
michael@0 | 50 | <field name="_viewStack" readonly="true"> |
michael@0 | 51 | document.getAnonymousElementByAttribute(this, "anonid", "viewStack"); |
michael@0 | 52 | </field> |
michael@0 | 53 | <field name="_panel" readonly="true"> |
michael@0 | 54 | this.parentNode; |
michael@0 | 55 | </field> |
michael@0 | 56 | |
michael@0 | 57 | <field name="_currentSubView">null</field> |
michael@0 | 58 | <field name="_anchorElement">null</field> |
michael@0 | 59 | <field name="_mainViewHeight">0</field> |
michael@0 | 60 | <field name="_subViewObserver">null</field> |
michael@0 | 61 | <field name="__transitioning">false</field> |
michael@0 | 62 | <field name="_ignoreMutations">false</field> |
michael@0 | 63 | |
michael@0 | 64 | <property name="showingSubView" readonly="true" |
michael@0 | 65 | onget="return this._viewStack.getAttribute('viewtype') == 'subview'"/> |
michael@0 | 66 | <property name="_mainViewId" onget="return this.getAttribute('mainViewId');" onset="this.setAttribute('mainViewId', val); return val;"/> |
michael@0 | 67 | <property name="_mainView" readonly="true" |
michael@0 | 68 | onget="return this._mainViewId ? document.getElementById(this._mainViewId) : null;"/> |
michael@0 | 69 | <property name="showingSubViewAsMainView" readonly="true" |
michael@0 | 70 | onget="return this.getAttribute('mainViewIsSubView') == 'true'"/> |
michael@0 | 71 | |
michael@0 | 72 | <property name="ignoreMutations"> |
michael@0 | 73 | <getter> |
michael@0 | 74 | return this._ignoreMutations; |
michael@0 | 75 | </getter> |
michael@0 | 76 | <setter><![CDATA[ |
michael@0 | 77 | this._ignoreMutations = val; |
michael@0 | 78 | if (!val && this._panel.state == "open") { |
michael@0 | 79 | if (this.showingSubView) { |
michael@0 | 80 | this._syncContainerWithSubView(); |
michael@0 | 81 | } else { |
michael@0 | 82 | this._syncContainerWithMainView(); |
michael@0 | 83 | } |
michael@0 | 84 | } |
michael@0 | 85 | ]]></setter> |
michael@0 | 86 | </property> |
michael@0 | 87 | |
michael@0 | 88 | <property name="_transitioning"> |
michael@0 | 89 | <getter> |
michael@0 | 90 | return this.__transitioning; |
michael@0 | 91 | </getter> |
michael@0 | 92 | <setter><![CDATA[ |
michael@0 | 93 | this.__transitioning = val; |
michael@0 | 94 | if (val) { |
michael@0 | 95 | this.setAttribute("transitioning", "true"); |
michael@0 | 96 | } else { |
michael@0 | 97 | this.removeAttribute("transitioning"); |
michael@0 | 98 | } |
michael@0 | 99 | ]]></setter> |
michael@0 | 100 | </property> |
michael@0 | 101 | <constructor><![CDATA[ |
michael@0 | 102 | this._clickCapturer.addEventListener("click", this); |
michael@0 | 103 | this._panel.addEventListener("popupshowing", this); |
michael@0 | 104 | this._panel.addEventListener("popupshown", this); |
michael@0 | 105 | this._panel.addEventListener("popuphidden", this); |
michael@0 | 106 | this._subViews.addEventListener("overflow", this); |
michael@0 | 107 | this._mainViewContainer.addEventListener("overflow", this); |
michael@0 | 108 | |
michael@0 | 109 | // Get a MutationObserver ready to react to subview size changes. We |
michael@0 | 110 | // only attach this MutationObserver when a subview is being displayed. |
michael@0 | 111 | this._subViewObserver = |
michael@0 | 112 | new MutationObserver(this._syncContainerWithSubView.bind(this)); |
michael@0 | 113 | this._mainViewObserver = |
michael@0 | 114 | new MutationObserver(this._syncContainerWithMainView.bind(this)); |
michael@0 | 115 | |
michael@0 | 116 | this._mainViewContainer.setAttribute("panelid", |
michael@0 | 117 | this._panel.id); |
michael@0 | 118 | |
michael@0 | 119 | if (this._mainView) { |
michael@0 | 120 | this.setMainView(this._mainView); |
michael@0 | 121 | } |
michael@0 | 122 | this.setAttribute("viewtype", "main"); |
michael@0 | 123 | ]]></constructor> |
michael@0 | 124 | |
michael@0 | 125 | <destructor><![CDATA[ |
michael@0 | 126 | if (this._mainView) { |
michael@0 | 127 | this._mainView.removeAttribute("mainview"); |
michael@0 | 128 | } |
michael@0 | 129 | this._mainViewObserver.disconnect(); |
michael@0 | 130 | this._subViewObserver.disconnect(); |
michael@0 | 131 | this._panel.removeEventListener("popupshowing", this); |
michael@0 | 132 | this._panel.removeEventListener("popupshown", this); |
michael@0 | 133 | this._panel.removeEventListener("popuphidden", this); |
michael@0 | 134 | this._subViews.removeEventListener("overflow", this); |
michael@0 | 135 | this._mainViewContainer.removeEventListener("overflow", this); |
michael@0 | 136 | this._clickCapturer.removeEventListener("click", this); |
michael@0 | 137 | ]]></destructor> |
michael@0 | 138 | |
michael@0 | 139 | <method name="setMainView"> |
michael@0 | 140 | <parameter name="aNewMainView"/> |
michael@0 | 141 | <body><![CDATA[ |
michael@0 | 142 | if (this._mainView) { |
michael@0 | 143 | this._mainViewObserver.disconnect(); |
michael@0 | 144 | this._subViews.appendChild(this._mainView); |
michael@0 | 145 | this._mainView.removeAttribute("mainview"); |
michael@0 | 146 | } |
michael@0 | 147 | this._mainViewId = aNewMainView.id; |
michael@0 | 148 | aNewMainView.setAttribute("mainview", "true"); |
michael@0 | 149 | this._mainViewContainer.appendChild(aNewMainView); |
michael@0 | 150 | ]]></body> |
michael@0 | 151 | </method> |
michael@0 | 152 | |
michael@0 | 153 | <method name="showMainView"> |
michael@0 | 154 | <body><![CDATA[ |
michael@0 | 155 | if (this.showingSubView) { |
michael@0 | 156 | let viewNode = this._currentSubView; |
michael@0 | 157 | let evt = document.createEvent("CustomEvent"); |
michael@0 | 158 | evt.initCustomEvent("ViewHiding", true, true, viewNode); |
michael@0 | 159 | viewNode.dispatchEvent(evt); |
michael@0 | 160 | |
michael@0 | 161 | viewNode.removeAttribute("current"); |
michael@0 | 162 | this._currentSubView = null; |
michael@0 | 163 | |
michael@0 | 164 | this._subViewObserver.disconnect(); |
michael@0 | 165 | |
michael@0 | 166 | this._transitioning = true; |
michael@0 | 167 | |
michael@0 | 168 | this._viewContainer.addEventListener("transitionend", function trans() { |
michael@0 | 169 | this._viewContainer.removeEventListener("transitionend", trans); |
michael@0 | 170 | this._transitioning = false; |
michael@0 | 171 | }.bind(this)); |
michael@0 | 172 | this._viewContainer.style.height = this._mainViewHeight + "px"; |
michael@0 | 173 | |
michael@0 | 174 | this.setAttribute("viewtype", "main"); |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | this._shiftMainView(); |
michael@0 | 178 | ]]></body> |
michael@0 | 179 | </method> |
michael@0 | 180 | |
michael@0 | 181 | <method name="showSubView"> |
michael@0 | 182 | <parameter name="aViewId"/> |
michael@0 | 183 | <parameter name="aAnchor"/> |
michael@0 | 184 | <body><![CDATA[ |
michael@0 | 185 | let viewNode = this.querySelector("#" + aViewId); |
michael@0 | 186 | viewNode.setAttribute("current", true); |
michael@0 | 187 | // Emit the ViewShowing event so that the widget definition has a chance |
michael@0 | 188 | // to lazily populate the subview with things. |
michael@0 | 189 | let evt = document.createEvent("CustomEvent"); |
michael@0 | 190 | evt.initCustomEvent("ViewShowing", true, true, viewNode); |
michael@0 | 191 | viewNode.dispatchEvent(evt); |
michael@0 | 192 | if (evt.defaultPrevented) { |
michael@0 | 193 | return; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | this._currentSubView = viewNode; |
michael@0 | 197 | |
michael@0 | 198 | // Now we have to transition the panel. There are a few parts to this: |
michael@0 | 199 | // |
michael@0 | 200 | // 1) The main view content gets shifted so that the center of the anchor |
michael@0 | 201 | // node is at the left-most edge of the panel. |
michael@0 | 202 | // 2) The subview deck slides in so that it takes up almost all of the |
michael@0 | 203 | // panel. |
michael@0 | 204 | // 3) If the subview is taller then the main panel contents, then the panel |
michael@0 | 205 | // must grow to meet that new height. Otherwise, it must shrink. |
michael@0 | 206 | // |
michael@0 | 207 | // All three of these actions make use of CSS transformations, so they |
michael@0 | 208 | // should all occur simultaneously. |
michael@0 | 209 | this.setAttribute("viewtype", "subview"); |
michael@0 | 210 | this._shiftMainView(aAnchor); |
michael@0 | 211 | |
michael@0 | 212 | this._mainViewHeight = this._viewStack.clientHeight; |
michael@0 | 213 | |
michael@0 | 214 | this._transitioning = true; |
michael@0 | 215 | this._viewContainer.addEventListener("transitionend", function trans() { |
michael@0 | 216 | this._viewContainer.removeEventListener("transitionend", trans); |
michael@0 | 217 | this._transitioning = false; |
michael@0 | 218 | }.bind(this)); |
michael@0 | 219 | |
michael@0 | 220 | let newHeight = this._heightOfSubview(viewNode, this._subViews); |
michael@0 | 221 | this._viewContainer.style.height = newHeight + "px"; |
michael@0 | 222 | |
michael@0 | 223 | this._subViewObserver.observe(viewNode, { |
michael@0 | 224 | attributes: true, |
michael@0 | 225 | characterData: true, |
michael@0 | 226 | childList: true, |
michael@0 | 227 | subtree: true |
michael@0 | 228 | }); |
michael@0 | 229 | ]]></body> |
michael@0 | 230 | </method> |
michael@0 | 231 | |
michael@0 | 232 | <method name="_shiftMainView"> |
michael@0 | 233 | <parameter name="aAnchor"/> |
michael@0 | 234 | <body><![CDATA[ |
michael@0 | 235 | if (aAnchor) { |
michael@0 | 236 | // We need to find the edge of the anchor, relative to the main panel. |
michael@0 | 237 | // Then we need to add half the width of the anchor. This is the target |
michael@0 | 238 | // that we need to transition to. |
michael@0 | 239 | let anchorRect = aAnchor.getBoundingClientRect(); |
michael@0 | 240 | let mainViewRect = this._mainViewContainer.getBoundingClientRect(); |
michael@0 | 241 | let center = aAnchor.clientWidth / 2; |
michael@0 | 242 | let direction = aAnchor.ownerDocument.defaultView.getComputedStyle(aAnchor, null).direction; |
michael@0 | 243 | let edge, target; |
michael@0 | 244 | if (direction == "ltr") { |
michael@0 | 245 | edge = anchorRect.left - mainViewRect.left; |
michael@0 | 246 | target = "-" + (edge + center); |
michael@0 | 247 | } else { |
michael@0 | 248 | edge = mainViewRect.right - anchorRect.right; |
michael@0 | 249 | target = edge + center; |
michael@0 | 250 | } |
michael@0 | 251 | this._mainViewContainer.style.transform = "translateX(" + target + "px)"; |
michael@0 | 252 | aAnchor.setAttribute("panel-multiview-anchor", true); |
michael@0 | 253 | } else { |
michael@0 | 254 | this._mainViewContainer.style.transform = ""; |
michael@0 | 255 | if (this.anchorElement) |
michael@0 | 256 | this.anchorElement.removeAttribute("panel-multiview-anchor"); |
michael@0 | 257 | } |
michael@0 | 258 | this.anchorElement = aAnchor; |
michael@0 | 259 | ]]></body> |
michael@0 | 260 | </method> |
michael@0 | 261 | |
michael@0 | 262 | <method name="handleEvent"> |
michael@0 | 263 | <parameter name="aEvent"/> |
michael@0 | 264 | <body><![CDATA[ |
michael@0 | 265 | if (aEvent.type.startsWith("popup") && aEvent.target != this._panel) { |
michael@0 | 266 | // Shouldn't act on e.g. context menus being shown from within the panel. |
michael@0 | 267 | return; |
michael@0 | 268 | } |
michael@0 | 269 | switch (aEvent.type) { |
michael@0 | 270 | case "click": |
michael@0 | 271 | if (aEvent.originalTarget == this._clickCapturer) { |
michael@0 | 272 | this.showMainView(); |
michael@0 | 273 | } |
michael@0 | 274 | break; |
michael@0 | 275 | case "overflow": |
michael@0 | 276 | if (aEvent.target.localName == "vbox") { |
michael@0 | 277 | // Resize the right view on the next tick. |
michael@0 | 278 | if (this.showingSubView) { |
michael@0 | 279 | setTimeout(this._syncContainerWithSubView.bind(this), 0); |
michael@0 | 280 | } else if (!this.transitioning) { |
michael@0 | 281 | setTimeout(this._syncContainerWithMainView.bind(this), 0); |
michael@0 | 282 | } |
michael@0 | 283 | } |
michael@0 | 284 | break; |
michael@0 | 285 | case "popupshowing": |
michael@0 | 286 | this.setAttribute("panelopen", "true"); |
michael@0 | 287 | // Bug 941196 - The panel can get taller when opening a subview. Disabling |
michael@0 | 288 | // autoPositioning means that the panel won't jump around if an opened |
michael@0 | 289 | // subview causes the panel to exceed the dimensions of the screen in the |
michael@0 | 290 | // direction that the panel originally opened in. This property resets |
michael@0 | 291 | // every time the popup closes, which is why we have to set it each time. |
michael@0 | 292 | this._panel.autoPosition = false; |
michael@0 | 293 | this._syncContainerWithMainView(); |
michael@0 | 294 | |
michael@0 | 295 | this._mainViewObserver.observe(this._mainView, { |
michael@0 | 296 | attributes: true, |
michael@0 | 297 | characterData: true, |
michael@0 | 298 | childList: true, |
michael@0 | 299 | subtree: true |
michael@0 | 300 | }); |
michael@0 | 301 | |
michael@0 | 302 | break; |
michael@0 | 303 | case "popupshown": |
michael@0 | 304 | this._setMaxHeight(); |
michael@0 | 305 | break; |
michael@0 | 306 | case "popuphidden": |
michael@0 | 307 | this.removeAttribute("panelopen"); |
michael@0 | 308 | this._mainView.style.removeProperty("height"); |
michael@0 | 309 | this.showMainView(); |
michael@0 | 310 | this._mainViewObserver.disconnect(); |
michael@0 | 311 | break; |
michael@0 | 312 | } |
michael@0 | 313 | ]]></body> |
michael@0 | 314 | </method> |
michael@0 | 315 | |
michael@0 | 316 | <method name="_shouldSetHeight"> |
michael@0 | 317 | <body><![CDATA[ |
michael@0 | 318 | return this.getAttribute("nosubviews") != "true"; |
michael@0 | 319 | ]]></body> |
michael@0 | 320 | </method> |
michael@0 | 321 | |
michael@0 | 322 | <method name="_setMaxHeight"> |
michael@0 | 323 | <body><![CDATA[ |
michael@0 | 324 | if (!this._shouldSetHeight()) |
michael@0 | 325 | return; |
michael@0 | 326 | |
michael@0 | 327 | // Ignore the mutation that'll fire when we set the height of |
michael@0 | 328 | // the main view. |
michael@0 | 329 | this.ignoreMutations = true; |
michael@0 | 330 | this._mainView.style.height = |
michael@0 | 331 | this.getBoundingClientRect().height + "px"; |
michael@0 | 332 | this.ignoreMutations = false; |
michael@0 | 333 | ]]></body> |
michael@0 | 334 | </method> |
michael@0 | 335 | <method name="_syncContainerWithSubView"> |
michael@0 | 336 | <body><![CDATA[ |
michael@0 | 337 | // Check that this panel is still alive: |
michael@0 | 338 | if (!this._panel || !this._panel.parentNode) { |
michael@0 | 339 | return; |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | if (!this.ignoreMutations && this.showingSubView) { |
michael@0 | 343 | let newHeight = this._heightOfSubview(this._currentSubView, this._subViews); |
michael@0 | 344 | this._viewContainer.style.height = newHeight + "px"; |
michael@0 | 345 | } |
michael@0 | 346 | ]]></body> |
michael@0 | 347 | </method> |
michael@0 | 348 | <method name="_syncContainerWithMainView"> |
michael@0 | 349 | <body><![CDATA[ |
michael@0 | 350 | // Check that this panel is still alive: |
michael@0 | 351 | if (!this._panel || !this._panel.parentNode || !this._shouldSetHeight()) { |
michael@0 | 352 | return; |
michael@0 | 353 | } |
michael@0 | 354 | |
michael@0 | 355 | if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) { |
michael@0 | 356 | let height; |
michael@0 | 357 | if (this.showingSubViewAsMainView) { |
michael@0 | 358 | height = this._heightOfSubview(this._mainView); |
michael@0 | 359 | } else { |
michael@0 | 360 | height = this._mainView.scrollHeight; |
michael@0 | 361 | } |
michael@0 | 362 | this._viewContainer.style.height = height + "px"; |
michael@0 | 363 | } |
michael@0 | 364 | ]]></body> |
michael@0 | 365 | </method> |
michael@0 | 366 | |
michael@0 | 367 | <method name="_heightOfSubview"> |
michael@0 | 368 | <parameter name="aSubview"/> |
michael@0 | 369 | <parameter name="aContainerToCheck"/> |
michael@0 | 370 | <body><![CDATA[ |
michael@0 | 371 | function getFullHeight(element) { |
michael@0 | 372 | //XXXgijs: unfortunately, scrollHeight rounds values, and there's no alternative |
michael@0 | 373 | // that works with overflow: auto elements. Fortunately for us, |
michael@0 | 374 | // we have exactly 1 (potentially) scrolling element in here (the subview body), |
michael@0 | 375 | // and rounding 1 value is OK - rounding more than 1 and adding them means we get |
michael@0 | 376 | // off-by-1 errors. Now we might be off by a subpixel, but we care less about that. |
michael@0 | 377 | // So, use scrollHeight *only* if the element is vertically scrollable. |
michael@0 | 378 | let height; |
michael@0 | 379 | let elementCS; |
michael@0 | 380 | if (element.scrollTopMax) { |
michael@0 | 381 | height = element.scrollHeight; |
michael@0 | 382 | // Bounding client rects include borders, scrollHeight doesn't: |
michael@0 | 383 | elementCS = win.getComputedStyle(element); |
michael@0 | 384 | height += parseFloat(elementCS.borderTopWidth) + |
michael@0 | 385 | parseFloat(elementCS.borderBottomWidth); |
michael@0 | 386 | } else { |
michael@0 | 387 | height = element.getBoundingClientRect().height; |
michael@0 | 388 | if (height > 0) { |
michael@0 | 389 | elementCS = win.getComputedStyle(element); |
michael@0 | 390 | } |
michael@0 | 391 | } |
michael@0 | 392 | if (elementCS) { |
michael@0 | 393 | // Include margins - but not borders or paddings because they |
michael@0 | 394 | // were dealt with above. |
michael@0 | 395 | height += parseFloat(elementCS.marginTop) + parseFloat(elementCS.marginBottom); |
michael@0 | 396 | } |
michael@0 | 397 | return height; |
michael@0 | 398 | } |
michael@0 | 399 | let win = aSubview.ownerDocument.defaultView; |
michael@0 | 400 | let body = aSubview.querySelector(".panel-subview-body"); |
michael@0 | 401 | let height = getFullHeight(body || aSubview); |
michael@0 | 402 | if (body) { |
michael@0 | 403 | let header = aSubview.querySelector(".panel-subview-header"); |
michael@0 | 404 | let footer = aSubview.querySelector(".panel-subview-footer"); |
michael@0 | 405 | height += (header ? getFullHeight(header) : 0) + |
michael@0 | 406 | (footer ? getFullHeight(footer) : 0); |
michael@0 | 407 | } |
michael@0 | 408 | if (aContainerToCheck) { |
michael@0 | 409 | let containerCS = win.getComputedStyle(aContainerToCheck); |
michael@0 | 410 | height += parseFloat(containerCS.paddingTop) + parseFloat(containerCS.paddingBottom); |
michael@0 | 411 | } |
michael@0 | 412 | return Math.round(height); |
michael@0 | 413 | ]]></body> |
michael@0 | 414 | </method> |
michael@0 | 415 | |
michael@0 | 416 | </implementation> |
michael@0 | 417 | </binding> |
michael@0 | 418 | |
michael@0 | 419 | <binding id="panelview"> |
michael@0 | 420 | <implementation> |
michael@0 | 421 | <property name="panelMultiView" readonly="true"> |
michael@0 | 422 | <getter><![CDATA[ |
michael@0 | 423 | if (this.parentNode.localName != "panelmultiview") { |
michael@0 | 424 | return document.getBindingParent(this.parentNode); |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | return this.parentNode; |
michael@0 | 428 | ]]></getter> |
michael@0 | 429 | </property> |
michael@0 | 430 | </implementation> |
michael@0 | 431 | </binding> |
michael@0 | 432 | </bindings> |