1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.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: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 25 /** 26 * @constructor 27 * @class 28 * 29 * @param attributes 30 * @param {XModel} model the model 31 * @param {Object} instance the data instance 32 * @param {DwtComposite} dwtContainer the container 33 * 34 * @private 35 */ 36 XForm = function(attributes, model, instance, dwtContainer, contextId) { 37 if (attributes) { 38 for (var prop in attributes) { 39 this[prop] = attributes[prop]; 40 } 41 } 42 43 // get a unique id for this form 44 this.assignGlobalId(this, this.id || contextId || "_XForm"); 45 DwtComposite.call(this, dwtContainer, "DWTXForm"); 46 47 if (this.itemDefaults) { 48 XFormItemFactory.initItemDefaults(this, this.itemDefaults); 49 } 50 51 // if they didn't pass in a model, make an empty one now 52 if (model) { 53 this.setModel(model); 54 } else { 55 this.xmodel = new XModel(); 56 } 57 if (instance) this.setInstance(instance); 58 59 this.__idIndex = {}; 60 this.__externalIdIndex = {}; 61 this.__itemsAreInitialized = false; 62 this.tabIdOrder = []; 63 this.tabGroupIDs = []; 64 } 65 XForm.prototype = new DwtComposite; 66 XForm.prototype.constructor = XForm; 67 XForm.FONT_WIDTH1 = 7; 68 XForm.FONT_WIDTH2 = 8; 69 XForm.toString = function() { return "[Class XForm]"; } 70 XForm.prototype.toString = function() { return "[XForm " + this.__id + "]"; } 71 XForm.prototype.getId = function () { return this.__id; } 72 73 /** 74 * A global handler for setTimeout/clearTimeout. This handler is used by onKeyPress event of all input fields. 75 **/ 76 XForm.keyPressDelayHdlr = null; 77 78 79 /** 80 * FORM DEFAULTS 81 **/ 82 XForm.prototype.numCols = 2; 83 XForm.prototype.defaultItemType = "output"; 84 XForm.prototype._isDirty = false; 85 XForm._showBorder = false; // if true, we write a border around form cells for debugging 86 87 // 88 // FORM CONSTANTS 89 // 90 91 // generic script constants 92 var _IGNORE_CACHE_ = "IGNORE_CACHE"; 93 var _UNDEFINED_; 94 95 var _UNDEFINED_; 96 var _ALL_ = "all"; 97 98 // possible values for "labelDirection", "align" and "valign" 99 var _NONE_ = "none"; 100 var _LEFT_ = "left"; 101 var _TOP_ = "top"; 102 var _RIGHT_ = "right"; 103 var _BOTTOM_ = "bottom"; 104 var _CENTER_ = "center"; 105 var _MIDDLE_ = "middle"; 106 var _INLINE_ = "inline"; 107 108 109 // values for "relevantBehavior" 110 var _HIDE_ = "hide"; 111 var _BLOCK_HIDE_ = "block_hide"; 112 var _DISABLE_ = "disable"; 113 var _SHOW_DISABLED_ = "show_disabled"; 114 var _PARENT_ = "parent"; // used in error location as well 115 116 // values for "errorLocation" 117 var _SELF_ = "self"; 118 // var _INHERIT_ = "inherit" -- this is defined in XModel.js 119 120 // values for "selection" 121 var _OPEN_ = "open"; 122 var _CLOSED_ = "closed"; 123 124 // possible values for "overflow" 125 var _HIDDEN_ = "hidden"; 126 var _SCROLL_ = "scroll"; 127 var _AUTO_ = "auto"; 128 var _VISIBLE_ = "visible"; 129 130 /** 131 * update the form with new values 132 * NOTE: this will be done automatically if you do a {@link #setInstance} 133 * This method is costly and should not be called unless the whole form needs to be refreshed. 134 * When a single or several values are changed on a form - use change events. 135 **/ 136 XForm.prototype.refresh = function () { 137 if(this.__drawn) 138 this.updateElementStates(); 139 } 140 141 142 // NOTE: THE FOLLOWING CODE SHOULD BE CONVERTED TO DWT 143 144 XForm.prototype.getGlobalRefString = function() { 145 return "XFG.cacheGet('" + this.__id + "')"; 146 } 147 148 XForm.prototype.assignGlobalId = function (object, prefix) { 149 return XFG.assignUniqueId(object, prefix); 150 } 151 XForm.prototype.getUniqueId = function (prefix) { 152 return XFG.getUniqueId(prefix); 153 } 154 155 XForm.prototype.getElement = function (id) { 156 if (id == null) id = this.getId(); 157 var el = XFG.getEl(id); 158 if (el == null) { 159 DBG.println(AjxDebug.DBG2, "getElement(",id,"): no element found"); 160 } 161 return el; 162 } 163 164 XForm.prototype.showElement = function (id) { 165 if (id == null) id = this.getId(); 166 return XFG.showEl(id); 167 } 168 XForm.prototype.hideElement = function (id,isBlock) { 169 if (id == null) id = this.getId(); 170 return XFG.hideEl(id,isBlock); 171 } 172 173 XForm.prototype.createElement = function (id, parentEl, tagName, contents) { 174 if (id == null) id = this.getId(); 175 return XFG.createEl(id, parentEl, tagName, contents); 176 } 177 178 // NOTE: END DWT CONVERSION NEEDED 179 180 XForm.prototype.focusElement = function (id) { 181 var el = this.getElement(id); 182 // if this is a div we will have problems. 183 if (el != null) { 184 var tagName = el.tagName; 185 if (tagName != "DIV" && tagName != "TD" && tagName != "TABLE") { 186 el.focus(); //MOW: el.select() ???? 187 this.onFocus(id); 188 } 189 } 190 191 return el; 192 }; 193 194 //set focus on the first element in the actuve tab group or in the first element in the form 195 XForm.prototype.focusFirst = function(currentTabId) { 196 var tabIdOrder=null; 197 if (currentTabId != null ) { 198 tabIdOrder = this.tabIdOrder[currentTabId]; 199 } else { 200 for(var a in this.tabIdOrder) { 201 if(this.getItemById(a).getIsVisible() && this.getItemById(a).getIsEnabled() && this.tabIdOrder[a] && this.tabIdOrder[a].length > 0) { 202 tabIdOrder = this.tabIdOrder[a]; 203 break; 204 } 205 } 206 } 207 if(tabIdOrder) { 208 var cnt = tabIdOrder.length; 209 for (var i = 0; i < cnt; i++) { 210 var nextItem = this.getItemById(tabIdOrder[i]); 211 if(nextItem && nextItem.focusable && nextItem.getIsVisible() && nextItem.getIsEnabled()) { 212 return this.focusElement(tabIdOrder[i]); 213 } 214 } 215 } 216 return null; 217 }; 218 219 XForm.prototype.addTabGroup = function(item, tabGroupKeyAttr) { 220 tabGroupKeyAttr = tabGroupKeyAttr ? tabGroupKeyAttr : "tabGroupKey"; 221 var tabGroupKey = item.getInheritedProperty(tabGroupKeyAttr) ? item.getInheritedProperty(tabGroupKeyAttr) : item.getId(); 222 this.tabGroupIDs[tabGroupKey] = item.getId(); 223 } 224 225 XForm.prototype.focusNext = function(id, currentTabId) { 226 var myId = id ? id : null; 227 var tabIdOrder = null ; 228 if (currentTabId != null ) { 229 tabIdOrder = this.tabIdOrder[currentTabId]; 230 } else { 231 tabIdOrder = this.tabIdOrder ; 232 } 233 234 if(tabIdOrder && tabIdOrder.length > 0) { 235 var cnt = tabIdOrder.length; 236 //DBG.println(AjxDebug.DBG1, "TabIdOrder: length = " + tabIdOrder.length + "<br />" + tabIdOrder.toString()); 237 if (myId != null) { 238 for (var i = 0; i < cnt; i++) { 239 if(tabIdOrder[i] == myId) { 240 var elIndex = ((i+1) % cnt); 241 if(tabIdOrder[elIndex]) { 242 var nextEl = this.getItemById(tabIdOrder[elIndex]); 243 if(nextEl.focusable && nextEl.getIsVisible() && nextEl.getIsEnabled()) { 244 return this.focusElement(tabIdOrder[elIndex]); 245 } else { 246 myId=tabIdOrder[elIndex]; 247 } 248 } 249 } 250 } 251 } 252 return this.focusFirst(currentTabId); 253 } 254 return null; 255 }; 256 257 XForm.prototype.focusPrevious = function(id, currentTabId) { 258 var myId = id ? id : null; 259 var tabIdOrder = null ; 260 if (currentTabId != null ) { 261 tabIdOrder = this.tabIdOrder[currentTabId]; 262 } else { 263 tabIdOrder = this.tabIdOrder ; 264 } 265 266 if(tabIdOrder && tabIdOrder.length > 0) { 267 var cnt = tabIdOrder.length-1; 268 if (myId != null) { 269 for (var i = cnt; i >= 0; i--) { 270 if(tabIdOrder[i] == myId) { 271 var elIndex = ((i-1) % cnt); 272 if(tabIdOrder[elIndex]) { 273 var nextEl = this.getItemById(tabIdOrder[elIndex]); 274 if(nextEl.focusable && nextEl.getIsVisible() && nextEl.getIsEnabled()) { 275 return this.focusElement(tabIdOrder[elIndex]); 276 } else { 277 myId=tabIdOrder[elIndex]; 278 } 279 } 280 } 281 } 282 } 283 return this.focusFirst(currentTabId); 284 } 285 return null; 286 }; 287 288 XForm.prototype.getModel = function () { 289 return this.xmodel; 290 } 291 XForm.prototype.setModel = function (model) { 292 this.xmodel = model; 293 } 294 295 XForm.prototype.getInstance = function () { 296 return this.instance; 297 } 298 299 XForm.prototype.updateElementStates = function () { 300 if(!this.__drawn) 301 return; 302 303 this.items[0].updateVisibility(); 304 this.items[0].updateEnabledDisabled(); 305 this.items[0].updateElement(); 306 } 307 308 XForm.prototype.setInstance = function(instance) { 309 this.setIsDirty(false); 310 this.clearErrors(); 311 this.instance = instance; 312 if(this.__drawn) 313 this.updateElementStates(); 314 else 315 this.__updateStatesDelayed = true; 316 317 if (this.__drawn) { 318 this.notifyListeners(DwtEvent.XFORMS_INSTANCE_CHANGED, new DwtXFormsEvent(this)); 319 } 320 } 321 322 XForm.prototype.getInstance = function() { 323 return this.instance; 324 } 325 326 XForm.prototype.setInstanceValue = function(val, refPath) { 327 this.xmodel.setInstanceValue(this.instance,refPath,val); 328 } 329 330 XForm.prototype.getInstanceValue = function(refPath) { 331 return this.xmodel.getInstanceValue(this.instance,refPath); 332 } 333 334 XForm.checkInstanceValue = function(refPath,val) { 335 return (this.getInstanceValue(refPath) == val); 336 } 337 338 XForm.checkInstanceValueNot = function(refPath,val) { 339 return (this.getInstanceValue(refPath) != val); 340 } 341 342 XForm.checkInstanceValueEmty = function(refPath) { 343 return AjxUtil.isEmpty(this.getInstanceValue(refPath)); 344 } 345 346 XForm.checkInstanceValueNotEmty = function(refPath) { 347 return !AjxUtil.isEmpty(this.getInstanceValue(refPath)); 348 } 349 350 XForm.prototype.getController = function () { 351 return this.controller; 352 } 353 XForm.prototype.setController = function(controller) { 354 this.controller = controller; 355 } 356 357 358 359 360 XForm.prototype.getIsDirty = function () { 361 return this._isDirty; 362 } 363 XForm.prototype.setIsDirty = function(dirty, item) { 364 this._isDirty = (dirty == true); 365 //pass the current dirty XFORM item, so the event object can has the information which item is changed 366 if (typeof item == "undefined") item = null ; //to make it compatible with the previous version. 367 this.notifyListeners(DwtEvent.XFORMS_FORM_DIRTY_CHANGE, new DwtXFormsEvent(this, item, this._isDirty)); 368 } 369 370 371 372 373 374 375 XForm.prototype.initializeItems = function() { 376 // tell the model to initialize all its items first 377 // (its smart enough to only do this once) 378 this.xmodel.initializeItems(); 379 380 if (this.__itemsAreInitialized) return; 381 382 // create a group for the outside parameters and initialize that 383 // XXX SKIP THIS IF THERE IS ONLY ONE ITEM AND IT IS ALREADY A GROUP, SWITCH OR REPEAT??? 384 var outerGroup = { 385 id:"__outer_group__", 386 type:_GROUP_, 387 useParentTable:false, 388 389 numCols:this.numCols, 390 colSizes:this.colSizes, 391 items:this.items, 392 tableCssClass:this.tableCssClass, 393 tableCssStyle:this.tableCssStyle 394 } 395 this.items = this.initItemList([outerGroup]); 396 397 this.__itemsAreInitialized = true; 398 } 399 400 401 XForm.prototype.initItemList = function(itemAttrs, parentItem) { 402 var items = []; 403 for (var i = 0; i < itemAttrs.length; i++) { 404 var attr = itemAttrs[i]; 405 if (attr != null) { 406 items.push(this.initItem(attr, parentItem)); 407 } 408 } 409 this.__nestedItemCount += itemAttrs.length; //DEBUG 410 return items; 411 } 412 413 414 XForm.prototype.initItem = function(itemAttr, parentItem) { 415 // if we already have a form item, assume it's been initialized already! 416 if (itemAttr._isXFormItem) return itemAttr; 417 418 // create the XFormItem subclass from the item attributes passed in 419 // (also links to the model) 420 var item = XFormItemFactory.createItem(itemAttr, parentItem, this); 421 422 // have the item initialize it's sub-items, if necessary (may be recursive) 423 item.initializeItems(); 424 return item; 425 } 426 427 428 // add an item to our index, so we can find it easily later 429 XForm.prototype.indexItem = function(item, id) { 430 //DBG.println("id: "+id); 431 this.__idIndex[id] = item; 432 433 // Add the item to an existing array, or 434 var exId = item.getExternalId(); 435 if (exId == null || exId == "") return; 436 437 var arr = this.__externalIdIndex[exId]; 438 if (arr != null) { 439 arr.push(item); 440 } else { 441 arr = [item]; 442 } 443 this.__externalIdIndex[exId] = arr; 444 } 445 // refPath is ignored 446 // This is probably not useful to an Xforms client -- 447 // use getItemsById instead. 448 XForm.prototype.getItemById = function(id) { 449 if (id._isXFormItem) return id; 450 return this.__idIndex[id]; 451 } 452 453 // This is a method that can be called by an XForms client, but 454 // which doesn't have much internal use. 455 // gets an item by the id or the ref provided in the declaration 456 // This method returns an array, or null; 457 XForm.prototype.getItemsById = function (id) { 458 return this.__externalIdIndex[id]; 459 }; 460 461 XForm.prototype.get = function(path) { 462 if (path == null) return null; 463 if (path._isXFormItem) path = path.getRefPath(); 464 return this.xmodel.getInstanceValue(this.instance, path); 465 } 466 467 468 XForm.prototype.isDrawn = function () { 469 return (this.__drawn == true); 470 } 471 472 /** 473 * EMC 7/12/2005: I didn't want the extra div that DwtControl writes, 474 * since the xforms engine already has an outer div that we can use as 475 * container. 476 */ 477 XForm.prototype._replaceDwtContainer = function () { 478 var myDiv = document.getElementById(this.__id); 479 var dwtContainer = this.getHtmlElement(); 480 if (dwtContainer.parentNode) dwtContainer.parentNode.replaceChild(myDiv, dwtContainer); 481 this._htmlElId = this.__id; 482 }; 483 484 /** 485 * actually draw the form in the parentElement 486 * @param parentElement 487 * calls outputForm to generate all the form's HTML 488 **/ 489 XForm.prototype.draw = function (parentElement) { 490 this.initializeItems(); 491 492 // get the HTML output 493 var formOutput = this.outputForm(); 494 495 if (parentElement == null) parentElement = this.getHtmlElement(); 496 // if a parentElement was passed, stick the HTML in there and call the scripts 497 // if not, you'll have call put HTML somewhere and call the scripts yourself 498 if (parentElement) { 499 parentElement.innerHTML = formOutput; 500 } 501 502 // notify any listeners that we're "ready" 503 this.notifyListeners(DwtEvent.XFORMS_READY, new DwtXFormsEvent(this)); 504 505 // remember that we've been drawn 506 this.__drawn = true; 507 // and we're done! 508 509 if(this.__updateStatesDelayed && this.instance) { 510 this.updateElementStates(); 511 this.__updateStatesDelayed = false; 512 } 513 } 514 515 516 XForm.prototype.getItems = function () { 517 return this.items; 518 } 519 520 /** 521 * Prints out the form HTML 522 * calls outputItemList 523 **/ 524 XForm.prototype.outputForm = function () { 525 var t0 = new Date().getTime(); 526 527 var html = new AjxBuffer(); // holds the HTML output 528 var items = this.getItems(); 529 530 531 html.append('<div id="', this.__id, '"', 'style="height: 100%;"', 532 (this.cssClass != null && this.cssClass != '' ? ' class="' + this.cssClass + '"' : ""), 533 (this.cssStyle != null && this.cssStyle != '' ? ' style="' + this.cssStyle + ';"' : ""), 534 '>' 535 ); 536 537 this._itemsToInsert = {}; 538 this._itemsToCleanup = []; 539 540 541 DBG.timePt("starting outputItemList"); 542 // in initializeItems(), we guaranteed that there was a single outer item 543 // and that it is a group that sets certain properties that can be set at 544 // the form level. Just output that (and it will output all children) 545 546 // output the actual items of the form 547 this.outputItemList(items[0].items, items[0], html, this.numCols); 548 DBG.timePt("finished outputItemList"); 549 html.append("</div id=\"", this.__id,"\">"); 550 551 // save the HTML in this.__html (for debugging and such) 552 this.__HTMLOutput = html.toString(); 553 554 //DBG.println("outputForm() took " + (new Date().getTime() - t0) + " msec"); 555 556 return this.__HTMLOutput; 557 } 558 559 XForm.prototype.getOutstandingRowSpanCols = function (parentItem) { 560 if (parentItem == null) return 0; 561 var outstandingRowSpanCols = 0; 562 var previousRowSpans = parentItem.__rowSpanItems; 563 if (previousRowSpans) { 564 /* 565 for (var i = 0; i < previousRowSpans.length; i++) { 566 var previousItem = previousRowSpans[i]; 567 //DBG.println("outputing ", previousItem.__numDrawnCols," rowSpan columns for ", previousItem); 568 outstandingRowSpanCols += previousItem.__numDrawnCols; 569 570 previousItem.__numOutstandingRows -= 1; 571 if ( previousItem.__numOutstandingRows == 0) { 572 if (previousRowSpans.length == 1) { 573 delete parentItem.__rowSpanItems; 574 } else { 575 parentItem.__rowSpanItems = [].concat(previousRowSpans.slice(0,i), previousRowSpans.slice(i+1)); 576 } 577 } 578 } 579 */ 580 for (var i = previousRowSpans.length-1; i >= 0; i--) { 581 var previousItem = previousRowSpans[i]; 582 //DBG.println("outputing ", previousItem.__numDrawnCols," rowSpan columns for ", previousItem); 583 previousItem.__numOutstandingRows -= 1; 584 if ( previousItem.__numOutstandingRows == 0) { 585 if (previousRowSpans.length == 1) { 586 delete parentItem.__rowSpanItems; 587 } else { 588 parentItem.__rowSpanItems.pop(); 589 } 590 } else { 591 outstandingRowSpanCols += previousItem.__numDrawnCols; 592 } 593 } 594 595 } 596 return outstandingRowSpanCols; 597 } 598 599 /** 600 * This method will iterate through all the items (XFormItem) in the form and call outputMethod on each of them. 601 * @param items 602 * @param parentItem 603 * @param html 604 * @param numCols 605 * @param currentCol 606 * @param skipTable 607 **/ 608 XForm.prototype.outputItemList = function (items, parentItem, html, numCols, currentCol, skipTable, skipOuter) { 609 if (parentItem.outputHTMLStart) { 610 parentItem.outputHTMLStart(html, currentCol); 611 } 612 var drawTable = (parentItem.getUseParentTable() == false && skipTable != true); 613 var outerStyle = null; 614 if(!skipOuter) { 615 outerStyle = parentItem.getCssString(); 616 if (outerStyle != null && outerStyle != "") { 617 parentItem.outputElementDivStart(html); 618 } 619 } 620 621 if (drawTable) { 622 var colSizes = parentItem.getColSizes(); 623 624 //XXX MOW: appending an elementDiv around the container if we need to style it 625 var cellspacing = parentItem.getInheritedProperty("cellspacing"); 626 var cellpadding = parentItem.getInheritedProperty("cellpadding"); 627 var border = parentItem.getInheritedProperty("border"); 628 if (border == 0 && XForm._showBorder) { 629 border = 1; 630 } 631 html.append("<table cellspacing=",cellspacing," cellpadding=",cellpadding, 632 " border=" , border, 633 " id=\"", parentItem.getId(),"_table\" ", parentItem.getTableCssString(),">"); 634 if (colSizes != null) { 635 html.append( " <colgroup>"); 636 for (var i = 0; i < colSizes.length; i++) { 637 var size = colSizes[i]; 638 if(!isNaN(size)) { 639 if (size < 1) 640 size = size * 100 + "%"; 641 } 642 html.append( "<col width='", size, "'>"); 643 } 644 html.append( "</colgroup>"); 645 } 646 html.append( "<tbody>"); 647 } 648 649 numCols = Math.max(1, numCols); 650 if (currentCol == null) currentCol = 0; 651 //DBG.println("outputItemList: numCols:",numCols, " currentCol:", currentCol); 652 653 654 for (var itemNum = 0; itemNum < items.length; itemNum++) { 655 var item = items[itemNum]; 656 var isNestingItem = (item.getItems() != null); 657 var itemUsesParentTable = (item.getUseParentTable() != false); 658 659 item.__numDrawnCols = 0; 660 661 // write the beginning of the update script 662 // (one of the routines below may want to modify it) 663 664 var label = item.getLabel(); 665 var labelLocation = item.getLabelLocation(); 666 var showLabel = (label != null && (labelLocation == _LEFT_ || labelLocation == _RIGHT_)); 667 668 var colSpan = item.getColSpan(); 669 if (colSpan == "*") colSpan = Math.max(1, (numCols - currentCol)); 670 var rowSpan = item.getRowSpan(); 671 672 var totalItemCols = item.__numDrawnCols = parseInt(colSpan) + (showLabel ? 1 : 0); 673 if (rowSpan > 1 && parentItem) { 674 if (parentItem.__rowSpanItems == null) parentItem.__rowSpanItems = []; 675 parentItem.__rowSpanItems.push(item); 676 item.__numOutstandingRows = rowSpan; 677 } 678 //DBG.println("rowSpan = " + rowSpan); 679 if(currentCol==0) 680 html.append( "<tr>"); 681 682 // write the label to the left if desired 683 if (label != null && labelLocation == _LEFT_) { 684 //DBG.println("writing label"); 685 item.outputLabelCellHTML(html, rowSpan, labelLocation); 686 } 687 688 var writeElementDiv = item.getWriteElementDiv(); 689 var outputMethod = item.getOutputHTMLMethod(); 690 if (isNestingItem && itemUsesParentTable) { 691 // actually write out the item 692 if (outputMethod) outputMethod.call(item, html, currentCol); 693 694 } else { 695 696 // write the cell that contains the item 697 // NOTE: this is currently also the container! 698 item.outputContainerTDStartHTML(html, colSpan, rowSpan); 699 700 // begin the element div, if required 701 if (writeElementDiv) item.outputElementDivStart(html); 702 703 // actually write out the item 704 if (outputMethod) outputMethod.call(item, html, 0); 705 706 707 // end the element div, if required 708 if (writeElementDiv) item.outputElementDivEnd(html); 709 710 711 // end the cell that contains the item 712 item.outputContainerTDEndHTML(html); 713 714 } 715 716 currentCol += totalItemCols; 717 718 // write the label to the right, if desired 719 if (label != null && labelLocation == _RIGHT_) { 720 //DBG.println("writing label"); 721 item.outputLabelCellHTML(html, rowSpan); 722 } 723 724 // now end the update script if necessary 725 726 if ( currentCol >= numCols) { 727 html.append( "</tr>"); 728 currentCol = this.getOutstandingRowSpanCols(parentItem); 729 //DBG.println("creating new row: currentCol is now ", currentCol, (currentCol > 0 ? " due to outstanding rowSpans" : "")); 730 } 731 732 // if the number of outstanding rows is the same as the number of columns we're to generate 733 // output an empty row for each 734 while (currentCol >= numCols) { 735 //DBG.println("outputting empty row because outstandingRowSpanCols >= numCols"); 736 html.append("</tr id='numCols'>");//\r<tr id='numCols'>"); 737 currentCol = this.getOutstandingRowSpanCols(parentItem); 738 } 739 740 if(parentItem) 741 parentItem.registerActiveChild(item); 742 743 item.signUpForEvents(); 744 745 } 746 747 748 if (drawTable) { 749 html.append("</tbody></table>"); 750 } 751 752 if (outerStyle != null && outerStyle != "") { 753 parentItem.outputElementDivEnd(html); 754 } 755 756 757 if (parentItem.outputHTMLEnd) { 758 parentItem.outputHTMLEnd(html, currentCol); 759 } 760 761 } 762 763 764 765 766 767 768 // 769 // NOTE: properties of individual items moved to XForm_item_properties.js 770 // 771 772 773 // CHANGE HANDLING 774 775 776 XForm.prototype.onFocus = function(id) { 777 this.__focusObject = id; 778 } 779 780 XForm.prototype.onBlur = function(id) { 781 this.__focusObject = null; 782 } 783 784 XForm.prototype.subItemChanged = function (id, value, event, quite) { 785 //console.log("XForm.prototype.itemChanged start (" + id +","+value+","+event+")"); 786 var item = this.getItemById(id); 787 if (item == null) return alert("Couldn't get item for " + id); // EXCEPTION 788 789 // tell the item that it's display is dirty so it might have to update 790 item.dirtyDisplay(); 791 792 // validate value 793 var modelItem = item.getSubModelItem(); 794 var errorCorrected = false; 795 if (modelItem != null) { 796 try { 797 value = modelItem.validate(value, this, item, this.getInstance()); 798 if(item.hasError()) { 799 errorCorrected = true; 800 } 801 item.clearError(); 802 } 803 catch (message) { 804 item.setError(message); 805 var event = new DwtXFormsEvent(this, item, value); 806 this.notifyListeners(DwtEvent.XFORMS_VALUE_ERROR, event); 807 return; 808 } 809 } 810 811 // if there is an onChange handler, call that 812 var onChangeMethod = item.cacheInheritedMethod("onSubChange","$onSubChange","value,event,form"); 813 814 if (typeof onChangeMethod == "function") { 815 // DBG.println("itemChanged(", item.ref, ").onChange = ", onChangeMethod); 816 value = onChangeMethod.call(item, value, event, this); 817 } else { 818 var oldVal = item.getInstanceValue(item.getInheritedProperty("subRef")); 819 if(oldVal == value) { 820 if(errorCorrected && !quite) 821 this.notifyListeners(DwtEvent.XFORMS_VALUE_CHANGED, event); 822 823 return; 824 } 825 item.setInstanceValue(value, item.getSubRefPath()); 826 } 827 828 var event = new DwtXFormsEvent(this, item, value); 829 if(!quite) 830 this.notifyListeners(DwtEvent.XFORMS_VALUE_CHANGED, event); 831 832 this.setIsDirty(true, item); 833 //console.log("XForm.prototype.itemChanged end (" + id +","+value+","+event+")"); 834 } 835 836 XForm.prototype.itemChanged = function (id, value, event, quite) { 837 //console.log("XForm.prototype.itemChanged start (" + id +","+value+","+event+")"); 838 var item = this.getItemById(id); 839 if (item == null) return alert("Couldn't get item for " + id); // EXCEPTION 840 841 // tell the item that it's display is dirty so it might have to update 842 item.dirtyDisplay(); 843 844 // validate value 845 var modelItem = item.getModelItem(); 846 var errorCorrected = false; 847 if (modelItem != null) { 848 try { 849 value = modelItem.validate(value, this, item, this.getInstance()); 850 if(item.hasError()) { 851 errorCorrected = true; 852 } 853 item.clearError(); 854 } 855 catch (message) { 856 item.setError(message); 857 var event = new DwtXFormsEvent(this, item, value); 858 this.notifyListeners(DwtEvent.XFORMS_VALUE_ERROR, event); 859 return; 860 } 861 } 862 863 // if there is an onChange handler, call that 864 var onChangeMethod = item.getOnChangeMethod(); 865 866 if (typeof onChangeMethod == "function") { 867 // DBG.println("itemChanged(", item.ref, ").onChange = ", onChangeMethod); 868 value = onChangeMethod.call(item, value, event, this); 869 } else { 870 var oldVal = item.getInstanceValue(); 871 if(oldVal == value) { 872 if(errorCorrected && !quite) 873 this.notifyListeners(DwtEvent.XFORMS_VALUE_CHANGED, event); 874 875 return; 876 } 877 item.setInstanceValue(value); 878 } 879 880 var event = new DwtXFormsEvent(this, item, value); 881 if(!quite) 882 this.notifyListeners(DwtEvent.XFORMS_VALUE_CHANGED, event); 883 884 this.setIsDirty(true, item); 885 //console.log("XForm.prototype.itemChanged end (" + id +","+value+","+event+")"); 886 } 887 888 889 XForm.prototype.getItemsInErrorState = function () { 890 if (this.__itemsInErrorState == null) { 891 this.__itemsInErrorState = new Object(); 892 this.__itemsInErrorState.size = 0; 893 } 894 return this.__itemsInErrorState; 895 }; 896 897 XForm.prototype.addErrorItem = function ( item ) { 898 var errs = this.getItemsInErrorState(); 899 var oldItem = errs[item.getId()]; 900 errs[item.getId()] = item; 901 if (oldItem == null){ 902 errs.size++; 903 } 904 }; 905 906 XForm.prototype.removeErrorItem = function ( item ) { 907 if (item != null) { 908 var errs = this.getItemsInErrorState(); 909 var id = item.getId(); 910 var oldItem = errs[id]; 911 if (oldItem != null) { 912 delete errs[id]; 913 errs.size--; 914 } 915 } 916 }; 917 918 XForm.prototype.hasErrors = function () { 919 var errs = this.getItemsInErrorState(); 920 return (errs != null && errs.size > 0); 921 }; 922 923 XForm.prototype.clearErrors = function () { 924 var errs = this.getItemsInErrorState(); 925 if (errs.size > 0) { 926 var k; 927 for (k in errs) { 928 if (k == 'size') continue; 929 errs[k].clearError(); 930 delete errs[k]; 931 } 932 errs.size = 0; 933 } 934 } 935 936 XForm.prototype.onCloseForm = function () { 937 if (this.__focusObject != null) { 938 var item = this.getItemById(this.__focusObject); 939 var element = item.getElement(); 940 941 //alert("onCloseForm() not implemented"); 942 // this.itemChanged(this.__focusObject, VALUE???) 943 if (element && element.blur) { 944 element.blur(); 945 } 946 this.__focusObject = null; 947 } 948 } 949 950 //Hack: to fix the cursor on input field not shown in FF 951 //see https://bugzilla.mozilla.org/show_bug.cgi?id=167801#c58 952 XForm.prototype.releaseFocus = function () { 953 if (this.__focusObject != null) { 954 var item = this.getItemById(this.__focusObject); 955 var element = item.getElement(); 956 957 if (element && element.blur) { 958 element.blur(); 959 } 960 this.__focusObject = null; 961 } 962 } 963 964 965 966 /** @private */ 967 XForm.prototype._reparentDwtObject = function (dwtObj, newParent) { 968 var dwtE = dwtObj.getHtmlElement(); 969 if (dwtE.parentNode) dwtE.parentNode.removeChild(dwtE); 970 newParent.appendChild(dwtE); 971 } 972 973 XForm.prototype.shouldInsertItem = function (item) { 974 return (this._itemsToInsert[item.getId()] != null) 975 } 976 977 XForm.prototype.insertExternalWidget = function (item) { 978 DBG.println("insertExternalWidget(): inserting ref=", item.ref," type=", item.type, " id=", item.getId()); 979 980 var insertMethod = item.getInsertMethod(); 981 982 var widget = item.getWidget(); 983 if (widget && widget.insertIntoXForm instanceof Function) { 984 widget.insertIntoXForm(this, item, item.getElement()); 985 986 } else if (typeof this[insertMethod] == "function") { 987 this[insertMethod](item, item.getElement()); 988 989 } else { 990 DBG.println("insertExternalWidget(): don't know how to insert item ", item.ref," type=", item.type); 991 } 992 993 // take the item out of the list to insert so we don't insert it more than once 994 delete this._itemsToInsert[item.getId()]; 995 } 996