1 /* See the NOTICE file distributed with 2 * this work for additional information regarding copyright ownership. 3 * Esri Inc. licenses this file to You under the Apache License, Version 2.0 4 * (the "License"); you may not use this file except in compliance with 5 * the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 /** 16 * gxe.js (v1.1.1) 17 * Geoportal XML editor. 18 */ 19 20 dojo.require("dijit.Dialog"); 21 22 /** 23 * @fileOverview Geoportal XML Editor (GXE). 24 * @version 1.1.1 25 */ 26 27 /** 28 * @class GXE configuration object structure (JSON). 29 * <br/>Represents the logical structure for the response of a request 30 * for a Geoportal XML editor definition. 31 * @name cfgObject 32 * @property {String} name the qualified configuration name 33 * @property {String} value the value (optional) 34 * @property {cfgAttribute[]} attributes the configuration attributes 35 * @property {cfgObject[]} children the children (optional) 36 */ 37 38 /** 39 * @class GXE configuration attribute structure (JSON). 40 * @name cfgAttribute 41 * @property {String} name the qualified configuration name 42 * @property {String} value the value 43 */ 44 45 /** 46 * @class Primary namespace for the Geoportal XML Editor (GXE). 47 * @static 48 * @name gxe 49 */ 50 var gxe = { 51 52 /** 53 * @class Static utilities for processing JSON based configuration objects associated 54 * with an editor definition. 55 * @static 56 * @name gxe.cfg 57 * @property {String} pfxGxe the GXE namespace prefix 58 * <br/>(="g") 59 * @property {String} pfxHtml the GXE HTML namespace prefix 60 * <br/>(="h") 61 * @property {String} uriGxe the GXE namespace URI 62 * <br/>(="http://www.esri.com/geoportal/gxe") 63 * @property {String} uriGxeHtml the GXE HTML namespace URI 64 * <br/>(="http://www.esri.com/geoportal/gxe/html") 65 */ 66 cfg: { 67 pfxGxe: "g", 68 pfxHtml: "h", 69 uriGxe: "http://www.esri.com/geoportal/gxe", 70 uriGxeHtml: "http://www.esri.com/geoportal/gxe/html", 71 72 /** 73 * Finds an immediate child of a configuration object. 74 * @example Example: 75 * gxe.cfg.findChild(cfgObject,"http://www.esri.com/geoportal/gxe","options"); 76 * @function 77 * @name findChild 78 * @memberOf gxe.cfg 79 * @param {cfgObject} cfgObject the configuration object to process 80 * @param {String} namespace the configuration namespace of the child to find 81 * @param {String} name the configuration name of the child to find 82 * @return {cfgObject} the located child (null if not found) 83 */ 84 findChild: function(cfgObject,namespace,name) { 85 var children = cfgObject.children; 86 var nChildren = 0; 87 if (children != null) nChildren = children.length; 88 for (var i=0; i<nChildren; i++) { 89 var child = children[i]; 90 if ((child.namespace == namespace) && (child.name == name)) { 91 return child; 92 } 93 } 94 return null; 95 }, 96 97 /** 98 * Finds an immediate child of a configuration object within the GXE namespace. 99 * <br/>(i.e. namespace "http://www.esri.com/geoportal/gxe") 100 * @example Example: 101 * gxe.cfg.findGxeChild(cfgObject,"options"); 102 * @function 103 * @name findGxeChild 104 * @memberOf gxe.cfg 105 * @param {cfgObject} cfgObject the configuration object to process 106 * @param {String} name the configuration name of the child to find 107 * @return {cfgObject} the located child (null if not found) 108 */ 109 findGxeChild: function(cfgObject,name) { 110 return this.findChild(cfgObject,this.uriGxe,name); 111 }, 112 113 /** 114 * Executes a function for each immediate and matching child of a configuration object. 115 * <br/> 116 * <br/>The callback function will have the following signature: function(cfgChild) {} 117 * <br/>The callback function can return the String "break" to terminate the loop. 118 * @function 119 * @name forEachChild 120 * @memberOf gxe.cfg 121 * @param {cfgObject} cfgObject the configuration object to process 122 * @param {String} namespace the configuration namesapce to match (* to match any) 123 * @param {String} name the configuration name to match (* to match any) 124 * @param {function} callback the callback function 125 */ 126 forEachChild: function(cfgObject,namespace,name,callback) { 127 var children = cfgObject.children; 128 var nChildren = 0; 129 if (children != null) nChildren = children.length; 130 for (var i=0; i<nChildren; i++) { 131 var child = children[i]; 132 var bMatch = ((namespace == "*") || (child.namespace == namespace)); 133 if (bMatch) bMatch = ((name == "*") || (child.name == name)); 134 if (bMatch) { 135 var _ret = callback(child); 136 if ((typeof(_ret) == "string") && (_ret == "break")) break; 137 } 138 } 139 }, 140 141 /** 142 * Executes a function for each HTML attribute associated with a configuration object. 143 * <br/>(i.e. each attribute within namespace "http://www.esri.com/geoportal/gxe/html") 144 * <br/> 145 * <br/>The callback function will have the following signature: function(cfgAttribute) {} 146 * <br/>The callback function can return the String "break" to terminate the loop. 147 * @function 148 * @name forEachHtmlAttribute 149 * @memberOf gxe.cfg 150 * @param {cfgObject} cfgObject the configuration object to process 151 * @param {function} callback the callback function 152 */ 153 forEachHtmlAttribute: function(cfgObject,callback) { 154 var attributes = cfgObject.attributes; 155 var nAttributes = 0; 156 if (attributes != null) nAttributes = attributes.length; 157 for (var i=0; i<nAttributes; i++) { 158 var attribute = attributes[i]; 159 if (attribute.namespace == this.uriGxeHtml) { 160 var _ret = callback(attribute); 161 if ((typeof(_ret) == "string") && (_ret == "break")) break; 162 } 163 } 164 }, 165 166 /** 167 * Gets an attribute value. 168 * @example Example: 169 * gxe.cfg.getAttributeValue(cfgObject,"http://www.esri.com/geoportal/gxe","minOccurs"); 170 * @function 171 * @name getAttributeValue 172 * @memberOf gxe.cfg 173 * @param {cfgObject} cfgObject the configuration object to process 174 * @param {String} namespace the configuration namespace of the attribute to find 175 * @param {String} name the configuration name of the attribute to find 176 * @return {Object} the attribute value (null if not found) 177 */ 178 getAttributeValue: function(cfgObject,namespace,name) { 179 var attributes = cfgObject.attributes; 180 var nAttributes = 0; 181 if (attributes != null) nAttributes = attributes.length; 182 for (var i=0; i<nAttributes; i++) { 183 var attribute = attributes[i]; 184 if ((attribute.namespace == namespace) && (attribute.name == name)) { 185 return attribute.value; 186 } 187 } 188 return null; 189 }, 190 191 /** 192 * Gets an attribute value within the GXE namespace. 193 * <br/>(i.e. namespace "http://www.esri.com/geoportal/gxe") 194 * @example Example: 195 * gxe.cfg.getGxeAttributeValue(cfgObject,"minOccurs"); 196 * @function 197 * @name getGxeAttributeValue 198 * @memberOf gxe.cfg 199 * @param {cfgObject} cfgObject the configuration object to process 200 * @param {String} name the configuration name of the attribute to find 201 * @return {Object} the attribute value (null if not found) 202 */ 203 getGxeAttributeValue: function(cfgObject,name) { 204 return this.getAttributeValue(cfgObject,this.uriGxe,name); 205 }, 206 207 /** 208 * Gets an attribute value within the GXE HTML namespace. 209 * <br/>(i.e. namespace "http://www.esri.com/geoportal/gxe/html") 210 * @example Example: 211 * gxe.cfg.getGxeHtmlAttributeValue(cfgObject,"maxlength"); 212 * @function 213 * @name getGxeHtmlAttributeValue 214 * @memberOf gxe.cfg 215 * @param {cfgObject} cfgObject the configuration object to process 216 * @param {String} name the configuration name of the attribute to find 217 * @return {Object} the attribute value (null if not found) 218 */ 219 getGxeHtmlAttributeValue: function(cfgObject,name) { 220 return this.getAttributeValue(cfgObject,this.uriGxeHtml,name); 221 }, 222 223 /** 224 * Gets the g:label attribute value associated with a configuration object. 225 * <br/>If null, the associated XML target name will be returned. 226 * @function 227 * @name getLabelText 228 * @memberOf gxe.cfg 229 * @param {cfgObject} cfgObject the configuration object to process 230 * @return {String} the attribute value (null if not found) 231 */ 232 getLabelText: function(cfgObject) { 233 var sLabel = this.getGxeAttributeValue(cfgObject,"label"); 234 if ((sLabel != null) && (sLabel.length > 0)) { 235 return sLabel; 236 } else { 237 return this.getGxeAttributeValue(cfgObject,"targetName"); 238 } 239 }, 240 241 /** 242 * Gets the g:maxOccurs attribute value associated with a configuration object. 243 * @function 244 * @name getMaxOccurs 245 * @memberOf gxe.cfg 246 * @param {cfgObject} cfgObject the configuration object to process 247 * @return {String} the attribute value (null if not found) 248 */ 249 getMaxOccurs: function(cfgObject) { 250 return this.getGxeAttributeValue(cfgObject,"maxOccurs"); 251 }, 252 253 /** 254 * Gets the g:minOccurs attribute value associated with a configuration object. 255 * @function 256 * @name getMinOccurs 257 * @memberOf gxe.cfg 258 * @param {cfgObject} cfgObject the configuration object to process 259 * @return {String} the attribute value (null if not found) 260 */ 261 getMinOccurs: function(cfgObject) { 262 return this.getGxeAttributeValue(cfgObject,"minOccurs"); 263 }, 264 265 /** 266 * Gets the name for the XML target associated with a configuration object. 267 * @function 268 * @name getTargetName 269 * @memberOf gxe.cfg 270 * @param {cfgObject} cfgObject the configuration object to process 271 * @return {String} the target name 272 */ 273 getTargetName: function(cfgObject) { 274 if (cfgObject.targetNameOverride != null) return cfgObject.targetNameOverride; 275 else return this.getGxeAttributeValue(cfgObject,"targetName"); 276 }, 277 278 /** 279 * Gets the namespace for the XML target associated with a configuration object. 280 * <br/>If the supplied configuration object was not directly configured 281 * with a g:targetNS attribute, then the value will be inherited from the first 282 * applicable ancestor. 283 * @function 284 * @name getTargetNS 285 * @memberOf gxe.cfg 286 * @param {cfgObject} cfgObject the configuration object to process 287 * @return {String} the target namespace 288 */ 289 getTargetNS: function(cfgObject) { 290 if (cfgObject.targetNSOverride != null) return cfgObject.targetNSOverride; 291 var cfgCheck = cfgObject; 292 while ((cfgCheck != undefined) && (cfgCheck != null)) { 293 var ns = this.getGxeAttributeValue(cfgCheck,"targetNS"); 294 if (ns != null) return ns; 295 cfgCheck = cfgCheck.parent; 296 } 297 }, 298 299 /** 300 * Initializes the configured definition for an editor. 301 * @function 302 * @name initialize 303 * @memberOf gxe.cfg 304 * @param {cfgObject} cfgDefinition the configured editor definition (JSON) 305 */ 306 initialize: function(cfgDefinition) { 307 this._initializeObject(cfgDefinition,null,null,false); 308 var namespaces = new gxe.xml.XmlNamespaces(); 309 var cfgRoot = gxe.cfg.findChild(cfgDefinition,this.uriGxe,"rootElement"); 310 if (cfgRoot != null) { 311 this._initializeObject(cfgRoot,null,null,false); 312 var cfgNamespaces = this.findChild(cfgRoot,this.uriGxe,"namespaces"); 313 if (cfgNamespaces != null) { 314 this._initializeObject(cfgNamespaces,cfgRoot,null,false); 315 gxe.cfg.forEachChild(cfgNamespaces,this.uriGxe,"namespace",dojo.hitch(this,function(cfgNS) { 316 this._initializeObject(cfgNS,cfgNamespaces,null,false); 317 var pfx = this.getGxeAttributeValue(cfgNS,"prefix"); 318 var uri = this.getGxeAttributeValue(cfgNS,"uri"); 319 namespaces.add(new gxe.xml.XmlNamespace(pfx,uri)); 320 })); 321 } 322 } 323 this._initializeObject(cfgDefinition,null,namespaces,true); 324 }, 325 326 // initializes a configuration object 327 _initializeObject: function(cfgObject,cfgParent,gxeNamespaces,bRecurse) { 328 cfgObject.parent = cfgParent; 329 var attributes = cfgObject.attributes; 330 var nAttributes = 0; 331 if (attributes != null) nAttributes = attributes.length; 332 for (var i=0; i<nAttributes; i++) { 333 var attribute = attributes[i]; 334 attribute.parent = cfgObject; 335 this._initializeObjectNS(attribute); 336 } 337 var children = cfgObject.children; 338 var nChildren = 0; 339 if (children != null) nChildren = children.length; 340 for (var i=0; i<nChildren; i++) { 341 var child = children[i]; 342 this._initializeObjectNS(child); 343 if (bRecurse) this._initializeObject(child,cfgObject,gxeNamespaces,true); 344 } 345 this._initializeTargetNS(cfgObject,gxeNamespaces); 346 }, 347 348 // initializes a configuration object name and namespace 349 _initializeObjectNS: function(cfgObject) { 350 var namespace = cfgObject.namespace; 351 var name = cfgObject.name; 352 var prefix = null; 353 var localName = name; 354 var nIdx = name.indexOf(":"); 355 if (nIdx != -1) { 356 prefix = name.substring(0,nIdx); 357 localName = name.substring(nIdx+1); 358 } 359 if (namespace == null) { 360 if (prefix == this.pfxGxe) { 361 cfgObject.namespace = this.uriGxe; 362 cfgObject.name = localName; 363 } else if (prefix == this.pfxHtml) { 364 cfgObject.namespace = this.uriGxeHtml; 365 cfgObject.name = localName; 366 } 367 } 368 }, 369 370 // initializes the XML target name and namespace associated with a configuration object 371 _initializeTargetNS: function(cfgObject,gxeNamespaces) { 372 if (gxeNamespaces != null) { 373 var namespace = this.getGxeAttributeValue(cfgObject,"targetNS"); 374 var name = this.getGxeAttributeValue(cfgObject,"targetName"); 375 if ((name != null) && (name.length > 0)) { 376 var nIdx = name.indexOf(":"); 377 if (nIdx != -1) { 378 var prefix = name.substring(0,nIdx); 379 var localName = name.substring(nIdx+1); 380 var uri = gxeNamespaces.getUri(prefix); 381 if (uri == null) { 382 } else if ((namespace != null) && (namespace != uri)) { 383 } else { 384 cfgObject.targetNSOverride = uri; 385 cfgObject.targetNameOverride = localName; 386 } 387 } 388 } 389 } 390 } 391 392 } 393 394 }; 395 396 /** 397 * @class Provides client functionality for executing AJAX calls to the server. 398 * @name gxe.Client 399 */ 400 dojo.provide("gxe.Client"); 401 dojo.declare("gxe.Client",null,{ 402 403 /** 404 * Handles an error condition. 405 * @function 406 * @name onError 407 * @memberOf gxe.Client# 408 * @param {Error} error the error 409 * @param {Object} ioArgs the Dojo i/o arguments 410 */ 411 onError: function(error,ioArgs) { 412 var msg = null; 413 if (ioArgs == null) { 414 msg = error.message; 415 } else { 416 if ((ioArgs.xhr.status >= 200) && (ioArgs.xhr.status < 300)) { 417 msg = error.message; 418 } else { 419 msg = " HTTP: " +ioArgs.xhr.status+", "+ioArgs.args.url; 420 } 421 } 422 if (msg != null) alert(msg); 423 }, 424 425 /** 426 * Loads a JSON based editor definition. 427 * <br/><br/>This method is geared towards the Geoportal Server end-point 428 * for loading an editor definition:<br/>[context path]/gxe/definition<br/> 429 * @function 430 * @name queryDefinition 431 * @memberOf gxe.Client# 432 * @param {gxe.Context} context the editor context 433 * @param {String} sParam a URL parameter name (key|loc) 434 * @param {String} sParamValue a URL parameter value 435 * <br/>when sParam="key", use the key for the standard (e.g. "fgdc") 436 * <br/>when sParam="loc", use the location (e.g. "gpt/gxe/fgdc/fgdc-editor.xml") 437 * @param {function} callback function to call once the definition has been 438 * successfully retrieved 439 * <br/>signature: function(responseObject,ioArgs) 440 * <br/>--- where responseObject is the JSON definition for the editor 441 */ 442 queryDefinition: function(context,sParam,sParamValue,callback) { 443 var u = context.contextPath+"/gxe/definition" 444 u += "?"+encodeURIComponent(sParam)+"="+encodeURIComponent(sParamValue)+"&f=json"; 445 dojo.xhrGet({ 446 handleAs: "json", 447 preventCache: true, 448 url: u, 449 error: dojo.hitch(this,"onError"), 450 load: dojo.hitch(this,function(responseObject,ioArgs) { 451 callback(responseObject,ioArgs); 452 }) 453 }); 454 }, 455 456 /** 457 * Loads an XML document. 458 * <br/><br/>This method is geared towards the Geoportal Server rest end-point 459 * for document management:<br/>[context path]/rest/manage/document<br/> 460 * @function 461 * @name queryDocument 462 * @memberOf gxe.Client# 463 * @param {gxe.Context} context the editor context 464 * @param {String} id the document identifier 465 * @param {function} callback function to call once the document has been 466 * successfully retrieved 467 * <br/>signature: function(responseObject,ioArgs) 468 * <br/>--- where responseObject is the XML DOM 469 */ 470 queryDocument: function(context,id,callback) { 471 var u = context.contextPath+"/rest/manage/document?id="+encodeURIComponent(id); 472 dojo.xhrGet({ 473 handleAs: "xml", 474 preventCache: true, 475 url: u, 476 error: dojo.hitch(this,"onError"), 477 load: dojo.hitch(this,function(responseObject,ioArgs) { 478 context.openDocumentId = id; 479 callback(responseObject,ioArgs); 480 }) 481 }); 482 }, 483 484 /** 485 * Saves an XML document. 486 * <br/><br/>This method is geared towards the Geoportal Server rest end-point 487 * for document management:<br/>[context path]/rest/manage/document<br/> 488 * @function 489 * @name saveDocument 490 * @memberOf gxe.Client# 491 * @param {gxe.Context} context the editor context 492 * @param {String} id the document identifier 493 * (can be null for documents that are internally identified) 494 * @param {String} sXml the XML to save 495 * @param {boolean} asDraft true if document is being saved as a draft 496 * @param {function} callback optional function to call once the save has 497 * successfully executed 498 * <br/>signature: function(responseObject,ioArgs) 499 */ 500 saveDocument: function(context,id,sXml,asDraft,callback) { 501 var u = context.contextPath+"/rest/manage/document?publicationMethod=editor&errorsAsJson=jErr"; 502 if (id == null) id = context.openDocumentId; 503 if (id == null) id = context.newDocumentId; 504 if ((id != null) && (id.length > 0)) u += "&id="+encodeURIComponent(id); 505 if (asDraft) u += "&asDraft=true"; 506 507 var dialog = new dijit.Dialog({ 508 title: context.getI18NString("client.saving.title"), 509 style: "width: 300px; display: none;" 510 }); 511 dojo.addClass(dialog.domNode,"tundra"); 512 dialog.show(); 513 514 dojo.xhrPut({ 515 handleAs: "text", 516 preventCache: true, 517 url: u, 518 putData: sXml, 519 error: dojo.hitch(this,function(errorObject,ioArgs) { 520 dialog.hide(); 521 dialog.destroy(); 522 this.onError(errorObject,ioArgs); 523 }), 524 load: dojo.hitch(this,function(responseObject,ioArgs) { 525 dialog.hide(); 526 dialog.destroy(); 527 try { 528 if (responseObject!=null) { 529 jErr = null; 530 eval(responseObject); 531 if (jErr!=null) { 532 if (jErr.errors!=null && jErr.errors.length>0) { 533 for (var m=0; m<jErr.errors.length; m++) { 534 context.messageArea.addError(jErr.errors[m]); 535 } 536 } else { 537 context.messageArea.addError(jErr.message); 538 } 539 jErr = null; 540 } 541 } 542 } catch (err) { 543 // handle eval error 544 } 545 if (typeof(callback) == "function") callback(responseObject,ioArgs); 546 }) 547 }); 548 } 549 550 }); 551 552 /** 553 * @class Provides a context for the editor. 554 * @name gxe.Context 555 * @property {String} contextPath the wep-app context path 556 * @property {cfgObject} cfgContext the g:context portion configured editor definition (JSON) 557 * @property {cfgObject} cfgDefinition the configured editor definition (JSON) 558 * @property {GptMapConfig} gptMapConfig the interactive map configuration 559 * @property {Element} htmlParentElement the parent HTML element 560 * @property {String} newDocumentId the ID to use for a newly created document 561 * @property {String} openDocumentId the ID for the document that was opened 562 * @property {gxe.xml.XmlDocument} xmlDocument the target XML document for the editor 563 * @property {String} idPrefix the prefix to use when generating HTML element IDs 564 * @property {gxe.control.MessageArea} messageArea the message area 565 */ 566 dojo.provide("gxe.Context"); 567 dojo.declare("gxe.Context",null,{ 568 contextPath: null, 569 cfgContext: null, 570 cfgDefinition: null, 571 gptMapConfig: null, 572 htmlParentElement: null, 573 newDocumentId: null, 574 openDocumentId: null, 575 xmlDocument: null, 576 idPrefix: "gxeId", 577 messageArea: null, 578 _uniqueId: 0, 579 580 /** 581 * Builds the editor user interface. 582 * @function 583 * @name buildUI 584 * @memberOf gxe.Context# 585 * @param {cfgObject} cfgDefinition the configured editor definition (JSON) 586 * @param {Element} htmlParentElement the parent HTML element (the editor 587 * will be appended to this parent) 588 * @param {DOM} domDocument the XML target document 589 * (can be null, used when opening an existing document) 590 */ 591 buildUI: function(cfgDefinition,htmlParentElement,domDocument) { 592 this.cfgDefinition = cfgDefinition; 593 this.cfgContext = gxe.cfg.findGxeChild(cfgDefinition,"context"); 594 this.htmlParentElement = htmlParentElement; 595 596 var elMessageArea = dojo.byId("gxeMessageArea"); 597 this.messageArea = new gxe.control.MessageArea(); 598 this.messageArea.context = this; 599 this.messageArea.build(dojo.byId(elMessageArea),null,null); 600 601 this.xmlDocument = new gxe.xml.XmlDocument(); 602 var xmlRoot = this.xmlDocument.initializeRoot(this,cfgDefinition); 603 var domProcessor = null; 604 var domRoot = null; 605 if (domDocument != null) { 606 var ndRoot = domDocument.documentElement; 607 var processor = new gxe.xml.DomProcessor(); 608 if (processor.isMatching(ndRoot,xmlRoot.cfgObject)) { 609 domProcessor = processor; 610 domRoot = ndRoot; 611 } else { 612 throw new Error("The XML root element does not match the editor definition."); 613 } 614 } 615 616 var ctl = this.makeXhtmlControl(xmlRoot.cfgObject,null,true); 617 ctl.xmlParentElement = null; 618 ctl.xmlNode = xmlRoot; 619 ctl.build(htmlParentElement,domProcessor,domRoot); 620 }, 621 622 /** 623 * Generates a unique ID. 624 * <br/>(String, prefixed with this.idPrefix). 625 * @function 626 * @name generateUniqueId 627 * @memberOf gxe.Context# 628 * @return {String} the ID 629 */ 630 generateUniqueId: function() { 631 this._uniqueId++; 632 return this.idPrefix+this._uniqueId; 633 }, 634 635 /** 636 * Gets a localized message string associated with the editor context. 637 * @function 638 * @name getI18NString 639 * @memberOf gxe.Context# 640 * @param {String} sKey the key for the message string 641 * @return {String} the message string 642 */ 643 getI18NString: function(sKey) { 644 var sValue = sKey; 645 if (this.cfgContext != null) { 646 gxe.cfg.forEachChild(this.cfgContext,gxe.cfg.uriGxe,"i18n",dojo.hitch(this,function(cfgChild) { 647 var sk = gxe.cfg.getGxeAttributeValue(cfgChild,"key"); 648 if (sk == sKey) { 649 sValue = cfgChild.value; 650 // return "break"; // quicker but disable to allow profile override 651 } 652 })); 653 } 654 return sValue; 655 }, 656 657 /** 658 * Makes a GXE HTML based user interface control. 659 * <br/>By default, a new gxe.control.Control object will be instantiated. If the supplied 660 * configuration object has a configured g:jsClass attribute, the attribute value will be used 661 * to instantiatethe control object (it is assumed that any supplied g:jsClass will extend from 662 * gxe.control.control). 663 * @function 664 * @name makeXhtmlControl 665 * @memberOf gxe.Context# 666 * @param {cfgObject} cfgObject the associated editor configuration object 667 * @param {gxe.control.Control} ctlParent the parent control 668 * @param {boolean} bInitialize if true then run new control's initialize function 669 * @return {String} the new control 670 */ 671 makeXhtmlControl: function(cfgObject,ctlParent,bInitialize) { 672 var ctl = null; 673 var sJsClass = gxe.cfg.getGxeAttributeValue(cfgObject,"jsClass"); 674 if ((sJsClass != null) && (sJsClass.length > 0)) { 675 ctl = eval("new "+sJsClass+"()"); 676 } else { 677 ctl = new gxe.control.Control(); 678 } 679 ctl.parentControl = ctlParent; 680 if (bInitialize) { 681 ctl.initialize(this,cfgObject); 682 } 683 return ctl; 684 } 685 686 }); 687 688 689 /* Utility classes =================================================== */ 690 691 /** 692 * @class Simulates some methods associated with an ArrayList data structure. 693 * @name gxe.util.ArrayList 694 * @property {Array} _array The underlying JavaScript array. 695 */ 696 dojo.provide("gxe.util.ArrayList"); 697 dojo.declare("gxe.util.ArrayList",null,{ 698 _array: null, 699 700 /** @constructor */ 701 constructor: function() { 702 this._array = new Array(); 703 }, 704 705 /** 706 * Appends an object to the collection (same as push()). 707 * @function 708 * @name add 709 * @memberOf gxe.util.ArrayList# 710 * @param {Object} obj the object to add 711 */ 712 add: function(obj) { 713 this._array.push(obj); 714 }, 715 716 /** 717 * Gets the item at the specified index. 718 * @function 719 * @name getItem 720 * @memberOf gxe.util.ArrayList# 721 * @param {Integer} nIndex the index 722 * @returns {Object} the corresponding object 723 */ 724 getItem: function(nIndex) { 725 return this._array[nIndex]; 726 }, 727 728 /** 729 * Gets the length of the array. 730 * @function 731 * @name getLength 732 * @memberOf gxe.util.ArrayList# 733 * @returns {Integer} the length 734 */ 735 getLength: function() { 736 return this._array.length; 737 }, 738 739 /** 740 * Inserts an object at a specified index. 741 * @function 742 * @name insertAt 743 * @memberOf gxe.util.ArrayList# 744 * @param {Integer} nIndex the index (same as JavaScript Array.splice) 745 * @param {Object} obj the object to insert 746 */ 747 insertAt: function(nIndex,obj) { 748 this._array.splice(nIndex,0,obj); 749 }, 750 751 /** 752 * Appends an object to the collection. 753 * @function 754 * @name push 755 * @memberOf gxe.util.ArrayList# 756 * @param {Object} obj the object to add 757 */ 758 push: function(obj) { 759 this._array.push(obj); 760 }, 761 762 /** 763 * Removes the object at the specified index from the collection. 764 * @function 765 * @name removeIndex 766 * @memberOf gxe.util.ArrayList# 767 * @param {Integer} nIndex the index of the object to remove 768 */ 769 removeIndex: function(nIndex) { 770 this._array.splice(nIndex,1); 771 }, 772 773 /** 774 * Swaps the positions of two objects within the collection. 775 * @function 776 * @name swapPosition 777 * @memberOf gxe.util.ArrayList# 778 * @param {Integer} nFromIndex the from index 779 * @param {Integer} nToIndex the to index 780 */ 781 swapPosition: function(nFromIndex,nToIndex) { 782 var a = this._array[nFromIndex]; 783 var b = this._array[nToIndex]; 784 this._array[nFromIndex] = b; 785 this._array[nToIndex] = a; 786 } 787 788 }); 789 790 /** 791 * @class Simulates some methods associated with an StringBuffer data structure. 792 * @name gxe.util.StringBuffer 793 * @property {String} _text The underlying JavaScript String. 794 */ 795 dojo.provide("gxe.util.StringBuffer"); 796 dojo.declare("gxe.util.StringBuffer",null,{ 797 _text: "", 798 799 /** 800 * Constructor. 801 * @function 802 * @name constructor 803 * @constructor 804 * @memberOf gxe.util.StringBuffer# 805 * @param {String} text the initial text 806 * @returns {gxe.util.StringBuffer} the new instance 807 */ 808 constructor: function(text) { 809 if (text != null) this._text = text; 810 }, 811 812 /** 813 * Appends a string. 814 * @function 815 * @name append 816 * @memberOf gxe.util.StringBuffer# 817 * @param {String} s the string to append 818 * @returns {gxe.util.StringBuffer} this instance 819 */ 820 append: function(s) { 821 this._text += s; 822 return this; 823 }, 824 825 /** 826 * Returns the associated string. 827 * @function 828 * @name toString 829 * @memberOf gxe.util.StringBuffer# 830 * @returns {String} this string 831 */ 832 toString: function() { 833 return this._text; 834 } 835 }); 836 837 /** 838 * @class Represents an HTML based attribute. 839 * @name gxe.html.HtmlAttribute 840 * @property {String} name The attribute name. 841 * @property {Object} value The attribute value. 842 */ 843 dojo.provide("gxe.html.HtmlAttribute"); 844 dojo.declare("gxe.html.HtmlAttribute",null,{ 845 name: null, 846 value: null 847 }); 848 849 /** 850 * @class A collection of HTML based attributes. 851 * @name gxe.html.HtmlAttributes 852 * @extends gxe.util.ArrayList 853 */ 854 dojo.provide("gxe.html.HtmlAttributes"); 855 dojo.declare("gxe.html.HtmlAttributes",gxe.util.ArrayList,{ 856 857 /** 858 * Applies the attribute collection to an HTML DOM Element. 859 * (i.e. sets all attribute values) 860 * @function 861 * @name apply 862 * @memberOf gxe.html.HtmlAttributes# 863 * @param {Element} elHtml the corresponding HTML DOM Element 864 */ 865 apply: function(elHtml) { 866 var n = this.getLength(); 867 for (var i=0; i<n; i++) { 868 var attr = this.getItem(i); 869 if (attr != null) { 870 if ((attr.name != null) && (attr.value != null)) { 871 var value = attr.value; 872 if (typeof(value) =="string") { 873 if (value.indexOf("$fire.") == 0) value = null; 874 } 875 if (value != null) { 876 var s = attr.name.toLowerCase(); 877 if (s != "tag") { 878 elHtml.setAttribute(attr.name,value); 879 if (dojo.isIE <= 8) { 880 if (attr.name == "class") { 881 elHtml.className = value; 882 // 883 } else if (attr.name = "readonly") { 884 //elHtml.readOnly = true; 885 } 886 } 887 } 888 } 889 } 890 } 891 } 892 }, 893 894 /** 895 * Finds an attribute with given name. 896 * @function 897 * @name find 898 * @memberOf gxe.html.HtmlAttributes# 899 * @param {String} name the name of the attribute to find 900 * @returns {gxe.html.HtmlAttribute} the corresponding attribute (null if not found) 901 */ 902 find: function(name) { 903 if (name != null) { 904 name = dojo.trim(name); 905 if (name.length > 0) { 906 var lc = name.toLowerCase(); 907 var n = this.getLength(); 908 for (var i=0; i<n; i++) { 909 var attr = this.getItem(i); 910 if (attr != null) { 911 if ((attr.name != null) && (attr.name.toLowerCase() == lc)) { 912 return attr; 913 } 914 } 915 } 916 } 917 } 918 return null; 919 }, 920 921 /** 922 * Adds an HTML attribute to the collection. 923 * If an attribute with the supplied name previously exists, its value will be updated. 924 * @function 925 * @name set 926 * @memberOf gxe.html.HtmlAttributes# 927 * @param {String} name the name of the attribute 928 * @param {Object} value the value of the attribute 929 */ 930 set: function(name,value) { 931 if (name != null) { 932 name = dojo.trim(name); 933 if (name.length > 0) { 934 var attr = this.find(name); 935 if (attr != null) { 936 attr.value = value; 937 } else { 938 attr = new gxe.html.HtmlAttribute(); 939 attr.name = name; 940 attr.value = value; 941 this.add(attr); 942 } 943 } 944 } 945 } 946 }); 947 948 949 /* XML related classes =============================================== */ 950 951 /** 952 * @class Provides utility functions for processing an XML document. 953 * <br/>(used when opening an existing document) 954 * @name gxe.xml.DomProcessor 955 */ 956 dojo.provide("gxe.xml.DomProcessor"); 957 dojo.declare("gxe.xml.DomProcessor",null,{ 958 959 // some new methods have been added that can be fully leveraged at a later date 960 // forEachElementNode, forEachMatchingElementNode, splitQualifiedName 961 962 // DOM node types 963 nodeTypes: { 964 ELEMENT_NODE: 1, 965 ATTRIBUTE_NODE: 2, 966 TEXT_NODE: 3, 967 CDATA_SECTION_NODE: 4, 968 ENTITY_REFERENCE_NODE: 5, 969 ENTITY_NODE: 6, 970 PROCESSING_INSTRUCTION_NODE: 7, 971 COMMENT_NODE: 8, 972 DOCUMENT_NODE: 9, 973 DOCUMENT_TYPE_NODE: 10, 974 DOCUMENT_FRAGMENT_NODE: 11, 975 NOTATION_NODE: 12 976 }, 977 978 // this function is not in use, development only 979 buildUI: function(context,cfgDefinition,htmlParentElement,sXml) { 980 if (window.DOMParser) { 981 var parser = new DOMParser(); 982 var dom = parser.parseFromString(sXml,"text/xml"); 983 context.buildUI(cfgDefinition,htmlParentElement,dom); 984 } else if (window.ActiveXObject) { 985 var dom = new ActiveXObject("MSXML2.DOMDocument"); 986 dom.async = "false"; 987 dom.loadXML(sXml); 988 context.buildUI(cfgDefinition,htmlParentElement,dom); 989 } 990 }, 991 992 /** 993 * Attempts to find the attribute of a DOM Node that matches the XML target 994 * associated with an editor configuration object. 995 * @see gxe.xml.DomProcessor#isMatching 996 * @function 997 * @name findMatchingChildAttribute 998 * @memberOf gxe.xml.DomProcessor# 999 * @param {Node} domParentNode the DOM node whose attributes will be searched 1000 * @param {Object} cfgChild the editor configuration object that 1001 * will be used to determine a match 1002 * @returns {Node} a matching DOM attribute (null if no match) 1003 */ 1004 findMatchingChildAttribute: function(domParentNode,cfgChild) { 1005 var attributes = domParentNode.attributes; 1006 if ((attributes != null) && (attributes.length > 0)) { 1007 var n = attributes.length; 1008 for (var i=0; i<n; i++) { 1009 var attribute = attributes.item(i); 1010 if (this.isMatching(attribute,cfgChild)) return attribute; 1011 } 1012 } 1013 return null; 1014 }, 1015 1016 /** 1017 * Attempts to find an immediate child element of a DOM Node that matches 1018 * the XML target associated with an editor configuration object. 1019 * @see gxe.xml.DomProcessor#isMatching 1020 * @function 1021 * @name findMatchingChildElement 1022 * @memberOf gxe.xml.DomProcessor# 1023 * @param {Node} domParentNode the DOM node whose children will be searched 1024 * @param {Object} cfgChild the editor configuration object that 1025 * will be used to determine a match 1026 * @returns {Node} a matching DOM element (null if no match) 1027 */ 1028 findMatchingChildElement: function(domParentNode,cfgChild) { 1029 var children = domParentNode.childNodes; 1030 if ((children != null) && (children.length > 0)) { 1031 var n = children.length; 1032 for (var i=0; i<n; i++) { 1033 var child = children[i]; 1034 if (child.nodeType == this.nodeTypes.ELEMENT_NODE) { 1035 if (this.isMatching(child,cfgChild)) return child; 1036 } 1037 } 1038 } 1039 return null; 1040 }, 1041 1042 /** 1043 * Attempts to find the immediate child elements of a DOM Node that match 1044 * the XML target associated with an editor configuration object. 1045 * @see gxe.xml.DomProcessor#isMatching 1046 * @function 1047 * @name findMatchingChildElements 1048 * @memberOf gxe.xml.DomProcessor# 1049 * @param {Node} domParentNode the DOM node whose children will be searched 1050 * @param {Object} cfgChild the editor configuration object that 1051 * will be used to determine a match 1052 * @returns {Node[]} the matching DOM elements (null if no match) 1053 */ 1054 findMatchingChildElements: function(domParentNode,cfgChild) { 1055 var matches = null; 1056 var children = domParentNode.childNodes; 1057 if ((children != null) && (children.length > 0)) { 1058 var n = children.length; 1059 for (var i=0; i<n; i++) { 1060 var child = children[i]; 1061 if (child.nodeType == this.nodeTypes.ELEMENT_NODE) { 1062 if (this.isMatching(child,cfgChild)) { 1063 if (matches == null) matches = new Array(); 1064 matches.push(child); 1065 } 1066 } 1067 } 1068 } 1069 return matches; 1070 }, 1071 1072 /** 1073 * Executes a function for each immediate child element of a DOM Node. 1074 * <br/>Only child elements of nodeType=1 (ELEMENT_NODE) will be considered. 1075 * <br/> 1076 * <br/>The callback function will have the following signature: function(domChildNode) {} 1077 * <br/>The callback function can return the String "break" to terminate the loop. 1078 * @function 1079 * @name forEachElementNode 1080 * @memberOf gxe.xml.DomProcessor# 1081 * @param {Node} domParentNode the DOM node whose children will be searched 1082 * @param {function} callback the callback function 1083 */ 1084 forEachElementNode: function(domParentNode,callback) { 1085 var children = domParentNode.childNodes; 1086 if ((children != null) && (children.length > 0)) { 1087 var n = children.length; 1088 for (var i=0; i<n; i++) { 1089 var child = children[i]; 1090 if (child.nodeType == this.nodeTypes.ELEMENT_NODE) { 1091 var _ret = callback(child); 1092 if ((typeof(_ret) == "string") && (_ret == "break")) break; 1093 } 1094 } 1095 } 1096 }, 1097 1098 1099 /** 1100 * Executes a function for each immediate child element of a DOM Node that matches 1101 * a supplied namespace and loca name. 1102 * <br/>Only child elements of nodeType=1 (ELEMENT_NODE) will be considered. 1103 * <br/> 1104 * <br/>The callback function will have the following signature: function(domChildNode) {} 1105 * <br/>The callback function can return the String "break" to terminate the loop. 1106 * @function 1107 * @name forEachMatchingElementNode 1108 * @memberOf gxe.xml.DomProcessor# 1109 * @param {Node} domParentNode the DOM node whose children will be searched 1110 * @param {String} sNamespaceUri the namespace URI to match (can be null) 1111 * @param {String} sLocalName the local node name to match (i.e unqualified name) 1112 * @param {function} callback the callback function 1113 */ 1114 forEachMatchingElementNode: function(domParentNode,sNamespaceUri,sLocalName,callback) { 1115 var targetNS = sNamespaceUri; 1116 if ((targetNS != null) && (targetNS.length == 0)) targetNS = null; 1117 this.forEachElementNode(domParentNode,dojo.hitch(this,function(domChildNode) { 1118 if (domChildNode.namespaceURI == targetNS) { 1119 var pfxPlusLocal = this.splitQualifiedName(domChildNode.nodeName); 1120 if (pfxPlusLocal.localName == sLocalName) { 1121 var _ret = callback(domChildNode); 1122 if ((typeof(_ret) == "string") && (_ret == "break")) return "break"; 1123 } 1124 } 1125 })); 1126 }, 1127 1128 /** 1129 * Gets the text content of a DOM Node (element or attribute). 1130 * @function 1131 * @name getNodeText 1132 * @memberOf gxe.xml.DomProcessor# 1133 * @param {Node} domNode the DOM node that is actively being processed 1134 * @returns {String} the text content (can be null) 1135 */ 1136 getNodeText: function(domNode) { 1137 var s; 1138 if (domNode.nodeType == this.nodeTypes.ELEMENT_NODE) { 1139 var children = domNode.childNodes; 1140 if ((children != null) && (children.length > 0)) { 1141 var n = children.length; 1142 for (var i=0; i<n; i++) { 1143 var child = children[i]; 1144 if (child.nodeType == this.nodeTypes.TEXT_NODE) { 1145 s = child.nodeValue; 1146 if (s != null) s = dojo.trim(s); 1147 return s; 1148 } 1149 } 1150 } 1151 return ""; 1152 } else { 1153 s = domNode.nodeValue; 1154 if (s != null) s = dojo.trim(s); 1155 return s; 1156 } 1157 return null; 1158 }, 1159 1160 /** 1161 * Determines if a DOM Node has either: attributes, child elements or element text. 1162 * @function 1163 * @name hasChildrenOrAttributes 1164 * @memberOf gxe.xml.DomProcessor# 1165 * @param {Node} domNode the DOM node that is actively being processed 1166 * @returns {boolean} true if there is a match 1167 */ 1168 hasChildrenOrAttributes: function(domNode) { 1169 if (domNode != null) { 1170 var attributes = domNode.attributes; 1171 if ((attributes != null) && (attributes.length > 0)) return true; 1172 var children = domNode.childNodes; 1173 if ((children != null) && (children.length > 0)) { 1174 var n = children.length; 1175 for (var i=0; i<n; i++) { 1176 if (children[i].nodeType == this.nodeTypes.ELEMENT_NODE) { 1177 return true; 1178 } else if (children[i].nodeType == this.nodeTypes.TEXT_NODE) { 1179 var s = children[i].nodeValue; 1180 if ((s != null) && (dojo.trim(s).length > 0)) return true; 1181 } 1182 } 1183 } 1184 } 1185 return false; 1186 }, 1187 1188 /** 1189 * Determines if the qualified name associated with a DOM Node matches the XML target 1190 * associated with an editor configuration object. 1191 * <br/>Editor configuration example for referencing a target XML element:<br/> 1192 * <g:element g:targetName="pfx:name" .. 1193 * @function 1194 * @name isMatching 1195 * @memberOf gxe.xml.DomProcessor# 1196 * @param {Node} domNode the DOM node that is actively being processed 1197 * @param {Object} cfgObject the associated editor configuration object 1198 * @returns {boolean} true if there is a match 1199 */ 1200 isMatching: function(domNode,cfgObject) { 1201 var targetNS = gxe.cfg.getTargetNS(cfgObject); 1202 if ((targetNS != null) && (targetNS.length == 0)) targetNS = null; 1203 1204 if (dojo.isIE <= 8) { 1205 if (targetNS == null) targetNS = ""; 1206 } 1207 1208 if (domNode.namespaceURI == targetNS) { 1209 var targetName = gxe.cfg.getTargetName(cfgObject); 1210 var nodeName = domNode.nodeName; 1211 var prefix = null; 1212 var localName = nodeName; 1213 var nIdx = nodeName.indexOf(":"); 1214 if (nIdx != -1) { 1215 prefix = nodeName.substring(0,nIdx); 1216 localName = nodeName.substring(nIdx+1); 1217 } 1218 if (localName == targetName) return true; 1219 } 1220 return false; 1221 }, 1222 1223 /** 1224 * Determines if there is a match between a supplied DOM Node and a descendant condition. 1225 * <br/><br/>Note: matchTopElement does not support full XPath expressions 1226 * @function 1227 * @name matchTopElement 1228 * @memberOf gxe.xml.DomProcessor# 1229 * @param {gxe.xml.XmlNamespaces} xmlNamespaces a configured list of target namespaces 1230 * @param {Node} domNode the DOM node that is actively being processed 1231 * @param {String} sMatchPath the relative path for the element to match 1232 * (a simple path relative to the supplied parent DOM node) 1233 * @param {String} sMatchTextNodeValue the text node value to match 1234 * (null indicates no test should be made) 1235 * @param {boolean} bMust true indicates that there must be a match 1236 * (false indicates must not) 1237 * @returns {boolean} true if there is a match 1238 */ 1239 1240 matchTopElement: function(xmlNamespaces,domNode,sMatchPath,sMatchTextNodeValue,bMust) { 1241 var tokens = sMatchPath.split("/"); 1242 var nTokens = tokens.length; 1243 var domFinalMatches = new Array(); 1244 var domCurrentNodes = new Array(); 1245 domCurrentNodes.push(domNode); 1246 1247 for (var i=0; i<nTokens; i++) { 1248 var bIsLast = (i == (nTokens - 1)); 1249 var uri = null; 1250 var pfxPlusLocal = this.splitQualifiedName(tokens[i]); 1251 var localName = pfxPlusLocal.localName; 1252 if (pfxPlusLocal.prefix != null) { 1253 uri = xmlNamespaces.getUri(pfxPlusLocal.prefix); 1254 } 1255 1256 var domCurrentMatches = new Array(); 1257 for (var j=0; j<domCurrentNodes.length; j++) { 1258 this.forEachMatchingElementNode(domCurrentNodes[j],uri,localName, 1259 dojo.hitch(this,function(domChildNode) { 1260 if (bIsLast) { 1261 if (sMatchTextNodeValue == null) { 1262 domCurrentMatches.push(domChildNode); 1263 } else { 1264 var s = this.getNodeText(domChildNode); 1265 if (s == sMatchTextNodeValue) { 1266 domCurrentMatches.push(domChildNode); 1267 } 1268 } 1269 } else { 1270 domCurrentMatches.push(domChildNode); 1271 } 1272 } 1273 )); 1274 } 1275 domCurrentNodes = domCurrentMatches; 1276 if (domCurrentNodes.length == 0) break; 1277 if (bIsLast) domFinalMatches = domCurrentNodes; 1278 } 1279 1280 if (bMust) return (domFinalMatches.length > 0); 1281 else return (domFinalMatches.length == 0); 1282 }, 1283 1284 /** 1285 * Splits a qualified name into a prefix plus localName pair. 1286 * @function 1287 * @name splitQualifiedName 1288 * @memberOf gxe.xml.DomProcessor# 1289 * @param {String} sQualifiedName the qualified name 1290 * @returns {"prefix":{String}, "localName":{String}} the prefix plus localName pair 1291 */ 1292 splitQualifiedName: function(sQualifiedName) { 1293 var prefixPlusLocalName = {"prefix": null, "localName": sQualifiedName}; 1294 var tokens = sQualifiedName.split(":"); 1295 if (tokens.length == 2) { 1296 prefixPlusLocalName.prefix = tokens[0]; 1297 prefixPlusLocalName.localName = tokens[1]; 1298 } 1299 return prefixPlusLocalName; 1300 } 1301 1302 }); 1303 1304 /** 1305 * @class Serializes an XML document. 1306 * @name gxe.xml.Generator 1307 * @property {gxe.Context} context the editor context 1308 * @property {String} documentTitle the document title 1309 * @property {boolean} hadValidationErrors true if validation errors were encountered 1310 * @property {boolean} isValidating true if all content should be validated 1311 * @property {boolean} isValidatingTitleOnly true if only the title should be validated 1312 * @property {boolean} isSaveAsDraft true if serialization is for a draft document 1313 */ 1314 dojo.provide("gxe.xml.Generator"); 1315 dojo.declare("gxe.xml.Generator",null,{ 1316 context: null, 1317 documentTitle: null, 1318 hadValidationErrors: false, 1319 isValidating: false, 1320 isValidatingTitleOnly: false, 1321 isSaveAsDraft: false, 1322 1323 /** 1324 * Executes the text content of an XML attribute. 1325 * @function 1326 * @name escAttribute 1327 * @memberOf gxe.xml.Generator# 1328 * @param {String} s the string to escape 1329 * @returns {String} the escaped string 1330 */ 1331 escAttribute: function(s) { 1332 return this._execEscape(s,true); 1333 }, 1334 1335 /** 1336 * Executes the text content of an XML element. 1337 * @function 1338 * @name escElement 1339 * @memberOf gxe.xml.Generator# 1340 * @param {String} s the string to escape 1341 * @returns {String} the escaped string 1342 */ 1343 escElement: function(s) { 1344 return this._execEscape(s,true); 1345 }, 1346 1347 /** 1348 * Executes and XML escape against a string. 1349 * @function 1350 * @name _execEscape 1351 * @memberOf gxe.xml.Generator# 1352 * @param {String} s the string to escape 1353 * @param {boolean} bEscapeApostrophe true if apostrophies should be escaped 1354 * @returns {String} the escaped string 1355 */ 1356 _execEscape: function(s,bEscapeApostrophe) { 1357 if (s == null) { 1358 return null; 1359 } else if (s.length == 0) { 1360 return s; 1361 } else { 1362 var sApos = "'"; 1363 if (!bEscapeApostrophe) sApos = "'"; 1364 var sb = ""; 1365 for (var i=0; i<s.length; i++) { 1366 var c = s.charAt(i); 1367 if (c == "&") sb += "&"; 1368 else if (c == "<") sb += "<"; 1369 else if (c == '>') sb += ">"; 1370 else if (c == '\'') sb += sApos; 1371 else if (c == '"') sb += """; 1372 else sb += c; 1373 } 1374 return sb; 1375 } 1376 }, 1377 1378 /** 1379 * Serializes an XML document. 1380 * @function 1381 * @name generate 1382 * @memberOf gxe.xml.Generator# 1383 * @param {gxe.Context} context the editor context 1384 * @param {boolean} asDraft true if the serialization is for a draft document 1385 * (minimal validation) 1386 * @returns {String} the serialized string 1387 */ 1388 generate: function(context,asDraft) { 1389 this.context = context; 1390 this.documentTitle = null; 1391 this.isValidating = true; 1392 this.isValidatingTitleOnly = false; 1393 this.isSaveAsDraft = asDraft; 1394 this.context.messageArea.clearAll(); 1395 var stringBuffer = new gxe.util.StringBuffer(); 1396 context.xmlDocument.rootElement.echo(this,stringBuffer,0); 1397 1398 var sXml = stringBuffer.toString(); 1399 sXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+sXml; 1400 return sXml; 1401 }, 1402 1403 /** 1404 * Handles a validation error. 1405 * @function 1406 * @name handleValidationError 1407 * @memberOf gxe.xml.Generator# 1408 * @param {sMessage} sMessage the message 1409 * @param {gxe.xml.XmlNode} xmlNode the target XML node that threw the error 1410 * @returns {gxe.control.InputBase} inputControl the input control for the target node 1411 */ 1412 handleValidationError: function(sMessage,xmlNode,inputControl) { 1413 var bHadErrors = false; 1414 if (!this.isSaveAsDraft) { 1415 this.hadValidationErrors = true; 1416 this.context.messageArea.addValidationError(sMessage,xmlNode,inputControl); 1417 } else { 1418 var bIsTitle = xmlNode.nodeInfo.isDocumentTitle; 1419 if (bIsTitle) { 1420 this.hadValidationErrors = true; 1421 this.context.messageArea.addValidationError(sMessage,xmlNode,inputControl); 1422 } 1423 } 1424 if (!bHadErrors && this.hadValidationErrors) { 1425 dojo.addClass(this.context.htmlParentElement,"gxeRepairMode"); 1426 } 1427 } 1428 1429 }); 1430 1431 /** 1432 * @class A target XML document. 1433 * @name gxe.xml.XmlDocument 1434 * @property {gxe.Context} context the editor context 1435 * @property {gxe.xml.XmlNamespaces} namespaces the namespaces associated with the document 1436 * @property {gxe.xml.XmlElement} rootElement the root element for the document 1437 */ 1438 dojo.provide("gxe.xml.XmlDocument"); 1439 dojo.declare("gxe.xml.XmlDocument",null,{ 1440 context: null, 1441 namespaces: null, 1442 rootElement: null, 1443 1444 /** constructor */ 1445 constructor: function() { 1446 this.namespaces = new gxe.xml.XmlNamespaces(); 1447 }, 1448 1449 /** 1450 * Initializes the root element and XML namespaces. 1451 * @function 1452 * @name initializeRoot 1453 * @memberOf gxe.xml.XmlDocument# 1454 * @param {gxe.Context} context the editor context 1455 * @param {Object} cfgDefinition the configured editor definition (JSON) 1456 * @returns {gxe.xml.XmlElement} the root element for the document 1457 */ 1458 initializeRoot: function(context,cfgDefinition) { 1459 this.context = context; 1460 this.namespaces = new gxe.xml.XmlNamespaces(); 1461 1462 var cfgRoot = gxe.cfg.findGxeChild(cfgDefinition,"rootElement"); 1463 if (cfgRoot == null) { 1464 throw new Error("The editor definition contains no g:rootElement."); 1465 } 1466 1467 var cfgItems = gxe.cfg.findChild(cfgRoot,gxe.cfg.uriGxe,"namespaces"); 1468 if (cfgItems != null) { 1469 gxe.cfg.forEachChild(cfgItems,gxe.cfg.uriGxe,"namespace",dojo.hitch(this,function(cfgNS) { 1470 var pfx = gxe.cfg.getGxeAttributeValue(cfgNS,"prefix"); 1471 var uri = gxe.cfg.getGxeAttributeValue(cfgNS,"uri"); 1472 this.namespaces.add(new gxe.xml.XmlNamespace(pfx,uri)); 1473 })); 1474 } 1475 1476 this.rootElement = new gxe.xml.XmlElement(this,null,cfgRoot); 1477 1478 // assume all namespace prefixes are specified on the root node 1479 var n = this.namespaces.getLength(); 1480 for (var i=0; i<n; i++) { 1481 var ns = this.namespaces.getItem(i); 1482 if ((ns != null) && (ns.uri != null) && (ns.uri.length > 0)) { 1483 var ni = new gxe.xml.XmlNodeInfo(); 1484 if ((ns.prefix != null) && (ns.prefix.length > 0)) { 1485 ni.namespacePrefix ="xmlns"; 1486 ni.localName = ns.prefix; 1487 ni.nodeValue = ns.uri; 1488 } else { 1489 ni.localName ="xmlns"; 1490 ni.nodeValue = ns.uri; 1491 } 1492 var attr = new gxe.xml.XmlAttribute(this,this.rootElement,null); 1493 attr.nodeInfo = ni; 1494 this.rootElement.attributes.add(attr); 1495 } 1496 } 1497 1498 return this.rootElement; 1499 } 1500 1501 }); 1502 1503 /** 1504 * @class A target XML node (element or attribute). 1505 * @name gxe.xml.XmlNode 1506 * @property {Object} cfgObject the associated editor configuration object 1507 * @property {boolean} isOptionalPlaceHolder true if this node is an optional place holder only 1508 * (no serialization) 1509 * @property {boolean} isPlaceHolder true if this node is a place holder only 1510 * (no serialization) 1511 * @property {gxe.xml.XmlNodeInfo} nodeInfo the node information 1512 * @property {gxe.xml.XmlDocument} parentDocument the parent XML document 1513 * @property {Element} parentElement the parent HTML element 1514 */ 1515 dojo.provide("gxe.xml.XmlNode"); 1516 dojo.declare("gxe.xml.XmlNode",null,{ 1517 _dataExclusiveRadioButton: null, 1518 _dataInputControl: null, 1519 cfgObject: null, 1520 isOptionalPlaceHolder: false, 1521 isPlaceHolder: false, 1522 nodeInfo: null, 1523 parentDocument: null, 1524 parentElement: null, 1525 1526 /** 1527 * Constructor. 1528 * @function 1529 * @name constructor 1530 * @memberOf gxe.xml.XmlNode# 1531 * @param {gxe.xml.XmlDocument} parentDocument the parent XML document 1532 * @param {Element} parentElement the parent HTML element 1533 * @param {Object} cfgObject the associated editor configuration object 1534 */ 1535 constructor: function(parentDocument,parentElement,cfgObject) { 1536 this.parentDocument = parentDocument; 1537 this.parentElement = parentElement; 1538 this.cfgObject = cfgObject; 1539 this.intitalizeNodeInfo(); 1540 }, 1541 1542 /** 1543 * Serializes XML content. 1544 * @function 1545 * @name echo 1546 * @memberOf gxe.xml.XmlNode# 1547 * @param {gxe.xml.Generator} xmlGenerator the XML generator 1548 * @param {gxe.util.StringBuffer} stringBuffer the buffer to which content will be written 1549 * @param {Integer} nDepth the indentation depth 1550 */ 1551 echo: function(xmlGenerator,stringBuffer,nDepth) {}, 1552 1553 /** 1554 * Formats a validation message. 1555 * @function 1556 * @name formatValidationMessage 1557 * @memberOf gxe.xml.XmlNode# 1558 * @param {gxe.control.InputBase} inputControl the associated input control 1559 * @param {String} i18nContextKey the context key associated with the localized message string 1560 * @returns {String} the message 1561 */ 1562 formatValidationMessage: function(inputControl,i18nContextKey) { 1563 var l = inputControl.findParentLabelText(this); 1564 var f = this.parentDocument.context.getI18NString("validate.format"); 1565 var m = this.parentDocument.context.getI18NString(i18nContextKey); 1566 var s = l+" "+m; 1567 if (f != null) { 1568 if ((f.indexOf("{label}") != -1) && (f.indexOf("{message}") != -1)) { 1569 s = f.replace("{label}",l).replace("{message}",m); 1570 } 1571 } 1572 1573 /* 1574 if ((i18nContextKey != "validate.ok") && (inputControl.htmlElement != null)) { 1575 var sTip = inputControl.htmlElement.title; 1576 if ((typeof(sTip) != "undefined") && (sTip != null)) { 1577 sTip = dojo.trim(sTip); 1578 if (sTip.length > 0) s += " "+sTip; 1579 } 1580 } 1581 */ 1582 1583 return s; 1584 }, 1585 1586 /** 1587 * Indicates if this node should be serialized even if its content is empty. 1588 * <br/>Based upon cfg attribute g:serializeIfEmpty. 1589 * @function 1590 * @name getSerializeIfEmpty 1591 * @memberOf gxe.xml.XmlNode# 1592 * @returns {boolean} true if this node should be serialized when empty 1593 */ 1594 getSerializeIfEmpty: function() { 1595 if (this.cfgObject == null) return false; 1596 var s = gxe.cfg.getGxeAttributeValue(this.cfgObject,"serializeIfEmpty"); 1597 return (s == "true"); 1598 }, 1599 1600 1601 /** this pair no longer in use */ 1602 getExclusiveRadioButton: function() {return this._dataExclusiveRadioButton;}, 1603 setExclusiveRadioButton: function(ctl) {this._dataExclusiveRadioButton = ctl;}, 1604 1605 /** 1606 * Gets the input control associated with this node. 1607 * @function 1608 * @name getInputControl 1609 * @memberOf gxe.xml.XmlNode# 1610 * @returns {gxe.control.InputBase} the input control (can be null) 1611 */ 1612 getInputControl: function() {return this._dataInputControl;}, 1613 1614 /** 1615 * Sets the input control associated with this node. 1616 * @function 1617 * @name setInputControl 1618 * @memberOf gxe.xml.XmlNode# 1619 * @param {gxe.control.InputBase} ctl the input control 1620 */ 1621 setInputControl: function(ctl) {this._dataInputControl = ctl;}, 1622 1623 /** 1624 * Gets the default label text for the node. 1625 * @function 1626 * @name getLabelText 1627 * @memberOf gxe.xml.XmlNode# 1628 * @returns {String} the label text 1629 */ 1630 getLabelText: function() { 1631 return gxe.cfg.getLabelText(this.cfgObject); 1632 }, 1633 1634 /** 1635 * Gets the qualified URI for the node. 1636 * <br/>Format: namespaceURI#localName 1637 * @function 1638 * @name getQualifiedUri 1639 * @memberOf gxe.xml.XmlNode# 1640 * @returns {String} the qualified URI 1641 */ 1642 getQualifiedUri: function() { 1643 return this.nodeInfo.namespaceURI+"#"+this.nodeInfo.localName; 1644 }, 1645 1646 /** 1647 * Initializes the node info. 1648 * @function 1649 * @name intitalizeNodeInfo 1650 * @memberOf gxe.xml.XmlNode# 1651 */ 1652 intitalizeNodeInfo: function() { 1653 this.nodeInfo = new gxe.xml.XmlNodeInfo(); 1654 if (this.cfgObject == null) return; 1655 if (this.parentDocument == null) return; 1656 1657 var ni = this.nodeInfo; 1658 ni.namespaceURI = gxe.cfg.getTargetNS(this.cfgObject); 1659 ni.namespacePrefix = this.parentDocument.namespaces.getPrefix(ni.namespaceURI); 1660 ni.localName = gxe.cfg.getTargetName(this.cfgObject); 1661 1662 var sIsTitle = gxe.cfg.getGxeAttributeValue(this.cfgObject,"isDocumentTitle"); 1663 if (sIsTitle == "true") ni.isDocumentTitle = true; 1664 var isIsoCLV = gxe.cfg.getGxeAttributeValue(this.cfgObject,"isIsoCLV"); 1665 if (isIsoCLV == "true") ni.isIsoCodeListValue = true; 1666 var isIsoWMVL = gxe.cfg.getGxeAttributeValue(this.cfgObject,"isIsoWMVL"); 1667 if (isIsoWMVL == "true") ni.isIsoWrappedMultiValueList = true; 1668 }, 1669 1670 /** 1671 * Determines if the node represents an XML attribute. 1672 * @function 1673 * @name isAttribute 1674 * @memberOf gxe.xml.XmlNode# 1675 * @returns {boolean} true if the node is an attribute 1676 */ 1677 isAttribute: function() {return false;}, 1678 1679 /** 1680 * Determines if the node is repeatable. 1681 * @function 1682 * @name isRepeatable 1683 * @memberOf gxe.xml.XmlNode# 1684 * @returns {boolean} true if the node is repeatable 1685 */ 1686 isRepeatable: function() {return false;}, 1687 1688 /** 1689 * Resolves the minimum number of occurrences for the node. 1690 * @function 1691 * @name resolveMinOccurs 1692 * @memberOf gxe.xml.XmlNode# 1693 * @returns {Integer} the minimum number of occurrences 1694 */ 1695 resolveMinOccurs: function() { 1696 var nMinOccurs = null; 1697 var sMinOccurs = gxe.cfg.getGxeAttributeValue(this.cfgObject,"minOccurs"); 1698 if (sMinOccurs != null) { 1699 var pe = this.parentElement; 1700 if ((sMinOccurs == "$parent") && (pe != null)) { 1701 sMinOccurs = gxe.cfg.getGxeAttributeValue(pe.cfgObject,"minOccurs"); 1702 if (sMinOccurs == "$parent") { 1703 pe = pe.parentElement; 1704 if (pe != null) { 1705 sMinOccurs = gxe.cfg.getGxeAttributeValue(pe.cfgObject,"minOccurs"); 1706 } 1707 } 1708 } 1709 var n = parseInt(sMinOccurs); 1710 if (!isNaN(n)) { 1711 nMinOccurs = n; 1712 if (nMinOccurs < 0) nMinOccurs = 0; 1713 if ((nMinOccurs > 1) && this.isAttribute()) nMinOccurs = 1; 1714 } 1715 } 1716 if (nMinOccurs == null) { 1717 var sUse = gxe.cfg.getGxeAttributeValue(this.cfgObject,"use"); 1718 if (sUse == "optional") nMinOccurs = 0; 1719 else if (sUse == "required") nMinOccurs = 1; 1720 } 1721 if (nMinOccurs == null) { 1722 if (this.isAttribute()) nMinOccurs = 0; 1723 else nMinOccurs = 1; 1724 } 1725 return nMinOccurs; 1726 }, 1727 1728 /** 1729 * Validates the content of an input control. 1730 * @function 1731 * @name validateInput 1732 * @memberOf gxe.xml.XmlNode# 1733 * @param {gxe.control.InputBase} inputControl the input control 1734 * @param {boolean} bInFeedbackMode true if the request is part of validation feedback 1735 * @returns {"isValid":{boolean}, "message":{String}} the validation status 1736 */ 1737 validateInput: function(inputControl,bInFeedbackMode) { 1738 if (inputControl.getSupportsMultipleValues()) { 1739 return this.validateInputValues(inputControl,inputControl.getInputValues(bInFeedbackMode)); 1740 } else { 1741 return this.validateInputValue(inputControl,inputControl.getInputValue(bInFeedbackMode)); 1742 } 1743 }, 1744 1745 /** 1746 * Validates an input value associated with a control. 1747 * @function 1748 * @name validateInputValue 1749 * @memberOf gxe.xml.XmlNode# 1750 * @param {gxe.control.InputBase} inputControl the input control that generated the value 1751 * @param {String} value the input value 1752 * @returns {"isValid":{boolean}, "message":{String}} the validation status 1753 */ 1754 validateInputValue: function(inputControl,value) { 1755 var regexp; 1756 var status = {"isValid": true, "message": "?ok"}; 1757 1758 var sLabel = inputControl.findParentLabelText(this); 1759 status.message = this.formatValidationMessage(inputControl,"validate.ok"); 1760 1761 // check for empty input 1762 if ((value == null) || (dojo.trim(value).length == 0)) { 1763 var nMinOccurs = this.resolveMinOccurs(); 1764 if ((nMinOccurs >= 1) && !this.getSerializeIfEmpty()) { 1765 status.isValid = false; 1766 status.message = this.formatValidationMessage(inputControl,"validate.empty"); 1767 return status; 1768 } else { 1769 return status; 1770 } 1771 } 1772 1773 // check for acceptable alternate values 1774 if (!status.isValid) return status; 1775 var sAlternates = gxe.cfg.getGxeAttributeValue(this.cfgObject,"alternateValues"); 1776 if (sAlternates != null) { 1777 var aAlternates = sAlternates.split(","); 1778 for (var i=0;i<aAlternates.length;i++) { 1779 var sAlternate = dojo.trim(aAlternates[i]); 1780 if ((sAlternate.length > 0) && (sAlternate == value)) return status; 1781 } 1782 } 1783 1784 // check types, TODO not all xs: types are implemented 1785 if (!status.isValid) return status; 1786 var sType = gxe.cfg.getGxeAttributeValue(this.cfgObject,"valueType"); 1787 if (sType != null) sType = dojo.trim(sType); 1788 1789 if ((sType == "integer") || (sType == "xs:integer") || (sType == "xsd:integer")) { 1790 // the expression is not definitive 1791 //regexp = /^[-]?[0-9]+$/; 1792 regexp = /(^-?\d\d*$)/; 1793 if (!regexp.test(value)) { 1794 status.isValid = false; 1795 status.message = this.formatValidationMessage(inputControl,"validate.integer"); 1796 } 1797 1798 } else if ((sType == "decimal") || (sType == "xs:decimal") || (sType == "xsd:decimal") || 1799 (sType == "double") || (sType == "xs:double") || (sType == "xsd:double") || 1800 (sType == "float") || (sType == "xs:float") || (sType == "xsd:float") || 1801 (sType == "number")) { 1802 // same expression for any non-integer type, should be more explicit 1803 // the expression is not definitive 1804 regexp = /(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/; 1805 if (!regexp.test(value)) { 1806 status.isValid = false; 1807 status.message = this.formatValidationMessage(inputControl,"validate.number"); 1808 } 1809 1810 } else if ((sType == "date") || (sType == "xs:date") || (sType == "xsd:date")) { 1811 1812 // allows yyyy-mm-ddZ or yyyy-mm-dd or yyyy-mm or yyyy 1813 var bOk = false; 1814 var regexp1 = /^(\d{4})$/; 1815 var regexp2 = /^(\d{2})$/; 1816 var parts = value.split("-"); 1817 1818 if (regexp1.test(parts[0])) { 1819 if (parts.length > 1) { 1820 if (regexp2.test(parts[1])) { 1821 if (parts.length > 2) { 1822 if (parts.length == 3) { 1823 if (parts[2].charAt(parts[2].length-1) == 'Z') { 1824 parts[2] = parts[2].substring(0,parts[2].length-1); 1825 } 1826 if (regexp2.test(parts[2])) bOk = true; 1827 } 1828 } else bOk = true; 1829 } 1830 } else bOk = true; 1831 } 1832 1833 if (!bOk) { 1834 status.isValid = false; 1835 status.message = this.formatValidationMessage(inputControl,"validate.date"); 1836 } 1837 1838 } else if ((sType == "dateTime") || (sType == "xs:dateTime") || (sType == "xsd:dateTime")) { 1839 // TODO not handled 1840 1841 } else if (sType == "fgdc:date") { 1842 1843 // allows yyyymmdd or yyyymm or yyyy 1844 var bOk = false; 1845 var regexp1 = /^(\d{4})$/; 1846 var regexp2 = /^(\d{2})$/; 1847 var parts = new Array(); 1848 if (value.length == 8) { 1849 parts[0] = value.substring(0,4); 1850 parts[1] = value.substring(4,6); 1851 parts[2] = value.substring(6,8); 1852 } else if (value.length == 6) { 1853 parts[0] = value.substring(0,4); 1854 parts[1] = value.substring(4,6); 1855 } else if (value.length == 4) { 1856 parts[0] = value.substring(0,4); 1857 } 1858 if (parts.length > 0) { 1859 if (regexp1.test(parts[0])) { 1860 if (parts.length > 1) { 1861 if (regexp2.test(parts[1])) { 1862 if (parts.length > 2) { 1863 if (parts.length == 3) { 1864 if (parts[2].charAt(parts[2].length-1) == 'Z') { 1865 parts[2] = parts[2].substring(0,parts[2].length-1); 1866 } 1867 if (regexp2.test(parts[2])) bOk = true; 1868 } 1869 } else bOk = true; 1870 } 1871 } else bOk = true; 1872 } 1873 } 1874 if (!bOk) { 1875 status.isValid = false; 1876 status.message = this.formatValidationMessage(inputControl,"validate.date"); 1877 } 1878 1879 } else if (sType == "fgdc:time") { 1880 1881 // (hours minutes seconds) examples: hh hhmm hhmmss 1882 // (offset from GMT) examples: hh+hhmm hhmmss-hhmm 1883 // (suffixed with Z for Zulu time) examples: hhZ hhmmZ hhmmssZ 1884 // (decimal seconds are ssssssss) 1885 var regexp1 = /^\d{2}(\d{2}(\d{2,})?)?$/; 1886 var regexp2 = /^\d{2}(\d{2}(\d{2,})?)?[+\-]\d{4}$/; 1887 var regexp3 = /^\d{2}(\d{2}(\d{2,})?)?Z$/; 1888 if (!regexp1.test(value) && !regexp2.test(value) && !regexp3.test(value)) { 1889 status.isValid = false; 1890 status.message = this.formatValidationMessage(inputControl,"validate.other"); 1891 } 1892 1893 } 1894 1895 // check restrictions, TODO not all xs: restrictions are implemented 1896 if (!status.isValid) return status; 1897 gxe.cfg.forEachChild(this.cfgObject,gxe.cfg.uriGxe,"restriction",dojo.hitch(this,function(cfgRestriction) { 1898 gxe.cfg.forEachChild(cfgRestriction,gxe.cfg.uriGxe,"*",dojo.hitch(this,function(cfgChild) { 1899 1900 if (cfgChild.name == "pattern") { 1901 var pattern = gxe.cfg.getGxeAttributeValue(cfgChild,"value"); 1902 if (pattern != null) { 1903 try { 1904 // TODO what about ["g"|"i"|"gi"] 1905 // var regExp = new RegExp("PATTERN", ["g"|"i"|"gi"]); 1906 regexp = new RegExp(pattern); 1907 var obj = regexp.exec(value); 1908 if (!regexp.test(value)) { 1909 status.isValid = false; 1910 //status.message = "?invalid "+sLabel+" "+pattern; 1911 status.message = this.formatValidationMessage(inputControl,"validate.other"); 1912 return "break"; 1913 } 1914 } catch (err) { 1915 console.log(err+", "+gxe.cfg.getGxeAttributeValue(this.cfgObject,"targetName")+ 1916 ", the g:"+cfgChild.name+" is incorrectly defined, "+pattern); 1917 } 1918 } 1919 } 1920 1921 if ((cfgChild.name == "minExclusive") || (cfgChild.name == "minInclusive") || 1922 (cfgChild.name == "maxExclusive") || (cfgChild.name == "maxInclusive")) { 1923 var nValue = new Number(value); 1924 var sBound = gxe.cfg.getGxeAttributeValue(cfgChild,"value"); 1925 if (!isNaN(nValue)) { 1926 var nBound = new Number(sBound); 1927 if ((sBound != null) && !isNaN(nBound)) { 1928 if (cfgChild.name == "minExclusive") { 1929 if (nValue <= nBound) status.isValid = false; 1930 } else if (cfgChild.name == "minInclusive") { 1931 if (nValue < nBound) status.isValid = false; 1932 } else if (cfgChild.name == "maxExclusive") { 1933 if (nValue >= nBound) status.isValid = false; 1934 } else if (cfgChild.name == "maxInclusive") { 1935 if (nValue > nBound) status.isValid = false; 1936 } 1937 if (!status.isValid) { 1938 status.message = this.formatValidationMessage(inputControl,"validate.other"); 1939 } 1940 } else { 1941 console.log(gxe.cfg.getGxeAttributeValue(this.cfgObject,"targetName")+ 1942 ", the g:"+cfgChild.name+" bound is incorrectly defined, "+sBound); 1943 } 1944 } else { 1945 status.isValid = false; 1946 status.message = this.formatValidationMessage(inputControl,"validate.other"); 1947 } 1948 } 1949 1950 if ((cfgChild.name == "length") || 1951 (cfgChild.name == "minLength") || (cfgChild.name == "maxLength")) { 1952 var nLength = value.length; 1953 var sBound = gxe.cfg.getGxeAttributeValue(cfgChild,"value"); 1954 var nBound = new Number(sBound); 1955 if ((sBound != null) && !isNaN(nBound)) { 1956 if (cfgChild.name == "length") { 1957 if (nLength != nBound) status.isValid = false; 1958 } else if (cfgChild.name == "minLength") { 1959 if (nLength < nBound) status.isValid = false; 1960 } else if (cfgChild.name == "maxLength") { 1961 if (nLength > nBound) status.isValid = false; 1962 } 1963 if (!status.isValid) { 1964 status.message = this.formatValidationMessage(inputControl,"validate.other"); 1965 } 1966 } else { 1967 console.log(gxe.cfg.getGxeAttributeValue(this.cfgObject,"targetName")+ 1968 ", the g:"+cfgChild.name+" bound is incorrectly defined, "+sBound); 1969 } 1970 } 1971 1972 })); 1973 if (!status.isValid) return "break"; 1974 })); 1975 1976 return status; 1977 }, 1978 1979 /** 1980 * Validates multiple input values associated with a control. 1981 * @function 1982 * @name validateInputValues 1983 * @memberOf gxe.xml.XmlNode# 1984 * @param {gxe.control.InputBase} inputControl the input control that generated the values 1985 * @param {Array} values the value array 1986 * @returns {"isValid":{boolean}, "message":{String}} the validation status 1987 */ 1988 validateInputValues: function(inputControl,values) { 1989 var status = {"isValid": false, "message": null}; 1990 if ((values == null) || (values.length == 0)) { 1991 var nMinOccurs = this.resolveMinOccurs(); 1992 if ((nMinOccurs >= 1) && !this.getSerializeIfEmpty()) { 1993 status.isValid = false; 1994 status.message = this.formatValidationMessage(inputControl,"validate.empty"); 1995 } else { 1996 status.isValid = true; 1997 } 1998 } else { 1999 status.isValid = true; 2000 } 2001 return status; 2002 }, 2003 2004 /** 2005 * Determines if this element wraps an ISO19139 multi-value list. 2006 * <br/>(e.g. MD_TopicCategoryCode - InputSelectMany) 2007 * @function 2008 * @name wrapsIsoMultiValueList 2009 * @memberOf gxe.xml.XmlNode# 2010 * @returns {boolean} 2011 */ 2012 wrapsIsoMultiValueList: function() { 2013 return false; 2014 } 2015 2016 }); 2017 2018 /** 2019 * @class A target XML attribute. 2020 * @name gxe.xml.XmlAttribute 2021 * @extends gxe.xml.XmlNode 2022 */ 2023 dojo.provide("gxe.xml.XmlAttribute"); 2024 dojo.declare("gxe.xml.XmlAttribute",gxe.xml.XmlNode,{ 2025 2026 /** Override gxe.xml.XmlNode.echo() */ 2027 echo: function(xmlGenerator,stringBuffer,nDepth) { 2028 if (this.isPlaceHolder || this.isOptionalPlaceHolder) return; 2029 2030 var bSerialize = true; 2031 var bValidating = xmlGenerator.isValidating; 2032 var bIsTitle = this.nodeInfo.isDocumentTitle; 2033 if (bIsTitle && !bValidating) { 2034 if (xmlGenerator.isValidatingTitleOnly) bValidating = true; 2035 } 2036 var bSerializeIfEmpty = (this.getSerializeIfEmpty() || !bValidating); 2037 2038 var sNodeValue = this.nodeInfo.nodeValue; 2039 var inputControl = this.getInputControl(); 2040 if (inputControl != null) { 2041 this.nodeInfo.nodeValue = inputControl.getInputValue(); 2042 sNodeValue = this.nodeInfo.nodeValue; 2043 } 2044 if (sNodeValue == null) sNodeValue = ""; 2045 else sNodeValue = dojo.trim(sNodeValue); 2046 var bIsEmpty = (sNodeValue.length == 0); 2047 2048 if ((inputControl != null) && bValidating) { 2049 var status = this.validateInputValue(inputControl,sNodeValue); 2050 if (!status.isValid) { 2051 bSerialize = false; 2052 xmlGenerator.handleValidationError(status.message,this,inputControl); 2053 } 2054 } 2055 if (bIsTitle) xmlGenerator.documentTitle = sNodeValue; 2056 2057 if (bSerialize && (!bIsEmpty || bSerializeIfEmpty)) { 2058 var sNodeName = this.nodeInfo.localName; 2059 if (this.nodeInfo.namespacePrefix != null) { 2060 sNodeName = this.nodeInfo.namespacePrefix+":"+sNodeName; 2061 } 2062 sNodeValue = xmlGenerator.escAttribute(sNodeValue); 2063 stringBuffer.append(" "+sNodeName+"=\""+sNodeValue+"\""); 2064 } 2065 }, 2066 2067 /** Override gxe.xml.XmlNode.isAttribute() */ 2068 isAttribute: function() {return true;}, 2069 2070 /** Override gxe.xml.XmlNode.isRepeatable() */ 2071 isRepeatable: function() {return false;} 2072 }); 2073 2074 /** 2075 * @class A collection of XML attributes (of type gxe.xml.XmlAttribute). 2076 * @name gxe.xml.XmlAttributes 2077 * @extends gxe.util.ArrayList 2078 */ 2079 dojo.provide("gxe.xml.XmlAttributes"); 2080 dojo.declare("gxe.xml.XmlAttributes",gxe.util.ArrayList,{ 2081 2082 /** 2083 * Serializes XML content. 2084 * @function 2085 * @name echo 2086 * @memberOf gxe.xml.XmlAttributes# 2087 * @param {gxe.xml.Generator} xmlGenerator the XML generator 2088 * @param {gxe.util.StringBuffer} stringBuffer the buffer to which content will be written 2089 * @param {Integer} nDepth the indentation depth 2090 */ 2091 echo: function(xmlGenerator,stringBuffer,nDepth) { 2092 var n = this.getLength(); 2093 for (var i=0; i<n; i++) this.getItem(i).echo(xmlGenerator,stringBuffer,nDepth); 2094 } 2095 }); 2096 2097 /** 2098 * @class A target XML element. 2099 * @name gxe.xml.XmlElement 2100 * @extends gxe.xml.XmlNode 2101 */ 2102 dojo.provide("gxe.xml.XmlElement"); 2103 dojo.declare("gxe.xml.XmlElement",gxe.xml.XmlNode,{ 2104 attributes: null, 2105 children: null, 2106 exclusiveChoiceControl: null, 2107 repeatablesContainer: null, 2108 2109 /** constructor */ 2110 constructor: function() { 2111 this.attributes = new gxe.xml.XmlAttributes(); 2112 this.children = new gxe.xml.XmlElements(); 2113 }, 2114 2115 /** Override gxe.xml.XmlNode.echo() */ 2116 echo: function(xmlGenerator,stringBuffer,nDepth) { 2117 if (this.isPlaceHolder || this.isOptionalPlaceHolder) return; 2118 2119 var pfx = "\r\n"; 2120 for (var i=0; i<nDepth; i++) pfx += "\t"; 2121 if (this.exclusiveChoiceControl != null) { 2122 if (!this.exclusiveChoiceControl.isElementSelected(this)) return; 2123 } 2124 2125 var bValidating = xmlGenerator.isValidating; 2126 var bSerializeIfEmpty = (this.getSerializeIfEmpty() || !bValidating); 2127 var bIsTitle = this.nodeInfo.isDocumentTitle; 2128 var inputValues = null; 2129 var inputControl = this.getInputControl(); 2130 if (inputControl != null) { 2131 this.nodeInfo.nodeValue = null; 2132 if (inputControl.getSupportsMultipleValues()) { 2133 2134 inputValues = inputControl.getInputValues(); 2135 if ((inputValues != null) && (inputValues.length > 0)) { 2136 this.nodeInfo.nodeValue = inputValues[0]; 2137 } 2138 2139 if (bValidating || (bIsTitle && xmlGenerator.isValidatingTitleOnly)) { 2140 var status = this.validateInputValues(inputControl,inputValues); 2141 if (!status.isValid) { 2142 bSerializeIfEmpty = false; 2143 this.nodeInfo.nodeValue = null; 2144 inputValues = null; 2145 xmlGenerator.handleValidationError(status.message,this,inputControl); 2146 } 2147 } 2148 2149 } else { 2150 var inputValue = inputControl.getInputValue(); 2151 if (inputValue != null) { 2152 this.nodeInfo.nodeValue = inputValue; 2153 } 2154 if (bValidating || (bIsTitle && xmlGenerator.isValidatingTitleOnly)) { 2155 var status = this.validateInputValue(inputControl,this.nodeInfo.nodeValue); 2156 if (!status.isValid) { 2157 bSerializeIfEmpty = false; 2158 this.nodeInfo.nodeValue = null; 2159 xmlGenerator.handleValidationError(status.message,this,inputControl); 2160 } 2161 } 2162 } 2163 } 2164 if (bIsTitle) xmlGenerator.documentTitle = this.nodeInfo.nodeValue; 2165 2166 var nodeName = this.nodeInfo.localName; 2167 if (this.nodeInfo.namespacePrefix != null) { 2168 nodeName = this.nodeInfo.namespacePrefix+":"+nodeName; 2169 } 2170 var nodeValue = this.nodeInfo.nodeValue; 2171 2172 var sbAttributePortion = new gxe.util.StringBuffer(); 2173 this.attributes.echo(xmlGenerator,sbAttributePortion,nDepth); 2174 var sAttributePortion = sbAttributePortion.toString(); 2175 var bHasAttributePortion = (sAttributePortion.length > 0); 2176 2177 var bIsIsoCodeListValue = this.nodeInfo.isIsoCodeListValue; 2178 if (bIsIsoCodeListValue) { 2179 var n = this.attributes.getLength(); 2180 for (var i=0; i<n; i++) { 2181 var attr = this.attributes.getItem(i); 2182 if (attr.nodeInfo.localName == "codeListValue") { 2183 var ic = attr.getInputControl(); 2184 if (ic != null) { 2185 if (!ic.getSupportsMultipleValues()) { 2186 var sCode = ic.getInputValue(true); 2187 if ((sCode == null) || (sCode.length == 0)) return; 2188 else nodeValue = sCode; 2189 } 2190 } 2191 } 2192 } 2193 } 2194 2195 // isIsoWrappedMultiValueList, e.g. MD_TopicCategoryCode - InputSelectMany 2196 if (this.wrapsIsoMultiValueList()) { 2197 this.children.getItem(0).echo(xmlGenerator,stringBuffer,nDepth); 2198 return; 2199 } else if (this.nodeInfo.isIsoWrappedMultiValueList) { 2200 if ((inputValues != null) && (inputValues.length > 0)) { 2201 var pNodeInfo = this.parentElement.nodeInfo; 2202 var pNodeName = pNodeInfo.localName; 2203 if (pNodeInfo.namespacePrefix != null) { 2204 pNodeName = pNodeInfo.namespacePrefix+":"+pNodeName; 2205 } 2206 var n = inputValues.length; 2207 for (var i=0; i<n; i++) { 2208 stringBuffer.append(pfx+"<"+pNodeName+">"); 2209 stringBuffer.append(pfx+"\t<"+nodeName+">"); 2210 stringBuffer.append(xmlGenerator.escElement(inputValues[i])); 2211 stringBuffer.append("</"+nodeName+">"); 2212 stringBuffer.append(pfx+"</"+pNodeName+">"); 2213 } 2214 } 2215 return; 2216 } 2217 2218 if (nodeValue != null) nodeValue = xmlGenerator.escElement(nodeValue); 2219 var bHasValue = ((nodeValue != null) && (nodeValue.length > 0)); 2220 2221 var sbChildPortion = new gxe.util.StringBuffer(); 2222 this.children.echo(xmlGenerator,sbChildPortion,nDepth); 2223 var sChildPortion = sbChildPortion.toString(); 2224 var bHasChildPortion = (sChildPortion.length > 0); 2225 2226 if (bHasValue || bHasAttributePortion || bHasChildPortion) { 2227 var sbElement = new gxe.util.StringBuffer(); 2228 sbElement.append(pfx+"<"+nodeName); 2229 if (bHasAttributePortion) sbElement.append(sAttributePortion); 2230 if (bHasValue || bHasChildPortion) { 2231 sbElement.append(">"); 2232 if (bHasValue) sbElement.append(nodeValue); 2233 if (bHasChildPortion) { 2234 sbElement.append(sChildPortion); 2235 sbElement.append(pfx); 2236 } 2237 sbElement.append("</"+nodeName+">"); 2238 } else { 2239 sbElement.append("/>"); 2240 } 2241 stringBuffer.append(sbElement.toString()); 2242 } else { 2243 if (bSerializeIfEmpty) stringBuffer.append(pfx+"<"+nodeName+"/>"); 2244 } 2245 2246 if ((inputValues != null) && (inputValues.length > 1)) { 2247 var n = inputValues.length; 2248 for (var i=1; i<n; i++) { 2249 var xmlSibling = new gxe.xml.XmlElement(this.parentDocument,this.parentElement,this.cfgObject); 2250 xmlSibling.nodeInfo.nodeValue = inputValues[i]; 2251 xmlSibling.echo(xmlGenerator,stringBuffer,nDepth); 2252 } 2253 } 2254 }, 2255 2256 /** Override gxe.xml.XmlNode.isRepeatable() */ 2257 isRepeatable: function() { 2258 var sMaxOccurs = gxe.cfg.getGxeAttributeValue(this.cfgObject,"maxOccurs"); 2259 if (sMaxOccurs == "unbounded") { 2260 return true; 2261 } else { 2262 var nMaxOccurs = parseInt(sMaxOccurs); 2263 if (isNaN(nMaxOccurs)) nMaxOccurs = 1; 2264 return (nMaxOccurs > 1); 2265 } 2266 return false; 2267 }, 2268 2269 /** Override gxe.xml.XmlNode.wrapsIsoMultiValueList() */ 2270 wrapsIsoMultiValueList: function() { 2271 if (this.children.getLength() == 1) { 2272 return this.children.getItem(0).nodeInfo.isIsoWrappedMultiValueList; 2273 } 2274 return false; 2275 } 2276 2277 }); 2278 2279 /** 2280 * @class A collection of XML elements (of type gxe.xml.XmlElement). 2281 * @name gxe.xml.XmlElements 2282 * @extends gxe.util.ArrayList 2283 */ 2284 dojo.provide("gxe.xml.XmlElements"); 2285 dojo.declare("gxe.xml.XmlElements",gxe.util.ArrayList,{ 2286 2287 /** 2288 * Serializes XML content. 2289 * @function 2290 * @name echo 2291 * @memberOf gxe.xml.XmlElements# 2292 * @param {gxe.xml.Generator} xmlGenerator the XML generator 2293 * @param {gxe.util.StringBuffer} stringBuffer the buffer to which content will be written 2294 * @param {Integer} nDepth the indentation depth 2295 */ 2296 echo: function(xmlGenerator,stringBuffer,nDepth) { 2297 var n = this.getLength(); 2298 for (var i=0; i<n; i++) this.getItem(i).echo(xmlGenerator,stringBuffer,nDepth+1); 2299 }, 2300 2301 /** 2302 * Finds the array index associated with an element. 2303 * @function 2304 * @name findIndex 2305 * @memberOf gxe.xml.XmlElements# 2306 * @param {gxe.xml.XmlElement} xmlElement the subject element 2307 * @returns {Integer} the associated index (-1 if not located) 2308 */ 2309 findIndex: function(xmlElement) { 2310 var n = this.getLength(); 2311 for (var i=0; i<n; i++) { 2312 if (this.getItem(i) == xmlElement) return i; 2313 } 2314 return -1; 2315 } 2316 2317 }); 2318 2319 /** 2320 * @class An XML namespace. 2321 * @name gxe.xml.XmlNamespace 2322 * @extends gxe.util.ArrayList 2323 * @property {String} prefix the prefix 2324 * @property {String} uri the URI 2325 */ 2326 dojo.provide("gxe.xml.XmlNamespace"); 2327 dojo.declare("gxe.xml.XmlNamespace",null,{ 2328 prefix: null, 2329 uri: null, 2330 2331 /** 2332 * Constructor. 2333 * @function 2334 * @name constructor 2335 * @memberOf gxe.xml.XmlNamespaces# 2336 * @param {String} prefix the prefix 2337 * @param {String} uri the URI 2338 */ 2339 constructor: function(prefix,uri) { 2340 this.prefix = prefix; 2341 this.uri = uri; 2342 } 2343 }); 2344 2345 /** 2346 * @class A collection of XML namespaces. 2347 * @name gxe.xml.XmlNamespaces 2348 * @extends gxe.util.ArrayList 2349 */ 2350 dojo.provide("gxe.xml.XmlNamespaces"); 2351 dojo.declare("gxe.xml.XmlNamespaces",gxe.util.ArrayList,{ 2352 2353 /** 2354 * Gets the namespace prefix associated with a URI. 2355 * @function 2356 * @name getPrefix 2357 * @memberOf gxe.xml.XmlNamespaces# 2358 * @param {String} uri the URI 2359 * @returns {String} the namespace prefix for the URI (null if not located) 2360 */ 2361 getPrefix: function(uri) { 2362 var n = this.getLength(); 2363 for (var i=0; i<n; i++) { 2364 var ns = this.getItem(i); 2365 if ((ns != null) && (ns.uri == uri)) { 2366 return ns.prefix; 2367 } 2368 } 2369 return null; 2370 }, 2371 2372 /** 2373 * Gets the namespace URI associated with a prefix. 2374 * @function 2375 * @name getUri 2376 * @memberOf gxe.xml.XmlNamespaces# 2377 * @param {String} prefix the prefix 2378 * @returns {String} the namespace URI for the prefix (null if not located) 2379 */ 2380 getUri: function(prefix) { 2381 var n = this.getLength(); 2382 for (var i=0; i<n; i++) { 2383 var ns = this.getItem(i); 2384 if ((ns != null) && (ns.prefix == prefix)) { 2385 return ns.uri; 2386 } 2387 } 2388 return null; 2389 } 2390 }); 2391 2392 /** 2393 * @class Provides information about a target XML node. 2394 * @name gxe.xml.XmlNodeInfo 2395 * @property {boolean} isDocumentTitle true if this node represents the document title 2396 * @property {boolean} isIsoCodeListValue true if this node represents an 2397 * ISO19139 code list value 2398 * @property {boolean} isIsoWrappedMultiValueList true if this node represents an 2399 * ISO19139 wrapped multi-value list (e.g. MD_TopicCategoryCode - InputSelectMany) 2400 * @property {String} localName the local node name 2401 * @property {String} namespacePrefix the namespace prefix 2402 * @property {String} namespaceURI the namespace URI 2403 * @property {String} nodeValue the node value 2404 */ 2405 dojo.provide("gxe.xml.XmlNodeInfo"); 2406 dojo.declare("gxe.xml.XmlNodeInfo",null,{ 2407 localName: null, 2408 isDocumentTitle: false, 2409 isIsoCodeListValue: false, 2410 isIsoWrappedMultiValueList: false, 2411 namespacePrefix: null, 2412 namespaceURI: null, 2413 nodeValue: null 2414 }); 2415 2416 2417 /* UI controls ======================================================= */ 2418 2419 2420 /** 2421 * @class Base class for all renderable controls. 2422 * <br/><br/> 2423 * Controls are created and rendered based upon the configuration objects associated 2424 * with an editor (i.e. elements that define the editor). 2425 * <br/><br/> 2426 * Controls will only be rendered if an associated HTML tag name can be determined. 2427 * <ul> 2428 * <li>If a configuration object explicitly defines an h:tag attribute, it will be used to 2429 * create a corresponding HTML DOM element on the page.</li> 2430 * <li>If a configuration object is defined within the GXE HTML namespace, then the local name 2431 * of the configuration object will be used to create a corresponding HTML DOM element 2432 * on the page.</li> 2433 * </ul> 2434 * 2435 * <br/><br/> 2436 * The processing flow is as follows: 2437 * <ul> 2438 * <li>instantiate</li> 2439 * <li>...initialize</li> 2440 * <li>...build</li> 2441 * <li>...execBuild</li> 2442 * <li>......importCfgProperties</li> 2443 * <li>.........importHtmlAttributes</li> 2444 * <li>......createHtmlElement</li> 2445 * <li>......onHtmlElementCreated</li> 2446 * <li>......processChildren</li> 2447 * <li>......... (recursive on this process)</li> 2448 * <li>......onHtmlChildrenCreated</li> 2449 * <li>......parentControl.onChildControlCreated</li> 2450 * </ul> 2451 * 2452 * @name gxe.control.Control 2453 * @property {cfgObject} cfgObject the associated editor configuration object 2454 * (i.e. an editor definition element) 2455 * @property {gxe.Context} context the editor context 2456 * @property {gxe.html.HtmlAttributes} htmlAttributes the configured HTML attributes for the control 2457 * @property {Element} htmlElement the HTML element to which the control is attached 2458 * @property {String} htmlTag the configured tag name for the HTML element 2459 * @property {String} htmlTextContent the configured text node content for the HTML element 2460 * @property {String} gxeId a unique auto-generated ID for the control 2461 * @property {gxe.control.Control} parentControl the parent of this control 2462 * @property {gxe.xml.XmlNode} xmlNode the targeted XML node. 2463 * This is section based, many component controls target the same node. 2464 * (e.g. a header div, a label, an input text box can all target the same XML node) 2465 * @property {gxe.xml.XmlElement} xmlParentElement the parent element of the targeted XML node 2466 */ 2467 dojo.provide("gxe.control.Control"); 2468 dojo.declare("gxe.control.Control",null,{ 2469 2470 cfgObject: null, 2471 context: null, 2472 htmlAttributes: null, 2473 htmlClass: null, 2474 htmlElement: null, 2475 htmlTag: null, 2476 htmlTextContent: null, 2477 gxeId: null, 2478 parentControl: null, 2479 xmlNode: null, 2480 xmlParentElement: null, 2481 2482 /** 2483 * Initializes the instance. 2484 * @function 2485 * @name initialize 2486 * @memberOf gxe.control.Control# 2487 * @param {gxe.Context} context the editor context 2488 * @param {cfgObject} cfgObject the associated editor configuration object 2489 * (i.e. an editor definition element) 2490 */ 2491 initialize: function(context,cfgObject) { 2492 this.context = context; 2493 this.cfgObject = cfgObject; 2494 this.gxeId = this.context.generateUniqueId(); 2495 this.htmlAttributes = new gxe.html.HtmlAttributes(); 2496 this.htmlAttributes.set("id",this.gxeId); 2497 if (this.cfgObject != null) { 2498 var targetName = gxe.cfg.getGxeAttributeValue(this.cfgObject,"targetName"); 2499 if (targetName != null) { 2500 this.htmlAttributes.set("gxeTargetName",targetName); 2501 } 2502 } 2503 }, 2504 2505 /** 2506 * Builds the user interface control. 2507 * @function 2508 * @name build 2509 * @memberOf gxe.control.Control# 2510 * @param {Element} htmlParentElement the parent HTML element (a new 2511 * control will be appended to this parent) 2512 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 2513 * (when opening an existing document) 2514 * @param {Node} domNode the DOM node that is actively being processed 2515 * (when opening an existing document) 2516 */ 2517 build: function(htmlParentElement,domProcessor,domNode) { 2518 this.execBuild(htmlParentElement,domProcessor,domNode); 2519 }, 2520 2521 /** 2522 * Executes the build of the the user interface control. 2523 * @function 2524 * @name execBuild 2525 * @memberOf gxe.control.Control# 2526 * @param {Element} htmlParentElement the parent HTML element (a new 2527 * control will be appended to this parent) 2528 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 2529 * (when opening an existing document) 2530 * @param {Node} domNode the DOM node that is actively being processed 2531 * (when opening an existing document) 2532 */ 2533 execBuild: function(htmlParentElement,domProcessor,domNode) { 2534 this.importCfgProperties(this.cfgObject); 2535 2536 var bRendered = true; 2537 var oRendered = this.evaluateValue(gxe.cfg.getGxeAttributeValue(this.cfgObject,"rendered")); 2538 if (oRendered != null) { 2539 if (typeof(oRendered) == "boolean") { 2540 bRendered = (oRendered == true); 2541 } else if (typeof(oRendered) == "string") { 2542 if (oRendered == "$editor.isExpertMode") { 2543 bRendered = false; 2544 } else { 2545 bRendered = (oRendered == "true"); 2546 } 2547 } 2548 } 2549 if (!bRendered) return; 2550 2551 this.createHtmlElement(); 2552 if (this.htmlElement != null) { 2553 this.htmlElement.gxeControl = this; 2554 if (htmlParentElement != null) htmlParentElement.appendChild(this.htmlElement); 2555 2556 2557 gxe.cfg.forEachHtmlAttribute(this.cfgObject,dojo.hitch(this,function(cfgAttribute) { 2558 var name = cfgAttribute.name; 2559 var value = cfgAttribute.value; 2560 if ((name != null) && (value != null) && (typeof(value) == "string")) { 2561 if (value.indexOf("$fire.") == 0) { 2562 var htmlEventName = name; 2563 var gxeEventName = dojo.trim(value.substring(6)); 2564 this.htmlElement.setAttribute("gxeEventName",gxeEventName); 2565 dojo.connect(this.htmlElement,htmlEventName,this,dojo.hitch(this,function(e) { 2566 this.onEvent(e,this,htmlEventName,gxeEventName); 2567 })); 2568 } 2569 } 2570 })); 2571 2572 this.onHtmlElementCreated(domProcessor,domNode); 2573 this.processChildren(this.cfgObject,this.htmlElement,this.xmlNode,domProcessor,domNode); 2574 this.onHtmlChildrenCreated(domProcessor,domNode); 2575 if (this.parentControl != null) { 2576 this.parentControl.onChildControlCreated(this,domProcessor,domNode); 2577 } 2578 } 2579 2580 }, 2581 2582 /** 2583 * Creates the HTML element associated with the control. 2584 * @function 2585 * @name createHtmlElement 2586 * @memberOf gxe.control.Control# 2587 * @returns {Element} the HTML element (can be null) 2588 */ 2589 createHtmlElement: function() { 2590 var el = null; 2591 if (this.htmlTag != null) { 2592 var el = document.createElement(this.htmlTag); 2593 if (this.htmlClass != null) el.className = this.htmlClass; 2594 if (this.htmlAttributes != null) this.htmlAttributes.apply(el); 2595 if (this.htmlTextContent != null) { 2596 var text = dojo.trim(this.htmlTextContent); 2597 if (text.length > 0) { 2598 el.appendChild(document.createTextNode(text)); 2599 } 2600 } 2601 } 2602 this.htmlElement = el; 2603 }, 2604 2605 /** 2606 * Ensures the visibility of the HTML element associated with the control. 2607 * <br/>By default, no action is taken and the ensureVisibility method is triggered for the 2608 * parent control. 2609 * @function 2610 * @name ensureVisibility 2611 * @memberOf gxe.control.Control# 2612 * @param {gxe.xml.XmlNode} subjectXmlNode the subject XML node (can be null) 2613 */ 2614 ensureVisibility: function(subjectXmlNode) { 2615 if (this.parentControl != null) { 2616 this.parentControl.ensureVisibility(subjectXmlNode); 2617 } 2618 }, 2619 2620 /** 2621 * Evaluates a configuration value. 2622 * @function 2623 * @name evaluateValue 2624 * @memberOf gxe.control.Control# 2625 * @param {Object} value the value 2626 * @returns {Object} the evaluated value 2627 */ 2628 evaluateValue: function(value) { 2629 if ((value != null) && (typeof(value) == "string")) { 2630 if (value.indexOf("$eval.") == 0) { 2631 var sEval = value.substring(6); 2632 value = eval(sEval); 2633 } 2634 } 2635 return value; 2636 }, 2637 2638 /** 2639 * Finds the first child control that matches a Dojo query expression. 2640 * @function 2641 * @name findFirstChildControl 2642 * @memberOf gxe.control.Control# 2643 * @param {String} sDojoSelector the Dojo query expression 2644 * @param {gxe.control.Control} the first matching child control (null if none) 2645 */ 2646 findFirstChildControl: function(sDojoSelector) { 2647 var ctlFirst = null; 2648 if ((this.htmlElement != null) && (typeof(sDojoSelector) != "undefined") && (sDojoSelector != null)) { 2649 dojo.query(sDojoSelector,this.htmlElement).forEach(dojo.hitch(this,function(item) { 2650 if (ctlFirst == null) { 2651 var ctl = item.gxeControl; 2652 if ((typeof(ctl) != "undefined") && (ctl != null)) { 2653 ctlFirst = ctl; 2654 } 2655 } 2656 })); 2657 } 2658 return ctlFirst; 2659 }, 2660 2661 /** 2662 * Explicitly fires the onEvent method. 2663 * @event 2664 * @name fireOnEvent 2665 * @memberOf gxe.control.Control# 2666 * @param {Event} e the underlying browser event 2667 * @deprecated 2668 */ 2669 fireOnEvent: function(e) { 2670 this.onEvent(e,this,null,null); 2671 }, 2672 2673 /** 2674 * Ensures the visibility of and focuses the HTML element associated with the control. 2675 * @function 2676 * @name focus 2677 * @memberOf gxe.control.Control# 2678 * @param {gxe.xml.XmlNode} subjectXmlNode the subject XML node (can be null) 2679 */ 2680 focus: function(subjectXmlNode) { 2681 if (this.htmlElement != null) { 2682 this.ensureVisibility(subjectXmlNode); 2683 this.htmlElement.focus(); 2684 } 2685 }, 2686 2687 /** 2688 * Gets the label text associated with a control. 2689 * @function 2690 * @name getLabelText 2691 * @memberOf gxe.control.Control# 2692 * @return the label text (can be null) 2693 */ 2694 getLabelText: function() { 2695 var sLabel = gxe.cfg.getLabelText(this.cfgObject); 2696 if (sLabel == null) sLabel = this.xmlNode.getLabelText(); 2697 return sLabel; 2698 }, 2699 2700 /** 2701 * Imports configuration properties into the current control. 2702 * @function 2703 * @name importCfgProperties 2704 * @memberOf gxe.control.Control# 2705 * @param {cfgObject} cfgObject the associated configuration object 2706 */ 2707 importCfgProperties: function(cfgObject) { 2708 if ((this.htmlAttributes != null) && (cfgObject != null)) { 2709 var value = this.evaluateValue(cfgObject.value); 2710 var sHtmlTag = gxe.cfg.getGxeHtmlAttributeValue(cfgObject,"tag"); 2711 if (sHtmlTag != null) { 2712 if (sHtmlTag.length > 0) this.htmlTag = sHtmlTag; 2713 } else { 2714 if (cfgObject.namespace == gxe.cfg.uriGxeHtml) { 2715 this.htmlTag = cfgObject.name; 2716 } 2717 } 2718 this.htmlTextContent = value; 2719 this.importHtmlAttributes(cfgObject); 2720 gxe.cfg.forEachChild(cfgObject,gxe.cfg.uriGxe,"code",dojo.hitch(this,function(cfgCode) { 2721 if (cfgCode.value != null) eval(cfgCode.value); 2722 })); 2723 } 2724 }, 2725 2726 /** 2727 * Imports configuration attributes within the GXE HTML namespace into the current control. 2728 * <br/>(i.e. namespace "http://www.esri.com/geoportal/gxe/html") 2729 * @function 2730 * @name importHtmlAttributes 2731 * @memberOf gxe.control.Control# 2732 * @param {cfgObject} cfgObject the associated configuration object 2733 */ 2734 importHtmlAttributes: function(cfgObject) { 2735 if (cfgObject != null) { 2736 this.htmlAttributes.set("gxename",cfgObject.name); 2737 gxe.cfg.forEachHtmlAttribute(cfgObject,dojo.hitch(this,function(cfgAttribute) { 2738 var sName = cfgAttribute.name.toLowerCase(); 2739 var value = this.evaluateValue(cfgAttribute.value); 2740 if (sName!= "id") this.htmlAttributes.set(sName,value); 2741 })); 2742 } 2743 }, 2744 2745 /** 2746 * Fired on a parent control when a child has been created. 2747 * <br/>This event fires after the child has been fully processed. 2748 * @event 2749 * @name onChildControlCreated 2750 * @memberOf gxe.control.Control# 2751 * @param {gxe.control.Control} control the child control 2752 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 2753 * (when opening an existing document) 2754 * @param {Node} domNode the DOM node that is actively being processed 2755 * (when opening an existing document) 2756 */ 2757 onChildControlCreated: function(control,domProcessor,domNode) {}, 2758 2759 /** 2760 * Fired for certain events triggered by the control. 2761 * @event 2762 * @name onEvent 2763 * @memberOf gxe.control.Control# 2764 * @param {Event} e the underlying browser event 2765 * @param {gxe.control.Control} gxeControl the control that fired the event 2766 * @param {String} htmlEventName the HTML event name (e.g. "onclick") 2767 * @param {String} gxeEventName a GXE name for the event (e.g. "onLabelClicked") 2768 */ 2769 onEvent: function(e,gxeControl,htmlEventName,gxeEventName) {}, 2770 2771 /** 2772 * Fired when all of the children for a control have been created. 2773 * <br/>This event fires after all child controls have been added to the HTML DOM. 2774 * @event 2775 * @name onHtmlChildrenCreated 2776 * @memberOf gxe.control.Control# 2777 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 2778 * (when opening an existing document) 2779 * @param {Node} domNode the DOM node that is actively being processed 2780 * (when opening an existing document) 2781 */ 2782 onHtmlChildrenCreated: function(domProcessor,domNode) {}, 2783 2784 /** 2785 * Fired when the HTML element for a control has been created. 2786 * <br/>This event fires prior to the creation of child controls. 2787 * @event 2788 * @name onHtmlElementCreated 2789 * @memberOf gxe.control.Control# 2790 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 2791 * (when opening an existing document) 2792 * @param {Node} domNode the DOM node that is actively being processed 2793 * (when opening an existing document) 2794 */ 2795 onHtmlElementCreated: function(domProcessor,domNode) {}, 2796 2797 /** 2798 * Processes a configuration object associated with a targeted XML attribute (g:attribute). 2799 * @function 2800 * @name processCfgAttribute 2801 * @memberOf gxe.control.Control# 2802 * @param {cfgObject} cfgAttribute the configuration object to process 2803 * @param {Element} htmlParentElement the parent HTML element 2804 * (new controls will be appended to this parent) 2805 * @param {gxe.xml.XmlElement} xmlParentElement the targeted XML parent element 2806 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 2807 * (when opening an existing document) 2808 * @param {Node} domNode the DOM node that is actively being processed 2809 * (when opening an existing document) 2810 */ 2811 processCfgAttribute: function(cfgAttribute,htmlParentElement,xmlParentElement,domProcessor,domNode) { 2812 var sTargetName = gxe.cfg.getGxeAttributeValue(cfgAttribute,"targetName"); 2813 var xmlDocument = xmlParentElement.parentDocument; 2814 var xmlAttribute = new gxe.xml.XmlAttribute(xmlDocument,xmlParentElement,cfgAttribute); 2815 xmlParentElement.attributes.add(xmlAttribute); 2816 var domMatch = null; 2817 var sDefault = gxe.cfg.getGxeAttributeValue(this.cfgObject,"fixedValue"); 2818 if (sDefault == null) sDefault = gxe.cfg.getGxeAttributeValue(cfgAttribute,"value"); 2819 if ((domProcessor != null) && (domNode != null)) { 2820 domMatch = domProcessor.findMatchingChildAttribute(domNode,cfgAttribute); 2821 } else { 2822 if (sDefault != null) xmlAttribute.nodeInfo.nodeValue = sDefault; 2823 } 2824 2825 var ctl = this.context.makeXhtmlControl(cfgAttribute,this,true); 2826 ctl.xmlNode = xmlAttribute; 2827 ctl.xmlParentElement = xmlParentElement; 2828 ctl.build(htmlParentElement,domProcessor,domMatch); 2829 2830 if ((domMatch != null) && (xmlAttribute.getInputControl() == null)) { 2831 // can this put the document in an undesirable state? 2832 if (sDefault != null) { 2833 xmlAttribute.nodeInfo.nodeValue = sDefault; 2834 } else { 2835 var sNodeValue = domMatch.nodeValue; 2836 xmlAttribute.nodeInfo.nodeValue = sNodeValue; 2837 } 2838 } 2839 }, 2840 2841 /** 2842 * Processes a configuration object associated with a targeted XML element (g:element). 2843 * @function 2844 * @name processCfgElement 2845 * @memberOf gxe.control.Control# 2846 * @param {cfgObject} cfgElement the configuration object to process 2847 * @param {Element} htmlParentElement the parent HTML element 2848 * (new controls will be appended to this parent) 2849 * @param {gxe.xml.XmlElement} xmlParentElement the targeted XML parent element 2850 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 2851 * (when opening an existing document) 2852 * @param {Node} domNode the DOM node that is actively being processed 2853 * (when opening an existing document) 2854 */ 2855 processCfgElement: function(cfgElement,htmlParentElement,xmlParentElement,domProcessor,domNode) { 2856 var sTargetName = gxe.cfg.getGxeAttributeValue(cfgElement,"targetName"); 2857 var xmlDocument = xmlParentElement.parentDocument; 2858 var xmlElement = new gxe.xml.XmlElement(xmlDocument,xmlParentElement,cfgElement); 2859 xmlParentElement.children.add(xmlElement); 2860 var bRepeatable = xmlElement.isRepeatable(); 2861 2862 var domMatch = null; 2863 var domMatches = null; 2864 if ((domProcessor != null) && (domNode != null)) { 2865 2866 var cfgMatchTop = gxe.cfg.findGxeChild(cfgElement,"matchTopElements"); 2867 2868 if (cfgMatchTop == null) { 2869 if (!bRepeatable) { 2870 domMatch = domProcessor.findMatchingChildElement(domNode,cfgElement); 2871 if (domMatch != null) { 2872 domMatches = new Array(); 2873 domMatches.push(domMatch); 2874 } 2875 } else { 2876 domMatches = domProcessor.findMatchingChildElements(domNode,cfgElement); 2877 if ((domMatches != null) && (domMatches.length > 0)) domMatch = domMatches[0]; 2878 } 2879 2880 } else { 2881 domMatches = domProcessor.findMatchingChildElements(domNode,cfgElement); 2882 if ((domMatches != null) && (domMatches.length > 0)) { 2883 var ns = xmlDocument.namespaces; 2884 var topMatches = new Array(); 2885 for (var i=0; i<domMatches.length; i++) { 2886 var nd = domMatches[i]; 2887 var bMatched = true; 2888 var nConditions = 0; 2889 var nConditionsMatched = 0; 2890 gxe.cfg.forEachChild(cfgMatchTop,gxe.cfg.uriGxe,"match", 2891 dojo.hitch(this,function(cfgChild) { 2892 nConditions++; 2893 var qPath = gxe.cfg.getGxeAttributeValue(cfgChild,"qPath"); 2894 var qValue = gxe.cfg.getGxeAttributeValue(cfgChild,"qValue"); 2895 var qMode = gxe.cfg.getGxeAttributeValue(cfgChild,"qMode"); 2896 var bMust = (qMode != "mustNot"); 2897 if (qPath != null) { 2898 var b = domProcessor.matchTopElement(ns,nd,qPath,qValue,bMust); 2899 if (b) nConditionsMatched++; 2900 } 2901 } 2902 )); 2903 if (nConditions == nConditionsMatched) topMatches.push(nd); 2904 } 2905 domMatches = topMatches; 2906 if (topMatches.length > 0) domMatch = topMatches[0]; 2907 } 2908 } 2909 } 2910 2911 var ctl = this.context.makeXhtmlControl(cfgElement,this,true); 2912 ctl.xmlNode = xmlElement; 2913 ctl.xmlParentElement = xmlParentElement; 2914 ctl.build(htmlParentElement,domProcessor,domMatch); 2915 2916 if (bRepeatable && (domMatches != null) && (domMatches.length > 1)) { 2917 var ctlRepeatables = xmlElement.repeatablesContainer; 2918 if (ctlRepeatables != null) { 2919 var bRepeat = true; 2920 var ctlInput = xmlElement.getInputControl(); 2921 if ((ctlInput != null) && ctlInput.getSupportsMultipleValues()) bRepeat = false; 2922 else if (xmlElement.wrapsIsoMultiValueList()) bRepeat = false; 2923 if (bRepeat) { 2924 for (var i=1; i<domMatches.length; i++ ) { 2925 ctlRepeatables.repeatSection(domProcessor,domMatches[i]); 2926 } 2927 } 2928 } 2929 } 2930 2931 }, 2932 2933 /** 2934 * Processes the children of a configuration object. 2935 * @function 2936 * @name processChildren 2937 * @memberOf gxe.control.Control# 2938 * @param {cfgObject} cfgObject the configuration object to process 2939 * @param {Element} htmlParentElement the parent HTML element 2940 * (new controls will be appended to this parent) 2941 * @param {gxe.xml.XmlNode} xmlNode the target XML node 2942 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 2943 * (when opening an existing document) 2944 * @param {Node} domNode the DOM node that is actively being processed 2945 * (when opening an existing document) 2946 */ 2947 processChildren: function(cfgObject,htmlParentElement,xmlNode,domProcessor,domNode) { 2948 gxe.cfg.forEachChild(cfgObject,"*","*",dojo.hitch(this,function(cfgChild) { 2949 if (cfgChild.namespace == gxe.cfg.uriGxe) { 2950 2951 if (cfgChild.name == "attribute") { 2952 this.processCfgAttribute(cfgChild,htmlParentElement,xmlNode,domProcessor,domNode); 2953 2954 } else if ((cfgChild.name == "body") || 2955 (cfgChild.name == "elementText") || 2956 (cfgChild.name == "header")) { 2957 var ctl = this.context.makeXhtmlControl(cfgChild,this,true); 2958 ctl.xmlNode = xmlNode; 2959 ctl.xmlParentElement = xmlNode.parentElement; 2960 ctl.build(htmlParentElement,domProcessor,domNode); 2961 2962 } else if (cfgChild.name == "element") { 2963 this.processCfgElement(cfgChild,htmlParentElement,xmlNode,domProcessor,domNode); 2964 } else { 2965 2966 var sHtmlTag = gxe.cfg.getGxeHtmlAttributeValue(cfgChild,"tag"); 2967 if (sHtmlTag != null) { 2968 var ctl = this.context.makeXhtmlControl(cfgChild,this,true); 2969 ctl.xmlNode = xmlNode; 2970 ctl.xmlParentElement = xmlNode.parentElement; 2971 ctl.build(htmlParentElement,domProcessor,domNode); 2972 } 2973 } 2974 2975 } else if (cfgChild.namespace == gxe.cfg.uriGxeHtml) { 2976 var ctl = this.context.makeXhtmlControl(cfgChild,this,true); 2977 ctl.xmlNode = xmlNode; 2978 ctl.xmlParentElement = xmlNode.parentElement; 2979 ctl.build(htmlParentElement,domProcessor,domNode); 2980 } 2981 })); 2982 } 2983 2984 }); 2985 2986 /** 2987 * @class An array backed collection of GXE controls. 2988 * @name gxe.control.ControlArray 2989 * @extends gxe.control.Control 2990 * @property {boolean} displayInline indicates if the control should be displayed inline 2991 * (default=false, i.e. block display) 2992 * @property {boolean} isExclusive indicates if the control is using exclusive display 2993 * (default=false) 2994 */ 2995 dojo.provide("gxe.control.ControlArray"); 2996 dojo.declare("gxe.control.ControlArray",gxe.control.Control,{ 2997 _activeIndex: -1, 2998 _array: null, 2999 displayInline: false, 3000 isExclusive: false, 3001 3002 /** Override gxe.control.Control.initialize() */ 3003 initialize: function(context,cfgObject) { 3004 this._array = new Array(); 3005 this.inherited(arguments); 3006 var s = gxe.cfg.getGxeAttributeValue(this.cfgObject,"displayInline"); 3007 this.displayInline = (s == "true"); 3008 }, 3009 3010 /** 3011 * Activates the control at a given index. 3012 * @function 3013 * @name activateIndex 3014 * @memberOf gxe.control.ControlArray# 3015 * @param {Integer} nIndex the index of the control to activate 3016 */ 3017 activateIndex: function(nIndex) { 3018 this._activeIndex = nIndex; 3019 this.syncDisplay(true); 3020 }, 3021 3022 /** 3023 * Finds the index associated with a control. 3024 * @function 3025 * @name findIndex 3026 * @memberOf gxe.control.ControlArray# 3027 * @param {gxe.control.Control} control the subject control 3028 * @returns {Integer} the associated index (-1 if not located) 3029 */ 3030 findIndex: function(control) { 3031 var n = this._array.length;; 3032 for (var i=0; i<n; i++) { 3033 if (this.getItem(i) == control) return i; 3034 } 3035 return -1; 3036 }, 3037 3038 /** 3039 * Finds the index associated with a target XML node. 3040 * @function 3041 * @name findIndexByXmlNode 3042 * @memberOf gxe.control.ControlArray# 3043 * @param {gxe.xml.XmlNode} subjectXmlNode the subject XML node (element or attribute) 3044 * @returns {Integer} the associated index (-1 if not located) 3045 */ 3046 findIndexByXmlNode: function(subjectXmlNode) { 3047 if (subjectXmlNode != null) { 3048 var inputControl = subjectXmlNode.getInputControl(); 3049 var n = this._array.length;; 3050 for (var i=0; i<n; i++) { 3051 var ctl = this.getItem(i); 3052 if (ctl.xmlNode == subjectXmlNode) return i; 3053 if ((inputControl != null) && (ctl.htmlElement != null)) { 3054 var s = "[id='"+inputControl.gxeId+"']"; 3055 var bLocated = false; 3056 dojo.query(s,ctl.htmlElement).forEach(dojo.hitch(this,function(item) { 3057 var ctl2 = item.gxeControl; 3058 if ((typeof(ctl2) != "undefined") && (ctl2 != null)) { 3059 if (ctl2 == inputControl) bLocated = true; 3060 } 3061 })); 3062 if (bLocated) return i; 3063 } 3064 } 3065 return -1; 3066 } 3067 }, 3068 3069 /** 3070 * Fires the onArrayModified event. 3071 * @function 3072 * @name fireOnArrayModified 3073 * @memberOf gxe.control.ControlArray# 3074 */ 3075 fireOnArrayModified: function() { 3076 this.onArrayModified(this.getLength(),this._activeIndex); 3077 }, 3078 3079 /** 3080 * Gets the active index. 3081 * @function 3082 * @name getActiveIndex 3083 * @memberOf gxe.control.ControlArray# 3084 * @returns {Integer}the active index 3085 */ 3086 getActiveIndex: function() { 3087 return this._activeIndex; 3088 }, 3089 3090 /** 3091 * Gets the item at the specified index. 3092 * @function 3093 * @name getItem 3094 * @memberOf gxe.control.ControlArray# 3095 * @param {Integer} nIndex the index 3096 * @returns {gxe.control.Control} the corresponding control 3097 */ 3098 getItem: function(nIndex) { 3099 return this._array[nIndex]; 3100 }, 3101 3102 /** 3103 * Gets the length of the array. 3104 * @function 3105 * @name getLength 3106 * @memberOf gxe.control.ControlArray# 3107 * @returns {Integer} the length 3108 */ 3109 getLength: function() { 3110 return this._array.length; 3111 }, 3112 3113 /** 3114 * Inserts a control at a specified index. 3115 * @function 3116 * @name insertAt 3117 * @memberOf gxe.control.ControlArray# 3118 * @param {Integer} nIndex the index (same as JavaScript Array.splice) 3119 * @param {gxe.control.Control} control the control to insert 3120 * @param {boolean} bActivate (not currently used) 3121 */ 3122 insertAt: function(nIndex,control,bActivate) { 3123 var elRef = null; 3124 var nIdx = -1; 3125 var childNodes = this.htmlElement.childNodes; 3126 for (var i=0; i<childNodes.length; i++) { 3127 if (childNodes[i].nodeType == 1) { 3128 nIdx++; 3129 if (nIdx == nIndex) { 3130 elRef = childNodes[i]; 3131 break; 3132 } 3133 } 3134 } 3135 this._array.splice(nIndex,0,control); 3136 this.htmlElement.insertBefore(control.htmlElement,elRef); 3137 this.fireOnArrayModified(); 3138 }, 3139 3140 /** 3141 * Fired when the array structure has been modified. 3142 * @event 3143 * @name onArrayModified 3144 * @memberOf gxe.control.ControlArray# 3145 * @param {Integer} count the number of controls in the array 3146 * @param {Integer} activeIndex the index of the active control 3147 */ 3148 onArrayModified: function(count,activeIndex) {}, 3149 3150 /** 3151 * Appends a control to the array. 3152 * @function 3153 * @name push 3154 * @memberOf gxe.control.ControlArray# 3155 * @param {gxe.control.Control} control the control to add 3156 * @param {boolean} bActivate if true then activate the newly added control 3157 */ 3158 push: function(control,bActivate) { 3159 this._array.push(control); 3160 if (!this.isExclusive) { 3161 this.htmlElement.appendChild(control.htmlElement); 3162 } 3163 if (bActivate) { 3164 this._activeIndex = this.getLength() - 1; 3165 this.syncDisplay(true); 3166 } 3167 this.fireOnArrayModified(); 3168 }, 3169 3170 /** 3171 * Removes the control at the specified index from the array. 3172 * @function 3173 * @name removeIndex 3174 * @memberOf gxe.control.ControlArray# 3175 * @param {Integer} nIndex the index of the control to remove 3176 * @param {boolean} bActivate if true then activate an appropriate index following removal 3177 */ 3178 removeIndex: function(nIndex,bActivate) { 3179 var el = this.getItem(nIndex).htmlElement; 3180 el.parentNode.removeChild(el); 3181 this._array.splice(nIndex,1); 3182 3183 // which one is active following delete? 3184 if (bActivate) { 3185 if (this.getLength() == 1) { 3186 this._activeIndex = 0; 3187 this.syncDisplay(true); 3188 } else if (this.getLength() >= nIndex) { 3189 this._activeIndex = nIndex; 3190 if (this.getLength() == nIndex) this._activeIndex--; 3191 this.syncDisplay(true); 3192 } else { 3193 this._activeIndex = -1; 3194 this.syncDisplay(false); 3195 } 3196 } 3197 this.fireOnArrayModified(); 3198 }, 3199 3200 /** 3201 * Explicitly sets the display style for the control array. 3202 * @function 3203 * @name setDisplayStyle 3204 * @memberOf gxe.control.ControlArray# 3205 * @param {boolean} bShow if true then show the control 3206 */ 3207 setDisplayStyle: function(bShow) { 3208 var el = this.htmlElement; 3209 if (bShow) { 3210 if (this.displayInline) el.style.display = "inline"; 3211 else el.style.display = "block"; 3212 } else { 3213 el.style.display = "none"; 3214 } 3215 }, 3216 3217 /** 3218 * Swaps the positions of two controls within the array. 3219 * @function 3220 * @name swapPosition 3221 * @memberOf gxe.control.ControlArray# 3222 * @param {Integer} nFromIndex the from index 3223 * @param {Integer} nToIndex the to index 3224 * @param {boolean} bActivate if true then activate the "to" index following the swap 3225 */ 3226 swapPosition: function(nFromIndex,nToIndex,bActivate) { 3227 3228 if (nFromIndex == nToIndex) return; 3229 var a = this._array[nFromIndex]; 3230 var b = this._array[nToIndex]; 3231 3232 if (this.isExclusive) { 3233 this._array[nFromIndex] = b; 3234 this._array[nToIndex] = a; 3235 if (bActivate) { 3236 this._activeIndex = nToIndex; 3237 this.syncDisplay(true); 3238 } 3239 3240 } else { 3241 var bUp = (nFromIndex > nToIndex); 3242 var elA = a.htmlElement; 3243 var elB = b.htmlElement; 3244 var nRefIndex = nFromIndex; 3245 if (bUp) { 3246 elA = b.htmlElement; 3247 elB = a.htmlElement; 3248 nRefIndex = nToIndex; 3249 } 3250 this._array[nFromIndex] = b; 3251 this._array[nToIndex] = a; 3252 3253 var elARem = elA.parentNode.removeChild(elA); 3254 elB.parentNode.insertBefore(elARem,elB); 3255 var elRef = null; 3256 var nIdx = -1; 3257 var childNodes = elB.parentNode.childNodes; 3258 for (var i=0; i<childNodes.length; i++) { 3259 if (childNodes[i].nodeType == 1) { 3260 nIdx++; 3261 if (nIdx == nRefIndex) { 3262 elRef = childNodes[i]; 3263 break; 3264 } 3265 } 3266 } 3267 var elBRem = elB.parentNode.removeChild(elB); 3268 elARem.parentNode.insertBefore(elBRem,elRef); 3269 } 3270 3271 this.fireOnArrayModified(); 3272 }, 3273 3274 /** 3275 * Ensures that the active control for an exclusively displayed array is properly displayed. 3276 * @function 3277 * @name syncDisplay 3278 * @memberOf gxe.control.ControlArray# 3279 * @param {boolean} bForce if true then ensure that the control array itself is properly displayed 3280 */ 3281 syncDisplay: function(bForce) { 3282 if (this.isExclusive) { 3283 var n = this._array.length; 3284 for (var i=0; i<n; i++) { 3285 var el = this._array[i].htmlElement; 3286 if (i == this._activeIndex) { 3287 if (this.htmlElement.childNodes.length == 0) { 3288 this.htmlElement.appendChild(el); 3289 } else { 3290 this.htmlElement.replaceChild(el,this.htmlElement.childNodes[0]); 3291 } 3292 break; 3293 } 3294 } 3295 if (this._activeIndex == -1) { 3296 if (this.htmlElement.childNodes.length == 1) { 3297 this.htmlElement.removeChild(this.htmlElement.childNodes[0]); 3298 } 3299 } 3300 if (bForce) { 3301 if (this.displayInline) this.htmlElement.style.display = "inline"; 3302 else this.htmlElement.style.display = "block"; 3303 } 3304 } 3305 }, 3306 3307 /** 3308 * Toggles the display style of the control array. 3309 * @function 3310 * @name toggleDisplay 3311 * @memberOf gxe.control.ControlArray# 3312 */ 3313 toggleDisplay: function() { 3314 var el = this.htmlElement; 3315 if (el.style.display == "none") { 3316 if (this.displayInline) el.style.display = "inline"; 3317 else el.style.display = "block"; 3318 } else el.style.display = "none"; 3319 } 3320 3321 }); 3322 3323 /** 3324 * @class A control array using exclusive display. 3325 * @name gxe.control.ExclusiveControlArray 3326 * @extends gxe.control.ControlArray 3327 * @property {boolean} isExclusive true 3328 */ 3329 dojo.provide("gxe.control.ExclusiveControlArray"); 3330 dojo.declare("gxe.control.ExclusiveControlArray",gxe.control.ControlArray,{ 3331 isExclusive: true, 3332 3333 /** Override gxe.control.Control.onChildControlCreated() */ 3334 onChildControlCreated: function(control,domProcessor,domNode) { 3335 this.push(control,false); 3336 this.syncDisplay(true); 3337 } 3338 }); 3339 3340 /** 3341 * @class A control array using non-exclusive display. 3342 * @name gxe.control.NonExclusiveControlArray 3343 * @extends gxe.control.ControlArray 3344 * @property {boolean} isExclusive false 3345 */ 3346 dojo.provide("gxe.control.NonExclusiveControlArray"); 3347 dojo.declare("gxe.control.NonExclusiveControlArray",gxe.control.ControlArray,{ 3348 isExclusive: false, 3349 3350 /** Override gxe.control.Control.onChildControlCreated() */ 3351 onChildControlCreated: function(control,domProcessor,domNode) { 3352 this.push(control,false); 3353 this.syncDisplay(true); 3354 } 3355 }); 3356 3357 /** 3358 * @class Provides a container for repeatable element controls (multiplicity > 1). 3359 * @name gxe.control.RepeatablesContainer 3360 * @extends gxe.control.ControlArray 3361 */ 3362 dojo.provide("gxe.control.RepeatablesContainer"); 3363 dojo.declare("gxe.control.RepeatablesContainer",gxe.control.ControlArray,{ 3364 cfgSectionBody: null, 3365 isExclusive: false, 3366 multiplicityTools: null, 3367 xmlElementCfgObject: null, 3368 xmlParentElement: null, 3369 xmlPlaceHolder: null, 3370 3371 /** 3372 * Determines if elements can be removed. 3373 * @function 3374 * @name canRemove 3375 * @memberOf gxe.control.RepeatablesContainer# 3376 * @returns {boolean} true if elements can be removed 3377 */ 3378 canRemove: function() { 3379 var bCanRemove = false; 3380 if (this.isConfigured()) { 3381 var nSections = 0; 3382 var nSimiliar = this.countSimilarSections(); 3383 if (nSimiliar > 0) { 3384 var sMinOccurs = gxe.cfg.getMinOccurs(this.xmlElementCfgObject); 3385 var nMinOccurs = parseInt(sMinOccurs); 3386 if (isNaN(nMinOccurs)) nMinOccurs = 1; 3387 if (nMinOccurs < nSimiliar) { 3388 if (nSimiliar == 1) bCanRemove = false; 3389 else bCanRemove = true; 3390 } 3391 } 3392 } 3393 return bCanRemove; 3394 }, 3395 3396 /** 3397 * Determines if elements can be repeated. 3398 * @function 3399 * @name canRepeat 3400 * @memberOf gxe.control.RepeatablesContainer# 3401 * @returns {boolean} true if elements can be repeated 3402 */ 3403 canRepeat: function() { 3404 var bCanRepeat = false; 3405 if (this.isConfigured()) { 3406 var sMaxOccurs = gxe.cfg.getMaxOccurs(this.xmlElementCfgObject); 3407 if (sMaxOccurs == "unbounded") { 3408 bCanRepeat = true; 3409 } else { 3410 var nSimiliar = this.countSimilarSections(); 3411 var nMaxOccurs = parseInt(sMaxOccurs); 3412 if (isNaN(nMaxOccurs)) nMaxOccurs = 1; 3413 if ((nMaxOccurs > 0) && (nSimiliar < nMaxOccurs)) { 3414 bCanRepeat = true; 3415 } 3416 } 3417 } 3418 return bCanRepeat; 3419 }, 3420 3421 /** 3422 * Counts the number of similar sections. 3423 * @function 3424 * @name countSimilarSections 3425 * @memberOf gxe.control.RepeatablesContainer# 3426 * @returns {Integer} the number of similiar sections 3427 */ 3428 countSimilarSections: function() { 3429 return this.getLength(); 3430 }, 3431 3432 /** Override gxe.control.Control.ensureVisibility() */ 3433 ensureVisibility: function(subjectXmlNode) { 3434 if (this.isExclusive) { 3435 var nIndex = this.findIndexByXmlNode(subjectXmlNode); 3436 if ((nIndex != -1) && (nIndex != this.getActiveIndex())) { 3437 this.activateIndex(nIndex); 3438 this.fireOnArrayModified(); 3439 } 3440 } 3441 this.inherited(arguments); 3442 }, 3443 3444 /** 3445 * Handles multiplicity related events . 3446 * <br/>(repeatSection removeSection moveSectionUp moveSectionDown) 3447 * @event 3448 * @name handleEvent 3449 * @memberOf gxe.control.RepeatablesContainer# 3450 * @param {Event} e the underlying browser event 3451 * @param {gxe.control.Section} sectionControl the section from which the event was fired 3452 * @param {String} htmlEventName the HTML event name (e.g. "onclick") 3453 * @param {String} gxeEventName a GXE name for the event (e.g. "repeatSection") 3454 */ 3455 handleEvent: function(e,sectionControl,htmlEventName,gxeEventName) { 3456 if (gxeEventName == "repeatSection") { 3457 if (!this.isExclusive) { 3458 this.activateIndex(this.findIndex(sectionControl)); 3459 var nTargetIndex = this.getActiveIndex(); 3460 var nTargetLength = this.getLength(); 3461 var bOk = (nTargetIndex == (nTargetLength - 1)); 3462 if (bOk) this.repeatSection(null,null); 3463 } else { 3464 this.repeatSection(null,null); 3465 } 3466 } else if (gxeEventName == "removeSection") { 3467 if (!this.isExclusive) this.activateIndex(this.findIndex(sectionControl)); 3468 this.removeSection(); 3469 } else if (gxeEventName == "moveSectionUp") { 3470 if (!this.isExclusive) this.activateIndex(this.findIndex(sectionControl)); 3471 this.moveSection(true); 3472 } else if (gxeEventName == "moveSectionDown") { 3473 if (!this.isExclusive) this.activateIndex(this.findIndex(sectionControl)); 3474 this.moveSection(false); 3475 } 3476 }, 3477 3478 /** 3479 * Determines if the control has been properly configured. 3480 * @function 3481 * @name isConfigured 3482 * @memberOf gxe.control.RepeatablesContainer# 3483 * @returns {boolean} true if properly configured 3484 */ 3485 isConfigured: function() { 3486 return (this.xmlElementCfgObject != null) && (this.xmlParentElement != null); 3487 }, 3488 3489 /** 3490 * Moves a section. 3491 * @function 3492 * @name moveSection 3493 * @memberOf gxe.control.RepeatablesContainer# 3494 * @param {boolean} bUp true if the section (i.e. element should be move up within the target XML document) 3495 */ 3496 moveSection: function(bUp) { 3497 if (!this.isConfigured()) return; 3498 3499 // move up/down refers to up/down within the XML document 3500 3501 // determine the target and xml indices 3502 var nTargetIndex = this.getActiveIndex(); 3503 var nTargetLength = this.getLength(); 3504 if ((nTargetIndex < 0) || (nTargetLength <= 0)) return; 3505 if (bUp && (nTargetIndex == 0)) return; 3506 if (!bUp && (nTargetIndex == (nTargetLength - 1))) return; 3507 var xmlElement = this.getItem(nTargetIndex).xmlNode; 3508 var nXmlIndex = xmlElement.parentElement.children.findIndex(xmlElement); 3509 if (nXmlIndex == -1) return; 3510 3511 // determine the reposition indices 3512 var nNewTargetIndex = nTargetIndex + 1; 3513 var nNewXmlIndex = nXmlIndex + 1; 3514 if (bUp) { 3515 nNewTargetIndex = nTargetIndex - 1; 3516 nNewXmlIndex = nXmlIndex - 1; 3517 } 3518 if (nTargetIndex == nNewTargetIndex) return; 3519 3520 // reposition 3521 xmlElement.parentElement.children.swapPosition(nXmlIndex,nNewXmlIndex); 3522 this.swapPosition(nTargetIndex,nNewTargetIndex,true); 3523 }, 3524 3525 /** Override gxe.control.ControlArray.onArrayModified() */ 3526 onArrayModified: function(control,domProcessor,domNode) { 3527 this.syncTools(); 3528 }, 3529 3530 /** Override gxe.control.Control.onChildControlCreated() */ 3531 onChildControlCreated: function(control,domProcessor,domNode) { 3532 var bActivate = false; 3533 if (this.isExclusive) { 3534 bActivate = (domProcessor == null) || (this.getLength() == 0); 3535 } 3536 this.push(control,bActivate); 3537 this.syncDisplay(true); 3538 }, 3539 3540 /** 3541 * Removes a section. 3542 * @function 3543 * @name removeSection 3544 * @memberOf gxe.control.RepeatablesContainer# 3545 */ 3546 removeSection: function() { 3547 if (!this.canRemove()) return; 3548 3549 var nSections = this.getLength(); 3550 var nTargetIndex = this.getActiveIndex(); 3551 if (nTargetIndex < 0) return; 3552 var xmlElement = this.getItem(nTargetIndex).xmlNode; 3553 var nXmlIndex = xmlElement.parentElement.children.findIndex(xmlElement); 3554 if (nXmlIndex == -1) return; 3555 3556 if (nSections > 1) { 3557 xmlElement.parentElement.children.removeIndex(nXmlIndex); 3558 this.removeIndex(nTargetIndex,true); 3559 } else if (nSections == 1) { 3560 if (this.isExclusive) { 3561 xmlElement.isPlaceHolder = true; 3562 this.xmlPlaceHolder = xmlElement; 3563 this.removeIndex(nTargetIndex,true); 3564 } else { 3565 var ctlLast = this.getItem(0); 3566 var ctlBodyContainer = ctlLast.sectionBodyContainer; 3567 if ((typeof(ctlBodyContainer) != "undefined") && (ctlBodyContainer != null)) { 3568 this.cfgSectionBody = ctlBodyContainer.cfgSectionBody; 3569 xmlElement.isPlaceHolder = true; 3570 this.xmlPlaceHolder = xmlElement; 3571 ctlBodyContainer.removeIndex(0,true); 3572 } 3573 } 3574 } 3575 3576 }, 3577 3578 /** 3579 * Repeats a section. 3580 * @function 3581 * @name repeatSection 3582 * @memberOf gxe.control.RepeatablesContainer# 3583 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 3584 * (when opening an existing document) 3585 * @param {Node} domNode the DOM node that is actively being processed 3586 * (when opening an existing document) 3587 */ 3588 repeatSection: function(domProcessor,domNode) { 3589 3590 // initialize 3591 if (!this.canRepeat()) return; 3592 var nSections = this.getLength(); 3593 var xmlParent = this.xmlParentElement; 3594 var xmlDocument = xmlParent.parentDocument; 3595 var newXmlElement = new gxe.xml.XmlElement(xmlDocument,xmlParent,this.xmlElementCfgObject); 3596 var bUseAppend = true; 3597 3598 // determine the insertion index 3599 var nTargetIndex = -1; 3600 var nXmlIndex = -1; 3601 if (this.xmlPlaceHolder != null) { 3602 nXmlIndex = xmlParent.children.findIndex(this.xmlPlaceHolder); 3603 if (nXmlIndex != -1) nXmlIndex++; // insert after 3604 } else { 3605 if (bUseAppend) { 3606 var xmlLast = this.getItem(nSections - 1).xmlNode; 3607 nXmlIndex = xmlParent.children.findIndex(xmlLast); 3608 if (nXmlIndex != -1) nXmlIndex++; // insert after 3609 } else { 3610 nTargetIndex = this.getActiveIndex(); 3611 var xmlLast = this.getItem(this.getActiveIndex()).xmlNode; 3612 nXmlIndex = xmlParent.children.findIndex(xmlLast); 3613 if (nXmlIndex != -1) nXmlIndex++; // insert after 3614 } 3615 } 3616 if (nXmlIndex == -1) return; // exception message here?? 3617 3618 // repeat 3619 var elTmp = document.createElement("div"); 3620 3621 if (this.xmlPlaceHolder == null) { 3622 if (this.isExclusive) { 3623 xmlParent.children.insertAt(nXmlIndex,newXmlElement); 3624 this.processChildren(this.cfgObject,elTmp,newXmlElement,domProcessor,domNode); 3625 } else { 3626 xmlParent.children.insertAt(nXmlIndex,newXmlElement); 3627 var cfgElement = this.xmlElementCfgObject; 3628 var ctl = this.context.makeXhtmlControl(cfgElement,this,true); 3629 ctl.xmlNode = newXmlElement; 3630 ctl.xmlParentElement = newXmlElement.parentElement; 3631 ctl.build(elTmp,domProcessor,domNode); 3632 } 3633 3634 } else { 3635 var nPlaceHolderIndex = xmlParent.children.findIndex(this.xmlPlaceHolder); 3636 if (nPlaceHolderIndex == -1) return; // exception message here?? 3637 3638 if (this.isExclusive) { 3639 xmlParent.children.insertAt(nPlaceHolderIndex,newXmlElement); 3640 this.processChildren(this.cfgObject,elTmp,newXmlElement,domProcessor,domNode); 3641 xmlParent.children.removeIndex(nPlaceHolderIndex+1); 3642 this.xmlPlaceHolder = null; 3643 3644 } else { 3645 var ctlLast = this.getItem(0); 3646 ctlLast.xmlNode = newXmlElement; 3647 ctlLast.xmlParentElement = this.xmlNode.parentElement; 3648 var ctlBodyContainer = ctlLast.sectionBodyContainer; 3649 if ((typeof(ctlBodyContainer) != "undefined") && (ctlBodyContainer != null)) { 3650 xmlParent.children.insertAt(nPlaceHolderIndex,newXmlElement); 3651 var cfgElement = this.xmlElementCfgObject; 3652 var ctl = this.context.makeXhtmlControl(this.cfgSectionBody,ctlBodyContainer,true); 3653 ctl.xmlNode = newXmlElement; 3654 ctl.xmlParentElement = newXmlElement.parentElement; 3655 ctl.build(elTmp,domProcessor,domNode); 3656 xmlParent.children.removeIndex(nPlaceHolderIndex+1); 3657 this.xmlPlaceHolder = null; 3658 3659 } 3660 } 3661 } 3662 }, 3663 3664 /** 3665 * Synchronizes the multiplicity tools for a section. 3666 * @function 3667 * @name syncTools 3668 * @memberOf gxe.control.RepeatablesContainer# 3669 */ 3670 syncTools: function() { 3671 if (!this.isConfigured()) return; 3672 var aToolbars = new Array(); 3673 if (this.isExclusive) { 3674 aToolbars.push(this.multiplicityTools) 3675 } else { 3676 for (var i=0; i<this.getLength(); i++) aToolbars.push(this.getItem(i).multiplicityTools); 3677 } 3678 if (aToolbars.length > 0) { 3679 var _toggle = function(el,bEnabled) { 3680 if (bEnabled) { 3681 dojo.removeClass(el,"gxeDisabled"); 3682 el.disabled = false; 3683 } else { 3684 dojo.addClass(el,"gxeDisabled"); 3685 el.disabled = true; 3686 } 3687 }; 3688 3689 var nTargetIndex = this.getActiveIndex(); 3690 var nTargetLength = this.getLength(); 3691 for (var i=0; i<aToolbars.length; i++) { 3692 var ctlTools = aToolbars[i]; 3693 if ((typeof(ctlTools) != "undefined") && (ctlTools != null)) { 3694 if (!this.isExclusive) nTargetIndex = i; 3695 dojo.query("[gxeToolName='repeatSection']",ctlTools.htmlElement).forEach(dojo.hitch(this,function(item) { 3696 var b = this.canRepeat(); 3697 if (b && !this.isExclusive) b = (nTargetIndex == (nTargetLength - 1)); 3698 _toggle(item,b); 3699 })); 3700 dojo.query("[gxeToolName='removeSection']",ctlTools.htmlElement).forEach(dojo.hitch(this,function(item) { 3701 _toggle(item,this.canRemove()); 3702 })); 3703 dojo.query("[gxeToolName='moveSectionUp']",ctlTools.htmlElement).forEach(dojo.hitch(this,function(item) { 3704 _toggle(item,(nTargetIndex > 0)); 3705 })); 3706 dojo.query("[gxeToolName='moveSectionDown']",ctlTools.htmlElement).forEach(dojo.hitch(this,function(item) { 3707 _toggle(item,((nTargetLength > 1) && (nTargetIndex < (nTargetLength - 1)))); 3708 })); 3709 } 3710 } 3711 } 3712 } 3713 3714 }); 3715 3716 /** 3717 * @class Container supporting the display of an array of tabs. 3718 * <br/><br/>Typically associated with: 3719 * <br/>gpt/gxe/core/ui/Tabs.xml 3720 * <br/>gpt/gxe/core/xml/ElementChoice.xml 3721 * <br/><br/>Also extended by: 3722 * <br/>gxe.control.IndexedTabArray 3723 * <br/> 3724 * @name gxe.control.TabArray 3725 * @extends gxe.control.Control 3726 */ 3727 dojo.provide("gxe.control.TabArray"); 3728 dojo.declare("gxe.control.TabArray",gxe.control.Control,{ 3729 _activeIndex: -1, 3730 _array: null, 3731 3732 /** Override gxe.control.Control.initialize() */ 3733 initialize: function(context,cfgObject) { 3734 this._array = new Array(); 3735 this.inherited(arguments); 3736 }, 3737 3738 /** 3739 * Activates the tab at a given index. 3740 * Once activated, the onTabClicked event is fired. 3741 * @function 3742 * @name activateIndex 3743 * @memberOf gxe.control.TabArray# 3744 * @param {Integer} nIndex the index of the tab to activate 3745 */ 3746 activateIndex: function(nIndex) { 3747 this._activeIndex = nIndex; 3748 var nTabs = this._array.length; 3749 for (var i=0; i<nTabs; i++) this._setTabStyle(i); 3750 this.onTabClicked(this._activeIndex); 3751 }, 3752 3753 /** 3754 * Appends a radio button tab to the array. 3755 * @function 3756 * @name appendRadio 3757 * @memberOf gxe.control.TabArray# 3758 * @param {Integer} nIndex the index of the tab 3759 * @param {String} sLabel the label for the tab 3760 * @param {boolean} bIsActive true if the new tab should be active (i.e. selected) 3761 */ 3762 appendRadio: function(nIndex,sLabel,bIsActive) { 3763 var elItem = document.createElement("li"); 3764 this.htmlElement.appendChild(elItem); 3765 3766 var elLink = document.createElement("a"); 3767 elLink.tabindex = (nIndex + 1); 3768 elLink._gxeTabIndex = nIndex; 3769 elLink.setAttribute("href","javascript:void(0);"); 3770 if (bIsActive) elLink.className = "current"; 3771 elItem.appendChild(elLink); 3772 3773 var sChoices = this.gxeId+"Option"; 3774 var sOptionId = this.context.generateUniqueId(); 3775 var elOption = document.createElement("input"); 3776 if(dojo.isIE <= 8) { 3777 if (bIsActive) { 3778 elOption = document.createElement( 3779 "<input type=\"radio\" name=\""+sChoices+"\" checked=\"checked\"/>"); 3780 } else { 3781 elOption = document.createElement("<input type=\"radio\" name=\""+sChoices+"\"/>"); 3782 } 3783 } else { 3784 elOption.setAttribute("name",sChoices); 3785 elOption.setAttribute("type","radio"); 3786 if (bIsActive) elOption.setAttribute("checked","checked"); 3787 } 3788 3789 elOption.setAttribute("id",sOptionId); 3790 elOption.setAttribute("value",nIndex); 3791 elLink.appendChild(elOption); 3792 3793 var elLabel = document.createElement("label"); 3794 if(dojo.isIE <= 8) { 3795 elLabel = document.createElement("<label for=\""+sOptionId+"\"/>"); 3796 } else { 3797 elLabel.setAttribute("for",sOptionId); 3798 } 3799 3800 elLabel.appendChild(document.createTextNode(sLabel)); 3801 elLink.appendChild(elLabel); 3802 3803 dojo.connect(elOption,"onclick",this,dojo.hitch(this,function(e) { 3804 this.activateIndex(elLink._gxeTabIndex); 3805 })); 3806 this._array.push(elLink); 3807 if (bIsActive) this.activateIndex(nIndex); 3808 return elLink; 3809 }, 3810 3811 /** 3812 * Appends a tab to the array. 3813 * @function 3814 * @name appendTab 3815 * @memberOf gxe.control.TabArray# 3816 * @param {Integer} nIndex the index of the tab 3817 * @param {String} sLabel the label for the tab 3818 * @param {boolean} bIsActive true if the new tab should be active (i.e. selected) 3819 */ 3820 appendTab: function(nIndex,sLabel,bIsActive) { 3821 var elItem = document.createElement("li"); 3822 var elLink = document.createElement("a"); 3823 elLink.tabindex = (nIndex + 1); 3824 elLink._gxeTabIndex = nIndex; 3825 elLink.setAttribute("href","javascript:void(0);"); 3826 if (bIsActive) elLink.className = "current"; 3827 elLink.appendChild(document.createTextNode(sLabel)); 3828 elItem.appendChild(elLink); 3829 dojo.connect(elLink,"onclick",this,dojo.hitch(this,function(e) { 3830 this.activateIndex(elLink._gxeTabIndex); 3831 })); 3832 this.htmlElement.appendChild(elItem); 3833 this._array.push(elLink); 3834 if (bIsActive) this.activateIndex(nIndex); 3835 return elLink; 3836 }, 3837 3838 /** 3839 * Fired when a tab is clicked. 3840 * @event 3841 * @name onTabClicked 3842 * @memberOf gxe.control.TabArray# 3843 * @param {Integer} nIndex the index of the clicked tab 3844 */ 3845 onTabClicked: function(nIndex) {}, 3846 3847 /** 3848 * Sets the CSS class name for a tab. 3849 * @function 3850 * @name _setTabStyle 3851 * @memberOf gxe.control.TabArray# 3852 * @param {Integer} nIndex the index of the tab 3853 */ 3854 _setTabStyle: function(nIndex) { 3855 var elLink = this._array[nIndex]; 3856 var nTabIndex = elLink._gxeTabIndex; 3857 var bIsActive = (nTabIndex == this._activeIndex); 3858 if (bIsActive && elLink.className != "current") { 3859 elLink.className = "current"; 3860 } else if (!bIsActive && elLink.className == "current") { 3861 elLink.className = ""; 3862 } 3863 } 3864 3865 }); 3866 3867 /** 3868 * @class Provides an array of tabs indexed by the multiplicity of a targeted XML element. 3869 * <br/>An indexed tab array is used when the body of the target is display exclusively. 3870 * @name gxe.control.IndexedTabArray 3871 * @extends gxe.control.TabArray 3872 */ 3873 dojo.provide("gxe.control.IndexedTabArray"); 3874 dojo.declare("gxe.control.IndexedTabArray",gxe.control.TabArray,{ 3875 3876 /** 3877 * Auto configures the control. 3878 * @function 3879 * @name autoConfigure 3880 * @memberOf gxe.control.IndexedTabArray# 3881 * @param {gxe.Context} context the editor context 3882 * @param {gxe.control.Control} parentControl the parent control 3883 */ 3884 autoConfigure: function(context,parentControl) { 3885 var cfgAttr; 3886 var cfgObj = new Object(); 3887 cfgObj.namespace = gxe.cfg.uriGxeHtml; 3888 cfgObj.name = "ul"; 3889 cfgObj.parent = parentControl.cfgObject; 3890 cfgObj.attributes = new Array(); 3891 this.cfgObject = cfgObj; 3892 3893 cfgAttr = new Object(); 3894 cfgAttr.namespace = gxe.cfg.uriGxeHtml; 3895 cfgAttr.name = "class"; 3896 cfgAttr.value = "gxeTabArray"; 3897 cfgAttr.parent = cfgObj; 3898 cfgObj.attributes.push(cfgAttr); 3899 this.initialize(context,cfgObj); 3900 }, 3901 3902 /** 3903 * Synchronizes the array of indexed tabs. 3904 * @function 3905 * @name synchronize 3906 * @memberOf gxe.control.IndexedTabArray# 3907 * @param {Integer} count the number of repeated XML elements 3908 * @param {Integer} activeIndex the index of the active element 3909 */ 3910 synchronize: function(count,activeIndex) { 3911 var nTabs = this._array.length; 3912 if (nTabs < count) { 3913 for (var i=nTabs; i<count; i++) { 3914 var nTabIndex = this._array.length; 3915 var bIsActive = (nTabIndex == activeIndex); 3916 this.appendTab(nTabIndex,""+(nTabIndex+1),bIsActive); 3917 } 3918 } else if (nTabs > count) { 3919 var n = (nTabs - count); 3920 for (var i=(nTabs - 1); i>=count; i--) { 3921 var elTab = this._array[i]; 3922 elTab.parentNode.removeChild(elTab); 3923 this._array.pop(); 3924 } 3925 } 3926 this._activeIndex = activeIndex; 3927 nTabs = this._array.length; 3928 for (var i=0; i<nTabs; i++) { 3929 var elLink = this._array[i]; 3930 elLink.tabindex = (i + 1); 3931 elLink._gxeTabIndex = i; 3932 this._setTabStyle(i); 3933 } 3934 if (nTabs < 2) this.htmlElement.style.display = "none"; 3935 else this.htmlElement.style.display = "inline"; 3936 } 3937 3938 }); 3939 3940 /** 3941 * @class Supports the message area section of the page. 3942 * @name gxe.control.MessageArea 3943 * @extends gxe.control.Control 3944 */ 3945 dojo.provide("gxe.control.MessageArea"); 3946 dojo.declare("gxe.control.MessageArea",gxe.control.Control,{ 3947 ul: null, 3948 3949 /** 3950 * Adds an error message. 3951 * @function 3952 * @name addError 3953 * @memberOf gxe.control.MessageArea# 3954 * @param {String} sMessage the message 3955 */ 3956 addError: function(sMessage) { 3957 this.addMessage(sMessage,"error"); 3958 }, 3959 3960 /** 3961 * Adds a message. 3962 * @function 3963 * @name addMessage 3964 * @memberOf gxe.control.MessageArea# 3965 * @param {String} sMessage the message 3966 * @param {String} sClass the CSS class name (success|warning|error) 3967 */ 3968 addMessage: function(sMessage,sClass) { 3969 var elItem = document.createElement("li"); 3970 if (sClass != null) elItem.className = sClass; 3971 elItem.appendChild(document.createTextNode(sMessage)); 3972 this.ul.appendChild(elItem); 3973 this.ensureVisibility(); 3974 }, 3975 3976 /** 3977 * Adds a success message. 3978 * @function 3979 * @name addSuccess 3980 * @memberOf gxe.control.MessageArea# 3981 * @param {String} sMessage the message 3982 */ 3983 addSuccess: function(sMessage) { 3984 this.addMessage(sMessage,"success"); 3985 }, 3986 3987 /** 3988 * Adds a validation error to the control. 3989 * <br/>Validation errors are added as warning messages. 3990 * @function 3991 * @name addValidationError 3992 * @memberOf gxe.control.MessageArea# 3993 * @param {String} sMessage the message 3994 * @param {gxe.xml.XmlNode} xmlNode the targeted XML node (element or attribute) 3995 * @param {gxe.control.InputBase} the input control associated with the target 3996 */ 3997 addValidationError: function(sMessage,xmlNode,inputControl) { 3998 if (sMessage == null) sMessage = ""; 3999 4000 if (inputControl == null) { 4001 this.addMessage(sMessage,"error"); 4002 } else { 4003 4004 var elItem = document.createElement("li"); 4005 elItem.className = "warning"; 4006 this.ul.appendChild(elItem); 4007 var elLink = document.createElement("a"); 4008 elLink.setAttribute("href","javascript:void(0);"); 4009 elLink.appendChild(document.createTextNode(sMessage)); 4010 if (inputControl.htmlElement != null) { 4011 var sTip = inputControl.htmlElement.title; 4012 if ((typeof(sTip) != "undefined") && (sTip != null)) { 4013 sTip = dojo.trim(sTip); 4014 if (sTip.length > 0) elLink.title = sTip; 4015 } 4016 } 4017 4018 elItem.appendChild(elLink); 4019 var handle = dojo.connect(elLink,"onclick",this,dojo.hitch(this,function(e) { 4020 inputControl.focus(xmlNode); 4021 })); 4022 4023 dojo.connect(inputControl,"onInputChanged",this,dojo.hitch(this,function(inputControl2,value) { 4024 var status = xmlNode.validateInput(inputControl2,true); 4025 if (status.isValid) { 4026 elItem.className = "success"; 4027 } else { 4028 elItem.className = "warning"; 4029 } 4030 if (status.message != null) { 4031 while (elLink.childNodes.length >= 1) elLink.removeChild(elLink.firstChild); 4032 elLink.appendChild(document.createTextNode(status.message)); 4033 } 4034 })); 4035 4036 this.ensureVisibility(); 4037 } 4038 }, 4039 4040 /** 4041 * Adds a warning message. 4042 * @function 4043 * @name addWarning 4044 * @memberOf gxe.control.MessageArea# 4045 * @param {String} sMessage the message 4046 */ 4047 addWarning: function(sMessage) { 4048 this.addMessage(sMessage,"warning"); 4049 }, 4050 4051 /** Override gxe.control.Control.build() */ 4052 build: function(htmlParentElement,domProcessor,domNode) { 4053 this.htmlElement = htmlParentElement; 4054 this.htmlElement.style.display = "none"; 4055 4056 var elClear = document.createElement("a"); 4057 elClear.setAttribute("href","javascript:void(0);"); 4058 elClear.className = "gxeClearAll"; 4059 elClear.appendChild(document.createTextNode(this.context.getI18NString("button.clearMessages"))); 4060 dojo.connect(elClear,"onclick",this,"clearAll"); 4061 this.htmlElement.appendChild(elClear); 4062 4063 /* 4064 var elClear = document.createElement("button"); 4065 elClear.setAttribute("type","button"); 4066 elClear.appendChild(document.createTextNode(this.context.getI18NString("button.clearMessages"))); 4067 dojo.connect(elClear,"onclick",this,"clearAll"); 4068 this.htmlElement.appendChild(elClear); 4069 */ 4070 4071 var elWrapper = document.createElement("div"); 4072 elWrapper.className = "gxeMessageListWrapper"; 4073 this.htmlElement.appendChild(elWrapper); 4074 4075 this.ul = document.createElement("ul"); 4076 this.ul.className = "gxeMessageList"; 4077 elWrapper.appendChild(this.ul); 4078 }, 4079 4080 /** 4081 * Clears add messages and hides the message area. 4082 * @function 4083 * @name clearAll 4084 * @memberOf gxe.control.MessageArea# 4085 */ 4086 clearAll: function() { 4087 var aRemove = new Array(); 4088 var childNodes = this.ul.childNodes; 4089 for (var i=0; i<childNodes.length; i++) { 4090 if (childNodes[i].nodeType == 1) { 4091 aRemove.push(childNodes[i]); 4092 } 4093 } 4094 for (var i=0; i<aRemove.length; i++) { 4095 this.ul.removeChild(aRemove[i]); 4096 } 4097 this.htmlElement.style.display = "none"; 4098 if (dojo.hasClass(this.context.htmlParentElement,"gxeRepairMode")) { 4099 dojo.removeClass(this.context.htmlParentElement,"gxeRepairMode"); 4100 } 4101 }, 4102 4103 /** Override gxe.control.Control.ensureVisibility() */ 4104 ensureVisibility: function() { 4105 if (this.htmlElement.style.display == "none") this.htmlElement.style.display = "block"; 4106 }, 4107 4108 /** 4109 * Handles an exception by adding an error message to the control. 4110 * @function 4111 * @name handleException 4112 * @memberOf gxe.control.MessageArea# 4113 * @param {String} exception the exception 4114 */ 4115 handleException: function(exception) { 4116 this.addMessage(exception,"error"); 4117 } 4118 4119 }); 4120 4121 /** 4122 * @class Supports a UI section. 4123 * <br/>A section typically consists of a header and a body. 4124 * <br/><br/>Typically associated with: 4125 * <br/>gpt/gxe/core/ui/Section.xml 4126 * <br/><br/>Also referenced by: 4127 * <br/>gpt/gxe/core/xml/Attribute.xml 4128 * <br/>gpt/gxe/core/xml/Element.xml 4129 * <br/>gpt/gxe/core/xml/ElementTextOnly.xml 4130 * <br/> 4131 * @name gxe.control.Section 4132 * @extends gxe.control.Control 4133 */ 4134 dojo.provide("gxe.control.Section"); 4135 dojo.declare("gxe.control.Section",gxe.control.Control,{ 4136 repeatablesContainer: null, 4137 sectionContainer: null, 4138 sectionHeader: null, 4139 sectionBodyContainer: null, 4140 sectionBody: null, 4141 uiLabelText: null, 4142 useExclusiveDisplay: true, 4143 4144 cfgSectionBody: null, 4145 indexedTabArray: null, 4146 tabArray: null, 4147 4148 /** Override gxe.control.Control.initialize() */ 4149 initialize: function(context,cfgObject) { 4150 var s = gxe.cfg.getGxeAttributeValue(cfgObject,"useExclusiveDisplay"); 4151 if (this.useExclusiveDisplay) this.useExclusiveDisplay = (s != "false"); 4152 else this.useExclusiveDisplay = (s == "true"); 4153 this.inherited(arguments); 4154 }, 4155 4156 /** Override gxe.control.Control.build() */ 4157 build: function(htmlParentElement,domProcessor,domNode) { 4158 var bRepeatable = this.xmlNode.isRepeatable(); 4159 if ((this.parentControl == null) || this.useExclusiveDisplay || !bRepeatable) { 4160 this.inherited(arguments); 4161 return; 4162 } 4163 4164 var ctlContainer = this.parentControl.sectionContainer; 4165 if ((typeof(ctlContainer) == "undefined") || (ctlContainer == null)) { 4166 4167 4168 var cfgParent = this.parentControl.cfgObject; 4169 var cfgChild = this.cfgObject; 4170 4171 var cfgContainer = new Object(); 4172 cfgContainer.namespace = gxe.cfg.uriGxeHtml; 4173 cfgContainer.name = "div"; 4174 cfgContainer.parent = cfgParent; 4175 4176 var cfgAttribute; 4177 cfgContainer.attributes = new Array(); 4178 4179 cfgAttribute = new Object(); 4180 cfgAttribute.namespace = gxe.cfg.uriGxeHtml; 4181 cfgAttribute.name = "class"; 4182 cfgAttribute.value = "gxeSectionContainer"; 4183 cfgAttribute.parent = cfgContainer; 4184 cfgContainer.attributes.push(cfgAttribute); 4185 4186 cfgAttribute = new Object(); 4187 cfgAttribute.namespace = gxe.cfg.uriGxe; 4188 cfgAttribute.name = "jsClass"; 4189 cfgAttribute.value = "gxe.control.RepeatablesContainer"; 4190 cfgAttribute.parent = cfgContainer; 4191 cfgContainer.attributes.push(cfgAttribute); 4192 4193 ctlContainer = this.context.makeXhtmlControl(cfgContainer,this.parentControl,true); 4194 ctlContainer.isExclusive = false; 4195 4196 ctlContainer.sectionContainer = ctlContainer; 4197 this.sectionContainer = ctlContainer; 4198 ctlContainer.xmlNode = this.xmlNode; 4199 ctlContainer.xmlParentElement = this.xmlNode.parentElement; 4200 4201 ctlContainer.build(htmlParentElement,domProcessor,domNode); 4202 var elHtmlParent = ctlContainer.htmlElement; 4203 cfgContainer.children = new Array(); 4204 cfgContainer.children.push(this.cfgObject); 4205 4206 this.parentControl = ctlContainer; 4207 this.execBuild(elHtmlParent,domProcessor,domNode); 4208 } else { 4209 this.sectionContainer = ctlContainer; 4210 this.inherited(arguments); 4211 } 4212 4213 }, 4214 4215 /** 4216 * Initializes events associated with the section header label. 4217 * <br/>Adds a checkbox to the label when a targeted XML node is optional. 4218 * @function 4219 * @name initializeLabelEvents 4220 * @memberOf gxe.control.Section# 4221 * @param {gxe.xml.XmlNode} xmlNode the targeted XML node (element or attribute) 4222 * @param {gxe.control.SectionMenu} associated ctlMenu the section menu 4223 * (may be null) 4224 * @param {gxe.control.IndexedtabArray} ctlIndexedIabArray associated indexed tab array 4225 * (may be null) 4226 * @param {gxe.xml.DomProcessor} domProcessor an XML processor 4227 * (when opening an existing document) 4228 * @param {Node} domNode the DOM node that is actively being processed 4229 * (when opening an existing document) 4230 */ 4231 initializeLabelEvents: function(xmlNode,ctlMenu,ctlIndexedIabArray,domProcessor,domNode) { 4232 4233 var ctlHeader = this.findFirstChildControl(">[gxename='header']"); 4234 if (ctlHeader != null) { 4235 4236 var ctlLabel = ctlHeader.findFirstChildControl(">[gxename='label']"); 4237 if (ctlLabel != null) { 4238 4239 var cn = ctlLabel.htmlElement.childNodes; 4240 for (var i=0; i<cn.length; i++) { 4241 if (cn[i].nodeType == 3) { 4242 this.uiLabelText = cn[i].nodeValue; // (nodeType == 3) is a TEXT_NODE 4243 break; 4244 } 4245 } 4246 4247 if (ctlMenu == null) { 4248 var sInfo = gxe.cfg.getGxeAttributeValue(this.cfgObject,"info"); 4249 if (sInfo != null) this.makeControlMenu(ctlHeader,true); 4250 } 4251 4252 var nMinOccurs = xmlNode.resolveMinOccurs(); 4253 if (nMinOccurs >= 1) { 4254 ctlLabel.htmlElement.className = "required"; 4255 } else { 4256 4257 var bChecked = true; 4258 if (domProcessor == null) { 4259 bChecked = false; 4260 if (gxe.cfg.getGxeAttributeValue(this.cfgObject,"preferOpen") == "true") bChecked = true; 4261 } else { 4262 bChecked = domProcessor.hasChildrenOrAttributes(domNode); 4263 } 4264 4265 var sId = this.context.generateUniqueId(); 4266 var el = document.createElement("input"); 4267 el.setAttribute("type","checkbox"); 4268 el.setAttribute("id",sId); 4269 var elLabel = ctlLabel.htmlElement; 4270 elLabel.parentNode.insertBefore(el,elLabel); 4271 if (bChecked) el.setAttribute("checked","checked"); 4272 elLabel.setAttribute("for",sId); 4273 4274 var _toggleVisibility = function(ctl,bVisible) { 4275 if (ctl != null) { 4276 if (bVisible) ctl.htmlElement.style.visibility = "visible"; 4277 else ctl.htmlElement.style.visibility = "hidden"; 4278 } 4279 }; 4280 4281 var _onCheckBoxClicked = function(sbc,bWasChecked) { 4282 xmlNode.isOptionalPlaceHolder = !bWasChecked; 4283 _toggleVisibility(ctlMenu,bWasChecked); 4284 _toggleVisibility(ctlIndexedIabArray,bWasChecked); 4285 if (sbc != null) { 4286 sbc.setDisplayStyle(bWasChecked); 4287 var n = sbc.getLength(); 4288 for ( var i=0; i<sbc.getLength(); i++) { 4289 sbc.getItem(i).xmlNode.isOptionalPlaceHolder = !bWasChecked; 4290 } 4291 } 4292 }; 4293 4294 dojo.connect(el,"onclick",this,dojo.hitch(this,function(e) { 4295 _onCheckBoxClicked(this.sectionBodyContainer,el.checked); 4296 })); 4297 4298 if (!bChecked) _onCheckBoxClicked(this.sectionBodyContainer,bChecked); 4299 4300 } 4301 4302 } 4303 4304 var nSubHeaders = 0; 4305 dojo.query("[gxename='header']",this.htmlElement).forEach(dojo.hitch(this,function(item) { 4306 nSubHeaders++; 4307 })); 4308 if (nSubHeaders == 1) this.htmlElement.style.border = "none"; 4309 } 4310 }, 4311 4312 /** 4313 * Fired for certain events triggered from the section header. 4314 * @event 4315 * @name onHeaderEvent 4316 * @memberOf gxe.control.Section# 4317 * @param {Event} e the underlying browser event 4318 * @param {gxe.control.Control} gxeControl the control that fired the event 4319 * @param {String} htmlEventName the HTML event name (e.g. "onclick") 4320 * @param {String} gxeEventName a GXE name for the event (e.g. "onLabelClicked") 4321 * within the configuration object for the section control (for popup help) 4322 */ 4323 onHeaderEvent: function(e,gxeControl,htmlEventName,gxeEventName) { 4324 if (gxeEventName == "onLabelClicked") { 4325 if (this.sectionBodyContainer != null) { 4326 this.sectionBodyContainer.toggleDisplay(); 4327 } 4328 } else { 4329 if (this.repeatablesContainer != null) { 4330 this.repeatablesContainer.handleEvent(e,this,htmlEventName,gxeEventName); 4331 } 4332 } 4333 }, 4334 4335 /** Override gxe.control.Control.onHtmlChildrenCreated() */ 4336 onHtmlChildrenCreated: function(domProcessor,domNode) { 4337 var ctlContainer = this.sectionContainer; 4338 var ctlBodyContainer = this.sectionBodyContainer; 4339 var ctlHeader = this.findFirstChildControl("[gxename='header']"); 4340 var ctlBody = this.findFirstChildControl("[gxename='body']"); 4341 var ctlTabArray = null; 4342 var ctlIndexedTabs = null; 4343 4344 if (ctlHeader != null) { 4345 ctlTabArray = ctlHeader.findFirstChildControl("[gxename='tabArray']"); 4346 ctlIndexedTabs = ctlHeader.findFirstChildControl("[gxename='indexedTabArray']"); 4347 4348 /* 4349 dojo.query("[gxeEventName]",ctlHeader.htmlElement).forEach(dojo.hitch(this,function(item) { 4350 var ctl = item.gxeControl; 4351 if ((typeof(ctl) != "undefined") && (ctl != null)) { 4352 dojo.connect(ctl,"onEvent",this,"onHeaderEvent"); 4353 } 4354 })); 4355 */ 4356 } 4357 4358 if (!this.useExclusiveDisplay) { 4359 if (ctlContainer != null) this.repeatablesContainer = ctlContainer; 4360 } else { 4361 if (ctlBodyContainer != null) { 4362 this.repeatablesContainer = ctlBodyContainer; 4363 if (ctlIndexedTabs != null) { 4364 dojo.connect(ctlBodyContainer,"onArrayModified",ctlIndexedTabs,"synchronize"); 4365 dojo.connect(ctlIndexedTabs,"onTabClicked",ctlBodyContainer,"activateIndex"); 4366 } 4367 } 4368 } 4369 if (this.repeatablesContainer != null) { 4370 this.repeatablesContainer.xmlElementCfgObject = this.xmlNode.cfgObject; 4371 this.repeatablesContainer.xmlParentElement = this.xmlNode.parentElement; 4372 this.xmlNode.repeatablesContainer = this.repeatablesContainer; 4373 } 4374 }, 4375 4376 /** 4377 * Makes a menu bar for the section within the section header. 4378 * @function 4379 * @name makeControlMenu 4380 * @memberOf gxe.control.Section# 4381 * @param {gxe.control.SectionHeader} ctlHeader the section header 4382 * @param {boolean} bCheckForInfo if true then check for "g:info" attribute 4383 * within the configuration object for the section control (for popup help) 4384 * @returns {gxe.control.SectionMenu} the section menu 4385 */ 4386 makeControlMenu: function(ctlHeader,bCheckForInfo) { 4387 var ctlMenu = new gxe.control.SectionMenu(); 4388 ctlMenu.autoConfigure(this.context,ctlHeader); 4389 ctlMenu.build(ctlHeader.htmlElement,null,null); 4390 if (bCheckForInfo) { 4391 var sInfo = gxe.cfg.getGxeAttributeValue(this.cfgObject,"info"); 4392 if (sInfo != null) sInfo = dojo.trim(sInfo); 4393 if ((sInfo != null) && (sInfo.length > 0)) { 4394 var sImages = this.context.contextPath+"/catalog/images/"; 4395 ctlMenu.appendImageButton("info",sImages+"gxe-info.png",sInfo); 4396 // TODO: implement a popup bubble, 4397 // use XHR to call the server to return the content of an i18n key 4398 } 4399 } 4400 return ctlMenu; 4401 } 4402 4403 }); 4404 4405 /** 4406 * @class Supports the header portion of a UI section. 4407 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/Section.xml<br/> 4408 * @name gxe.control.SectionHeader 4409 * @extends gxe.control.Control 4410 */ 4411 dojo.provide("gxe.control.SectionHeader"); 4412 dojo.declare("gxe.control.SectionHeader",gxe.control.Control,{ 4413 }); 4414 4415 /** 4416 * @class Supports a menu bar associated with the header portion of a UI section. 4417 * <br/><br/>This class is not currently associated with any editor definition files.<br/><br/> 4418 * @name gxe.control.SectionMenu 4419 * @extends gxe.control.Control 4420 */ 4421 dojo.provide("gxe.control.SectionMenu"); 4422 dojo.declare("gxe.control.SectionMenu",gxe.control.Control,{ 4423 4424 /** 4425 * Appends an image button to the menu bar. 4426 * @function 4427 * @name appendImageButton 4428 * @memberOf gxe.control.SectionMenu# 4429 * @param {String} gxeEventName to associate with the click event of the new img element 4430 * @param {String} sSrc the src for the new img element 4431 * @param {String} sTip the tool tip for the new img element 4432 */ 4433 appendImageButton: function(gxeEventName,sSrc,sTip) { 4434 var el = document.createElement("img"); 4435 el.alt = sTip; 4436 el.title = sTip; 4437 el.src = sSrc; 4438 el.setAttribute("gxeToolName",gxeEventName); 4439 this.htmlElement.appendChild(el); 4440 dojo.connect(el,"onclick",this,dojo.hitch(this,function(e) { 4441 this.onEvent(e,this,"onclick",gxeEventName); 4442 })); 4443 return el; 4444 }, 4445 4446 /** 4447 * Auto configures the control. 4448 * @function 4449 * @name autoConfigure 4450 * @memberOf gxe.control.SectionMenu# 4451 * @param {gxe.Context} context the editor context 4452 * @param {gxe.control.Control} parentControl the parent control 4453 */ 4454 autoConfigure: function(context,parentControl) { 4455 var cfgAttr; 4456 var cfgObj = new Object(); 4457 cfgObj.namespace = gxe.cfg.uriGxeHtml; 4458 cfgObj.name = "span"; 4459 cfgObj.parent = parentControl.cfgObject; 4460 cfgObj.attributes = new Array(); 4461 this.cfgObject = cfgObj; 4462 4463 cfgAttr = new Object(); 4464 cfgAttr.namespace = gxe.cfg.uriGxeHtml; 4465 cfgAttr.name = "class"; 4466 cfgAttr.value = "gxeSectionMenu"; 4467 cfgAttr.parent = cfgObj; 4468 cfgObj.attributes.push(cfgAttr); 4469 this.initialize(context,cfgObj); 4470 } 4471 4472 }); 4473 4474 /** 4475 * @class Supports the body portion of a UI section. 4476 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/Section.xml<br/> 4477 * @name gxe.control.SectionBody 4478 * @extends gxe.control.Control 4479 */ 4480 dojo.provide("gxe.control.SectionBody"); 4481 dojo.declare("gxe.control.SectionBody",gxe.control.Control,{ 4482 4483 /** Override gxe.control.Control.build() */ 4484 build: function(htmlParentElement,domProcessor,domNode) { 4485 4486 var ctlContainer = this.parentControl.sectionBodyContainer; 4487 if ((typeof(ctlContainer) == "undefined") || (ctlContainer == null)) { 4488 var cfgParent = this.parentControl.cfgObject; 4489 var cfgChild = this.cfgObject; 4490 4491 var cfgContainer = new Object(); 4492 cfgContainer.namespace = gxe.cfg.uriGxeHtml; 4493 cfgContainer.name = "div"; 4494 cfgContainer.parent = cfgParent; 4495 4496 var cfgAttribute; 4497 cfgContainer.attributes = new Array(); 4498 4499 cfgAttribute = new Object(); 4500 cfgAttribute.namespace = gxe.cfg.uriGxeHtml; 4501 cfgAttribute.name = "class"; 4502 cfgAttribute.value = "gxeSectionBodyContainer"; 4503 cfgAttribute.parent = cfgContainer; 4504 cfgContainer.attributes.push(cfgAttribute); 4505 4506 cfgAttribute = new Object(); 4507 cfgAttribute.namespace = gxe.cfg.uriGxe; 4508 cfgAttribute.name = "jsClass"; 4509 cfgAttribute.value = "gxe.control.RepeatablesContainer"; 4510 cfgAttribute.parent = cfgContainer; 4511 cfgContainer.attributes.push(cfgAttribute); 4512 4513 ctlContainer = this.context.makeXhtmlControl(cfgContainer,this.parentControl,true); 4514 ctlContainer.isExclusive = true; 4515 ctlContainer.sectionBodyContainer = ctlContainer; 4516 ctlContainer.cfgSectionBody = this.cfgObject; 4517 this.parentControl.sectionBodyContainer = ctlContainer; 4518 ctlContainer.xmlNode = this.xmlNode; 4519 ctlContainer.xmlParentElement = this.xmlNode.parentElement; 4520 4521 ctlContainer.build(htmlParentElement,domProcessor,domNode); 4522 var elHtmlParent = ctlContainer.htmlElement; 4523 cfgContainer.children = new Array(); 4524 cfgContainer.children.push(this.cfgObject); 4525 4526 this.parentControl = ctlContainer; 4527 this.execBuild(elHtmlParent,domProcessor,domNode); 4528 } else { 4529 this.inherited(arguments); 4530 } 4531 4532 }, 4533 4534 /** Override gxe.control.Control.ensureVisibility() */ 4535 ensureVisibility: function(subjectXmlNode) { 4536 if (this.parentControl.sectionBodyContainer != null) { 4537 var el = this.parentControl.sectionBodyContainer.htmlElement; 4538 if (el.style.display == "none") { 4539 this.parentControl.sectionBodyContainer.toggleDisplay(); 4540 } 4541 } 4542 this.inherited(arguments); 4543 } 4544 4545 }); 4546 4547 /** 4548 * @class Supports a UI section. 4549 * @name gxe.control.Parameter 4550 * @extends gxe.control.Section 4551 * @deprecated use gxe.control.Section instead 4552 */ 4553 dojo.provide("gxe.control.Parameter"); 4554 dojo.declare("gxe.control.Parameter",gxe.control.Section,{ 4555 }); 4556 4557 /** 4558 * @class Supports a UI section associated with a targeted XML element. 4559 * <br/><br/>Typically associated with: 4560 * <br/>gpt/gxe/core/xml/Element.xml 4561 * <br/>gpt/gxe/core/xml/ElementTextOnly.xml 4562 * <br/> 4563 * @name gxe.control.Element 4564 * @extends gxe.control.Section 4565 */ 4566 dojo.provide("gxe.control.Element"); 4567 dojo.declare("gxe.control.Element",gxe.control.Section,{ 4568 multiplicityTools: null, 4569 4570 /** Override gxe.control.Control.onHtmlChildrenCreated() */ 4571 onHtmlChildrenCreated: function(domProcessor,domNode) { 4572 this.inherited(arguments); 4573 4574 var ctlMenu = null; 4575 var ctlIndexedIabArray = null; 4576 var sImages = this.context.contextPath+"/catalog/images/"; 4577 var ctlHeader = this.findFirstChildControl(">[gxename='header']"); 4578 if (ctlHeader != null) { 4579 4580 var bBuildRepeatables = false; 4581 if ((this.xmlNode != null) && this.xmlNode.isRepeatable()) { 4582 bBuildRepeatables = true; 4583 var ctlInput = this.xmlNode.getInputControl(); 4584 if ((ctlInput != null) && ctlInput.getSupportsMultipleValues()) { 4585 bBuildRepeatables = false; 4586 } else if (this.xmlNode.wrapsIsoMultiValueList()) { 4587 bBuildRepeatables = false; 4588 } 4589 } 4590 4591 if (bBuildRepeatables) { 4592 ctlMenu = this.makeControlMenu(ctlHeader,true); 4593 4594 ctlMenu.appendImageButton("repeatSection",sImages+"gxe-repeat.png", 4595 this.context.getI18NString("button.repeatSection")); 4596 ctlMenu.appendImageButton("removeSection",sImages+"gxe-remove.png", 4597 this.context.getI18NString("button.removeSection")); 4598 if (this.useExclusiveDisplay) { 4599 ctlMenu.appendImageButton("moveSectionUp",sImages+"gxe-move-left.png", 4600 this.context.getI18NString("button.moveSectionLeft")); 4601 ctlMenu.appendImageButton("moveSectionDown",sImages+"gxe-move-right.png", 4602 this.context.getI18NString("button.moveSectionRight")); 4603 } else { 4604 ctlMenu.appendImageButton("moveSectionUp",sImages+"gxe-move-up.png", 4605 this.context.getI18NString("button.moveSectionUp")); 4606 ctlMenu.appendImageButton("moveSectionDown",sImages+"gxe-move-down.png", 4607 this.context.getI18NString("button.moveSectionDown")); 4608 } 4609 dojo.connect(ctlMenu,"onEvent",this,"onHeaderEvent"); 4610 this.multiplicityTools = ctlMenu; 4611 4612 var ctlRepeatables = this.repeatablesContainer; 4613 if (ctlRepeatables != null) ctlRepeatables.multiplicityTools = ctlMenu; 4614 if (this.useExclusiveDisplay && (ctlRepeatables != null)) { 4615 ctlIndexedIabArray = new gxe.control.IndexedTabArray(); 4616 ctlIndexedIabArray.autoConfigure(this.context,ctlHeader); 4617 ctlIndexedIabArray.build(ctlHeader.htmlElement,domProcessor,domNode); 4618 dojo.connect(ctlRepeatables,"onArrayModified",ctlIndexedIabArray,"synchronize"); 4619 dojo.connect(ctlIndexedIabArray,"onTabClicked",ctlRepeatables,"activateIndex"); 4620 dojo.connect(ctlIndexedIabArray,"onTabClicked",ctlRepeatables,"syncTools"); 4621 } 4622 if (ctlRepeatables != null) ctlRepeatables.syncTools(); 4623 4624 } 4625 } 4626 4627 this.initializeLabelEvents(this.xmlNode,ctlMenu,ctlIndexedIabArray,domProcessor,domNode); 4628 } 4629 4630 }); 4631 4632 /** 4633 * @class Supports a UI section associated with a targeted XML attribute. 4634 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/xml/Attribute.xml<br/> 4635 * @name gxe.control.Attribute 4636 * @extends gxe.control.Parameter 4637 */ 4638 dojo.provide("gxe.control.Attribute"); 4639 dojo.declare("gxe.control.Attribute",gxe.control.Parameter,{ 4640 4641 /** Override gxe.control.Control.onHtmlChildrenCreated() */ 4642 onHtmlChildrenCreated: function(domProcessor,domNode) { 4643 this.inherited(arguments); 4644 this.initializeLabelEvents(this.xmlNode,null,null,domProcessor,domNode); 4645 } 4646 }); 4647 4648 /** 4649 * @class Supports the display of a label associated with a targeted XML node (element or attribute). 4650 * <br/><br/>Typically associated with: 4651 * <br/>gpt/gxe/core/ui/Section.xml 4652 * <br/>gpt/gxe/core/ui/TargetLabel.xml 4653 * <br/> 4654 * @name gxe.control.TargetLabel 4655 * @extends gxe.control.Control 4656 */ 4657 dojo.provide("gxe.control.TargetLabel"); 4658 dojo.declare("gxe.control.TargetLabel",gxe.control.Control,{ 4659 4660 /** Override gxe.control.Control.createHtmlElement() */ 4661 createHtmlElement: function() { 4662 var s = this.htmlTextContent; 4663 if ((typeof(s) == "undefined") || (s == null) || (s.length == 0)) { 4664 this.htmlTextContent = this.getLabelText(); 4665 } 4666 this.inherited(arguments); 4667 } 4668 4669 }); 4670 4671 /** 4672 * @class Supports the display of an exclusive element choice. 4673 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/xml/ElementChoice.xml<br/> 4674 * @name gxe.control.ElementChoice 4675 * @extends gxe.control.Control 4676 */ 4677 dojo.provide("gxe.control.ElementChoice"); 4678 dojo.declare("gxe.control.ElementChoice",gxe.control.Control,{ 4679 choiceBody: null, 4680 4681 /** 4682 * Determines if an XML element is associated with the selected choice. 4683 * <br/>The anchor is enabled when text has been entered into the associated input text box. 4684 * @function 4685 * @name isElementSelected 4686 * @memberOf gxe.control.ElementChoice# 4687 * @param {gxe.xml.XmlElement} xmlElement the XML element to test 4688 * @return {boolean} true if the XML element is selected 4689 */ 4690 isElementSelected: function(xmlElement) { 4691 if (this.choiceBody != null) { 4692 var nActiveIndex = this.choiceBody.getActiveIndex(); 4693 if (nActiveIndex >= 0) { 4694 var item = this.choiceBody.getItem(nActiveIndex); 4695 return (item.xmlNode == xmlElement); 4696 } 4697 } 4698 return false; 4699 }, 4700 4701 /** Override gxe.control.Control.onHtmlChildrenCreated() */ 4702 onHtmlChildrenCreated: function(domProcessor,domNode) { 4703 4704 var bAllowNone = (gxe.cfg.getMinOccurs(this.cfgObject) == "0"); 4705 var sLabel = gxe.cfg.getGxeAttributeValue(this.cfgObject,"label"); 4706 var sLabelNone = gxe.cfg.getGxeAttributeValue(this.cfgObject,"labelNone"); 4707 if (sLabelNone == null) sLabelNone = "??none??"; 4708 4709 // find the component controls, build the tabs based upon the content of the body 4710 var ctlHeader = this.findFirstChildControl("[gxename='header']"); 4711 if (ctlHeader != null) { 4712 var ctlTabArray = ctlHeader.findFirstChildControl("[gxename='tabArray']"); 4713 if (ctlTabArray != null) { 4714 var ctlBody = this.findFirstChildControl("[gxename='body']"); 4715 if (ctlBody != null) { 4716 this.choiceBody = ctlBody; 4717 dojo.connect(ctlTabArray,"onTabClicked",ctlBody,"activateIndex"); 4718 4719 var nSelectedIndex = ctlBody.defaultSelectedIndex; 4720 if (typeof(nSelectedIndex) != "number") nSelectedIndex = -1; 4721 if (bAllowNone) { 4722 //ctlTabArray.appendTab(-1,sLabelNone,(nSelectedIndex == -1)); 4723 ctlTabArray.appendRadio(-1,sLabelNone,(nSelectedIndex == -1)); 4724 } 4725 4726 var n = ctlBody.getLength(); 4727 for (var i=0; i<n; i++) { 4728 var bIsSelected = false; 4729 if (nSelectedIndex != -1) bIsSelected = (i == nSelectedIndex); 4730 else if (!bAllowNone && (i == 0)) bIsSelected = true; 4731 var ctl = ctlBody.getItem(i); 4732 var sLabel = ctl.getLabelText(); 4733 //ctlTabArray.appendTab(i,sLabel,bIsSelected); 4734 ctlTabArray.appendRadio(i,sLabel,bIsSelected); 4735 ctl.xmlNode.exclusiveChoiceControl = this; 4736 } 4737 } 4738 } 4739 } 4740 } 4741 4742 }); 4743 4744 /** 4745 * @class Supports the body section associated with an exclusive element choice. 4746 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/xml/ElementChoice.xml<br/> 4747 * @name gxe.control.ElementChoiceBody 4748 * @extends gxe.control.ExclusiveControlArray 4749 */ 4750 dojo.provide("gxe.control.ElementChoiceBody"); 4751 dojo.declare("gxe.control.ElementChoiceBody",gxe.control.ExclusiveControlArray,{ 4752 defaultSelectedIndex: -1, 4753 4754 /** Override gxe.control.Control.processChildren() */ 4755 processChildren: function(cfgObject,htmlParentElement,xmlNode,domProcessor,domNode) { 4756 4757 // Don't process all the children we only want the child "element"s 4758 var nIndex = -1; 4759 var nSelectedIndex = -1; 4760 gxe.cfg.forEachChild(this.cfgObject,"*","*",dojo.hitch(this,function(cfgChild) { 4761 if (cfgChild.namespace == gxe.cfg.uriGxe) { 4762 if (cfgChild.name == "element") { 4763 nIndex++; 4764 if (nSelectedIndex == -1) { 4765 if ((domProcessor != null) && (domNode != null)) { 4766 var domMatch = domProcessor.findMatchingChildElement(domNode,cfgChild); 4767 if (domMatch != null) nSelectedIndex = nIndex; 4768 } else { 4769 var sSelected = gxe.cfg.getGxeAttributeValue(cfgChild,"selected"); 4770 if (sSelected == "true") nSelectedIndex = nIndex; 4771 } 4772 } 4773 } 4774 } 4775 })); 4776 this.defaultSelectedIndex = nSelectedIndex; 4777 4778 gxe.cfg.forEachChild(this.cfgObject,"*","*",dojo.hitch(this,function(cfgChild) { 4779 if (cfgChild.namespace == gxe.cfg.uriGxe) { 4780 if (cfgChild.name == "element") { 4781 var elTmp = document.createElement("div"); 4782 this.processCfgElement(cfgChild,elTmp,this.xmlNode,domProcessor,domNode); 4783 } 4784 } 4785 })); 4786 } 4787 4788 }); 4789 4790 /** 4791 * @class Supports the display of a set of tabs. 4792 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/Tabs.xml<br/> 4793 * @name gxe.control.Tabs 4794 * @extends gxe.control.Control 4795 */ 4796 dojo.provide("gxe.control.Tabs"); 4797 dojo.declare("gxe.control.Tabs",gxe.control.Control,{ 4798 tabArray: null, 4799 tabsBody: null, 4800 4801 /** Override gxe.control.Control.ensureVisibility() */ 4802 ensureVisibility: function(subjectXmlNode) { 4803 if ((this.tabArray != null) && (this.tabsBody != null)) { 4804 var nIndex = this.tabsBody.findIndexByXmlNode(subjectXmlNode); 4805 if ((nIndex != -1) && (nIndex != this.tabsBody.getActiveIndex())) { 4806 this.tabArray.activateIndex(nIndex); 4807 } 4808 } 4809 this.inherited(arguments); 4810 }, 4811 4812 /** Override gxe.control.Control.onHtmlChildrenCreated() */ 4813 onHtmlChildrenCreated: function(domProcessor,domNode) { 4814 4815 // find the component controls, build the tabs based upon the content of the body 4816 var ctlHeader = this.findFirstChildControl("[gxename='header']"); 4817 if (ctlHeader != null) { 4818 var ctlTabArray = ctlHeader.findFirstChildControl("[gxename='tabArray']"); 4819 if (ctlTabArray != null) { 4820 this.tabArray = ctlTabArray; 4821 var ctlBody = this.findFirstChildControl("[gxename='body']"); 4822 if (ctlBody != null) { 4823 this.tabsBody = ctlBody; 4824 dojo.connect(ctlTabArray,"onTabClicked",ctlBody,"activateIndex"); 4825 var nSelectedIndex = -1; 4826 var n = ctlBody.getLength(); 4827 for (var i=0; i<n; i++) { 4828 var ctl = ctlBody.getItem(i); 4829 var sLabel = ctl.getLabelText(); 4830 var sSelected = gxe.cfg.getGxeAttributeValue(ctl.cfgObject,"selected"); 4831 if (sSelected == "true") nSelectedIndex = i; 4832 ctlTabArray.appendTab(i,sLabel,(nSelectedIndex == i)); 4833 } 4834 if ((n > 0) && (nSelectedIndex == -1)) ctlTabArray.activateIndex(0); 4835 } 4836 } 4837 } 4838 } 4839 4840 }); 4841 4842 /** 4843 * @class Supports the body section associated with a set of tabs. 4844 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/Tabs.xml<br/> 4845 * @name gxe.control.TabsBody 4846 * @extends gxe.control.ExclusiveControlArray 4847 */ 4848 dojo.provide("gxe.control.TabsBody"); 4849 dojo.declare("gxe.control.TabsBody",gxe.control.ExclusiveControlArray,{ 4850 }); 4851 4852 4853 /* Input controls ==================================================== */ 4854 4855 /** 4856 * @class Base class for all input controls. 4857 * @name gxe.control.InputBase 4858 * @extends gxe.control.Control 4859 * @property {boolean} supportsMultipleValues indicates whether or not the control supports 4860 * the input of multiple values 4861 */ 4862 dojo.provide("gxe.control.InputBase"); 4863 dojo.declare("gxe.control.InputBase",gxe.control.Control,{ 4864 supportsMultipleValues: false, 4865 4866 /** 4867 * Attempts to find label text associated with the input control. 4868 * The label text is used for validation feedback within the gxe.control.MessageArea 4869 * portion of the page. 4870 * @function 4871 * @name findParentLabelText 4872 * @memberOf gxe.control.InputBase# 4873 * @param {gxe.xml.XmlNode} xmlNode the targeted XML node for input control 4874 * @return {String} the label text 4875 */ 4876 findParentLabelText: function(xmlNode) { 4877 var ctl = this.parentControl; 4878 while ((typeof(ctl) != "undefined") && (ctl != null)) { 4879 var s = ctl.uiLabelText; 4880 if ((typeof(s) != "undefined") && (s != null)) return s; 4881 ctl = ctl.parentControl; 4882 } 4883 return "?"+xmlNode.nodeInfo.localName; 4884 }, 4885 4886 /** 4887 * Fires the onInputChanged() event. 4888 * @function 4889 * @name fireInputChanged 4890 * @memberOf gxe.control.InputBase# 4891 * @param {Event} e the underlying browser event 4892 */ 4893 fireInputChanged: function(e) { 4894 this.onInputChanged(this); 4895 }, 4896 4897 /** 4898 * Fires the onInputChanged() event based upon a browser onkeyup() event. 4899 * The onInputChanged() event will only be fired if the user key is 4900 * not 13 (carriage return) and not 9 (tab). 4901 * @function 4902 * @name fireInputChangedOnKeyUp 4903 * @memberOf gxe.control.InputBase# 4904 * @param {Event} e the underlying browser event 4905 */ 4906 fireInputChangedOnKeyUp: function(e) { 4907 if (!e) e = window.event; 4908 if (e) { 4909 var nKey = (e.keyCode) ? e.keyCode : e.which; 4910 // ignore carriage return and tab 4911 if ((nKey != 13) && (nKey != 9)) this.fireInputChanged(e); 4912 } 4913 }, 4914 4915 /** 4916 * Gets the value associated with the input control. 4917 * This method should be overridden for all sub-classes that support single valued 4918 * input (i.e. where this.supportsMultipleValues == false). 4919 * @function 4920 * @name getInputValue 4921 * @memberOf gxe.control.InputBase# 4922 * @param {boolean} bInFeedbackMode true if the value is being requested as validation feedback 4923 * @return {Object} the input value 4924 */ 4925 getInputValue: function(bInFeedbackMode) {return null;}, 4926 4927 /** 4928 * Gets the values associated with the input control. 4929 * This method should be overridden for all sub-classes that support multi-valued 4930 * input (i.e. where this.supportsMultipleValues == true). 4931 * @function 4932 * @name getInputValues 4933 * @memberOf gxe.control.InputBase# 4934 * @param {boolean} bInFeedbackMode true if the value is being requested as validation feedback 4935 * @return {Object[]} the input values 4936 */ 4937 getInputValues: function(bInFeedbackMode) {return null;}, 4938 4939 /** 4940 * Indicates whether or not the control supports the input of multiple values. 4941 * (simple wrapper for this.supportsMultipleValues) 4942 * @function 4943 * @name getSupportsMultipleValues 4944 * @memberOf gxe.control.InputBase# 4945 * @return {boolean} true if multi-valued input is supported 4946 */ 4947 getSupportsMultipleValues: function() {return this.supportsMultipleValues;}, 4948 4949 /** 4950 * Makes an HTML "input" element of type "text" supporting entry of "other" code values. 4951 * This function is useful when the user requires the ability to enter a value outside 4952 * of a coded domain. 4953 * @function 4954 * @name makeOtherInputText 4955 * @memberOf gxe.control.InputBase# 4956 * @param {Object} cfgOption the JSON configuration object associated with the input control 4957 * @return {Element} the HTML "input" element 4958 */ 4959 makeOtherInputText: function(cfgOption) { 4960 var elOther = document.createElement("input"); 4961 elOther.setAttribute("type","text"); 4962 gxe.cfg.forEachHtmlAttribute(cfgOption,dojo.hitch(this,function(cfgAttribute) { 4963 var sName = cfgAttribute.name.toLowerCase(); 4964 var sValue = cfgAttribute.name.toLowerCase(); 4965 if ((sName == "maxlength") || (sName != "size")) { 4966 elOther.setAttribute(sName,cfgAttribute.value); 4967 } 4968 })); 4969 return elOther; 4970 }, 4971 4972 /** Override gxe.control.onHtmlChildrenCreated() */ 4973 onHtmlChildrenCreated: function(domProcessor,domNode) { 4974 this.inherited(arguments); 4975 var sTip = gxe.cfg.getGxeAttributeValue(this.cfgObject,"tip"); 4976 if (sTip == null) sTip = gxe.cfg.getGxeAttributeValue(this.xmlNode.cfgObject,"tip"); 4977 if (sTip == null) { 4978 var sMinOccurs = gxe.cfg.getGxeAttributeValue(this.xmlNode.cfgObject,"minOccurs"); 4979 if (sMinOccurs == "$parent") { 4980 var pe = this.xmlNode.parentElement; 4981 if (pe != null) sTip = gxe.cfg.getGxeAttributeValue(pe.cfgObject,"tip"); 4982 } 4983 } 4984 /* 4985 if (sTip == null) { 4986 var sType = gxe.cfg.getGxeAttributeValue(this.xmlNode.cfgObject,"valueType"); 4987 if (sType != null) sType = dojo.trim(sType); 4988 if ((sType == "date") || (sType == "xs:date") || (sType == "xsd:date")) { 4989 } 4990 } 4991 */ 4992 if (sTip != null) this.htmlElement.title = sTip; 4993 }, 4994 4995 4996 /** 4997 * An event fired when input has changed. 4998 * @event 4999 * @name onInputChanged 5000 * @memberOf gxe.control.InputBase# 5001 * @param {Object} inputControl the input control that initiated the change 5002 */ 5003 onInputChanged: function(inputControl) {} 5004 5005 }); 5006 5007 /** 5008 * @class Supports the input of multiple values through a delimited "textarea" control. 5009 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/InputDelimitedTextArea.xml<br/> 5010 * @name gxe.control.InputDelimitedTextArea 5011 * @extends gxe.control.InputBase 5012 * @property {boolean} supportsMultipleValues true 5013 * @property {String} delimiter the delimiter (default="," configuration attribute g:delimiter) 5014 */ 5015 dojo.provide("gxe.control.InputDelimitedTextArea"); 5016 dojo.declare("gxe.control.InputDelimitedTextArea",gxe.control.InputBase,{ 5017 delimiter: ",", 5018 supportsMultipleValues: true, 5019 5020 /** Override gxe.control.Control.initialize() */ 5021 initialize: function(context,cfgObject) { 5022 this.inherited(arguments); 5023 var s = gxe.cfg.getGxeAttributeValue(cfgObject,"delimiter"); 5024 if ((s != null) && (s.length > 0)) this.delimiter = s; 5025 }, 5026 5027 /** Override gxe.control.InputBase.getInputValues() */ 5028 getInputValues: function(bInFeedbackMode) { 5029 if ((this.delimiter == null) || (this.delimiter.length == 0)) this.delimiter = ","; 5030 var values = new Array(); 5031 if (this.htmlElement != null) this._mergeTokens(values,this.htmlElement.value); 5032 return values; 5033 }, 5034 5035 /** 5036 * Tokenizes a supplied value and merges the result into a supplied array. 5037 * <br/>The supplied value will be split using this.delimiter plus characters: \r and \n 5038 * @function 5039 * @name _mergeTokens 5040 * @memberOf gxe.control.InputDelimitedTextArea# 5041 * @param {Array} values the values into which the tokens will be merged 5042 * @param {String} sValue the value that will be tokenized (using this.delimiter) 5043 */ 5044 _mergeTokens: function(values,sValue) { 5045 if (sValue != null) { 5046 sValue = sValue.replace(/(\r\n|\r|\n|\n\r)/g,this.delimiter); 5047 var tokens = sValue.split(this.delimiter); 5048 if (tokens != null) { 5049 for (var i=0; i<tokens.length; i++) { 5050 var sToken = dojo.trim(tokens[i]); 5051 if (sToken.length > 0) values.push(sToken); 5052 } 5053 } 5054 } 5055 }, 5056 5057 /** Override gxe.control.Control.onHtmlElementCreated() */ 5058 onHtmlElementCreated: function(domProcessor,domNode) { 5059 this.xmlNode.setInputControl(this); 5060 5061 // TODO set a default value? 5062 5063 if ((this.delimiter == null) && (this.delimiter.length == 0)) this.delimiter = ","; 5064 var domValues = new Array(); 5065 if ((domProcessor != null) && (domNode != null)) { 5066 if (this.xmlNode.nodeInfo.isIsoWrappedMultiValueList) { 5067 var domParentMatches = domProcessor.findMatchingChildElements( 5068 domNode.parentNode.parentNode,this.xmlNode.parentElement.cfgObject); 5069 if (domParentMatches != null) { 5070 for (var i=0; i<domParentMatches.length; i++) { 5071 var domMatches = domProcessor.findMatchingChildElements( 5072 domParentMatches[i],this.xmlNode.cfgObject); 5073 if (domMatches != null) { 5074 for (var j=0; j<domMatches.length; j++) { 5075 var domMatch = domMatches[j]; 5076 var sValue = domProcessor.getNodeText(domMatch); 5077 if (sValue != null) domValues.push(sValue); 5078 } 5079 } 5080 } 5081 } 5082 } else { 5083 var domMatches = domProcessor.findMatchingChildElements( 5084 domNode.parentNode,this.xmlNode.cfgObject); 5085 if (domMatches != null) { 5086 for (var i=0; i<domMatches.length; i++) { 5087 var domMatch = domMatches[i]; 5088 var sValue = domProcessor.getNodeText(domMatch); 5089 if (sValue != null) domValues.push(sValue); 5090 } 5091 } 5092 } 5093 } 5094 5095 if (domValues.length > 0) { 5096 var values = new Array(); 5097 for (var i=0; i<domValues.length; i++) { 5098 this._mergeTokens(values,domValues[i]); 5099 } 5100 if (values.length > 0) { 5101 var sValues = ""; 5102 for (var i=0; i<values.length; i++) { 5103 if (sValues.length > 0) sValues += this.delimiter; 5104 sValues += values[i]; 5105 } 5106 this.htmlElement.value = sValues; 5107 } 5108 } 5109 5110 dojo.connect(this.htmlElement,"onchange",this,"fireInputChanged"); 5111 dojo.connect(this.htmlElement,"onkeyup",this,"fireInputChangedOnKeyUp"); 5112 } 5113 }); 5114 5115 /** 5116 * @class Supports the input of multiple values through a collection of check boxes. 5117 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/InputSelectMany.xml<br/> 5118 * @name gxe.control.InputSelectMany 5119 * @extends gxe.control.InputBase 5120 * @property {boolean} supportsMultipleValues true 5121 */ 5122 dojo.provide("gxe.control.InputSelectMany"); 5123 dojo.declare("gxe.control.InputSelectMany",gxe.control.InputBase,{ 5124 _checkBoxes: null, 5125 supportsMultipleValues: true, 5126 5127 /** 5128 * Appends a checkbox option to the parent HTML DOM element. 5129 * @function 5130 * @name _appendCheckBox 5131 * @memberOf gxe.control.InputSelectMany# 5132 * @param {String} sLabel the label 5133 * @param {String} sValue a value associated with the checkbox 5134 * @param {boolean} bSelected true if the box should be checked 5135 */ 5136 _appendCheckBox: function(sLabel,sValue,bSelected) { 5137 var elListItem = document.createElement("li"); 5138 this.htmlElement.appendChild(elListItem); 5139 5140 var sCollectionName = this.gxeId+"Options"; 5141 var sOptionId = this.context.generateUniqueId(); 5142 var elOption = document.createElement("input"); 5143 if (this._dataName != null) { 5144 if(dojo.isIE <= 8) { 5145 elOption = document.createElement("<input name=\""+sCollectionName+"\"/>"); 5146 } else { 5147 elOption.setAttribute("name",sCollectionName); 5148 } 5149 } 5150 elOption.setAttribute("type","checkbox"); 5151 elOption.setAttribute("id",sOptionId); 5152 elOption.setAttribute("value",sValue); 5153 elListItem.appendChild(elOption); 5154 if (bSelected) elOption.setAttribute("checked","checked"); 5155 this._checkBoxes.push(elOption); 5156 5157 var elLabel = document.createElement("label"); 5158 elLabel.setAttribute("for",sOptionId); 5159 elLabel.appendChild(document.createTextNode(sLabel)); 5160 elListItem.appendChild(elLabel); 5161 return elListItem; 5162 }, 5163 5164 /** Override gxe.control.Control.focus() */ 5165 focus: function(subjectXmlNode) { 5166 this.inherited(arguments); 5167 if ((this._checkBoxes != null) && (this._checkBoxes.length > 0)) { 5168 this._checkBoxes[0].focus(); 5169 } 5170 }, 5171 5172 /** Override gxe.control.InputBase.getInputValues() */ 5173 getInputValues: function(bInFeedbackMode) { 5174 var values = new Array(); 5175 if (this._checkBoxes != null) { 5176 var n = this._checkBoxes.length; 5177 for (var i=0; i<n; i++) { 5178 var elCheckBox = this._checkBoxes[i]; 5179 if (elCheckBox.checked) { 5180 var oValue = elCheckBox.value; 5181 var elOther = elCheckBox.gxeOtherInputText; 5182 if (elOther != null) { 5183 var sValue = dojo.trim(elOther.value); 5184 if (sValue != elOther.value) elOther.value = sValue; 5185 if (sValue.length > 0) oValue = sValue; 5186 else oValue = null; 5187 } 5188 if (oValue != null) values.push(oValue); 5189 } 5190 } 5191 } 5192 return values; 5193 }, 5194 5195 /** Override gxe.control.Control.onHtmlElementCreated() */ 5196 onHtmlElementCreated: function(domProcessor,domNode) { 5197 this.xmlNode.setInputControl(this); 5198 this._checkBoxes = new Array(); 5199 var cfgInput = this.cfgObject; 5200 5201 var otherElements = new Array(); 5202 var otherCheckBoxes = new Array(); 5203 var knownValues = new Array(); 5204 var bUseDomValuesForSelected = (domProcessor != null); 5205 var domValues = new Array(); 5206 if ((domProcessor != null) && (domNode != null)) { 5207 if (this.xmlNode.nodeInfo.isIsoWrappedMultiValueList) { 5208 var domParentMatches = domProcessor.findMatchingChildElements( 5209 domNode.parentNode.parentNode,this.xmlNode.parentElement.cfgObject); 5210 if (domParentMatches != null) { 5211 for (var i=0; i<domParentMatches.length; i++) { 5212 var domMatches = domProcessor.findMatchingChildElements( 5213 domParentMatches[i],this.xmlNode.cfgObject); 5214 if (domMatches != null) { 5215 for (var j=0; j<domMatches.length; j++) { 5216 var domMatch = domMatches[j]; 5217 var sValue = domProcessor.getNodeText(domMatch); 5218 if (sValue != null) domValues.push(sValue); 5219 } 5220 } 5221 } 5222 } 5223 } else { 5224 var domMatches = domProcessor.findMatchingChildElements( 5225 domNode.parentNode,this.xmlNode.cfgObject); 5226 if (domMatches != null) { 5227 for (var i=0; i<domMatches.length; i++) { 5228 var domMatch = domMatches[i]; 5229 var sValue = domProcessor.getNodeText(domMatch); 5230 if (sValue != null) domValues.push(sValue); 5231 } 5232 } 5233 } 5234 } 5235 5236 var cfgOptions = gxe.cfg.findChild(this.cfgObject,gxe.cfg.uriGxe,"options"); 5237 if (cfgOptions != null) { 5238 gxe.cfg.forEachChild(cfgOptions,gxe.cfg.uriGxe,"option",dojo.hitch(this,function(cfgOption) { 5239 var sLabel = gxe.cfg.getGxeAttributeValue(cfgOption,"label"); 5240 var sValue = gxe.cfg.getGxeAttributeValue(cfgOption,"value"); 5241 var sAlias = gxe.cfg.getGxeAttributeValue(cfgOption,"alias"); 5242 var sSelected = gxe.cfg.getGxeAttributeValue(cfgOption,"selected"); 5243 var bSelected = false; 5244 if (!bUseDomValuesForSelected) { 5245 bSelected = (sSelected == "true"); 5246 } else { 5247 knownValues.push(sValue); 5248 for (var i=0; i<domValues.length; i++) { 5249 if (sValue == domValues[i]) { 5250 bSelected = true; 5251 break; 5252 } else if (sAlias != null) { 5253 if (sAlias == domValues[i]) { 5254 bSelected = true; 5255 break; 5256 } 5257 } 5258 } 5259 } 5260 var elListItem = this._appendCheckBox(sLabel,sValue,bSelected); 5261 var elCheckBox = this._checkBoxes[this._checkBoxes.length - 1]; 5262 var sOther = gxe.cfg.getGxeAttributeValue(cfgOption,"isOther"); 5263 if ((sOther != null) && (sOther == "true")) { 5264 var elOther = this.makeOtherInputText(cfgOption); 5265 elCheckBox.gxeOtherInputText = elOther; 5266 elListItem.appendChild(elOther); 5267 if (bUseDomValuesForSelected) { 5268 otherElements.push(elOther); 5269 otherCheckBoxes.push(elCheckBox); 5270 } 5271 dojo.connect(elOther,"onchange",this,"fireInputChanged"); 5272 dojo.connect(elOther,"onkeyup",this,"fireInputChangedOnKeyUp"); 5273 } 5274 dojo.connect(elCheckBox,"onchange",this,"fireInputChanged"); 5275 })); 5276 } 5277 5278 if (bUseDomValuesForSelected && (domValues.length > 0) && (otherElements.length > 0)) { 5279 var unknownValues = new Array(); 5280 for (var i=0; i<domValues.length; i++) { 5281 var domValue = domValues[i]; 5282 var bKnown = false; 5283 for (var j=0; j<knownValues.length; j++) { 5284 if (domValue == knownValues[j]) {bKnown = true;break;} 5285 } 5286 if (!bKnown) unknownValues.push(domValue); 5287 } 5288 for (var i=0; i<unknownValues.length; i++) { 5289 if (otherElements.length > i) { 5290 otherElements[i].value = unknownValues[i]; 5291 otherCheckBoxes[i].checked = true; 5292 } 5293 } 5294 } 5295 } 5296 5297 }); 5298 5299 /** 5300 * @class Supports the input of a single value through a drop down list ("select"). 5301 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/InputSelectOne.xml<br/> 5302 * @name gxe.control.InputSelectOne 5303 * @extends gxe.control.InputBase 5304 * @property {boolean} supportsMultipleValues false 5305 * @property {Element} _htmlOther reference to the "other" code input text box 5306 */ 5307 dojo.provide("gxe.control.InputSelectOne"); 5308 dojo.declare("gxe.control.InputSelectOne",gxe.control.InputBase,{ 5309 _htmlOther: null, 5310 supportsMultipleValues: false, 5311 5312 /** Override gxe.control.InputBase.getInputValue() */ 5313 getInputValue: function(bInFeedbackMode) { 5314 if ((this.htmlElement != null) && (this.htmlElement.selectedIndex != null) && 5315 (this.htmlElement.selectedIndex >= 0)) { 5316 var elOptions = this.htmlElement.options; 5317 var elOption = elOptions[this.htmlElement.selectedIndex]; 5318 if ((this._htmlOther != null) && (elOption.value == this._htmlOther.gxeOptionValue)) { 5319 var sValue = dojo.trim(this._htmlOther.value); 5320 if (!bInFeedbackMode) { 5321 if (sValue != this._htmlOther.value) this._htmlOther.value = sValue; 5322 } 5323 if (sValue.length > 0) return sValue; 5324 } else { 5325 return elOption.value; 5326 } 5327 } 5328 return null; 5329 }, 5330 5331 /** 5332 * Catches "onchange" events for the HTML "select" element. 5333 * <br/>This method triggers the firing of the inputChanged() event. 5334 * <br/>If applicable, this method toggles the display of the 5335 * "other" code input text box. 5336 * @function 5337 * @name _onChange 5338 * @memberOf gxe.control.InputSelectOne# 5339 * @param {Event} e the underlying browser event 5340 */ 5341 _onChange: function(e) { 5342 if (this._htmlOther != null) { 5343 var elOption = this.htmlElement.options[this.htmlElement.selectedIndex]; 5344 if (elOption.value == this._htmlOther.gxeOptionValue) { 5345 this._htmlOther.style.display = "inline"; 5346 } else { 5347 this._htmlOther.style.display = "none"; 5348 } 5349 } 5350 this.fireInputChanged(e); 5351 }, 5352 5353 /** Override gxe.control.Control.onHtmlElementCreated() */ 5354 onHtmlElementCreated: function(domProcessor,domNode) { 5355 this.xmlNode.setInputControl(this); 5356 5357 var sDomValue = null; 5358 var bUseDomValueForSelected = (domProcessor != null); 5359 if ((domProcessor != null) && (domNode != null)) { 5360 sDomValue = domProcessor.getNodeText(domNode); 5361 } 5362 var elOptions = this.htmlElement.options; 5363 var bFoundSelected = false; 5364 var nIndex = -1; 5365 var cfgOptions = gxe.cfg.findChild(this.cfgObject,gxe.cfg.uriGxe,"options"); 5366 5367 if (cfgOptions != null) { 5368 gxe.cfg.forEachChild(cfgOptions,gxe.cfg.uriGxe,"option",dojo.hitch(this,function(cfgOption) { 5369 nIndex++; 5370 var sLabel = gxe.cfg.getGxeAttributeValue(cfgOption,"label"); 5371 var sValue = gxe.cfg.getGxeAttributeValue(cfgOption,"value"); 5372 var sAlias = gxe.cfg.getGxeAttributeValue(cfgOption,"alias"); 5373 var bSelected = false; 5374 5375 if (!bFoundSelected) { 5376 if (!bUseDomValueForSelected) { 5377 var sSelected = gxe.cfg.getGxeAttributeValue(cfgOption,"selected"); 5378 bSelected = (sSelected == "true"); 5379 } else { 5380 bSelected = (sValue == sDomValue); 5381 if (!bSelected && (sAlias != null)) { 5382 bSelected = (sAlias == sDomValue); 5383 } 5384 } 5385 if (bSelected) bFoundSelected = true; 5386 } 5387 5388 var elOption = new Option(sLabel,sValue,bSelected,bSelected); 5389 elOptions[elOptions.length] = elOption; 5390 5391 if (this._htmlOther == null) { 5392 var sOther = gxe.cfg.getGxeAttributeValue(cfgOption,"isOther"); 5393 if ((sOther != null) && (sOther == "true")) { 5394 this._htmlOther = this.makeOtherInputText(cfgOption); 5395 this._htmlOther.gxeOptionValue = sValue; 5396 this._htmlOther.gxeOptionIndex = nIndex; 5397 if (!bSelected) this._htmlOther.style.display = "none"; 5398 this.htmlElement.parentNode.appendChild(this._htmlOther); 5399 } 5400 } 5401 })); 5402 } 5403 5404 if (!bFoundSelected && (elOptions.length > 0)) { 5405 var nSelectedIndex = 0; 5406 if (bUseDomValueForSelected && (sDomValue != null) && (this._htmlOther != null)) { 5407 nSelectedIndex = this._htmlOther.gxeOptionIndex; 5408 this._htmlOther.value = sDomValue; 5409 } 5410 this.htmlElement.selectedIndex = nSelectedIndex; 5411 this._onChange(); 5412 } 5413 dojo.connect(this.htmlElement,"onchange",this,"_onChange"); 5414 if (this._htmlOther != null) { 5415 dojo.connect(this._htmlOther,"onchange",this,"fireInputChanged"); 5416 dojo.connect(this._htmlOther,"onkeyup",this,"fireInputChangedOnKeyUp"); 5417 } 5418 } 5419 5420 }); 5421 5422 /** 5423 * @class Supports the input of a single value through a text box ("input" type="text"). 5424 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/InputText.xml<br/> 5425 * @name gxe.control.InputText 5426 * @extends gxe.control.InputBase 5427 * @property {boolean} supportsMultipleValues false 5428 */ 5429 dojo.provide("gxe.control.InputText"); 5430 dojo.declare("gxe.control.InputText",gxe.control.InputBase,{ 5431 supportsMultipleValues: false, 5432 5433 /** Override gxe.control.InputBase.getInputValue() */ 5434 getInputValue: function(bInFeedbackMode) { 5435 if ((this.htmlElement != null) && (this.htmlElement.value != null)) { 5436 var sValue = dojo.trim(this.htmlElement.value); 5437 if (!bInFeedbackMode) { 5438 if (sValue != this.htmlElement.value) this.htmlElement.value = sValue; 5439 } 5440 if (sValue.length > 0) return sValue; 5441 } 5442 return null; 5443 }, 5444 5445 /** Override gxe.control.Control.importHtmlAttributes() */ 5446 importHtmlAttributes: function(cfgObject) { 5447 this.inherited(arguments); 5448 this.htmlTag = "input"; 5449 this.htmlAttributes.set("type","text"); 5450 }, 5451 5452 /** Override gxe.control.Control.onHtmlElementCreated() */ 5453 onHtmlElementCreated: function(domProcessor,domNode) { 5454 this.xmlNode.setInputControl(this); 5455 var sValue = gxe.cfg.getGxeAttributeValue(this.cfgObject,"fixedValue"); 5456 if (sValue == null) { 5457 sValue = gxe.cfg.getGxeAttributeValue(this.xmlNode.cfgObject,"fixedValue"); 5458 } 5459 if (sValue == null) { 5460 if ((domProcessor != null) && (domNode != null)) { 5461 sValue = domProcessor.getNodeText(domNode); 5462 } else if (domProcessor == null) { 5463 sValue = gxe.cfg.getGxeAttributeValue(this.cfgObject,"value"); 5464 if (sValue == null) sValue = gxe.cfg.getGxeAttributeValue(this.xmlNode.cfgObject,"value"); 5465 } 5466 } 5467 if (sValue != null) { 5468 var sType = gxe.cfg.getGxeAttributeValue(this.xmlNode.cfgObject,"valueType"); 5469 if (sType != null) sType = dojo.trim(sType); 5470 if (sType == "fgdc:date") sValue = sValue.replace(/-/g,""); 5471 this.htmlElement.value = sValue; 5472 } 5473 dojo.connect(this.htmlElement,"onchange",this,"fireInputChanged"); 5474 dojo.connect(this.htmlElement,"onkeyup",this,"fireInputChangedOnKeyUp"); 5475 } 5476 5477 }); 5478 5479 /** 5480 * @class Supports the input of a single value through a "textarea" control. 5481 * <br/><br/>Typically associated with:<br/>gpt/gxe/core/ui/InputTextArea.xml<br/> 5482 * @name gxe.control.InputTextArea 5483 * @extends gxe.control.InputBase 5484 * @property {boolean} supportsMultipleValues false 5485 */ 5486 dojo.provide("gxe.control.InputTextArea"); 5487 dojo.declare("gxe.control.InputTextArea",gxe.control.InputBase,{ 5488 supportsMultipleValues: false, 5489 5490 /** Override gxe.control.InputBase.getInputValue() */ 5491 getInputValue: function(bInFeedbackMode) { 5492 if ((this.htmlElement != null) && (this.htmlElement.value != null)) { 5493 var sValue = dojo.trim(this.htmlElement.value); 5494 if (!bInFeedbackMode) { 5495 if (sValue != this.htmlElement.value) this.htmlElement.value = sValue; 5496 } 5497 if (sValue.length > 0) return sValue; 5498 } 5499 return null; 5500 }, 5501 5502 /** Override gxe.control.Control.onHtmlElementCreated() */ 5503 onHtmlElementCreated: function(domProcessor,domNode) { 5504 this.xmlNode.setInputControl(this); 5505 var sValue = gxe.cfg.getGxeAttributeValue(this.cfgObject,"fixedValue"); 5506 if (sValue == null) { 5507 sValue = gxe.cfg.getGxeAttributeValue(this.xmlNode.cfgObject,"fixedValue"); 5508 } 5509 if (sValue == null) { 5510 if ((domProcessor != null) && (domNode != null)) { 5511 sValue = domProcessor.getNodeText(domNode); 5512 } else if (domProcessor == null) { 5513 sValue = gxe.cfg.getGxeAttributeValue(this.cfgObject,"value"); 5514 if (sValue == null) { 5515 sValue = gxe.cfg.getGxeAttributeValue(this.xmlNode.cfgObject,"value"); 5516 } 5517 } 5518 } 5519 if (sValue != null) this.htmlElement.value = sValue; 5520 dojo.connect(this.htmlElement,"onchange",this,"fireInputChanged"); 5521 dojo.connect(this.htmlElement,"onkeyup",this,"fireInputChangedOnKeyUp"); 5522 } 5523 5524 }); 5525 5526 /** 5527 * @class Provides a popup dialog for GEMET keyword selection. 5528 * <br/><br/>Requires class Gemet from library [wepabb]/catalog/js/v[latest]/gemet.js. 5529 * @name gxe.control.InputGemetKeyword 5530 * @extends gxe.control.InputText 5531 * @property {boolean} supportsMultipleValues false 5532 * @property {Element} _gemetTool reference to the anchor element taht launches the GEMET dialog 5533 */ 5534 dojo.provide("gxe.control.InputGemetKeyword"); 5535 dojo.declare("gxe.control.InputGemetKeyword",gxe.control.InputText,{ 5536 supportsMultipleValues: false, 5537 _gemetTool: null, 5538 5539 /** 5540 * Utility to enable/disable the anchor that launches the GEMET dialog. 5541 * <br/>The anchor is enabled when text has been entered into the associated input text box. 5542 * @function 5543 * @name _enableDisable 5544 * @memberOf gxe.control.InputGemetKeyword# 5545 */ 5546 _enableDisable: function() { 5547 var bOk = false; 5548 if (this._gemetTool != null) bOk = (dojo.trim(this.htmlElement.value).length > 0); 5549 if (bOk) this._gemetTool.style.display = "inline"; 5550 else this._gemetTool.style.display = "none"; 5551 }, 5552 5553 /** Override gxe.control.InputBase.onInputChanged() */ 5554 onInputChanged: function() {this._enableDisable();}, 5555 5556 /** Override gxe.control.Control.onHtmlChildrenCreated() */ 5557 onHtmlChildrenCreated: function(domProcessor,domNode) { 5558 this.inherited(arguments); 5559 var elLink = document.createElement("a"); 5560 elLink.setAttribute("href","javascript:void(0);"); 5561 elLink.className = "gxeInputTool"; 5562 elLink.appendChild(document.createTextNode(this.context.getI18NString("gemet.find"))); 5563 this.htmlElement.parentNode.appendChild(elLink); 5564 5565 var gemet = new Gemet(); 5566 gemet.lblHelp = this.context.getI18NString("gemet.dialogHelp"); 5567 gemet.lblDialogTitle = this.context.getI18NString("gemet.dialogTitle"); 5568 gemet.lblWordNotFound = this.context.getI18NString("gemet.wordNotFound"); 5569 gemet.lblCancel = this.context.getI18NString("gemet.cancel"); 5570 gemet.lblOk = this.context.getI18NString("gemet.ok"); 5571 gemet.lblErrorKeywordEmpty = this.context.getI18NString("gemet.lblErrorKeywordEmpty"); 5572 gemet.lblLoadingMessage = this.context.getI18NString("gemet.connectingMessage"); 5573 gemet.proxyUrl = this.context.contextPath+"/catalog/download/proxy.jsp?{0}"; 5574 gemet.imgLoading = this.context.contextPath+"/catalog/images/loading.gif"; 5575 5576 dojo.connect(elLink,"onclick",this,dojo.hitch(this,function(e) { 5577 var sValue = dojo.trim(this.htmlElement.value); 5578 if (sValue.length > 0) { 5579 gemet.findConcepts(sValue,null,dojo.hitch(this,function(sGemetText) { 5580 if ((typeof(sGemetText) != "undefined") && (sGemetText != null)) { 5581 sGemetText = dojo.trim(sGemetText); 5582 if (sGemetText.length > 0) { 5583 this.htmlElement.value = sGemetText; 5584 //this.htmlElement.disabled = true; 5585 } 5586 } 5587 })); 5588 } 5589 })); 5590 5591 this._gemetTool = elLink; 5592 this._enableDisable(); 5593 } 5594 5595 }); 5596 5597 /** 5598 * @class Provides an interactive map control for the definition of a bounding spatial envelope. 5599 * <br/><br/>Requires library [wepabb]/catalog/js/v[latest]/gpt.js. 5600 * @name gxe.control.Map 5601 * @extends gxe.control.Control 5602 */ 5603 dojo.provide("gxe.control.Map"); 5604 dojo.declare("gxe.control.Map",gxe.control.Control,{ 5605 gptMap: null, 5606 gptMapToolbar: null, 5607 gptLocator: null, 5608 gptInpEnv: null, 5609 _wasMapInitialized: false, 5610 5611 /** 5612 * Appends an image button to a map toolbar. 5613 * @function 5614 * @name _appendImageButton 5615 * @memberOf gxe.control.Map# 5616 * @param {Element} elToolBar the parent HTML element for the toolbar 5617 * @param {String} sId the id for the new img element 5618 * @param {String} sSrc the src for the new img element 5619 * @param {String} sTip the tool tip for the new img element 5620 */ 5621 _appendImageButton: function(elToolBar,sId,sSrc,sTip) { 5622 var el = document.createElement("img"); 5623 el.id = sId; 5624 el.alt = sTip; 5625 el.title = sTip; 5626 el.src = sSrc; 5627 elToolBar.appendChild(el); 5628 return el; 5629 }, 5630 5631 /** Override gxe.control.Control.build() */ 5632 build: function(htmlParentElement,domProcessor,domNode) { 5633 this.inherited(arguments); 5634 this.htmlElement.style.display = "none"; 5635 5636 var idPfx = this.gxeId+"_"; 5637 var sImages = this.context.contextPath+"/catalog/images/"; 5638 var elImg; 5639 5640 var elUseMap = document.createElement("a"); 5641 elUseMap.setAttribute("href","javascript:void(0);"); 5642 elUseMap.className = "gxeUseMapButton"; 5643 elUseMap.appendChild(document.createTextNode( 5644 this.context.getI18NString("map.useMap"))); 5645 this.htmlElement.parentNode.insertBefore(elUseMap,this.htmlElement); 5646 5647 var elToolBar = document.createElement("div"); 5648 elToolBar.id = idPfx+"mapToolbar"; 5649 elToolBar.className = "mapToolbar"; 5650 this.htmlElement.appendChild(elToolBar); 5651 5652 elImg = this._appendImageButton(elToolBar,idPfx+"mapButton-zoomToWorld", 5653 sImages+"btn-zoomToWorld-off.gif", 5654 this.context.getI18NString("map.zoomToWorld")); 5655 5656 elImg = this._appendImageButton(elToolBar,idPfx+"mapButton-zoomToInputEnvelope", 5657 sImages+"btn-zoomToInputEnvelope-off.gif", 5658 this.context.getI18NString("map.zoomToInputEnvelope")); 5659 5660 elImg = this._appendImageButton(elToolBar,idPfx+"mapTool-drawInputEnvelope", 5661 sImages+"btn-drawInputEnvelope-off.gif", 5662 this.context.getI18NString("map.drawInputEnvelope")); 5663 elImg.className = "firstTool"; 5664 5665 elImg = this._appendImageButton(elToolBar,idPfx+"mapTool-deactivate", 5666 sImages+"btn-deactivate-off.gif", 5667 this.context.getI18NString("map.deactivate")); 5668 5669 var elLocatorInput = document.createElement("input"); 5670 elLocatorInput.setAttribute("type","text"); 5671 elLocatorInput.setAttribute("maxLength","1024"); 5672 elLocatorInput.id = idPfx+"mapInput-locate"; 5673 elLocatorInput.className = "locatorInput"; 5674 dojo.connect(elLocatorInput,"onkeypress",this,"onLocatorKeyPress"); 5675 elToolBar.appendChild(elLocatorInput); 5676 5677 elImg = this._appendImageButton(elToolBar,idPfx+"mapButton-locate", 5678 sImages+"btn-locate-off.gif", 5679 this.context.getI18NString("map.locate")); 5680 5681 var elCandidates = document.createElement("div"); 5682 elCandidates.id = idPfx+"locatorCandidates"; 5683 elCandidates.className = "locatorCandidates"; 5684 this.htmlElement.appendChild(elCandidates); 5685 5686 var elMapContainer = document.createElement("div"); 5687 elMapContainer.className = "gxeMapContainer"; 5688 this.htmlElement.appendChild(elMapContainer); 5689 5690 var elMapCanvas = document.createElement("div"); 5691 elMapCanvas.id = idPfx+"interactiveMap"; 5692 elMapCanvas.className = "gxeMapCanvas"; 5693 elMapContainer.appendChild(elMapCanvas); 5694 5695 var config = this.context.gptMapConfig; 5696 config.mapElementId = idPfx+"interactiveMap"; 5697 config.mapToolName = "drawInputEnvelope"; 5698 config.mapToolbarId = idPfx+"mapToolbar"; 5699 config.locatorInputId = idPfx+"mapInput-locate"; 5700 config.locatorCandidatesId = idPfx+"locatorCandidates"; 5701 5702 var el = this.htmlElement.parentNode; 5703 while (el != null) { 5704 var nl = dojo.query("[gxeMapPart='envelope_container']",el); 5705 if (nl.length == 1) { 5706 var elC = nl[0]; 5707 var nlN = dojo.query("[gxeMapPart='envelope_north']",elC); 5708 var nlS = dojo.query("[gxeMapPart='envelope_south']",elC); 5709 var nlE = dojo.query("[gxeMapPart='envelope_east']",elC); 5710 var nlW = dojo.query("[gxeMapPart='envelope_west']",elC); 5711 if ((nlN.length == 1) && (nlS.length == 1) && 5712 (nlE.length == 1) && (nlW.length == 1)) { 5713 config.inputEnvelopeXMinId = nlW[0].id; 5714 config.inputEnvelopeYMinId = nlS[0].id; 5715 config.inputEnvelopeXMaxId = nlE[0].id; 5716 config.inputEnvelopeYMaxId = nlN[0].id; 5717 } 5718 break; 5719 } 5720 el = el.parentNode; 5721 } 5722 5723 dojo.connect(elUseMap,"onclick",this,"_useMap"); 5724 }, 5725 5726 /** 5727 * Responds to a key press from the input text box associated with the locator (i.e. gazateer). 5728 * @function 5729 * @name onLocatorKeyPress 5730 * @memberOf gxe.control.Map# 5731 * @param {Event} e the underlying browser event 5732 */ 5733 onLocatorKeyPress: function(e) { 5734 if (!e) e = window.event; 5735 if (e) { 5736 var nKey = (e.keyCode) ? e.keyCode : e.which; 5737 if (nKey == 13) { 5738 if (this.gptLocator != null) this.gptLocator.locate(); 5739 return false; 5740 } 5741 } 5742 return true; 5743 }, 5744 5745 /** 5746 * Responds to a click of a map related button. 5747 * @function 5748 * @name onMapButtonClicked 5749 * @memberOf gxe.control.Map# 5750 * @param {String} sButtonName the map button name 5751 */ 5752 onMapButtonClicked: function(sButtonName) { 5753 if (sButtonName == "zoomToWorld") { 5754 if (this.gptMap != null) this.gptMap.zoomToWorld(); 5755 } else if (sButtonName == "zoomToInputEnvelope") { 5756 if (this.gptInpEnv != null) this.gptInpEnv.zoomToInputEnvelope(); 5757 } else if (sButtonName == "locate") { 5758 if (this.gptLocator != null) this.gptLocator.locate(); 5759 } 5760 }, 5761 5762 /** 5763 * Responds following the load of the underlying map control. 5764 * @function 5765 * @name onMapLoaded 5766 * @memberOf gxe.control.Map# 5767 */ 5768 onMapLoaded: function() { 5769 if (this.gptInpEnv != null) this.gptInpEnv.highlightInputEnvelope(); 5770 }, 5771 5772 /** 5773 * Repositions the underlying map control. 5774 * @function 5775 * @name repositionMap 5776 * @memberOf gxe.control.Map# 5777 */ 5778 repositionMap: function() { 5779 if (this.gptMap != null) this.gptMap.reposition(); 5780 }, 5781 5782 /** 5783 * Handles a "Use Map" click. 5784 * @function 5785 * @name _useMap 5786 * @memberOf gxe.control.Map# 5787 * @param {Event} e the underlying browser event 5788 */ 5789 _useMap: function(e) { 5790 if (this._wasMapInitialized) { 5791 this.repositionMap(); 5792 return; 5793 } 5794 5795 this.htmlElement.style.display = "block"; 5796 var config = this.context.gptMapConfig; 5797 5798 this.gptMap = new GptMap(); 5799 dojo.connect(this.gptMap,"onMapLoaded",this,"onMapLoaded"); 5800 5801 this.gptInpEnv = new GptInputEnvelope(); 5802 this.gptInpEnv.initialize(config,this.gptMap); 5803 5804 this.gptMap.initialize(config); 5805 this._wasMapInitialized = true; 5806 5807 this.gptMapToolbar = new GptMapToolbar(); 5808 dojo.connect(this.gptMapToolbar,"onMapButtonClicked",this,"onMapButtonClicked"); 5809 dojo.connect(this.gptMapToolbar,"onDrawInputEnvelope",this.gptInpEnv,"onDrawInputEnvelope"); 5810 this.gptMapToolbar.initialize(config,this.gptMap); 5811 5812 this.gptLocator = new GptLocator(); 5813 this.gptLocator.initialize(config,this.gptMap); 5814 5815 dojo.connect(window,"onresize",this,"repositionMap"); 5816 dojo.connect(window,"onscroll",this,"repositionMap"); 5817 } 5818 5819 }); 5820 5821 /** 5822 * @class Provides a popup dialog for the selection of keywords. 5823 * @name fgdc.control.KeywordSelector 5824 * @extends gxe.control.Control 5825 */ 5826 dojo.provide("fgdc.control.KeywordSelector"); 5827 dojo.declare("fgdc.control.KeywordSelector",gxe.control.Control,{ 5828 5829 /** 5830 * Appends a checkbox option to the popup dialog. 5831 * @function 5832 * @name _appendCheckBox 5833 * @memberOf fgdc.control.KeywordSelector# 5834 * @param {Element} el the parent HTML element 5835 * @param {String} sLabel the label 5836 * @param {String} sValue a value associated with the checkbox 5837 * @param {boolean} bSelected true if the box should be checked 5838 */ 5839 _appendCheckBox: function(el,sLabel,sValue,bSelected) { 5840 var sOptionId = this.context.generateUniqueId(); 5841 var elListItem = document.createElement("li"); 5842 el.appendChild(elListItem); 5843 var elOption = document.createElement("input"); 5844 elOption.setAttribute("type","checkbox"); 5845 elOption.setAttribute("id",sOptionId); 5846 elOption.setAttribute("value",sValue); 5847 elListItem.appendChild(elOption); 5848 if (bSelected) elOption.setAttribute("checked","checked"); 5849 5850 var elLabel = document.createElement("label"); 5851 elLabel.setAttribute("for",sOptionId); 5852 elLabel.appendChild(document.createTextNode(sLabel)); 5853 elListItem.appendChild(elLabel); 5854 return elListItem; 5855 }, 5856 5857 /** Override gxe.control.Control.build() */ 5858 build: function(htmlParentElement,domProcessor,domNode) { 5859 this.inherited(arguments); 5860 5861 if (this.htmlElement != null) { 5862 dojo.connect(this.htmlElement,"onclick",this,dojo.hitch(this,function(e) { 5863 5864 var delimitedTextArea = null; 5865 var aCurrentValues = null; 5866 var siblings = this.xmlNode.parentElement.children; 5867 for (var i=0;i<siblings.getLength();i++) { 5868 var sibling = siblings.getItem(i); 5869 if (sibling.getInputControl() != null) { 5870 if (sibling.getInputControl().getSupportsMultipleValues()) { 5871 var s = sibling.nodeInfo.localName; 5872 if (s.length > 3) { 5873 s = s.substring(s.length-3); 5874 if (s == "key") { 5875 delimitedTextArea = sibling.getInputControl(); 5876 aCurrentValues = delimitedTextArea.getInputValues(true); 5877 break; 5878 } 5879 } 5880 } 5881 } 5882 } 5883 5884 if (delimitedTextArea != null) { 5885 var elListDiv = document.createElement("div"); 5886 dojo.style(elListDiv,{margin:"5px",padding:"5px",border:"1px solid #CCC"}); 5887 var elList = document.createElement("ul"); 5888 elList.className = "gxeSelectMany"; 5889 elListDiv.appendChild(elList); 5890 var cfgOptions = gxe.cfg.findChild(this.cfgObject,gxe.cfg.uriGxe,"options"); 5891 if (cfgOptions != null) { 5892 gxe.cfg.forEachChild(cfgOptions,gxe.cfg.uriGxe,"option",dojo.hitch(this,function(cfgOption) { 5893 var sLabel = gxe.cfg.getGxeAttributeValue(cfgOption,"label"); 5894 var sValue = gxe.cfg.getGxeAttributeValue(cfgOption,"value"); 5895 var sAlias = gxe.cfg.getGxeAttributeValue(cfgOption,"alias"); 5896 var sSelected = gxe.cfg.getGxeAttributeValue(cfgOption,"selected"); 5897 var bSelected = false; 5898 if (aCurrentValues != null) { 5899 for (var iCur=0; iCur<aCurrentValues.length; iCur++) { 5900 if (aCurrentValues[iCur] == sValue) { 5901 bSelected = true; 5902 break; 5903 } 5904 } 5905 } 5906 this._appendCheckBox(elList,sLabel,sValue,bSelected); 5907 })); 5908 } 5909 5910 var sTitle = this.htmlTextContent; 5911 if (sTitle == null) sTitle = "?Select"; 5912 var dialog = new dijit.Dialog({ 5913 title: sTitle, 5914 style: "display: none; border: 1px solid #000000; background: #FFFFFF;", 5915 autofocus: false 5916 }); 5917 dojo.addClass(dialog.domNode,"tundra"); 5918 dialog.domNode.appendChild(elListDiv); 5919 5920 var elButtonDiv = document.createElement("div"); 5921 dojo.style(elButtonDiv,{marginLeft:"auto",marginRight:"auto",width:"50%",padding:"5px"}); 5922 dialog.domNode.appendChild(elButtonDiv); 5923 var elOk = document.createElement("button"); 5924 elOk.appendChild(document.createTextNode(this.context.getI18NString("dialog.ok"))); 5925 elButtonDiv.appendChild(elOk); 5926 var elCancel = document.createElement("button"); 5927 elCancel.appendChild(document.createTextNode(this.context.getI18NString("dialog.cancel"))); 5928 elButtonDiv.appendChild(elCancel); 5929 5930 dojo.connect(elOk,"onclick",this,dojo.hitch(this,function(e) { 5931 var sCheckedValues = ""; 5932 dojo.query("[type='checkbox']",dialog.domNode).forEach(dojo.hitch(this,function(item) { 5933 if (item.checked) { 5934 if (sCheckedValues.length > 0) sCheckedValues += delimitedTextArea.delimiter; 5935 sCheckedValues += item.value; 5936 } 5937 })); 5938 var sThesaurus = gxe.cfg.getGxeAttributeValue(this.cfgObject,"thesaurus"); 5939 if (sThesaurus == null) sThesaurus = ""; 5940 this.xmlNode.getInputControl().htmlElement.value = sThesaurus; 5941 this.xmlNode.getInputControl().fireInputChanged(); 5942 delimitedTextArea.htmlElement.value = sCheckedValues; 5943 delimitedTextArea.fireInputChanged(); 5944 dialog.hide(); 5945 dialog.destroy(); 5946 })); 5947 5948 dojo.connect(elCancel,"onclick",this,dojo.hitch(this,function(e) { 5949 dialog.hide(); 5950 dialog.destroy(); 5951 })); 5952 dialog.show(); 5953 } 5954 })); 5955 } 5956 } 5957 5958 }); 5959 5960 5961 5962 5963