1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is MozMill Test code. 15 * 16 * The Initial Developer of the Original Code is the Mozilla Foundation. 17 * Portions created by the Initial Developer are Copyright (C) 2010 18 * the Initial Developer. All Rights Reserved. 19 * 20 * Contributor(s): 21 * Geo Mealer <gmealer@mozilla.com> 22 * Henrik Skupin <hskupin@mozilla.com> 23 * 24 * Alternatively, the contents of this file may be used under the terms of 25 * either the GNU General Public License Version 2 or later (the "GPL"), or 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 * in which case the provisions of the GPL or the LGPL are applicable instead 28 * of those above. If you wish to allow use of your version of this file only 29 * under the terms of either the GPL or the LGPL, and not to allow others to 30 * use your version of this file under the terms of the MPL, indicate your 31 * decision by deleting the provisions above and replace them with the notice 32 * and other provisions required by the GPL or the LGPL. If you do not delete 33 * the provisions above, a recipient may use your version of this file under 34 * the terms of any one of the MPL, the GPL or the LGPL. 35 * 36 * ***** END LICENSE BLOCK ***** */ 37 38 /** 39 * @name widgets 40 * @namespace Defines proxy classes for creation of a hierarchical map 41 */ 42 43 var Inheritance = require("../external/inheritance"); 44 var DomUtils = require("../dom_utils"); 45 var Services = require("../services"); 46 47 // How long we're willing to implicitly wait for an HTML/XUL element to be available 48 // before giving up. Available, right now, means "exists." This should eventually be 49 // made configurable by an external file or similar. 50 const ELEM_WAIT_TIME = 5000; 51 52 var Element = Inheritance.Class.create( 53 /** @lends widgets.Element */ 54 { 55 /** 56 * Element is a proxy for an element of a DOM. It defines the core binding 57 * behavior: locating the element via any of serveral lookup methods. 58 * It also provides the owner/owned relationship that allows Elements to be 59 * arranged in a tree-shaped Element map. 60 * 61 * @class A proxy for an element of a DOM 62 * @constructs 63 * @memberOf widgets 64 * 65 * @param {String} locatorType 66 * The type of locator being supplied. Choices are: 67 * <dl> 68 * <dd>node</dd> <dt>A node, as in Mozmill's Elem()</dt> 69 * <dd>id</dd> <dt>An ID string, as in Mozmill's ID()</dt> 70 * <dd>xpath</dd> <dt>An XPath string, as in Mozmill's XPath()</dt> 71 * <dd>name</dd> <dt>A name string, as in Mozmill's Name()</dt> 72 * <dd>lookup</dd> <dt>A lookup string, as in Mozmill's Lookup()</dt> 73 * <dd>tag</dd> <dt>A JQuery-style element selector string</dt> 74 * </dl> 75 * @param {node|String} locator 76 * The actual locator. If locatorType is "node," a node object is expected. 77 * For all other types, a String is expected. 78 * @param {document|Element} owner 79 * The owner (parent) of this Element. The top of an Element map is owned by 80 * a document. Other members of the map are owned by their parent Elements. 81 */ 82 initialize: function Element_initialize(locatorType, locator, owner) { 83 // Locators are used to find the element. See _locateElem(). 84 this._validateLocatorType(locatorType); 85 if (!locator) { 86 throw new Error("Missing locator"); 87 } 88 89 this._locator = locator; 90 this._locatorType = locatorType; 91 92 // Owner can be either the document for the top level of the map, 93 // or another Element that owns this one. 94 if (owner) { 95 if (owner instanceof Element) { 96 // We must be an owned element. Get doc and controller from our owner. 97 this._owner = owner; 98 this._document = owner._document; 99 this._controller = owner._controller; 100 } 101 else { 102 // We must be a top-level element. We sent in a document as owner. 103 this._owner = undefined; 104 this._document = owner; 105 this._controller = mozmill.controller.MozMillController(this._document.defaultView); 106 } 107 } 108 else { 109 // Not supplied at all, so we're top level and our doc is the current window. 110 this._owner = undefined; 111 this._controller = mozmill.getBrowserController(); 112 this._document = this._controller.window.document; 113 } 114 115 // We'll lazy-get these when requested 116 this._elem = undefined; 117 }, 118 119 _getCollector: function Element_getCollector() { 120 // Collectors take either a parent node or a parent document. 121 // If we have an owner, supply its node. Otherwise, supply the 122 // attached document (we're top level). 123 if (this._owner) 124 return new DomUtils.nodeCollector(this._owner.node); 125 else 126 return new DomUtils.nodeCollector(this._document); 127 }, 128 129 _validateLocatorType: function element_validateLocatorType(locatorType) { 130 switch (locatorType) { 131 case "node": 132 case "id": 133 case "xpath": 134 case "name": 135 case "lookup": 136 case "tag": 137 // locatorType is valid, do nothing 138 break; 139 default: 140 throw new Error("Invalid locator type: " + locatorType); 141 } 142 }, 143 144 _locateElem: function Element_locateElem() { 145 switch (this._locatorType) { 146 // First the standard Elem constructors. 147 case "node": 148 this._elem = new elementslib.Elem(this._locator); 149 break; 150 case "id": 151 this._elem = new elementslib.ID(this._document, this._locator); 152 break; 153 case "xpath": 154 this._elem = new elementslib.XPath(this._document, this._locator); 155 break; 156 case "name": 157 this._elem = new elementslib.Name(this._document, this._locator); 158 break; 159 case "lookup": 160 this._elem = new elementslib.Lookup(this._document, this._locator); 161 break; 162 163 // Finally, the nodeCollector. 164 // XXX: I'm calling this tag instead of selector, because I have 165 // intentions of introducing a path chain of selectors like 166 // "#foo/#bar/#baz" that mean "node with selector '#baz' under node 167 // with selector '#bar' under node with selector #foo, all under the 168 // owner of this Element". That will give us huge, huge flexibility in 169 // specifying node queries. However, we still have to account for 170 // property and anonymous locators, so there's significant work to 171 // be done here. Ultimately, I'm not 100% sure what a final tag will 172 // look like. 173 case "tag": 174 var collector = this._getCollector(); 175 collector.queryNodes(this._locator); 176 if (collector.nodes.length < 1) 177 throw new Error("Could not find node for tag: " + this._locator); 178 if (collector.nodes.length > 1) 179 throw new Error("Found more than one node for tag: " + this._locator); 180 this._elem = new elementslib.Elem(collector.nodes[0]); 181 break; 182 default: 183 throw new Error("Unknown locator type: " + this._locatorType); 184 } 185 }, 186 187 // XXX: note that properties don't properly inherit yet. Need work in 188 // inheritance.js. In the meantime, if need to override, have to split 189 // out the guts of the property into a private _function so that the 190 // parent() function will work correctly, then call private _function 191 // from the property. 192 193 /** 194 * The DOM document that is at the top of the map containing this Element. 195 * 196 * @name document 197 * @type document 198 * @fieldOf widgets.Element# 199 */ 200 get document() { 201 return this._document; 202 }, 203 204 /** 205 * The Mozmill controller object associated with this Element. 206 * 207 * @name controller 208 * @type controller 209 * @fieldOf widgets.Element# 210 */ 211 get controller() { 212 return this._controller; 213 }, 214 215 /** 216 * The DOM window object associated with this Element. 217 * 218 * @name window 219 * @type window 220 * @fieldOf widgets.Element# 221 */ 222 get window() { 223 return this._controller.window; 224 }, 225 226 /** 227 * The Mozmill elementslib object associated with this Element. 228 * 229 * @name elem 230 * @type elementslib 231 * @fieldOf widgets.Element# 232 */ 233 get elem() { 234 if (!this._elem) 235 this._locateElem(); 236 237 return this._elem; 238 }, 239 240 /** 241 * The DOM node object associated with this Element. 242 * 243 * @name node 244 * @type node 245 * @fieldOf widgets.Element# 246 */ 247 get node() { 248 return this.elem.getNode(); 249 } 250 }); 251 252 var XmlElement = Inheritance.Class.extend(Element, 253 /** @lends widgets.XmlElement */ 254 { 255 /** 256 * XmlElement is an Element that corresponds to a single element in 257 * an XML document. 258 * 259 * @class An Element that corresponds to a single element in an XML document 260 * @constructs 261 * @memberOf widgets 262 * @extends widgets.Element 263 * 264 * @param {String} locatorType 265 * The type of locator being supplied. Choices are: 266 * <dl> 267 * <dd>node</dd> <dt>A node, as in Mozmill's Elem()</dt> 268 * <dd>id</dd> <dt>An ID string, as in Mozmill's ID()</dt> 269 * <dd>xpath</dd> <dt>An XPath string, as in Mozmill's XPath()</dt> 270 * <dd>name</dd> <dt>A name string, as in Mozmill's Name()</dt> 271 * <dd>lookup</dd> <dt>A lookup string, as in Mozmill's Lookup()</dt> 272 * <dd>tag</dd> <dt>A JQuery-style element selector string</dt> 273 * </dl> 274 * @param {node|String} locator 275 * The actual locator. If locatorType is "node," a node object is expected. 276 * For all other types, a String is expected. 277 * @param {document|Element} owner 278 * The owner (parent) of this Element. The top of an Element map is owned by 279 * a document. Other members of the map are owned by their parent Elements. 280 */ 281 initialize: function XmlElement_initialize(locatorType, locator, owner) { 282 this.parent(locatorType, locator, owner); 283 } 284 }); 285 286 var XmlTree = Inheritance.Class.extend(Element, 287 /** @lends widgets.XmlTree */ 288 { 289 /** 290 * XmlTree is an Element that corresponds to a subtree of an XML document. 291 * 292 * @class An Element that corresponds to a subtree in an XML document. 293 * @constructs 294 * @memberOf widgets 295 * @extends widgets.Element 296 * 297 * @param {String} locatorType 298 * The type of locator being supplied. Choices are: 299 * <dl> 300 * <dd>node</dd> <dt>A node, as in Mozmill's Elem()</dt> 301 * <dd>id</dd> <dt>An ID string, as in Mozmill's ID()</dt> 302 * <dd>xpath</dd> <dt>An XPath string, as in Mozmill's XPath()</dt> 303 * <dd>name</dd> <dt>A name string, as in Mozmill's Name()</dt> 304 * <dd>lookup</dd> <dt>A lookup string, as in Mozmill's Lookup()</dt> 305 * <dd>tag</dd> <dt>A JQuery-style element selector string</dt> 306 * </dl> 307 * @param {node|String} locator 308 * The actual locator. If locatorType is "node," a node object is expected. 309 * For all other types, a String is expected. 310 * @param {document|Element} owner 311 * The owner (parent) of this Element. The top of an Element map is owned by 312 * a document. Other members of the map are owned by their parent Elements. 313 */ 314 initialize: function XmlTree_initialize(locatorType, locator, owner) { 315 this.parent(locatorType, locator, owner); 316 } 317 }); 318 319 var HtmlXulElement = Inheritance.Class.extend(Element, 320 /** @lends widgets.HtmlXulElement */ 321 { 322 /** 323 * HtmlXulElement is an Element that corresponds to an element of a 324 * HTML or Chrome document. More specific behavior for each is defined in 325 * the child classes HtmlElement and XulElement. 326 * 327 * @class An Element that corresponds to an element of an HTML or Chrome document 328 * @constructs 329 * @memberOf widgets 330 * @extends widgets.Element 331 * 332 * @param {String} locatorType 333 * The type of locator being supplied. Choices are: 334 * <dl> 335 * <dd>node</dd> <dt>A node, as in Mozmill's Elem()</dt> 336 * <dd>id</dd> <dt>An ID string, as in Mozmill's ID()</dt> 337 * <dd>xpath</dd> <dt>An XPath string, as in Mozmill's XPath()</dt> 338 * <dd>name</dd> <dt>A name string, as in Mozmill's Name()</dt> 339 * <dd>lookup</dd> <dt>A lookup string, as in Mozmill's Lookup()</dt> 340 * <dd>tag</dd> <dt>A JQuery-style element selector string</dt> 341 * </dl> 342 * @param {node|String} locator 343 * The actual locator. If locatorType is "node," a node object is expected. 344 * For all other types, a String is expected. 345 * @param {document|Element} owner 346 * The owner (parent) of this Element. The top of an Element map is owned by 347 * a document. Other members of the map are owned by their parent Elements. 348 */ 349 initialize: function HtmlXulElement_initialize(locatorType, locator, owner) { 350 this.parent(locatorType, locator, owner); 351 }, 352 353 /** 354 * Clicks on the Element with the left mouse button. 355 * 356 * @name click 357 * @methodOf widgets.HtmlXulElement# 358 * 359 * @param {Number} [left=0] Relative horizontal coordinate inside Element. 360 * @param {Number} [top=0] Relative vertical coordinate inside Element. 361 * @returns {Boolean} true if succeeded, false otherwise. 362 */ 363 click: function HtmlXulElement_click(left, top) { 364 return this.controller.click(this.elem, left, top); 365 }, 366 367 /** 368 * Double-clicks on the Element with the left mouse button. 369 * 370 * @name doubleClick 371 * @methodOf widgets.HtmlXulElement# 372 * 373 * @param {Number} [left=0] Relative horizontal coordinate inside Element. 374 * @param {Number} [top=0] Relative vertical coordinate inside Element. 375 * @returns {Boolean} true if succeeded, false otherwise. 376 */ 377 doubleClick: function HtmlXulElement_doubleClick(left, top) { 378 return this.controller.click(this.elem, left, top); 379 }, 380 381 /** 382 * Performs a key press for the given keycode. 383 * Try to avoid the usage of the ctrlKey and metaKey modifiers if the 384 * shortcut is a combination of Ctrl (Windows/Linux) and Cmd (Mac). In 385 * this case, use accelKey instead which will work across operating systems. 386 * 387 * @name keyPress 388 * @methodOf widgets.HtmlXulElement# 389 * 390 * @param {String} keycode Either a literal like 'b' or an enum like 'VK_ESCAPE'. 391 * @param {Object} [modifiers={}] Indicates modifier keys. true means pressed. 392 * @param {Boolean} [modifiers.ctrlKey=false] The Ctrl key. 393 * @param {Boolean} [modifiers.altKey=false] The Alt/Option key. 394 * @param {Boolean} [modifiers.shiftKey=false] The Shift key. 395 * @param {Boolean} [modifiers.metaKey=false] The Meta/Cmd key. 396 * @param {Boolean} [modifiers.accelKey=false] Ctrl key on Windows/Linux, 397 Cmd key on Mac. 398 * @return {Boolean} true if succeeded, false otherwise. 399 */ 400 keyPress: function HtmlXulElement_keypress(keycode, modifiers) { 401 modifiers = modifiers || {}; 402 return this.controller.keypress(this.elem, keycode, modifiers); 403 }, 404 405 /** 406 * Presses the selected mouse button down on the Element. 407 * 408 * @name mouseDown 409 * @methodOf widgets.HtmlXulElement# 410 * 411 * @param {Number} [button=0] The id of the button to press (0 - left, 1 - middle, 2 - right). 412 * @param {Number} [left=0] Relative horizontal coordinate inside Element. 413 * @param {Number} [top=0] Relative vertical coordinate inside Element. 414 * @returns {Boolean} true if succeeded, false otherwise. 415 */ 416 mouseDown: function HtmlXulElement_mouseDown(button, left, top) { 417 return this.controller.mouseDown(this.elem, button, left, top); 418 }, 419 420 /** 421 * Releases the selected mouse button on the Element. 422 * 423 * @name mouseUp 424 * @methodOf widgets.HtmlXulElement# 425 * 426 * @param {Number} [button=0] The id of the button to press (0 - left, 1 - middle, 2 - right). 427 * @param {Number} [left=0] Relative horizontal coordinate inside Element. 428 * @param {Number} [top=0] Relative vertical coordinate inside Element. 429 * @returns {Boolean} true if succeeded, false otherwise. 430 */ 431 mouseUp: function HtmlXulElement_mouseUp(button, left, top) { 432 return this.controller.mouseUp(this.elem, button, left, top); 433 }, 434 435 /** 436 * Clicks on the Element with the right mouse button. 437 * 438 * @name rightClick 439 * @methodOf widgets.HtmlXulElement# 440 * 441 * @param {Number} [left=0] Relative horizontal coordinate inside Element. 442 * @param {Number} [top=0] Relative vertical coordinate inside Element. 443 * @returns {Boolean} true if succeeded, false otherwise. 444 */ 445 rightClick: function HtmlXulElement_rightClick(left, top) { 446 return this.controller.rightClick(this.elem, left, top); 447 } 448 }); 449 450 var HtmlElement = Inheritance.Class.extend(HtmlXulElement, 451 /** @lends widgets.HtmlElement */ 452 { 453 /** 454 * HtmlElement is an Element that corresponds to an element of an HTML document. 455 * 456 * @class An Element that corresponds to an element of an HTML document. 457 * @constructs 458 * @memberOf widgets 459 * @extends widgets.HtmlXulElement 460 * 461 * @param {String} locatorType 462 * The type of locator being supplied. Choices are: 463 * <dl> 464 * <dd>node</dd> <dt>A node, as in Mozmill's Elem()</dt> 465 * <dd>id</dd> <dt>An ID string, as in Mozmill's ID()</dt> 466 * <dd>xpath</dd> <dt>An XPath string, as in Mozmill's XPath()</dt> 467 * <dd>name</dd> <dt>A name string, as in Mozmill's Name()</dt> 468 * <dd>lookup</dd> <dt>A lookup string, as in Mozmill's Lookup()</dt> 469 * <dd>tag</dd> <dt>A JQuery-style element selector string</dt> 470 * </dl> 471 * @param {node|String} locator 472 * The actual locator. If locatorType is "node," a node object is expected. 473 * For all other types, a String is expected. 474 * @param {document|Element} owner 475 * The owner (parent) of this Element. The top of an Element map is owned by 476 * a document. Other members of the map are owned by their parent Elements. 477 */ 478 initialize: function HtmlElement_initialize(locatorType, locator, owner) { 479 this.parent(locatorType, locator, owner); 480 }, 481 }); 482 483 var HtmlRegion = Inheritance.Class.extend(HtmlElement, 484 /** @lends widgets.HtmlRegion */ 485 { 486 /** 487 * HtmlRegion is an Element that corresponds to a grouping container in an HTML 488 * document. 489 * 490 * @class An Element that corresponds to a grouping container in an HTML document. 491 * @constructs 492 * @memberOf widgets 493 * @extends widgets.HtmlElement 494 * 495 * @param {String} locatorType 496 * The type of locator being supplied. Choices are: 497 * <dl> 498 * <dd>node</dd> <dt>A node, as in Mozmill's Elem()</dt> 499 * <dd>id</dd> <dt>An ID string, as in Mozmill's ID()</dt> 500 * <dd>xpath</dd> <dt>An XPath string, as in Mozmill's XPath()</dt> 501 * <dd>name</dd> <dt>A name string, as in Mozmill's Name()</dt> 502 * <dd>lookup</dd> <dt>A lookup string, as in Mozmill's Lookup()</dt> 503 * <dd>tag</dd> <dt>A JQuery-style element selector string</dt> 504 * </dl> 505 * @param {node|String} locator 506 * The actual locator. If locatorType is "node," a node object is expected. 507 * For all other types, a String is expected. 508 * @param {document|Element} owner 509 * The owner (parent) of this Element. The top of an Element map is owned by 510 * a document. Other members of the map are owned by their parent Elements. 511 */ 512 initialize: function HtmlRegion_initialize(locatorType, locator, owner) { 513 this.parent(locatorType, locator, owner); 514 }, 515 }); 516 517 var XulElement = Inheritance.Class.extend(HtmlXulElement, 518 /** @lends widgets.XulElement */ 519 { 520 /** 521 * XulElement is an Element that corresponds to an element of a Chrome 522 * document. 523 * 524 * @class An Element that corresponds to an element of a Chrome document. 525 * @constructs 526 * @memberOf widgets 527 * @extends widgets.HtmlXulElement 528 * 529 * @param {String} locatorType 530 * The type of locator being supplied. Choices are: 531 * <dl> 532 * <dd>node</dd> <dt>A node, as in Mozmill's Elem()</dt> 533 * <dd>id</dd> <dt>An ID string, as in Mozmill's ID()</dt> 534 * <dd>xpath</dd> <dt>An XPath string, as in Mozmill's XPath()</dt> 535 * <dd>name</dd> <dt>A name string, as in Mozmill's Name()</dt> 536 * <dd>lookup</dd> <dt>A lookup string, as in Mozmill's Lookup()</dt> 537 * <dd>tag</dd> <dt>A JQuery-style element selector string</dt> 538 * </dl> 539 * @param {node|String} locator 540 * The actual locator. If locatorType is "node," a node object is expected. 541 * For all other types, a String is expected. 542 * @param {document|Element} owner 543 * The owner (parent) of this Element. The top of an Element map is owned by 544 * a document. Other members of the map are owned by their parent Elements. 545 */ 546 initialize: function XulElement_initialize(locatorType, locator, owner) { 547 this.parent(locatorType, locator, owner); 548 }, 549 }); 550 551 var XulRegion = Inheritance.Class.extend(XulElement, 552 /** @lends widgets.XulRegion */ 553 { 554 /** 555 * XulRegion is an Element that corresponds to a grouping container in a Chrome 556 * document. 557 * 558 * @class An Element that corresponds to a grouping container in a Chrome document. 559 * @constructs 560 * @memberOf widgets 561 * @extends widgets.XulElement 562 * 563 * @param {String} locatorType 564 * The type of locator being supplied. Choices are: 565 * <dl> 566 * <dd>node</dd> <dt>A node, as in Mozmill's Elem()</dt> 567 * <dd>id</dd> <dt>An ID string, as in Mozmill's ID()</dt> 568 * <dd>xpath</dd> <dt>An XPath string, as in Mozmill's XPath()</dt> 569 * <dd>name</dd> <dt>A name string, as in Mozmill's Name()</dt> 570 * <dd>lookup</dd> <dt>A lookup string, as in Mozmill's Lookup()</dt> 571 * <dd>tag</dd> <dt>A JQuery-style element selector string</dt> 572 * </dl> 573 * @param {node|String} locator 574 * The actual locator. If locatorType is "node," a node object is expected. 575 * For all other types, a String is expected. 576 * @param {document|Element} owner 577 * The owner (parent) of this Element. The top of an Element map is owned by 578 * a document. Other members of the map are owned by their parent Elements. 579 */ 580 initialize: function XulRegion_initialize(locatorType, locator, owner) { 581 this.parent(locatorType, locator, owner); 582 }, 583 }); 584 585 var Button = Inheritance.Class.extend(XulElement, { 586 // XXX: stub 587 }); 588 589 var TextBox = Inheritance.Class.extend(XulElement, { 590 getText: function TextBox_getText() { 591 return this.node.value; 592 }, 593 594 type: function TextBox_type(text) { 595 this.controller.type(this.elem, text); 596 } 597 }); 598 599 var Button_Menu = Inheritance.Class.extend(Button, { 600 // XXX: stub 601 }); 602 603 var Button_MenuButton = Inheritance.Class.extend(Button, { 604 // XXX: stub 605 }); 606 607 var TextBox_Multi = Inheritance.Class.extend(TextBox, { 608 // XXX: stub 609 }); 610 611 var TextBox_Number = Inheritance.Class.extend(TextBox, { 612 // XXX: stub 613 }); 614 615 var TextBox_Password = Inheritance.Class.extend(TextBox, { 616 // XXX: stub 617 }); 618 619 var TextBox_Auto = Inheritance.Class.extend(TextBox, { 620 // XXX: stub 621 }); 622 623 // Exported Classes 624 exports.Element = Element; 625 exports.XmlElement = XmlElement; 626 exports.HtmlXulElement = HtmlXulElement; 627 exports.HtmlElement = HtmlElement; 628 exports.HtmlRegion = HtmlRegion; 629 exports.XulElement = XulElement; 630 exports.XulRegion = XulRegion; 631 exports.Button = Button; 632 exports.TextBox = TextBox; 633 exports.Button_Menu = Button_Menu; 634 exports.Button_MenuButton = Button_MenuButton; 635 exports.TextBox_Multi = TextBox_Multi; 636 exports.TextBox_Number = TextBox_Number; 637 exports.TextBox_Password = TextBox_Password; 638 exports.TextBox_Auto = TextBox_Auto;