1 /* 2 * Copyright (C) 2008-2009 WaveMaker Software, Inc. 3 * 4 * This file is part of the WaveMaker Client Runtime. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 dojo.provide("wm.base.Control"); 19 20 wm.splitUnits = function(inUnitValue) { 21 var m = (inUnitValue || "").match(wm.splitUnits.Rx); 22 return { value: Number(m[1]) || 0, units: m[2] || "px" }; 23 } 24 wm.splitUnits.Rx = /(\d*)(.*)/; 25 26 /** 27 Manages geometry for a rectangle, including margins, borders, and padding and frame-of-reference calculations. 28 @class 29 @name wm.Bounds 30 */ 31 dojo.declare("wm.Bounds", null, { 32 /** @lends wm.Bounds.prototype */ 33 padding: "", 34 border: "", 35 margin: "", 36 constructor: function() { 37 this.bounds = {l:0, t:0, w:96, h:64}; 38 this.borderExtents = {l:0, t:0, r:0, b: 0}; 39 this.paddingExtents = {l:0, t:0, r:0, b: 0}; 40 this.marginExtents = {l:0, t:0, r:0, b: 0, w: 0, h:0}; 41 this.padBorderMargin = {}; 42 this.calcPadBorderMargin(); 43 }, 44 getBounds: function() { 45 return this.bounds; 46 }, 47 /** 48 Set the outermost area of this box, including margin, border, and padding. 49 l, t describe the position of the outer most corner of this box. 50 w, h describe the size of the box, including margin, border, and padding. 51 @param {Object} inBox {l: Number, t: Number, w: Number, h: Number } 52 */ 53 setBounds: function(inL, inT, inW, inH) { 54 if (arguments.length == 1) { 55 return this.setBounds(inL.l, inL.t, inL.w, inL.h) 56 } 57 var b = this.bounds; 58 if (!isNaN(inL) && b.l != inL) { 59 b.l = inL; 60 } 61 if (!isNaN(inT) && b.t != inT) { 62 b.t = inT; 63 } 64 if (inW >= 0 && b.w != inW) { 65 b.w = inW; 66 this._boundsDirty = true; 67 } 68 if (inH >= 0 && b.h != inH) { 69 b.h = inH; 70 this._boundsDirty = true; 71 } 72 b.r = b.l + b.w; 73 b.b = b.t + b.h; 74 return b; 75 }, 76 setContentBounds: function(inBox) { 77 var b= {}; 78 var sm = this.getScrollMargins(); 79 if ("w" in inBox) { 80 b.w = inBox.w + this.padBorderMargin.w + sm.w; 81 } 82 if ("h" in inBox) { 83 b.h = inBox.h + this.padBorderMargin.h + sm.h; 84 } 85 return this.setBounds(b); 86 }, 87 _parseExtents: function(inExtents) { 88 var r = {}; 89 if (typeof inExtents == "number") 90 r = { l: inExtents, t: inExtents, r: inExtents, b: inExtents }; 91 else { 92 var ex = inExtents.split(","); 93 var l = ex.length; 94 r.t = parseFloat(ex[0]) || 0; 95 r.r = l < 2 ? r.t : parseFloat(ex[1]) || 0; 96 r.b = l < 3 ? r.t : parseFloat(ex[2]) || 0; 97 r.l = l < 4 ? r.r : parseFloat(ex[3]) || 0; 98 } 99 return r; 100 }, 101 /** 102 Set padding extents in pixels. 103 @param {String||Number} inPadding "t, <r, b, l>" || Number 104 */ 105 setPadding: function(inPadding) { 106 this.padding = String(inPadding); 107 this.paddingExtents = this._parseExtents(this.padding); 108 this.padBorderMarginChanged(); 109 }, 110 /** 111 Set border extents in pixels. 112 @param {String||Number} inBorder "t, <r, b, l>" || Number 113 */ 114 setBorder: function(inBorder) { 115 this.border = String(inBorder); 116 this.borderExtents = this._parseExtents(this.border); 117 this.padBorderMarginChanged(); 118 }, 119 /** 120 Set margin extents in pixels. 121 @param {String||Number} inMargin "t, <r, b, l>" || Number 122 */ 123 setMargin: function(inMargin) { 124 this.margin = String(inMargin); 125 var me = this.marginExtents = this._parseExtents(inMargin); 126 me.h = me.t + me.b; 127 me.w = me.l + me.r; 128 this.padBorderMarginChanged(); 129 }, 130 /** 131 Update metrics when padBorderMargin has changed. 132 @protected 133 */ 134 padBorderMarginChanged: function() { 135 this.calcPadBorderMargin(); 136 }, 137 /** 138 Accumulate padBorderMargin extents. 139 @private 140 */ 141 _edges: {l:1, t:1, r:1, b:1}, 142 calcPadBorderMargin: function() { 143 var pbm = this.padBorderMargin; 144 for(var e in this._edges) 145 pbm[e] = this.borderExtents[e] + this.paddingExtents[e] + this.marginExtents[e]; 146 pbm.w = pbm.l + pbm.r; 147 pbm.h = pbm.t + pbm.b; 148 }, 149 getScrollMargins: function() { 150 return {w:0, h:0}; 151 }, 152 /** 153 Get an object describing the content-box area. 154 l, t describe the position of the origin for objects in this frame. 155 w, h describe the size of the content area of the box (inside margin, border, padding, and scrollbars). 156 @return {Object} {l: Number, t: Number, w: Number, h: Number} 157 */ 158 getContentBounds: function() { 159 var sm = this.getScrollMargins(); 160 var b = { 161 l: this.paddingExtents.l, 162 t: this.paddingExtents.t, 163 w: this.bounds.w - this.padBorderMargin.w - sm.w, 164 h: this.bounds.h - this.padBorderMargin.h - sm.h 165 }; 166 b.r = b.l + b.w; 167 b.b = b.t + b.h; 168 return b; 169 }, 170 getStyleBounds: function() { 171 var pbm = (this.dom.node.tagName.toLowerCase() == "button") ? this.marginExtents : this.padBorderMargin; 172 var b = { 173 l: this.bounds.l, 174 t: this.bounds.t, 175 w: this.bounds.w - pbm.w, 176 h: this.bounds.h - pbm.h 177 }; 178 b.r = b.l + b.w; 179 b.b = b.t + b.h; 180 return b; 181 }, 182 cloneBounds: function() { 183 with (this.bounds) { 184 return {l:l, t:t, w:w, h:h, r:r, b:b}; 185 } 186 } 187 }); 188 189 dojo.declare("wm.DomNode", null, { 190 constructor: function(inNode) { 191 this.node = inNode || document.createElement('div'); 192 }, 193 append: function(inDomNode) { 194 this.node.appendChild(inDomNode.node); 195 }, 196 remove: function(inDomNode) { 197 this.node.removeChild(inDomNode.node); 198 }, 199 getWidth: function() { 200 return this.node.offsetWidth; 201 }, 202 getHeight: function() { 203 return this.node.offsetHeight; 204 }, 205 setBox: function(inBox, inSingleLine) { 206 var s = this.node.style; 207 var bl = inBox.l + "px"; 208 if (!isNaN(inBox.l) && s.left != bl) { 209 s.left = bl; 210 } 211 var bt = inBox.t + "px"; 212 if (!isNaN(inBox.t) && s.top != bt) { 213 s.top = bt; 214 } 215 var bw = inBox.w + "px"; 216 if (inBox.w >=0 && s.width != bw) { 217 s.width = bw; 218 } 219 var bh = inBox.h + "px"; 220 if (inBox.h >= 0) { 221 //if (s.height != bh) 222 s.height = bh; 223 s.lineHeight = inSingleLine ? bh : "normal"; 224 } else if (!inSingleLine) { 225 //s.lineHeight = "normal"; 226 } 227 }, 228 setCssText: function(inText) { 229 this.node.style.cssText += ";" + inText; 230 }, 231 addCssText: function(inText) { 232 this.node.style.cssText += inText; 233 } 234 }); 235 236 wm.aligns = [ 237 "topLeft", "center", "bottomRight", "justified" 238 ]; 239 240 /** 241 Base class for all <i>visual</i> components. 242 @name wm.Control 243 @class 244 @extends wm.Component 245 */ 246 wm.define("wm.Control", [wm.Component, wm.Bounds], { 247 /** @lends wm.Control.prototype */ 248 published: { 249 autoScroll: {ignore: 1}, 250 bounds: {ignore: 1}, 251 border: {group: "style"}, 252 borderColor: {group: "style"}, 253 //backgroundColor: {group: "style"}, 254 backgroundColor: {ignore: 1}, 255 margin: {group: "style"}, 256 padding: {group: "style"}, 257 scrollX: {group: "style"}, 258 scrollY: {group: "style"}, 259 left: {writeonly: 1, ignore: 1}, 260 top: {writeonly: 1, ignore: 1} 261 }, 262 backgroundColor: "", 263 //border: 1, 264 borderColor: "#F0F0F0", 265 binding: '(data binding)', 266 classNames: '', 267 id: '', 268 autoSize: false, 269 /* 270 flex: '', 271 left: '', 272 top: '', 273 */ 274 /** 275 Display width specified as a string with units.<br> 276 <br> 277 Supports CSS units and <i>flex</i> units.<br> 278 @example 279 this.button.setValue("width", "96px"); 280 this.text.setValue("width", "4em"); 281 this.box.setValue("width", "1flex"); 282 @type String 283 */ 284 width: '', 285 /** 286 Display height specified as a string with units.<br> 287 <br> 288 Supports CSS units and <i>flex</i> units.<br> 289 @example 290 this.button.setValue("height", "96px"); 291 this.text.setValue("height", "4em"); 292 this.box.setValue("height", "1flex"); 293 @type String 294 */ 295 height: '', 296 left: 0, 297 top: 0, 298 group: '', 299 styles: '', 300 state : null, 301 /** 302 Showing state.<br> 303 <br> 304 Whether the widget if shown on the display.<br> 305 @see <a href="#hide">hide</a>, <a href="#show">show</a>. 306 @example 307 this.button.setValue("showing", false); 308 this.panel.show(); 309 this.label.hide(); 310 @type Boolean 311 */ 312 showing: true, 313 /** 314 Disabled state.<br> 315 <br> 316 Some widgets change behavior or display based on the disabled state.<br> 317 @see <a href="#disable">disable</a>, <a href="#enable">enable</a>. 318 @example 319 this.button.setValue("disabled", true); 320 this.panel.disable(); 321 this.label.enable(); 322 @type Boolean 323 */ 324 disabled: false, 325 container: false, 326 _classes: null, 327 scrollX: false, 328 scrollY: false, 329 //=========================================================================== 330 // Construction 331 //=========================================================================== 332 constructor: function() { 333 this.widgets = {}; 334 this._classes = dojo.mixin({}, this._classes); 335 }, 336 postscript: function(inProps) { 337 this.inherited(arguments); 338 }, 339 create: function() { 340 this._cupdating = true; 341 this.inherited(arguments); 342 }, 343 build: function() { 344 this.domNode = dojo.byId(this.domNode||/*this.id||*/undefined); 345 if (!this.domNode) 346 this.domNode = document.createElement('div'); 347 }, 348 init: function() { 349 this.dom = new wm.DomNode(this.domNode); 350 this.inherited(arguments); 351 this.bc(); 352 // 353 this.domNode.style.position = "absolute"; 354 this.setBorder(this.border); 355 this.setMargin(this.margin); 356 this.setPadding(this.padding); 357 // 358 // if (this.domNode) { 359 this.setParent(this.parent); 360 this.setDomNode(this.domNode); 361 //if (this.autoSize) 362 // this.size = ""; 363 //this.setFlex(this.flex) 364 // BC 365 this.doSetSizeBc(); 366 //this._flexChanged(); 367 // FIXME: need a generalized way of processing properties at creation time 368 // setShowing will nop unless this.showing would be changed 369 if (!this.showing) { 370 this.showing = true; 371 this.setShowing(false); 372 } 373 this.setDisabled(this.disabled); 374 // } 375 if (this.styles) { 376 this.set_styles(this.styles); 377 this.styles = ""; 378 } 379 }, 380 bc: function() { 381 // do BC fixups 382 /*if (this.layoutFlex) { 383 this.fluidSize = this.layoutFlex; 384 }*/ 385 delete this.layoutFlex; 386 delete this.fluidSize; 387 }, 388 postInit: function() { 389 this._cupdating = false; 390 this.inherited(arguments); 391 if (!this.$.binding) 392 new wm.Binding({name: "binding", owner: this}); 393 }, 394 destroy: function() { 395 var wids = []; 396 for (var n in this.widgets) 397 wids.push(this.widgets[n]); 398 for(var i=0, w; (w=wids[i]); i++) 399 w.destroy(); 400 this.parentNode = null 401 this.setParent(null); 402 dojo._destroyElement(this.domNode); 403 this.inherited(arguments); 404 }, 405 loaded: function() { 406 this.inherited(arguments); 407 this.initUserClasses(); 408 }, 409 setDomNode: function(inDomNode) { 410 var n = this.domNode = inDomNode; 411 if (dojo.isIE) { 412 // forcing a size on the node now seems to help IE 413 // honor auto sizing later 414 n.style.width = "0px"; 415 } 416 // id 417 this.updateId(); 418 // classes 419 dojo.addClass(n, this.classNames + (this.owner ? ' ' + this.owner.declaredClass + '-' + this.name : '')); 420 this.initUserClasses(); 421 this.updateBounds(); 422 }, 423 //=========================================================================== 424 // Name & Id 425 //=========================================================================== 426 updateId: function() { 427 this.inherited(arguments); 428 if (this.domNode) { 429 var rid = this.getRuntimeId(); 430 this.domNode.rid = rid; 431 this.domNode.id = rid.replace(/\./g, "_"); 432 } 433 }, 434 //=========================================================================== 435 // Ownership 436 //=========================================================================== 437 getUniqueName: function(inName) { 438 return wm.findUniqueName(inName, [this, this.components, this.widgets]); 439 }, 440 //=========================================================================== 441 // Parentage 442 //=========================================================================== 443 setName: function(inName) { 444 if (!inName) 445 return; 446 if (this.parent) 447 this.parent.removeWidget(this); 448 this.addRemoveDefaultCssClass(false); 449 this.inherited(arguments); 450 if (this.parent) 451 this.parent.addWidget(this); 452 this.addRemoveDefaultCssClass(true); 453 }, 454 addWidget: function(inWidget){ 455 this.widgets[inWidget.name] = inWidget; 456 var p = this.containerNode || this.domNode; 457 if (inWidget.domNode.parentNode != p) 458 p.appendChild(inWidget.domNode); 459 }, 460 removeWidget: function(inWidget){ 461 delete this.widgets[inWidget.name]; 462 }, 463 adjustChildProps: function(inCtor, inProps) { 464 if (inCtor.prototype instanceof wm.Widget) 465 dojo.mixin(inProps, {owner: this.owner, parent: this}); 466 else 467 this.inherited(arguments); 468 }, 469 //======================================================= 470 // Properties 471 //======================================================= 472 listProperties: function() { 473 var p = this.inherited(arguments); 474 p.autoSize.ignore = (!this.isSizeable() && !this.autoSize) || (this.schema.autoSize && this.schema.autoSize.ignore); 475 //p.width.ignore = p.width.writeonly = !this.isSizeable() || !this.canSetWidth(); 476 //p.height.ignore = p.height.writeonly = !this.isSizeable() || !this.canSetHeight(); 477 p.width.ignore = p.width.writeonly = !this.isSizeable() || !this.canSetWidth(); 478 p.height.ignore = p.height.writeonly = !this.isSizeable() || !this.canSetHeight(); 479 // _classes as array for bc; now an object that supports storing sets of classes 480 p._classes.writeonly = (dojo.isArray(this._classes) && this._classes.length) || !wm.isEmpty(this._classes); 481 return p; 482 }, 483 //=========================================================================== 484 // Custom Node Events / Node Bounds 485 //=========================================================================== 486 /* 487 nodeAutoSize: function(inAtWidth, inAtHeight) { 488 if (this.autoSize) { 489 var s = this.domNode.style; 490 if (dojo.isFF && dojo.isFF < 3) 491 s.overflow = "visible"; 492 if (!inAtWidth) 493 s.width = ""; 494 if (!inAtHeight) 495 s.height = ""; 496 this.doAutoSize(inAtWidth, inAtHeight); 497 if (dojo.isFF && dojo.isFF < 3) 498 s.overflow = ""; 499 } 500 }, 501 */ 502 doAutoSize: function(inAtWidth, inAtHeight) { 503 this.sizeFromNode(); 504 }, 505 //=========================================================================== 506 // Bounds 507 //=========================================================================== 508 // BC --> 509 doSetSizeBc: function() { 510 /*if (!this.width) { 511 this.setSizeProp("width", "100%"); 512 } 513 if (!this.height) { 514 this.setSizeProp("height", "100%"); 515 }*/ 516 if (this.sizeUnits == "flex") { 517 this.setFlex(this.size); 518 } else if (this.sizeUnits) { 519 var b = this.getParentBox(), p = {v: "height", h: "width"}[b]; 520 this.setSizeProp(p, this.size + this.sizeUnits); 521 } else if (this.flex) { 522 this.setFlex(this.flex); 523 } 524 }, 525 setFlex: function(inFlex) { 526 var box = this.getParentBox(); 527 if (box) { 528 var ex = {h: "width", v: "height"}[box]; 529 this.setSizeProp(ex, inFlex*100 + "%"); 530 this._boundsDirty = true; 531 } else { 532 this.setSizeProp("width", inFlex*100 + "%"); 533 this.setSizeProp("height", inFlex*100 + "%"); 534 } 535 }, 536 isFlex: function() { 537 var box = this.getParentBox(); 538 if (!box) 539 return false; 540 var ex = {h: "width", v: "height"}[box]; 541 return (this[ex].indexOf("flex")>=0); 542 }, 543 // <-- BC 544 getScrollMargins: function() { 545 return {w: this.scrollY ? 17 : 0, h: this.scrollX ? 17 : 0}; 546 }, 547 padBorderMarginChanged: function() { 548 this.inherited(arguments); 549 if (!this._cupdating) { 550 this.parent ? this.parent.reflow() : this.render(), wm.fire(this, "flow"); 551 } 552 }, 553 /** 554 Update width and height properties after bounds change. 555 */ 556 boundsResized: function() { 557 var box = dojo.marginBox(this.dom.node); 558 if (this.bounds.w != box.w) { 559 this.width = this.bounds.w + "px"; 560 } 561 if (this.bounds.h != box.h) { 562 this.height = this.bounds.h + "px"; 563 } 564 this.updateBounds(); 565 }, 566 /** 567 Update bounds and flex properties based on width/height properties 568 */ 569 updateBounds: function() { 570 //this.domNode.flex = 0; 571 //this.fluidSize = 0; 572 this._percEx = {w:0, h: 0}; 573 // 574 //var pd = this.getParentBox(); 575 // 576 var su = wm.splitUnits(this.width); 577 var w = su.value; 578 switch (su.units) { 579 // FIXME: 'flex' and 'em' are deprecated, probably this should be in BC block 580 case "flex": 581 w *= 100; 582 this._percEx.w = w; 583 this.width = w + "%"; 584 w = NaN; 585 break; 586 case "em": 587 w *= 18; 588 this.width = w + "px"; 589 break; 590 case "%": 591 this._percEx.w = w; 592 w = NaN; 593 break; 594 } 595 // 596 su = wm.splitUnits(this.height); 597 var h = su.value; 598 switch (su.units) { 599 // FIXME: 'flex' and 'em' are deprecated, probably this should be in BC block 600 case "flex": 601 h *= 100; 602 this._percEx.h = h; 603 this.height = h + "%"; 604 h = NaN; 605 break; 606 case "em": 607 h *= h * 18; 608 this.height = h + "px"; 609 break; 610 case "%": 611 this._percEx.h = h; 612 h = NaN; 613 break; 614 } 615 //console.log(w, h); 616 //this.setBounds(NaN, NaN, w, h); 617 this.setBounds(this.left, this.top, w, h); 618 }, 619 // return the 'box' setting of our parentNode 620 getParentBox: function() { 621 var n = (this.domNode || 0).parentNode; 622 return n && (n.box || (n.getAttribute && n.getAttribute("box"))) || (this.parent||0).box || ''; 623 }, 624 // return true if width is not controlled by layout 625 canSetWidth: function() { 626 return this.getParentBox() != 'v'; 627 }, 628 // return true if height is not controlled by layout 629 canSetHeight: function() { 630 return this.getParentBox() != 'h'; 631 }, 632 setSizeProp: function(n, v) { 633 /* 634 var ex = {h: "width", v: "height"}[this.getParentBox()]; 635 if (ex == n) 636 this.autoSize = false; 637 */ 638 this[n] = v; 639 this.updateBounds(); 640 if (!this._cupdating) 641 this.reflowParent(); 642 }, 643 setWidth: function(inWidth) { 644 this.setSizeProp("width", inWidth); 645 }, 646 setHeight: function(inHeight) { 647 this.setSizeProp("height", inHeight); 648 }, 649 sizeFromNode: function() { 650 //var box = wm.getNaturalBox(this.domNode); 651 var box = dojo.marginBox(this.domNode); 652 var pb = this.getParentBox(); 653 if (pb) { 654 p = {v:"h", h:"w"}[pb], 655 sz = box[p]; 656 p = {v: "height", h: "width"}[pb]; 657 this[p] = (sz > 0 ? sz : 16) + "px"; 658 } else { 659 this.width = box.w + 'px'; 660 this.height = box.h + 'px'; 661 } 662 }, 663 // FIXME: need custom auto-fit for widgets whose size is not automatic 664 // (e.g. contain abs position children) 665 setAutoSize: function(inAutoSize) { 666 this.autoSize = inAutoSize; 667 if (this.autoSize) 668 this.reflowParent(); 669 }, 670 //=========================================================================== 671 // Rendering 672 //=========================================================================== 673 render: function() { 674 //this.renderHtml(); 675 this.renderCss(); 676 return true; 677 }, 678 renderCss: function() { 679 this.dom.setCssText(this.getCssText()); 680 this.renderBounds(); 681 }, 682 getCssText: function() { 683 var t = 684 "margin:" + (this.margin.split(",").join("px ") || 0) + "px;" 685 + "padding:" + (this.padding.split(",").join("px ") || 0) + "px;" 686 + "border:0 solid;" 687 + "border-width:" + (this.border.split(",").join("px ") || 0) + "px;" 688 + "border-color:" + this.borderColor + ";" 689 + (this.backgroundColor ? "background-color:" + this.backgroundColor + ";" : "") 690 + "overflow:" + (this.autoScroll ? "auto" : "hidden") + ";" 691 + (this.scrollX ? "overflow-x:scroll;" : "") 692 + (this.scrollY ? "overflow-y:scroll;" : "") 693 + (this.cssText || "") 694 ; 695 return t; 696 }, 697 renderBounds: function() { 698 this.dom.setBox(this.getStyleBounds(), this.singleLine); 699 // bc 700 if (this.designWrapper) 701 this.designWrapper.controlBoundsChange(); 702 }, 703 //=========================================================================== 704 // Flow 705 //=========================================================================== 706 // FIXME: controversial update implementation cribbed from Layers.js 707 /* 708 beginUpdate: function() { 709 this.domNode._reflowing = true; 710 }, 711 endUpdate: function() { 712 this.domNode._reflowing = false; 713 }, 714 */ 715 reflow: function() { 716 //wm.fire(this.domNode, "reflow"); 717 }, 718 reflowParent: function() { 719 wm.fire(this.parent, "reflow"); 720 //wm.fire(this.domNode.parentNode, "reflow"); 721 }, 722 setScrollX: function(inScrollX) { 723 this.scrollX = inScrollX; 724 this.reflowParent(); 725 }, 726 setScrollY: function(inScrollY) { 727 this.scrollY = inScrollY; 728 this.reflowParent(); 729 }, 730 //=========================================================================== 731 // Groups 732 //=========================================================================== 733 groupHandler: function(inMessage, inArgument) { 734 switch(inMessage){ 735 case "disabled": 736 this.setDisabled(inDisabled); 737 break; 738 } 739 }, 740 //=========================================================================== 741 // Convenience 742 //=========================================================================== 743 /** 744 Set <a href="#showing">showing</a> property true. 745 */ 746 show: function() { 747 this.setValue("showing", true); 748 }, 749 /** 750 Set <a href="#showing">showing</a> property false. 751 */ 752 hide: function() { 753 this.setValue("showing", false); 754 }, 755 /** 756 Set <a href="#disabled">disabled</a> property true. 757 */ 758 disable: function() { 759 this.setValue("disabled", true); 760 }, 761 /** 762 Set <a href="#disabled">disabled</a> property false. 763 */ 764 enable: function() { 765 this.setValue("disabled", false); 766 }, 767 //=========================================================================== 768 // Setters 769 //=========================================================================== 770 setParent: function(inParent) { 771 var op = this.parent, p = this.parent = inParent; 772 if (op && op != p) { 773 op.removeWidget(this); 774 // BC: we still have non-container parents (e.g. wm.Dialog) 775 if (op.removeControl) 776 op.removeControl(this); 777 } 778 if (p) { 779 p.addWidget(this); 780 // BC: we still have non-container parents (e.g. wm.Dialog) 781 if (p.addControl) 782 p.addControl(this); 783 } 784 // BC: wm.Layout 785 else if (this.parentNode && this.domNode) { 786 this.parentNode.appendChild(this.domNode); 787 } 788 if (p && op) 789 dojo.publish("wmwidget-parentChange", [op, p, this]); 790 }, 791 canChangeShowing: function() { 792 return true; 793 }, 794 setShowing: function(inShowing) { 795 if (!this.canChangeShowing()) 796 return; 797 if (this.showing != inShowing) { 798 this.showing = inShowing; 799 this.domNode.style.display = inShowing ? '' : 'none'; 800 this.reflowParent(); 801 } 802 }, 803 /** 804 Set disabled property for this widget.<br/> 805 <br/> 806 Some widgets change behavior or display based on the disabled state.<br> 807 @param {Boolean} inDisabled True to set disabled. 808 */ 809 setDisabled: function(inDisabled) { 810 for (var i in this.widgets) { 811 this.widgets[i].setDisabled(inDisabled); 812 } 813 this.disabled = inDisabled; 814 }, 815 setGroup: function(inGroup) { 816 this.group = inGroup; 817 dojo.unsubscribe(this._subscription); 818 if (this.group) 819 this._subscription = dojo.subscribe(this.group, this, "groupHandler"); 820 }, 821 setBackgroundColor: function(inColor) { 822 this.backgroundColor = inColor; 823 this.render(); 824 }, 825 setBorderColor: function(inColor) { 826 this.borderColor = inColor; 827 this.render(); 828 }, 829 //=========================================================================== 830 // Default and User Style Classes 831 //=========================================================================== 832 addRemoveDefaultCssClass: function(inAdd) { 833 if (this.owner) 834 dojo[inAdd ? "addClass" : "removeClass"](this.domNode, this.owner.declaredClass + '-' + this.name); 835 }, 836 getUserNodeClasses: function(inNodeName) { 837 var klasses = this._classes; 838 for (var i in klasses) { 839 if (inNodeName == i) 840 return klasses[i].join(' '); 841 } 842 return ""; 843 }, 844 initUserClasses: function() { 845 // bc 846 if (dojo.isArray(this._classes)) 847 this._classes = {domNode: this._classes}; 848 var klasses = this._classes; 849 for (var i in klasses) 850 this.initUserNodeClasses(klasses[i], i); 851 }, 852 initUserNodeClasses: function(inClasses, inNodeName) { 853 var k = inClasses || [], n = this[inNodeName]; 854 if (n) 855 // add classes together for speed; we don't care about checking if the class is already on the node 856 dojo.addClass(n, k.join(' ')); 857 }, 858 /** 859 Add CSS class to a widget node.<br/> 860 @param {String} inClass The class to add. 861 @param {String} inNodeName (Optional) a property in this widget that references a node. 862 If ommitted, the default node is used. 863 @example this.panel.addUserClass("hilite-border"); 864 */ 865 addUserClass: function(inClass, inNodeName) { 866 inNodeName = inNodeName || "domNode"; 867 var cs = this._classes[inNodeName] = this._classes[inNodeName] || []; 868 cs.push(inClass); 869 var n = this[inNodeName]; 870 if (n) 871 dojo.addClass(n, inClass); 872 }, 873 /** 874 Remove a CSS class from a widget node.<br/> 875 @param {String} inClass The class to remove. 876 @param {String} inNodeName (Optional) a property in this widget that references a node. 877 If ommitted, the default node is used. 878 @example this.panel.removeUserClass("hilite-border"); 879 */ 880 removeUserClass: function(inClass, inNodeName) { 881 inNodeName = inNodeName || "domNode"; 882 var n = this[inNodeName]; 883 if (n) 884 dojo.removeClass(n, inClass); 885 var cs = this._classes[inNodeName] || []; 886 for (var i=0, c; c=cs[i]; i++) 887 if (c == inClass) 888 cs.splice(i--, 1); 889 if (!cs.length) 890 delete this._classes[inNodeName]; 891 }, 892 getOrderedWidgets: function() { 893 return []; 894 } 895 }); 896 897 // layout specific 898 899 /* 900 wm.Control.extend({ 901 //fluidSize: 0, 902 //alignInParent: "justified", 903 //setFluidSize: function(inFluidSize) { 904 // this.fluidSize = inFluidSize; 905 // this.reflowParent(); 906 //} 907 }); 908 909 wm.Object.extendSchema(wm.Control, { 910 //fluidSize: {group: "layout"}, 911 }); 912 */ 913 914 wm.Widget = wm.Control; 915