1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2013, 2014, 2015, 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, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 25 /** 26 * Generic Property Editor Widget. 27 * 28 * @author Mihai Bazon 29 * 30 * See initProperties() below 31 * 32 * @extends DwtComposite 33 * @private 34 */ 35 DwtPropertyEditor = function(parent, useDwtInputField, className, posStyle, deferred) { 36 if (arguments.length > 0) { 37 if (!className) 38 className = "DwtPropertyEditor"; 39 DwtComposite.call(this, {parent:parent, className:className, posStyle:posStyle, deferred:deferred}); 40 this._useDwtInputField = useDwtInputField != null ? useDwtInputField : true; 41 this._schema = null; 42 this._init(); 43 } 44 }; 45 46 DwtPropertyEditor.MSG_TIMEOUT = 4000; // 4 seconds should be plenty 47 48 DwtPropertyEditor.MSG = { 49 // Now these 2 are kind of pointless... 50 // We should allow a message in the prop. object. 51 mustMatch : "This field does not match validators: REGEXP", 52 mustNotMatch : "This field matches anti-validators: REGEXP" // LOL 53 }; 54 55 DwtPropertyEditor.prototype = new DwtComposite; 56 DwtPropertyEditor.prototype.constructor = DwtPropertyEditor; 57 58 DwtPropertyEditor.prototype.toString = function() { return "DwtPropertyEditor"; } 59 60 DwtPropertyEditor.prototype._init = function() { 61 var div = document.createElement("div"); 62 div.id = this._relDivId = Dwt.getNextId(); 63 div.style.position = "relative"; 64 var table = document.createElement("table"); 65 table.id = this._tableId = Dwt.getNextId(); 66 table.cellSpacing = table.cellPadding = 0; 67 table.appendChild(document.createElement("tbody")); 68 div.appendChild(table); 69 this.getHtmlElement().appendChild(div); 70 this.maxLabelWidth = 0; 71 this.maxFieldWidth = 0; 72 this._setMouseEventHdlrs(); 73 this._onMouseDown = new AjxListener(this, this._onMouseDown); 74 this.addListener(DwtEvent.ONMOUSEDOWN, this._onMouseDown); 75 }; 76 77 DwtPropertyEditor.prototype.getRelDiv = function() { 78 return document.getElementById(this._relDivId); 79 }; 80 81 DwtPropertyEditor.prototype.getTable = function() { 82 return document.getElementById(this._tableId); 83 }; 84 85 DwtPropertyEditor.prototype._onMouseDown = function(event) { 86 var target = event.target; 87 var tag = target.tagName.toLowerCase(); 88 if (tag == "input") { 89 event._stopPropagation = false; 90 event._returnValue = true; 91 return true; 92 } 93 if (this._currentInputField && !this._currentInputField.onblur()) { 94 event._stopPropagation = true; 95 event._returnValue = false; 96 return false; 97 } 98 try { 99 while (target && tag != "tr") { 100 target = target.parentNode; 101 tag = target.tagName.toLowerCase(); 102 } 103 if (target && target.__msh_doMouseDown) 104 target.__msh_doMouseDown(event); 105 } catch(ex) {}; 106 }; 107 108 /** 109 * Call this function to retrieve an object that contains all properties, 110 * indexed by name. Any "struct" property will map to an object that contains 111 * its child properties. 112 * 113 * For the sample schema below (see comments on initProperties()), we would 114 * retrieve an object like this (dots represent the edited value, or the 115 * initial value if the property wasn't modified): 116 * 117 * { 118 * userName : ... , 119 * address : { 120 * street : ... , 121 * country : ... , 122 * }, 123 * age : ... , 124 * birthday : ... 125 * } 126 */ 127 DwtPropertyEditor.prototype.getProperties = function() { 128 if (this._currentInputField) 129 // make sure we get the value 130 this._currentInputField.onblur(); 131 function rec(schema) { 132 var prop = {}, tmp, n = schema.length; 133 for (var i = 0; i < n; ++i) { 134 tmp = schema[i]; 135 if (tmp.type == "struct") 136 prop[tmp.name] = rec(tmp.children); 137 else 138 prop[tmp.name] = tmp.value; 139 } 140 return prop; 141 }; 142 return rec(this._schema); 143 }; 144 145 DwtPropertyEditor.prototype.validateData = function() { 146 var valid = true; 147 function rec(schema) { 148 var tmp, n = schema.length; 149 for (var i = 0; i < n; ++i) { 150 tmp = schema[i]; 151 if (tmp.type == "struct") 152 rec(tmp.children); 153 else if (!tmp._validate()) 154 valid = false; 155 } 156 }; 157 rec(this._schema); 158 return valid; 159 }; 160 161 /** This function will initialize the Property Editor with a given schema and 162 * property set. 163 * 164 * @param schema - declares which properties/types are allowed; see below 165 * @param parent - parent schema, for subproperties 166 * 167 * "schema" is an object that maps property names to property declaration. 168 * Here's an example of what I have in mind: 169 * 170 * [ 171 * { 172 * label : "User Name", 173 * id : "userName", 174 * type : "string", 175 * value : "", 176 * minLength : 4, 177 * maxLength : 8, 178 * mustMatch : /^[a-z][a-z0-9_]+$/i, 179 * mustNotMatch : /^(admin|root|guest)$/i 180 * }, 181 * { 182 * label : "Address", 183 * id : "address", 184 * type : "struct", 185 * children : [ // this is a nested schema definition 186 * { label : "Street", id: "street", type: "string" }, 187 * { label : "Country", 188 * id : "country", 189 * type : "enum", 190 * item : [ 191 { label : "US", value : "US" }, 192 { label : "UK", value : "UK" }, 193 { label : "KR", value : "KR" } 194 ] 195 } 196 * ] 197 * }, 198 * { 199 * label : "Age", 200 * id : "age", 201 * type : "integer", 202 * minValue : 18, 203 * maxValue : 80 204 * }, 205 * { 206 * label : "Birthday", 207 * id : "birthday", 208 * type : "date", 209 * minValue : "YYYY/MM/DD" // can we restrict the DwtCalendar? 210 * } 211 * ] 212 * 213 * The types we will support for now are: 214 * 215 * - "number" / "integer" : Allows floating point numbers or integers only. 216 * Properties: "minValue", "maxValue". 217 * 218 * - "string" : Allows any string to be inserted. "minLength", "maxLength", 219 * "mustMatch", "mustNotMatch". 220 * 221 * - "password" : Same as "string", only it's not displayed. 222 * 223 * - "enum" : One of the strings listed in the value. 224 * 225 * - "boolean" : Checkbox that yields string value of "true" and "false". 226 * 227 * - "struct" : Composite property; doesn't have a value by itself, but has 228 * child properties (the "children" array) that are defined in the same way 229 * as a toplevel property. 230 * 231 * All types except "struct" will allow a "value" property which is expected 232 * to be of a valid type that matches all validating properties (such as 233 * minLength, etc.). The value of this property will be displayed initially 234 * when the widget is constructed. 235 * 236 * Also, all types will support a "readonly" property. 237 */ 238 DwtPropertyEditor.prototype.initProperties = function(schema, parent) { 239 if (parent == null) { 240 this._schema = schema; 241 parent = null; 242 } 243 for (var i = 0; i < schema.length; ++i) 244 this._createProperty(schema[i], parent); 245 }; 246 247 DwtPropertyEditor.prototype._createProperty = function(prop, parent) { 248 var level = parent ? parent._level + 1 : 0; 249 var tr = this.getTable().firstChild.appendChild(document.createElement("tr")); 250 251 // Initialize the "prop" object with some interesting attributes... 252 prop._parent = parent; 253 prop._level = level; 254 prop._rowElId = tr.id = Dwt.getNextId(); 255 prop._propertyEditor = this; 256 257 // ... and methods. 258 for (var i in DwtPropertyEditor._prop_functions) 259 prop[i] = DwtPropertyEditor._prop_functions[i]; 260 261 prop._init(); 262 263 // indent if needed 264 tr.className = "level-" + level; 265 266 if (prop.visible == "false") 267 tr.className += " invisible"; 268 269 if (prop.readonly) 270 tr.className += " readonly"; 271 272 if (prop.type != "struct") { 273 tr.className += " " + prop.type; 274 275 // this is a simple property, create a label and value cell. 276 var tdLabel = document.createElement("td"); 277 tdLabel.className = "label"; 278 279 if(prop.type=="checkboxgroup"){ 280 tdLabel.className+=" grouplabel"; 281 } 282 283 tr.appendChild(tdLabel); 284 var html = AjxStringUtil.htmlEncode(prop.label || prop.name); 285 if (prop.required) 286 html += "<span class='DwtPropertyEditor-required'>*</span>"; 287 tdLabel.innerHTML = html; 288 var tdField = document.createElement("td"); 289 tdField.className = "field"; 290 tr.appendChild(tdField); 291 292 switch (prop.type) { 293 case "boolean" : this._createCheckbox(prop, tdField); break; 294 case "enum" : this._createDropDown(prop, tdField); break; 295 case "date" : this._createCalendar(prop, tdField); break; 296 case "checkboxgroup" : this._createCheckBoxGroup(prop,tdField); break; 297 default : 298 if (this._useDwtInputField) 299 this._createInputField(prop, tdField); 300 else { 301 tdField.innerHTML = prop._makeDisplayValue(); 302 tr.__msh_doMouseDown = DwtPropertyEditor.simpleClosure(prop._edit, prop); 303 } 304 break; 305 } 306 307 prop._fieldCellId = tdField.id = Dwt.getNextId(); 308 // prop._labelCellId = tdLabel.id = Dwt.getNextId(); 309 310 if (tdLabel.offsetWidth > this.maxLabelWidth) 311 this.maxLabelWidth = tdLabel.offsetWidth; 312 if (tdField.offsetWidth > this.maxFieldWidth) 313 this.maxFieldWidth = tdField.offsetWidth; 314 } else { 315 var td = document.createElement("td"); 316 td.colSpan = 2; 317 tr.appendChild(td); 318 td.className = "label"; 319 tr.className += " expander-collapsed"; 320 td.innerHTML = [ "<div>", AjxStringUtil.htmlEncode(prop.label), "</div>" ].join(""); 321 this.initProperties(prop.children, prop); 322 tr.__msh_doMouseDown = DwtPropertyEditor.simpleClosure(prop._toggle, prop); 323 } 324 325 // collapsed by default 326 if (level > 0) { 327 tr.style.display = "none"; 328 parent._hidden = true; 329 } 330 }; 331 332 // <FIXME: this will create problems when the first property is a "struct"> 333 DwtPropertyEditor.prototype.setFixedLabelWidth = function(w) { 334 try { 335 this.getTable().rows[0].cells[0].style.width = (w || this.maxLabelWidth) + "px"; 336 } catch(ex) {}; 337 }; 338 339 DwtPropertyEditor.prototype.setFixedFieldWidth = function(w) { 340 try { 341 this.getTable().rows[0].cells[1].style.width = (w || this.maxFieldWidth) + "px"; 342 } catch(ex) {}; 343 }; 344 // </FIXME> 345 346 DwtPropertyEditor.prototype._setCurrentMsgDiv = function(div) { 347 this._currentMsgDiv = div; 348 this._currentMsgDivTimer = setTimeout( 349 DwtPropertyEditor.simpleClosure(this._clearMsgDiv, this), 350 DwtPropertyEditor.MSG_TIMEOUT); 351 }; 352 353 DwtPropertyEditor.prototype._clearMsgDiv = function() { 354 try { 355 this._stopMsgDivTimer(); 356 } catch(ex) {}; 357 var div = this._currentMsgDiv; 358 if (div) { 359 div.parentNode.removeChild(div); 360 this._currentMsgDiv = div = null; 361 this._currentMsgDivTimer = null; 362 } 363 }; 364 365 DwtPropertyEditor.prototype._stopMsgDivTimer = function() { 366 if (this._currentMsgDivTimer) { 367 clearTimeout(this._currentMsgDivTimer); 368 this._currentMsgDivTimer = null; 369 } 370 }; 371 372 // This is bad. We're messing with internals. I think there should be an 373 // option in DwtComposite to specify the element where to add the child, rather 374 // than simply getHtmlElement().appendChild(child). 375 DwtPropertyEditor.prototype.addChild = function(child) { 376 if (!this._currentFieldCell) 377 DwtComposite.prototype.addChild.call(this, child); 378 else { 379 this._children.add(child); 380 this._currentFieldCell.appendChild(child.getHtmlElement()); 381 } 382 }; 383 384 DwtPropertyEditor.prototype._createCheckbox = function(prop, target) { 385 386 var checkbox = document.createElement("input"); 387 checkbox._prop = prop; 388 checkbox.id = prop.name; 389 checkbox.type = 'checkbox'; 390 391 if (checkbox.attachEvent) { 392 checkbox.attachEvent("onclick",prop._onCheckboxChange); 393 } 394 else if (checkbox.addEventListener) { 395 checkbox.addEventListener("click", prop._onCheckboxChange, false); 396 } 397 398 this._children.add(checkbox); 399 target.appendChild(checkbox); 400 if (prop.value == 'true') 401 checkbox.checked = prop.value; 402 }; 403 404 DwtPropertyEditor.prototype._createCheckBoxGroup = function(prop, target) { 405 406 var div = document.createElement("div"); 407 div._prop = prop; 408 div.id = prop.name; 409 //div._prop._checkBox = []; 410 prop._checkBox = []; 411 div.appendChild(document.createTextNode(prop.value)); 412 413 var table = document.createElement("table"); 414 table.id = Dwt.getNextId(); 415 table.border=0; 416 table.cellSpacing = table.cellPadding = 0; 417 table.appendChild(document.createElement("tbody")); 418 419 420 for(var i=0;i<prop.checkBox.length;i++){ 421 var tr = document.createElement("tr"); 422 423 var tdField1 = document.createElement("td"); 424 tdField1.className = "field"; 425 var checkBox = this._createCheckboxForGroup(prop,prop.checkBox[i],tdField1); 426 tr.appendChild(tdField1); 427 428 checkBox._label = prop.checkBox[i].label; 429 430 var tdField2 = document.createElement("td"); 431 tdField2.className = "field"; 432 tdField2.appendChild(document.createTextNode(prop.checkBox[i].label)); 433 tr.appendChild(tdField2); 434 435 table.firstChild.appendChild(tr); 436 437 // div._prop._checkBox[i]=checkBox; 438 prop._checkBox[i]=checkBox; 439 } 440 441 div.appendChild(table); 442 443 this._children.add(div); 444 target.appendChild(div); 445 return div; 446 }; 447 448 DwtPropertyEditor.prototype._createCheckboxForGroup = function(parent_prop,prop, target) { 449 var checkbox = document.createElement("input"); 450 checkbox._prop = parent_prop; 451 checkbox.id = prop.name; 452 checkbox.type = 'checkbox'; 453 if (prop.value == 'true') 454 checkbox.checked = prop.value; 455 if (checkbox.attachEvent) { 456 checkbox.attachEvent("onclick", parent_prop._onCheckboxGroupChange); 457 } 458 else if (checkbox.addEventListener) { 459 checkbox.addEventListener("click", parent_prop._onCheckboxGroupChange, false); 460 } 461 this._children.add(checkbox); 462 target.appendChild(checkbox); 463 return checkbox; 464 }; 465 466 467 DwtPropertyEditor.prototype._createDropDown = function(prop, target) { 468 this._currentFieldCell = target; 469 var item, sel, 470 i = 0, 471 options = [], 472 items = prop.item; 473 while (item = items[i]) 474 options[i++] = new DwtSelectOption(item.value, 475 item.value == prop.value, 476 item.label); 477 prop._select = sel = new DwtSelect({parent:this, options:options}); 478 sel.addChangeListener(new AjxListener(prop, prop._onSelectChange)); 479 sel.addListener(DwtEvent.ONMOUSEDOWN, this._onMouseDown); 480 this._currentFieldCell = null; 481 }; 482 483 DwtPropertyEditor.prototype._createCalendar = function(prop, target) { 484 this._currentFieldCell = target; 485 var btn = new DwtButton({parent:this}); 486 this._currentFieldCell = null; 487 488 btn.setText(prop._makeDisplayValue()); 489 var menu = new DwtMenu({parent:btn, style:DwtMenu.CALENDAR_PICKER_STYLE}); 490 menu.setAssociatedObj(btn); 491 var cal = new DwtCalendar({parent:menu}); 492 var date = new Date(); 493 date.setTime(prop.value); 494 cal.setDate(date); 495 cal.setSize(150, "auto"); 496 cal.addSelectionListener(new AjxListener(prop, prop._onCalendarSelect)); 497 btn.setMenu(menu); 498 499 prop._dateButton = btn; 500 prop._dateCalendar = cal; 501 }; 502 503 DwtPropertyEditor.DWT_INPUT_FIELD_TYPES = { 504 "string" : DwtInputField.STRING, 505 "password" : DwtInputField.PASSWORD, 506 "integer" : DwtInputField.INTEGER, 507 "number" : DwtInputField.FLOAT 508 }; 509 510 DwtPropertyEditor.prototype._createInputField = function(prop, target) { 511 this._currentFieldCell = target; 512 var type = DwtPropertyEditor.DWT_INPUT_FIELD_TYPES[prop.type] 513 || DwtInputField.STRING; 514 var field = new DwtInputField({parent: this, type: type, initialValue: prop.value, maxLen: prop.maxLength, rows: prop.rows}); 515 if (type == DwtInputField.INTEGER || type == DwtInputField.FLOAT) { 516 field.setValidNumberRange(prop.minValue || null, 517 prop.maxValue || null); 518 if (prop.decimals != null) 519 field.setNumberPrecision(prop.decimals); 520 } 521 if (type == DwtInputField.STRING || type == DwtInputField.PASSWORD) 522 field.setValidStringLengths(prop.minLength, prop.maxLength); 523 if (prop.required) 524 field.setRequired(true); 525 this._currentFieldCell = null; 526 prop._inputField = field; 527 field.setValue(prop.value); 528 if (prop.readonly) 529 field.setReadOnly(true); 530 field.setValidationCallback(new AjxCallback(prop, prop._onDwtInputFieldValidated)); 531 }; 532 533 // these will be merged to each prop object that comes in the schema 534 DwtPropertyEditor._prop_functions = { 535 536 _init : function() { 537 this.type != null || (this.type = "string"); 538 this.value != null || (this.value = ""); 539 this._initialVal = this.value; 540 541 if (this.type == "date") { 542 if (!this.value) { 543 // var tmp = new Date(); 544 // tmp.setHours(0); 545 // tmp.setMinutes(0); 546 // tmp.setSeconds(0); 547 this.value = new Date().getTime(); 548 } 549 if (!this.format) 550 this.format = AjxDateUtil.getSimpleDateFormat().toPattern(); 551 } 552 }, 553 554 _modified : function() { 555 return this._initialVal != this.value; 556 }, 557 558 _getRowEl : function() { 559 return document.getElementById(this._rowElId); 560 }, 561 562 _makeDisplayValue : function() { 563 var val = this._getValue(); 564 switch (this.type) { 565 case "password" : 566 val = val.replace(/./g, "*"); 567 break; 568 case "date" : 569 var date = new Date(); 570 date.setTime(val); 571 val = AjxDateFormat.format(this.format, date); 572 break; 573 } 574 if (val == "") 575 val = "<br />"; 576 else 577 val = AjxStringUtil.htmlEncode(String(val)); 578 return val; 579 }, 580 581 _display : function(visible) { 582 var 583 c = this.children, 584 d = visible ? "" : "none"; 585 if (c) { 586 var i = c.length; 587 while (--i >= 0) { 588 c[i]._getRowEl().style.display = d; 589 if (!visible) 590 c[i]._display(false); 591 } 592 this._hidden = !visible; 593 594 // change the class name accordingly 595 var tr = this._getRowEl(); 596 tr.className = tr.className.replace( 597 /expander-[^\s]+/, 598 visible ? "expander-expanded" : "expander-collapsed"); 599 } 600 }, 601 602 _toggle : function() { this._display(this._hidden); }, 603 604 _edit : function() { 605 // Depending on the type, this should probably create different 606 // fields for editing. For instance, in a "date" property we 607 // would want a calendar, while in a "list" property we would 608 // want a drop-down select box. 609 610 if (this.readonly) 611 return; 612 613 switch (this.type) { 614 case "string" : 615 case "number" : 616 case "integer" : 617 case "password" : 618 setTimeout( 619 DwtPropertyEditor.simpleClosure( 620 this._createInputField, this), 50); 621 break; 622 623 // default : 624 // alert("We don't support this type yet"); 625 } 626 }, 627 628 _createInputField : function() { 629 var pe = this._propertyEditor; 630 var td = document.getElementById(this._fieldCellId); 631 var canvas = pe.getRelDiv(); 632 var input = document.createElement("input"); 633 634 input.className = "DwtPropertyEditor-input " + this.type; 635 input.setAttribute("autocomplete", "off"); 636 637 input.type = this.type == "password" 638 ? "password" 639 : "text"; 640 641 var left = td.offsetLeft, top = td.offsetTop; 642 if (AjxEnv.isGeckoBased) { 643 --left; 644 --top; 645 } 646 input.style.left = left + "px"; 647 input.style.top = top + "px"; 648 input.style.width = td.offsetWidth + 1 + "px"; 649 input.style.height = td.offsetHeight + 1 + "px"; 650 651 input.value = this._getValue(); 652 653 canvas.appendChild(input); 654 input.focus(); 655 656 input.onblur = DwtPropertyEditor.simpleClosure(this._saveInput, this); 657 input.onkeydown = DwtPropertyEditor.simpleClosure(this._inputKeyPress, this); 658 659 this._propertyEditor._currentInputField = this._inputField = input; 660 if (!AjxEnv.isGeckoBased) 661 input.select(); 662 else 663 input.setSelectionRange(0, input.value.length); 664 }, 665 666 _getValue : function() { 667 return this.value || ""; 668 }, 669 670 _checkValue : function(val) { 671 var empty = val == ""; 672 673 if (empty) { 674 if (!this.required) 675 return val; 676 this._displayMsg(AjxMsg.valueIsRequired); 677 return null; 678 } 679 680 if (this.maxLength != null && val.length > this.maxLength) { 681 this._displayMsg(AjxMessageFormat.format(AjxMsg.stringTooLong, this.maxLength)); 682 return null; 683 } 684 685 if (this.minLength != null && val.length < this.minLength) { 686 this._displayMsg(AjxMessageFormat.format(AjxMsg.stringTooShort, this.minLength)); 687 return null; 688 } 689 690 if (this.mustMatch && !this.mustMatch.test(val)) { 691 this._displayMsg(this.msg_mustMatch || 692 DwtPropertyEditor.MSG.mustMatch.replace( 693 /REGEXP/, this.mustMatch.toString())); 694 return null; 695 } 696 697 if (this.mustNotMatch && this.mustNotMatch.test(val)) { 698 this._displayMsg(this.msg_mustNotMatch || 699 DwtPropertyEditor.MSG.mustNotMatch.replace( 700 /REGEXP/, this.mustNotMatch.toString())); 701 return null; 702 } 703 704 switch (this.type) { 705 case "integer" : 706 case "number" : 707 var n = new Number(val); 708 if (isNaN(n)) { 709 this._displayMsg(AjxMsg.notANumber); 710 return null; 711 } 712 if (this.type == "integer" && Math.round(n) != n) { 713 this._displayMsg(AjxMsg.notAnInteger); 714 return null; 715 } 716 if (this.minValue != null && n < this.minValue) { 717 this._displayMsg(AjxMessageFormat.format(AjxMsg.numberLessThanMin, this.minValue)); 718 return null; 719 } 720 if (this.maxValue != null && n > this.maxValue) { 721 this._displayMsg(AjxMessageFormat.format(AjxMsg.numberMoreThanMax, this.maxValue)); 722 return null; 723 } 724 val = n; 725 if (this.type == "number" && this.decimals != null) { 726 var str = val.toString(); 727 var pos = str.indexOf("."); 728 if (pos == -1) 729 pos = str.length; 730 val = val.toPrecision(pos + this.decimals); 731 } 732 break; 733 } 734 return val; 735 }, 736 737 _displayMsg : function(msg) { 738 var x, y, w, h; 739 var pe = this._propertyEditor; 740 var div = pe._currentMsgDiv; 741 742 if (!div) { 743 div = document.createElement("div"); 744 div.className = "DwtPropertyEditor-ErrorMsg"; 745 pe.getRelDiv().appendChild(div); 746 } else 747 pe._stopMsgDivTimer(); 748 div.style.visibility = "hidden"; 749 div.innerHTML = AjxStringUtil.htmlEncode(msg); 750 // position & size 751 var table = pe.getTable(); 752 w = table.offsetWidth; // padding & border! 753 if (!AjxEnv.isIE) 754 w -= 12; 755 x = table.offsetLeft; 756 div.style.left = x + "px"; 757 div.style.width = w + "px"; 758 h = div.offsetHeight; 759 var td = document.getElementById(this._fieldCellId); 760 y = td.offsetTop + td.offsetHeight; 761 if (y + h > table.offsetTop + table.offsetHeight) 762 y = td.offsetTop - h; 763 div.style.top = y + "px"; 764 div.style.visibility = ""; 765 pe._setCurrentMsgDiv(div); 766 }, 767 768 _saveInput : function() { 769 var input = this._inputField; 770 var val = this._checkValue(input.value); 771 if (val != null) { 772 this._setValue(val); 773 input.onblur = input.onkeyup = input.onkeydown = input.onkeypress = null; 774 var td = document.getElementById(this._fieldCellId); 775 td.innerHTML = this._makeDisplayValue(); 776 this._inputField = null; 777 this._propertyEditor._currentInputField = null; 778 this._propertyEditor._clearMsgDiv(); 779 input.parentNode.removeChild(input); 780 return true; 781 } else { 782 if (input.className.indexOf(" DwtPropertyEditor-input-error") == -1) 783 input.className += " DwtPropertyEditor-input-error"; 784 input.focus(); 785 return false; 786 } 787 }, 788 789 _inputKeyPress : function(ev) { 790 ev || (ev = window.event); 791 var input = this._inputField; 792 if (ev.keyCode == 13) { 793 this._saveInput(); 794 } else if (ev.keyCode == 27) { 795 input.value = this._getValue(); 796 this._saveInput(); 797 } else { 798 this._propertyEditor._clearMsgDiv(); 799 input.className = input.className.replace(/ DwtPropertyEditor-input-error/, ""); 800 } 801 }, 802 803 _onCheckboxChange : function(ev) { 804 ev || (ev = window.event); 805 var el = AjxEnv.isIE ? ev.srcElement : ev.target; 806 el._prop._setValue(el.checked ? "true" : "false"); 807 }, 808 809 _onSelectChange : function() { 810 this._setValue(this._select.getValue()); 811 }, 812 813 _onCheckboxGroupChange : function(ev) { 814 ev || (ev = window.event); 815 var el = AjxEnv.isIE ? ev.srcElement : ev.target; 816 var chkBxs=el._prop._checkBox; 817 var val = []; 818 for(var i=0;i<chkBxs.length;i++){ 819 if(chkBxs[i].checked){ 820 val.push(chkBxs[i]._label); 821 } 822 } 823 el._prop._setValue(val); 824 }, 825 826 _onCalendarSelect : function() { 827 this._setValue(this._dateCalendar.getDate().getTime()); 828 this._dateButton.setText(this._makeDisplayValue()); 829 }, 830 831 _onDwtInputFieldValidated : function(dwtField, validated, value) { 832 if (validated) 833 this._setValue(value); 834 }, 835 836 _setValue : function(val) { 837 this.value = val; 838 var tr = this._getRowEl(); 839 tr.className = tr.className.replace(/ dirty/, ""); 840 if (this._modified()) 841 tr.className += " dirty"; 842 }, 843 844 _validate : function() { 845 if (this._inputField) { 846 if (this._inputField instanceof DwtInputField) 847 return this._inputField.validate(); 848 else 849 return this._inputField.onblur(); 850 } else 851 return true; 852 } 853 }; 854 855 // Since we don't like nested functions... 856 DwtPropertyEditor.simpleClosure = function(func, obj) { 857 return function() { return func.call(obj, arguments[0]); }; 858 }; 859