File:  [mozdev] / books / www / chapters / ch05.html
Revision 1.13: download - view: text, annotated - select for diffs - revision graph
Wed Dec 11 19:10:24 2002 UTC (17 years, 5 months ago) by petejc
Branches: MAIN
CVS tags: HEAD
--pete

    1:     <link rel="prev" href="http://books.mozdev.org/chapters/ch04.html" />
    2:     <link rel="next" href="http://books.mozdev.org/chapters/ch06.html" />
    3: 
    4:     <style type="text/css">
    5:       div.c15 {font-weight: bold; text-align: center}
    6:       div.c14 {text-align: center}
    7:     </style>
    8: 
    9:     <h2>Chapter 5</h2>
   10:     <h1><a name="77037"></a> Scripting Mozilla</h1>
   11:     <p>In Mozilla, scripting plays important roles in the XPFE.
   12:     Whether developers refer to script access and security, user
   13:     interface logic, XPCOM object invocation, or script execution
   14:     in element event handlers, scripting is so integral to
   15:     application development that Mozilla, as a development
   16:     platform, would be inconceivable without it.</p>
   17:     <p>The core scripting language used in Mozilla is JavaScript.
   18:     Although it has had a reputation as an unsophisticated language
   19:     used mostly in web pages, JavaScript is more like a first-tier
   20:     programming language. Modularity, good exception handing,
   21:     regular expression enhancement, and number formatting are just
   22:     some features of the new JavaScript 1.5,<a name="b260"></a><a
   23:     href="#260">[*]</a> which is based on the ECMA-262 standard.<a
   24:     name="b261"></a><a href="#261">[*]</a> JavaScript 2.0, due
   25:     sometime late in 2002, promises to be an even bigger promotion
   26:     of the language.</p>
   27:     <p>Three distinct levels of JavaScript are identified in this
   28:     chapter. A user interface level manipulates content through the
   29:     DOM, a client layer calls on the services provided by XPCOM,
   30:     and, finally, an application layer is available in which
   31:     JavaScript can create an XPCOM component. The following section
   32:     describes these levels in detail.</p>
   33:     <h2><a name="77038"></a> Faces of JavaScript in Mozilla</h2>
   34:     <p>As you have already 
   35:     <!--INDEX scripting:JavaScript, overview --> 
   36:     <!--INDEX JavaScript:overview --> seen in some examples in this
   37:     book, the user interface uses JavaScript extensively to create
   38:     behavior and to glue various widgets together into a coherent
   39:     whole. When you add code to the event handler of one element to
   40:     manipulate another-for example, when you update the value of a
   41:     textbox using a XUL button-you take advantage of this first
   42:     "level" of scriptability. In this role, JavaScript uses the
   43:     Document Object Model (DOM) to access parts of the user
   44:     interface as a hierarchical collection of objects. The section
   45:     <a href="#77054">"Adding Scripts to the UI</a>," later in this
   46:     chapter, discusses this highest level of scripting.</p>
   47:     <p>At a second level, JavaScript glues the entire user
   48:     interface to the XPCOM libraries beneath, which create the
   49:     application core. At this level, XPConnect (see the section <a
   50:     href="#77066">"What Is XPConnect?</a>" later in this chapter)
   51:     provides a bridge that makes these components "scriptable,"
   52:     which means that they can be invoked from JavaScript and used
   53:     from the user interface layer. When JavaScript calls methods
   54:     and gets data from scriptable components, it uses this second
   55:     layer of scriptability.</p>
   56:     <p>Finally, at the third and ultimate level of Mozilla
   57:     scripting, JavaScript can be used as a "first-order" language
   58:     for creating the application core itself, for writing software
   59:     components or libraries whose services are called. We discuss
   60:     this third level of scripting and provide a long example in the
   61:     section <a href="ch08.html#77065">"Creating a JavaScript XPCOM
   62:     Component</a>" in <a href="ch08.html#77048">Chapter 8</a>.</p>
   63:     <p>When you use JavaScript in these contexts, the application
   64:     architecture looks something like <a href="#77002">Figure
   65:     5-1</a>, in which scripting binds the user interface to the
   66:     application core through XPConnect and can reside as a software
   67:     component using such technologies as XPIDL and XPCOM.</p>
   68:     <div class="c14">
   69:       <img src="foo.gif">
   70:     </div>
   71:     <p><i>Figure 5-1: <a name="77002"></a></i> <i>Scripting in
   72:     Mozilla</i></p>
   73:     <h2><a name="77039"></a> JavaScript and the DOM</h2>
   74:     <p>In the application 
   75:     <!--INDEX DOM (Document Object Model):overview --> layer of
   76:     Mozilla, there is little distinction between a web page and the
   77:     graphical user interface. Mozilla's implementation of the DOM
   78:     is fundamentally the same for both XUL and HTML. In both cases,
   79:     state changes and events are propagated through various DOM
   80:     calls, meaning that the UI itself is content-not unlike that of
   81:     a web page. In application development, where the difference
   82:     between application "chrome" and rendered content is typically
   83:     big, this uniformity is a significant step forward.</p>
   84:     <h3><a name="77040"></a> What Is the DOM?</h3>
   85:     <p>The DOM is an API used to access HTML and XML documents. It
   86:     does two things for web developers: provides a structural
   87:     representation of the document and defines the way the
   88:     structure should be accessed from script. In the Mozilla XPFE
   89:     framework, this functionality allows you to manipulate the user
   90:     interface as a structured group of nodes, create new UI and
   91:     content, and remove elements as needed.</p>
   92:     <p>Because it is designed to access arbitrary HTML and XML, the
   93:     DOM applies not only to XUL, but also to MathML, SVG, and other
   94:     XML markup. By connecting web pages and XML documents to
   95:     scripts or programming languages, the DOM is not a particular
   96:     application, product, or proprietary ordering of web pages.
   97:     Rather, it is an <i>API-</i>an interface that vendors must
   98:     implement if their products are to conform to the W3C DOM
   99:     standard. Mozilla's commitment to standards ensures that its
  100:     applications and tools do just that.</p>
  101:     <p>When you use JavaScript to create new elements in an HTML
  102:     file or change the attributes of a XUL <tt>button</tt>, you
  103:     access an object model in which these structures are organized.
  104:     This model is the DOM for that document or data. The DOM
  105:     provides a context for the scripting language to operate in.
  106:     The specific context for web and XML documents-the top-level
  107:     <tt>window</tt> object, the elements that make up a web
  108:     document, and the data stored in those elements as children-is
  109:     standardized in several different specifications, the most
  110:     recent of which is the upcoming DOM Level 3 standard.</p>
  111:     <h3><a name="77041"></a> The DOM Standards and Mozilla</h3>
  112:     <p>The DOM <!--INDEX DOM (Document Object Model):levels --> 
  113:     <!--INDEX standards, DOM levels --> specifications are split
  114:     into different levels overseen by the W3C. Each level provides
  115:     its own features and Mozilla has varying, but nearly complete,
  116:     levels of support for each. Currently, Mozilla's support for
  117:     the DOM can be summarized as follows:</p>
  118:     <ul>
  119:       <li>DOM Level 1: Excellent</li>
  120:       <li>DOM Level 2: Good</li>
  121:       <li>DOM Level 3: Poor; under construction</li>
  122:     </ul>
  123:     <p>Mozilla strives to be standards-compliant, but typically
  124:     reaches full support only when those standards have become
  125:     recommendations rather than working drafts. Currently, Level 1
  126:     and Level 2 are recommendations and Level 3 is a working
  127:     draft.</p>
  128:     <p>Standards like the DOM make Mozilla an especially attractive
  129:     software development kit (SDK) for web developers. The same
  130:     layout engine that renders web content also draws the GUI and
  131:     pushes web development out of the web page into the application
  132:     chrome. The DOM provides a consistent, unified interface for
  133:     accessing all the documents you develop, making the content and
  134:     chrome accessible for easy cross-platform development and
  135:     deployment.</p>
  136:     <h3><a name="77042"></a> DOM Methods and Properties</h3>
  137:     <p>Methods in <!--INDEX DOM (Document Object Model):methods -->
  138:     <!--INDEX methods:DOM --> the DOM allow you to access and
  139:     manipulate any element in the user interface or in the content
  140:     of a web page. Getting and setting attributes, creating
  141:     elements, hiding elements, and appending children all involve
  142:     direct manipulation of the DOM. The DOM mediates all
  143:     interaction between scripts and the interface itself, so even
  144:     when you do something as simple as changing an image when the
  145:     user clicks a button, you use the DOM to register an event
  146:     handler with the <tt>button</tt> and DOM attributes on the
  147:     <tt>image</tt> element to change its source.</p>
  148:     <p>The DOM Level 1 and Level 2 Core specifications contain
  149:     multiple interfaces, including <i>Node</i>, <i>NodeList</i>,
  150:     <i>Element</i>, and <i>Document</i>. The following sections
  151:     describe some interface methods used to manipulate the object
  152:     model of application chrome, documents, or metadata in Mozilla.
  153:     The <i>Document</i> and <i>Element</i> interfaces, in
  154:     particular, contain useful methods for XUL developers.</p>
  155:     <h4><a name="77043"></a> Using dump( ) to print to STDOUT</h4>
  156:     <p>The code <!--INDEX STDOUT, printing and --> 
  157:     <!--INDEX printing, STDOUT --> <!--INDEX dump( ) method --> 
  158:     <!--INDEX DOM (Document Object Model):methods:printing STDOUT -->
  159:     <!--INDEX methods:DOM:printing STDOUT --> samples in this
  160:     chapter use a method called <tt>dump( )</tt> to print data to
  161:     STDOUT. This method is primarily used for debugging your code
  162:     and is turned on using a <tt>PREF</tt>. You can turn this
  163:     <tt>PREF</tt> on using the following code:</p>
  164: <pre>
  165: const PREFS_CID      = "@mozilla.org/preferences;1";
  166: const PREFS_I_PREF   = "nsIPref";
  167: const PREF_STRING    = "browser.dom.window.dump.enabled";
  168: try {
  169: var Pref        = new Components.Constructor(PREFS_CID, PREFS_I_PREF);
  170: var pref        = new Pref( );
  171: pref.SetBoolPref(PREF_STRING, true);
  172: } catch(e) {}
  173: </pre>
  174:     <p>This code is necessary only if you are doing development
  175:     with a release distribution build of Mozilla. If you are using
  176:     a debug or nightly build, this <tt>PREF</tt> can be set from
  177:     the preferences panel by selecting Edit &gt; Preferences &gt;
  178:     Debug &gt; Enable JavaScript dump( ) output.</p>
  179:     <h4><a name="77044"></a> getElementById</h4>
  180:     <p><tt>getElementById(aId)</tt> is 
  181:     <!--INDEX elements:referencing --> 
  182:     <!--INDEX referencing:elements --> 
  183:     <!--INDEX getElementById( ) method --> 
  184:     <!--INDEX DOM (Document Object Model):methods:referencing elements -->
  185:     <!--INDEX methods:DOM:referencing elements --> perhaps the most
  186:     commonly used DOM method in any programming domain. This is a
  187:     convenient way to get a reference to an element object by
  188:     passing that element's <tt>id</tt> as an argument, where the
  189:     <tt>id</tt> acts as a unique identifier for that element.</p>
  190:     <p>DOM calls like this are at the heart of Mozilla UI
  191:     functionality. <tt>getElementById</tt> is the main programmatic
  192:     entry point into the chrome and is essential for any dynamic
  193:     manipulation of XUL elements. For example, to get a box element
  194:     in script (i.e., to get a reference to it so you can call its
  195:     methods or read data from it), you must refer to it by using
  196:     the box <tt>id</tt>:</p>
  197: <pre>
  198: &lt;box id="my-id" /&gt;
  199: </pre>
  200:     <p>Since the return value of <tt>getElementById</tt> is a
  201:     reference to the specified element object, you usually assign
  202:     it to a variable like this:</p>
  203: <pre>
  204: var boxEl = document.getElementById('my-id');
  205: dump("boxEl="+boxEl+"\n");
  206: console output: boxEl=[object XULElement]
  207: </pre>
  208:     <p>Once you have the box element available as <tt>boxEl</tt>,
  209:     you can use other DOM methods like <tt>getAttribute</tt> and
  210:     <tt>setAttribute</tt> to change its layout, its position, its
  211:     state, or other features.</p>
  212:     <h4><a name="77045"></a> getAttribute</h4>
  213:     <p>Attributes <!--INDEX attributes:referencing --> 
  214:     <!--INDEX referencing:attributes --> 
  215:     <!--INDEX getAttribute( ) method --> 
  216:     <!--INDEX DOM (Document Object Model):methods:referencing attributes -->
  217:     <!--INDEX methods:DOM:referencing attributes --> are properties
  218:     that are defined directly on an element. XUL elements have
  219:     attributes such as <tt>disabled</tt>, <tt>height</tt>,
  220:     <tt>style</tt>, <tt>orient</tt>, and <tt>label</tt>.</p>
  221: <pre>
  222: &lt;box id="my-id" foo="hello 1" bar="hello 2" /&gt;
  223: </pre>
  224:     <p>In the snippet above, the strings "my-id," "hello 1," and
  225:     "hello 2" are values of the box element attributes. Note that
  226:     Gecko does not enforce a set of attributes for XUL elements.
  227:     XUL documents must be well-formed, but they are not validated
  228:     against any particular XUL DTD or schema. This lack of
  229:     enforcement means that attributes can be placed on elements ad
  230:     hoc. Although this placement can be confusing, particularly
  231:     when you look at the source code for the Mozilla browser
  232:     itself, it can be very helpful when you create your own
  233:     applications and want to track the data that interests you.</p>
  234:     <p>Once you have an object assigned to a variable, you can use
  235:     the DOM method <tt>getAttribute</tt> to get a reference to any
  236:     attribute in that object. The <tt>getAttribute</tt> method
  237:     takes the name of the desired attribute as a string. For
  238:     example, if you add an attribute called <tt>foo</tt> to a box
  239:     element, you can access that attribute's value and assign it to
  240:     a variable:</p>
  241: <pre>
  242: &lt;box id="my-id" foo="this is the foo attribute" /&gt;
  243: &lt;script&gt;
  244: var boxEl = document.getElementById('my-id');
  245:  var foo   = boxEl.getAttribute('foo');
  246:  dump(foo+'\n');
  247: &lt;/script&gt;
  248: </pre>
  249:     <p>The <tt>dump</tt> method outputs the string "this is the foo
  250:     attribute," which is the value of the attribute <tt>foo</tt>.
  251:     You can also add or change existing attributes with the
  252:     <tt>setAttribute</tt> DOM method.</p>
  253:     <h4><a name="77046"></a> setAttribute</h4>
  254:     <p>The <tt>setAttribute</tt> 
  255:     <!--INDEX attributes:changing values --> 
  256:     <!--INDEX setAttribute( ) method --> 
  257:     <!--INDEX DOM (Document Object Model):methods:changing attribute values -->
  258:     <!--INDEX methods:DOM:changing attribute values --> method
  259:     changes an existing attribute value. This method is useful for
  260:     changing the state of an element-its visibility, size, order
  261:     within a parent, layout and position, style, etc. It takes two
  262:     arguments: the attribute name and the new value.</p>
  263: <pre>
  264: &lt;box id="my-id" foo="this is the foo attribute" /&gt;
  265: &lt;script&gt;
  266: boxEl=document.getElementById('my-id');
  267: boxEl.setAttribute('foo', 'this is the foo attribute changed');
  268: var foo = boxEl.getAttribute('foo');
  269: dump(foo+'\n');
  270: &lt;/script&gt;
  271: </pre>
  272:     <p>The script above outputs the string "this is the foo
  273:     attribute changed" to the console. You can also use
  274:     <tt>setAttribute</tt> to create a new attribute if it does not
  275:     already exist:</p>
  276: <pre>
  277: &lt;box id="my-id" /&gt;
  278: &lt;script&gt;
  279: boxEl=document.getElementById('my-id');
  280: boxEl.setAttribute('bar', 'this is the new attribute bar');
  281: &lt;/script&gt;
  282: </pre>
  283:     <p>By setting an attribute that doesn't already exist, you
  284:     create it dynamically, adding a value to the hierarchical
  285:     representation of nodes that form the current document object.
  286:     After this code is executed, the <tt>boxEl</tt> element is the
  287:     same as an element whose <tt>bar</tt> attribute was hardcoded
  288:     into the XUL:</p>
  289: <pre>
  290: &lt;box id="my-id" bar="this is the new attribute bar" /&gt;
  291: </pre>
  292:     <p>These sorts of ad hoc changes give you complete control over
  293:     the state of the application interface.</p>
  294:     <h4><a name="77047"></a> createElement</h4>
  295:     <p>If you <!--INDEX elements:creating --> 
  296:     <!--INDEX createElement( ) method --> 
  297:     <!--INDEX DOM (Document Object Model):methods:creating elements -->
  298:     <!--INDEX methods:DOM:creating elements --> need to dynamically
  299:     create an element that doesn't already exist-for example, to
  300:     add a new row to a table displaying rows of information, you
  301:     can use the method <tt>createElement</tt>. To create and add a
  302:     text element to your box example, for example, you can use the
  303:     following code:</p>
  304: <pre>
  305: &lt;box id="my-id" /&gt;
  306: &lt;script&gt;
  307: boxEl = document.getElementById('my-id');
  308: var textEl  = document.createElement('description');
  309: boxEl.appendChild(textEl);
  310: &lt;/script&gt;
  311: </pre>
  312:     <p>Once you create the new element and assign it to the
  313:     <tt>textEl</tt> variable, you can use <tt>appendChild</tt> to
  314:     insert it into the object tree. In this case, it is appended to
  315:     <tt>boxEl</tt>, which becomes the insertion point.</p>
  316:     <blockquote>
  317:       <div class="c15">
  318:         NOTE
  319:       </div>
  320:       <p>For mixed namespace documents like XUL and HTML, you can
  321:       use a variation of <tt>createElement</tt> called
  322:       <tt>createElementNS</tt>. To create a mixed namespace
  323:       element, use this code:</p>
  324:     </blockquote>
  325:     var node = document.createElementNS('<a href=
  326:     "http://www.w3.org/1999.xhtml">http://www.w3.org/1999.xhtml</a>',
  327:     'html:div'); 
  328:     <blockquote>
  329:       <p>Namespace variations for other functions include
  330:       <tt>setAttributeNS</tt>, <tt>getElementsByTagNameNS</tt>, and
  331:       <tt>hasAttributeNS</tt>.</p>
  332:     </blockquote>
  333:     <h4><a name="77048"></a> createTextNode</h4>
  334:     <p>In <!--INDEX text nodes, creating --> 
  335:     <!--INDEX createTextNode( ) method --> 
  336:     <!--INDEX DOM (Document Object Model):methods:creating text nodes -->
  337:     <!--INDEX methods:DOM:creating text nodes --> addition to
  338:     setting the <tt>label</tt> attribute on an element, you can
  339:     create new text in the interface by using the DOM method
  340:     <tt>createTextNode</tt>, as shown in the following example:</p>
  341: <pre>
  342: &lt;description id="explain" /&gt;
  343: &lt;script&gt;
  344: var description = document.getElementById("explain");
  345: if (description) {
  346: if (!description.childNodes.length) {
  347: var textNode = document.createTextNode("Newly text");
  348: description.appendChild(textNode);
  349: }
  350: else if (description.childNodes.length == 1 ) {
  351: description.childNodes[0].nodeValue = "Replacement text";
  352: }
  353: }
  354: &lt;/script&gt;
  355: </pre>
  356:     <p>Notice the use of <tt>appendChild</tt>. This method,
  357:     discussed next, is used to insert the new element or text node
  358:     into the DOM tree after it is created. Create-and-append is a
  359:     common two-step process for adding new elements to the object
  360:     model.</p>
  361:     <h4><a name="77049"></a> appendChild</h4>
  362:     <p>To dynamically <!--INDEX elements:adding dynamically --> 
  363:     <!--INDEX appendChild( ) method --> 
  364:     <!--INDEX DOM (Document Object Model):methods:adding elements dynamically -->
  365:     <!--INDEX methods:DOM:adding elements dynamically --> add an
  366:     element to a document, you need to use the method
  367:     <tt>appendChild( )</tt>. This method adds a newly created
  368:     element to an existing parent node by appending to it. If a
  369:     visible widget is added, this change is visible in the
  370:     interface immediately.</p>
  371: <pre>
  372: &lt;groupbox id="my-id" /&gt;
  373: &lt;script&gt;
  374: var existingEl  = document.getElementById('my-id');
  375: var captionEl   = document.createElement('caption');
  376: existingEl.appendChild(captionEl);
  377: captionEl.setAttribute('label', 'This is a new caption');
  378: captionEl.setAttribute('style', 'color: blue;');
  379: &lt;/script&gt;
  380: </pre>
  381:     <p>This example creates a new element, gets an existing parent
  382:     element from the document, and then uses <tt>appendChild(
  383:     )</tt> to insert that new element into the document. It also
  384:     uses <tt>setAttribute</tt> to add an attribute value and some
  385:     CSS style rules, which can highlight the new element in the
  386:     existing interface.</p>
  387:     <h4><a name="77050"></a> cloneNode</h4>
  388:     <p>For <!--INDEX nodes:copying --> 
  389:     <!--INDEX cloneNode( ) method --> 
  390:     <!--INDEX DOM (Document Object Model):methods:copying nodes -->
  391:     <!--INDEX methods:DOM:copying nodes --> elements that already
  392:     exist, a copy method allows you to duplicate elements to avoid
  393:     having to recreate them from scratch. <tt>cloneNode</tt>, which
  394:     is a method on the <tt>element</tt> object rather than the
  395:     <tt>document</tt>, returns a copy of the given node.</p>
  396: <pre>
  397: &lt;script&gt;
  398: // this is untested --pete
  399: var element = document.getElementById('my-id');
  400: var clone = element.cloneNode(false);
  401: dump(`element='+element+'\n');
  402: dump(`clone='+clone+'\n');
  403: &lt;/script&gt;
  404: </pre>
  405:     <p>The method takes a Boolean-optional parameter that specifies
  406:     whether the copy is "deep." Deep copies duplicate all
  407:     descendants of a node as well as the node itself.</p>
  408:     <h4><a name="77051"></a> getElementsByTagName</h4>
  409:     <p>Another <!--INDEX elements:arrays, returning --> 
  410:     <!--INDEX getElementsByTagName( ) method --> 
  411:     <!--INDEX DOM (Document Object Model):methods:returning element arrays -->
  412:     <!--INDEX methods:DOM:returning element arrays --> very useful
  413:     method is <tt>getElementsByTagName</tt>. This method returns an
  414:     array of elements of the specified type. The argument used is
  415:     the string <i>element type</i>. "box," for example, could be
  416:     used to obtain an array of all boxes in a document. The array
  417:     is zero-based, so the elements start at 0 and end with the last
  418:     occurrence of the element in the document. If you have three
  419:     boxes in a document and want to reference each box, you can do
  420:     it as follows:</p>
  421: <pre>
  422: &lt;box id="box-one" /&gt;
  423: &lt;box id="box-two" /&gt;
  424: &lt;box id="box-three" /&gt;
  425: &lt;script&gt;
  426: document.getElementsByTagName('box')[0];
  427: document.getElementsByTagName('box')[1];
  428: document.getElementsByTagName('box')[2];
  429: &lt;/script&gt;
  430: </pre>
  431:     <p>Or you can get the array and index into it like this:</p>
  432: <pre>
  433: var box = document.getElementsByTagName('box');
  434: </pre>
  435:     <p>box[0], the first object in the returned array, is a XUL
  436:     box.</p>
  437:     <p>To see the number of boxes on a page, you can use the
  438:     <tt>length</tt> property of an array:</p>
  439: <pre>
  440: var len = document.getElementsByTagName('box').length;
  441: dump(len+'\n');
  442: console output: 3
  443: </pre>
  444:     <p>To output the <tt>id</tt> of the box:</p>
  445: <pre>
  446: &lt;box id="box-one" /&gt;
  447: &lt;box id="box-two" /&gt;
  448: &lt;box id="box-three" /&gt;
  449: &lt;script&gt;
  450: var el      = document.getElementsByTagName('box');
  451: var tagId   = el[0].id;
  452: dump(tagId+"\n");
  453: &lt;/script&gt;
  454: console output: box-one
  455: </pre>
  456:     <p>To get to an attribute of the second box:</p>
  457: <pre>
  458: &lt;box id="box-one" /&gt;
  459: &lt;box id="box-two" foo="some attribute for the second box" /&gt;
  460: &lt;box id="box-three" /&gt;
  461: &lt;script&gt;
  462: var el        = document.getElementsByTagName('box');
  463: var att       = el[1].getAttribute('foo');
  464: dump(att      +"\n");
  465: &lt;/script&gt;
  466: console output: some attribute for the second box
  467: </pre>
  468:     <p><tt>getElementsByTagName</tt> is a handy way to obtain DOM
  469:     elements without using <tt>getElementById</tt>. Not all
  470:     elements have <tt>id</tt> attributes, so other means of getting
  471:     at the elements must be used occasionally.<a name="b262"></a><a
  472:     href="#262">[*]</a></p>
  473:     <h4><a name="77052"></a> Getting an element object and its
  474:     properties</h4>
  475:     <p>In addition <!--INDEX properties:elements:viewing --> 
  476:     <!--INDEX elements:properties:viewing --> to a basic set of
  477:     attributes, an element may have many properties. These
  478:     properties don't typically appear in the markup for the
  479:     element, so they can be harder to learn and remember. To see
  480:     the properties of an element object node, however, you can use
  481:     a JavaScript <tt>for</tt> <tt>in</tt> loop to iterate through
  482:     the list, as shown in <a href="#77012">Example 5-1</a>.</p>
  483:     <p><i>Example 5-1: <a name="77012"></a></i> <i>Printing element
  484:     properties to the console</i></p>
  485: <pre>
  486:  &lt;box id="my-id" /&gt;
  487:  &lt;script&gt;
  488:    var el = document.getElementById('my-id');
  489:    for (var list in el)
  490:      dump("property  = "+list+"\n");
  491:  &lt;/script&gt;
  492:  console output(subset):
  493:  property = id
  494:  property = className
  495:  property = style
  496:  property = boxObject
  497:  property = tagName
  498:  property = nodeName
  499:  . . .
  500: </pre>
  501:     <p>Note the implicit functionality in the <tt>el</tt> object
  502:     itself: when you iterate over the object reference, you ask for
  503:     all members of the class of which that object is an instance.
  504:     This simple example "spells" the object out to the console.
  505:     Since the DOM recognizes the window as another element (albeit
  506:     the root element) in the Document Object Model, you can use a
  507:     similar script in <a href="#77014">Example 5-2</a> to get the
  508:     properties of the window itself.</p>
  509:     <p><i>Example 5-2: <a name="77014"></a></i> <i>Printing the
  510:     window properties</i></p>
  511: <pre>
  512:  &lt;script&gt;
  513:    var el        = document.getElementById('test-win');
  514:    for(var list in el)
  515:      dump("property  = "+list+"\n");
  516:  &lt;/script&gt;
  517:  &lt;/td&gt;console output(subset):
  518:  property = nodeName
  519:  property = nodeValue
  520:  property = nodeType
  521:  property = parentNode
  522:  property = childNodes
  523:  property = firstChild
  524:  . . .
  525: </pre>
  526:     <p>The output in <a href="#77014">Example 5-2</a> is a small
  527:     subset of all the DOM properties associated with a XUL window
  528:     and the other XUL elements, but you can see all of them if you
  529:     run the example. Analyzing output like this can familiarize you
  530:     with the interfaces available from <tt>window</tt> and other
  531:     DOM objects.</p>
  532:     <h4><a name="77053"></a> Retrieving elements by property</h4>
  533:     <p>You can also use a 
  534:     <!--INDEX properties:elements:returning --> 
  535:     <!--INDEX elements:properties:returning --> DOM method to
  536:     access elements with specific properties by using
  537:     <tt>getElementsByAttribute</tt>. This method takes the name and
  538:     value of the attribute as arguments and returns an array of
  539:     nodes that contain these attribute values:</p>
  540: <pre>
  541: &lt;checkbox id="box-one" /&gt;
  542: &lt;checkbox id="box-two" checked="true"/&gt;
  543: &lt;checkbox id="box-three" checked="true"/&gt;
  544: &lt;script&gt;
  545: var chcks = document.getElementsByAttribute("checked", "true");
  546: var count = chcks.length;
  547: dump(count + " items checked \n");
  548: &lt;/script&gt;
  549: </pre>
  550:     <p>One interesting use of this method is to toggle the state of
  551:     elements in an interface, as when you get all menu items whose
  552:     <tt>disabled</tt> attribute is set to true and set them to
  553:     false. In the xFly sample, you can add this functionality with
  554:     a few simple updates. In the <i>xfly.js</i> file in the xFly
  555:     package, add the function defined in <a href="#77016">Example
  556:     5-3</a>.</p>
  557:     <p><i>Example 5-3: <a name="77016"></a></i> <i>Adding toggle
  558:     functionality to xFly</i></p>
  559: <pre>
  560:  function toggleCheck( ) {
  561:    // get the elements before you make any changes
  562:    var chex   = document.getElementsByAttribute("disabled", "true");
  563:    var unchex = document.getElementsByAttribute("disabled", "false");
  564:    for (var i=0; i&lt;chex.length; i++)
  565:        chex&lt;/td&gt;[i].setAttributte("checked", "false");
  566:    for (var i=0; i&lt;unchex.length; i++)
  567:        unchex&lt;/td&gt;[i].setAttributte("checked", "true");
  568:  }
  569: </pre>
  570:     <p>Although this example doesn't update elements whose
  571:     <tt>disabled</tt> attribute is not specified, you can call this
  572:     function from a new menu item and have it update all menus
  573:     whose checked state you do monitor, as shown in <a href=
  574:     "#77018">Example 5-4</a>.</p>
  575:     <p><i>Example 5-4: <a name="77018"></a></i> <i>Adding Toggle
  576:     menus to xFly</i></p>
  577: <pre>
  578:  &lt;menubar id="appbar"&gt;
  579:    &lt;menu label="File"&gt;
  580:       &lt;menupopup&gt;
  581:         &lt;menuitem label="New"/&gt;
  582:         &lt;menuitem label="Open"/&gt;
  583:       &lt;/menupopup&gt;
  584:    &lt;/menu&gt;
  585:    &lt;menu label="Edit"&gt;
  586:       &lt;menupopup&gt;
  587:         &lt;menuitem label="Toggle" oncommand="toggleCheck( );" /&gt;
  588:       &lt;/menupopup&gt;
  589:    &lt;/menu&gt;
  590:    &lt;menu label="Fly Types"&gt;
  591:       &lt;menupopup&gt;
  592:         &lt;menuitem label="House" disabled="true" /&gt;
  593:         &lt;menuitem label="Horse" disabled="true" /&gt;
  594:         &lt;menuitem label="Fruit" disabled="false" /&gt;
  595:       &lt;/menupopup&gt;
  596:    &lt;/menu&gt;
  597:  &lt;/menubar&gt;
  598: </pre>
  599:     <p>When you add this to the xFly application window (from <a
  600:     href="ch02.html#77034">Example 2-10</a>, for example, above the
  601:     basic <tt>vbox</tt> structure), you get an application menu bar
  602:     with a menu item, Toggle, that reverses the checked state of
  603:     the three items in the "Fly Types" menu, as seen in <a href=
  604:     "#77004">Figure 5-2</a>.</p>
  605:     <div class="c14">
  606:       <img src="foo.gif">
  607:     </div>
  608:     <p><i>Figure 5-2: <a name="77004"></a></i> <i>Toggling the
  609:     state of menu items in xFly</i></p>
  610:     <p>The following section explains more about hooking scripts up
  611:     to the interface. Needless to say, when you use a method like
  612:     <tt>getElementsByAttribute</tt> that operates on all elements
  613:     with a particular attribute value, you must be careful not to
  614:     grab elements you didn't intend (like a button elsewhere in the
  615:     application that gets disabled for other purpose).</p>
  616:     <h2><a name="77054"></a> Adding Scripts to the UI</h2>
  617:     <p>Once you are comfortable with how JavaScript works in the
  618:     context of the user interface layer and are familiar with some
  619:     of the primary DOM methods used to manipulate the various
  620:     elements and attributes, you can add your own scripts to your
  621:     application. Though you can use other techniques to get scripts
  622:     into the UI, one of the most common methods is to use Mozilla's
  623:     event model, which is described in the next few sections.</p>
  624:     <h3><a name="77055"></a> Handling Events from a XUL
  625:     Element</h3>
  626:     <p><i>Events</i> are input 
  627:     <!--INDEX scripts:adding to UI, event handling and --> 
  628:     <!--INDEX event handling:adding scripts to UI --> messages 
  629:     <!--INDEX XUL (XML-based User-interface Language):elements:event handling -->
  630:     that pass information from the user interface to the
  631:     application code. Capturing this information, or <i>event
  632:     handling</i>, is how you usually tell scripts when to start and
  633:     stop.</p>
  634:     <p>When the user clicks a XUL button, for instance, the button
  635:     "listens" for the click event, and may also handle that event.
  636:     If the button itself does not handle the event (e.g., by
  637:     supplying executable JavaScript in an event handler attribute),
  638:     then the event "bubbles," or travels further up into the
  639:     hierarchy of elements above the button. The event handlers in
  640:     <a href="#77016">Example 5-3</a> use simple inline JavaScript
  641:     to show that the given event (e.g., the window loading in the
  642:     first example, the button getting clicked in the second, and so
  643:     on) was fired and handled.</p>
  644:     <p>As in HTML, predefined event handlers are available as
  645:     attributes on a XUL element. These attributes are entry points
  646:     where you can hook in your JavaScript code, as these examples
  647:     show. Note that event handler attributes are technically a
  648:     shortcut, for which the alternative is to register event
  649:     listeners explicitly to specified elements. The value of these
  650:     on[event] event handler attributes is the inline JavaScript
  651:     that should be executed when that event is triggered. <a href=
  652:     "#77020">Example 5-5</a> shows some basic button activation
  653:     events.</p>
  654:     <p><i>Example 5-5: <a name="77020"></a></i> <i>Basic event
  655:     handler attributes</i></p>
  656: <pre>
  657:  &lt;window onload="dump('this window has loaded\n');" /&gt;
  658:  &lt;button label="onclick-test"
  659:     onclick="dump('The event handler onclick has just been used\n');" /&gt;
  660:  &lt;button label="oncommand-test"
  661:     oncommand="dump('The event handler oncommand has just been used\n');" /&gt;
  662:  &lt;menulist id="custom"
  663:     onchange="doMyCustomFunction( );" /&gt;
  664: </pre>
  665:     <p>While the window and button events in <a href=
  666:     "#77020">Example 5-5</a> carry out some inline script, there is
  667:     a variation with the <tt>onchange</tt> handler attached to the
  668:     <tt>menulist</tt> element. <tt>onchange</tt> contains a
  669:     JavaScript function call whose definition may live in the XUL
  670:     document itself or in an external file that is included by
  671:     using the <tt>src</tt> attribute on a <tt>script</tt>
  672:     element:</p>
  673: <pre>
  674: &lt;script type="application/x-javascript" src="chrome://mypackage/content/myfile.js" /&gt;
  675: </pre>
  676:     <p>A large basic set of event handler attributes is available
  677:     for use on XUL elements (and HTML elements). <a href=
  678:     "appc.html#77003">Appendix C</a> has a full listing of these
  679:     events along with explanations. The following subset 
  680:     <!--INDEX attributes:event handling:XUL elements --> shows the
  681:     potential for script interaction when the UI uses event
  682:     handlers:</p>
  683: <pre>
  684: onabort
  685: onblur
  686: onerror
  687: onfocus
  688: onchange
  689: onclick
  690: oncontextmenu
  691: ondestroy
  692: onload
  693: onpaint
  694: onkeydown
  695: onkeypress
  696: onkeyup
  697: onunload
  698: onmousemove
  699: onmouseout
  700: onmouseover
  701: onmouseup
  702: onmousedown
  703: onrest
  704: onresize
  705: onscroll
  706: onselect
  707: onsubmit
  708: </pre>
  709:     <p>Some of these event handlers work only on particular
  710:     elements, such as <tt>window</tt>, which listens for the
  711:     <tt>load</tt> event, the <tt>paint</tt> event, and other
  712:     special events.</p>
  713:     <p>To see all event handler attributes on a particular element,
  714:     you can execute the short script in <a href="#77022">Example
  715:     5-6</a>, which uses the <tt>for</tt> <tt>in</tt> loop in
  716:     JavaScript to iterate over the members of an object-in this 
  717:     <!--INDEX attributes:event handling:viewing for elements -->
  718:     case, a XUL element.</p>
  719:     <p><i>Example 5-6: <a name="77022"></a></i> <i>Getting event
  720:     handler attributes from an element</i></p>
  721: <pre>
  722:  &lt;script type="application/x-javascript"&gt;
  723:    function listElementHandlers(aObj)
  724:    {
  725:      if(!aObj)
  726:        return null;
  727:      for(var list in aObj)
  728:        if(list.match(/^on/))
  729:          dump(list+'\n');
  730:    }
  731:  &lt;/script&gt;
  732:  &lt;button label="oncommand" oncommand="listElementHandlers(this);" /&gt;
  733: </pre>
  734:     <p>The function you added in <a href="#77018">Example 5-4</a>
  735:     is also an example of event handler code in an application's
  736:     interface.</p>
  737:     <h3><a name="77056"></a> Events and the Mozilla Event
  738:     Model</h3>
  739:     <p>The event model in Mozilla is the general framework for how
  740:     events work and move around in the user interface. As you've
  741:     already seen, events tend to rise up through the DOM
  742:     hierarchy-a natural process referred to as event propagation or
  743:     event bubbling. The next two sections describe event
  744:     propagation and its complement, event capturing.</p>
  745:     <h4><a name="77057"></a> Event propagation and event
  746:     bubbling</h4>
  747:     <p>This availability of events in 
  748:     <!--INDEX event handling:bubbling --> 
  749:     <!--INDEX bubbling (event handling) --> 
  750:     <!--INDEX hierarchy:event bubbling --> 
  751:     <!--INDEX elements:event handling, bubbling --> nodes above the
  752:     element of origin is known as event propagation or event
  753:     bubbling. Event bubbling means you can handle events anywhere
  754:     above the event-raising element in the hierarchy. When events
  755:     are handled by elements that did not initiate those events, you
  756:     must determine which element below actually raised the event.
  757:     For example, if an event handler in a menu element handles an
  758:     event raised by one of the menu items, then the menu should be
  759:     able to identify the raising element and take the appropriate
  760:     action, as shown in <a href="#77024">Example 5-7</a>. In this
  761:     example, a JavaScript function determines which
  762:     <tt>menuitem</tt> was selected and responds appropriately.</p>
  763:     <p><i>Example 5-7: <a name="77024"></a></i> <i>Event
  764:     propagation</i></p>
  765: <pre>
  766:  &lt;script type="application/x-javascript"&gt;
  767:  function doCMD(el) {
  768:      v = el.getAttribute("label")
  769:      switch (v) {
  770:        case "New":
  771:          alert('New clicked');
  772:           break;
  773:        case "Open":
  774:          alert('Open clicked');
  775:          break;
  776:        case "Close":
  777:          alert('Close clicked');
  778:          break;
  779:      }
  780:  }
  781:  &lt;/script&gt;
  782:  ...
  783:  &lt;menu class="menu" label="File" oncommand="doCMD(event.target)"&gt;
  784:    &lt;menupopup&gt;
  785:      &lt;menuitem label="New" /&gt;
  786:      &lt;menuitem label="Open" /&gt;
  787:      &lt;menuitem label="Close" /&gt;
  788:    &lt;/menupopup&gt;
  789:  &lt;/menu&gt;
  790: </pre>
  791:     <p>The event handler in the parent node menu finds out which
  792:     child <tt>menuitem</tt> was actually clicked by using
  793:     <tt>event.target</tt> and takes action accordingly. Let's walk
  794:     through another possible scenario. If a user of an application
  795:     selects an item from a menu list, you could get the node of
  796:     that item by using <tt>event.target</tt>. Your script could
  797:     then abstract that item's value or other information, if
  798:     necessary.</p>
  799:     <h5><a name="77058"></a> Trapping events</h5>
  800:     <p>When an <!--INDEX event handling:trapping --> 
  801:     <!--INDEX trapping events --> event is raised, it is typically
  802:     handled by any node interested in it as it continues its way up
  803:     the DOM hierarchy. In some cases, you may want to handle an
  804:     event and then prevent it from bubbling further up, which is
  805:     where the DOM Event 
  806:     <!--INDEX DOM (Document Object Model):event handling, trapping events -->
  807:     <!--INDEX stopPropagation( ) method --> method
  808:     <tt>stopPropagation( )</tt> comes in handy.</p>
  809:     <p><a href="#77026">Example 5-8</a> demonstrates how event
  810:     bubbling can be arrested very simply. When the XUL document in
  811:     <a href="#77026">Example 5-8</a> loads, an event listener is
  812:     registered with a <tt>row</tt> in the tree. The event listener
  813:     handles the event by executing the function <tt>stopEvent(
  814:     )</tt>. This function calls an event object method,
  815:     <tt>stopPropagation</tt>, which keeps the event from bubbling
  816:     further up into the DOM. Note that the tree itself has an
  817:     <tt>onclick</tt> event handler that should display a message
  818:     when clicked. However, the <!--INDEX stopEvent( ) method -->
  819:     <tt>stopEvent( )</tt> method has stopped propagation, so after
  820:     the data in the table is updated, the event phase is
  821:     effectively ended. In this case, the function was used to trap
  822:     the event and handle it only there.</p>
  823:     <p><i>Example 5-8: <a name="77026"></a></i> <i>stopPropagation(
  824:     ) event function</i></p>
  825: <pre>
  826:  &lt;?xml version="1.0"?&gt;
  827:  &lt;!DOCTYPE  window&gt;
  828:  &lt;window id="test-win"
  829:    xmlns="<a href=
  830: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul</a>"
  831:    orient="vertical"
  832:    onload="load( );"&gt;
  833:  &lt;script type="application/x-javascript"&gt;
  834:    function load( ) {
  835:      el = document.getElementById("t");
  836:      el.addEventListener("click", stopEvent, false);
  837:    }
  838:    &lt;/td&gt;function stopEvent(e) {
  839:      // this ought to keep t-daddy from getting the click.
  840:      e.stopPropagation( );
  841:    }
  842:  &lt;/script&gt;
  843:  &lt;tree&gt;
  844:    &lt;!-- tree columns definition omitted --&gt;
  845:    &lt;treechildren flex="1" &gt;
  846:      &lt;treeitem id="t-daddy"
  847:        &lt;/td&gt;onclick="alert('t-daddy');"  // this event is never fired
  848:        container="true" parent="true"&gt;
  849:        &lt;treerow id="t"&gt;
  850:          &lt;treecell label="O'Reilly" id="t1" /&gt;
  851:          &lt;treecell label="<a href=
  852: "http://www.oreilly.com">http://www.oreilly.com</a>" id="t2" /&gt;
  853:        &lt;/treerow&gt;
  854:       &lt;/treeitem&gt;
  855:    &lt;/treechildren&gt;
  856:  &lt;/tree&gt;
  857:  &lt;/window&gt;
  858: </pre>
  859:     <h4><a name="77059"></a> Capturing events</h4>
  860:     <p>Event <!--INDEX event handling:capturing events --> 
  861:     <!--INDEX capturing events --> capturing is the complement of
  862:     event bubbling. The DOM provides the <tt>addEventListener</tt>
  863:     method for creating event listeners on nodes that do not
  864:     otherwise supply them. When you register an event listener on
  865:     an ancestor of the event target (i.e., any node above the
  866:     event-raising element in the node hierarchy), you can use event
  867:     capturing to handle the event in the ancestor before it is
  868:     heard in the target itself or any intervening nodes.</p>
  869:     <p>To take advantage of event capturing (or event bubbling with
  870:     elements that do not already have event listeners), you must
  871:     add an event listener to the element that wants to capture
  872:     events occurring below it. Any XUL element may use the DOM
  873:     <tt>addEventListener</tt> 
  874:     <!--INDEX addEventListener( ) method --> 
  875:     <!--INDEX registering:elements, event capturing --> 
  876:     <!--INDEX elements:registering, event capturing --> 
  877:     <!--INDEX listeners, event capturing --> method to register
  878:     itself to capture events. The syntax for using this method in
  879:     XUL is shown here:</p>
  880: <pre>
  881: XULelement = document.getElementById("id of XULelement");
  882: XULelement.addEventListener("event name", "event handler code",
  883: useCapture bool);
  884: </pre>
  885:     <p>The event handler code argument can be inline code or the
  886:     name of a function. The <tt><!--INDEX useCapture parameter -->
  887:     useCapture</tt> parameter specifies whether the event listener
  888:     wants to use event capturing or be registered to listen for
  889:     events that bubble up the hierarchy normally. In <a href=
  890:     "#77006">Figure 5-3</a>, the alert dialog invoked by the
  891:     <tt>menuitem</tt> itself is not displayed, since the root
  892:     <tt>window</tt> element used event capture to handle the event
  893:     itself.</p>
  894:     <div class="c14">
  895:       <img src="foo.gif">
  896:     </div>
  897:     <p><i>Figure 5-3: <a name="77006"></a></i> <i>Event
  898:     capturing</i></p>
  899:     <p>An <tt><!--INDEX onload event handler --> onload</tt> 
  900:     <!--INDEX event handling:onload handler --> event handler for a
  901:     XUL window can also register a <tt>box</tt> element to capture
  902:     all click events that are raised from its child elements:</p>
  903: <pre>
  904: var bbox = document.getElementById("bigbox");
  905: if (bbox) {
  906: bbox.addEventListener("click", "alert('captured')", true);
  907: }
  908: ...
  909: &lt;box id="bigbox"&gt;
  910: &lt;menu label="File"&gt;
  911: &lt;menupopup&gt;
  912: &lt;menuitem label="New" onclick="alert('not captured')" /&gt;
  913: ...
  914: &lt;menupopup&gt;
  915: &lt;/menu&gt;
  916: &lt;/box&gt;
  917: </pre>
  918:     <h3><a name="77060"></a> Changing an Element's CSS Style Using
  919:     JavaScript</h3>
  920:     <p>Much of 
  921:     <!--INDEX CSS (Cascading Style Sheets):styles, changing with JavaScript -->
  922:     <!--INDEX styles:CSS, changing with JavaScript --> 
  923:     <!--INDEX JavaScript:Cascading Style Sheets and:changing styles -->
  924:     <!--INDEX scripting:CSS styles, changing --> what makes the
  925:     Mozilla UI both flexible and programmable is its ability to
  926:     dynamically alter the CSS style rules for elements at runtime.
  927:     For example, if you have a button, you can toggle its
  928:     visibility by using a simple combination of JavaScript and CSS.
  929:     Given a basic set of buttons like this:</p>
  930: <pre>
  931: &lt;button id="somebutton" class="testButton" label="foo" /&gt;
  932: &lt;spacer flex="1" /&gt;
  933: &lt;button id="ctlbutton"
  934: class="testButton"
  935: label="make disappear"
  936: oncommand="disappear( );" /&gt;
  937: </pre>
  938:     <p>as well as a stylesheet import statement at the top of the
  939:     XUL like this:</p>
  940: <pre>
  941: &lt;?xml-stylesheet href="test.css" type="text/css"?&gt;
  942: </pre>
  943:     <p>and a simple CSS file in your <i>chrome/xfly/content</i>
  944:     directory called <i>test.css</i> that contains the following
  945:     style rule:</p>
  946: <pre>
  947: #somebutton[hidden="true"]{ display: none; }
  948: .testButton{
  949: border            : 1px outset #cccccc;
  950: background-color  : #cccccc;
  951: padding           : 4px;
  952: margin            : 50px;
  953: }
  954: </pre>
  955:     <p>You can call <tt>
  956:     <!--INDEX setAttribute( ) method:calling at runtime -->
  957:     setAttribute</tt> in your script to hide the button at
  958:     runtime.</p>
  959: <pre>
  960: &lt;script&gt;
  961: function disappear( ){
  962: return document.getElementById('somebutton').setAttribute('hidden', true);
  963: }
  964: &lt;/script&gt;
  965: </pre>
  966:     <p>The previous code snippet makes a visible button disappear
  967:     by setting its <tt>hidden</tt> attribute to true. Adding a few
  968:     more lines, you can toggle the visibility of the button, also
  969:     making it appear if it is hidden:</p>
  970: <pre>
  971: &lt;script&gt;
  972: function disappear( ){
  973: const defaultLabel  = "make disappear";
  974: const newLabel      = "make reappear";
  975: var button          = document.getElementById('somebutton');
  976: var ctlButton       = document.getElementById('ctlbutton');
  977: if(!button.getAttribute('hidden')) {
  978: button.setAttribute('hidden', true);
  979: ctlButton.setAttribute('label', newLabel);
  980: } else  {
  981: button.removeAttribute('hidden');
  982: ctlButton.setAttribute('label', defaultLabel);
  983: }
  984: return;
  985: }
  986: &lt;/script&gt;
  987: </pre>
  988:     <p>Another useful application of this functionality is to
  989:     collapse elements such as toolbars, boxes, and iframes in your
  990:     application.</p>
  991:     <p>The <tt>setAttribute</tt> method can also be used to update
  992:     the element's <tt>class</tt> attribute with which style rules
  993:     are so often associated. <tt>toolbarbutton-1</tt> and
  994:     <tt>button-toolbar</tt> are two different classes of button.
  995:     You can change a button from a <tt>toolbarbutton-1-</tt>the
  996:     large button used in the browser-to a standard toolbar button
  997:     using the following DOM code:</p>
  998: <pre>
  999: // get the Back button in the browser
 1000: b1 = document.getElementById("back-button");\
 1001: b1.setAttribute("class", "button-toolbar");
 1002: </pre>
 1003:     <p>This dynamically demotes the Back button to an ordinary
 1004:     toolbar button. Code such as this assumes, of course, that you
 1005:     know the classes that are used to style the various widgets in
 1006:     the interface.</p>
 1007:     <p>You can also set the <tt>style</tt> attribute directly using
 1008:     the DOM:</p>
 1009: <pre>
 1010: el = document.getElementById("some-element");
 1011: el.setAttribute("style", "background-color:darkblue;");
 1012: </pre>
 1013:     <p>Be aware, however, that when you set the <tt>style</tt>
 1014:     attribute in this way, you are overwriting whatever style
 1015:     properties may already have been defined in the <tt>style</tt>
 1016:     attribute. If the document referenced in the snippet above by
 1017:     the ID <tt>some-element</tt> has a <tt>style</tt> attribute in
 1018:     which the font size is set to 18pc, for example, that
 1019:     information is erased when the style attribute is manipulated
 1020:     in this way.</p>
 1021:     <h3><a name="77061"></a> Creating Elements Dynamically</h3>
 1022:     <p>Using the <!--INDEX elements:creating dynamically --> 
 1023:     <!--INDEX XUL (XML-based User-interface Language):elements:creating dynamically -->
 1024:     <tt>createElement</tt> method in XUL lets you accomplish things
 1025:     similar to <tt>document.write</tt> in HTML, with which you can
 1026:     create new pages and parts of a web page. In <a href=
 1027:     "#77028">Example 5-9</a>, <tt>
 1028:     <!--INDEX createElement( ) method:dynamic element creation -->
 1029:     createElement</tt> is used to generate a menu dynamically.</p>
 1030:     <p><i>Example 5-9: <a name="77028"></a></i> <i>Dynamic menu
 1031:     generation</i></p>
 1032: <pre>
 1033:  &lt;?xml version="1.0"?&gt;
 1034:  &lt;?xml-stylesheet href="test.css" type="text/css"?&gt;
 1035:  &lt;!DOCTYPE  window&gt;
 1036:  &lt;window id="test-win"
 1037:          xmlns="<a href=
 1038: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul</a>"
 1039:          title="test"
 1040:          style="
 1041:          min-width : 200px;
 1042:          min-height: 200px;"&gt;
 1043:  &lt;script&gt;
 1044:  &lt;!&lt;/td&gt;[CDATA[
 1045:  function generate( ){
 1046:    var d           = document;
 1047:    var popup       = d.getElementById('menupopup');
 1048:    var menuitems   = new Array('menuitem_1',
 1049:                       'menuitem_2', 'menuitem_3',
 1050:                       'menuitem_4', 'menuitem_5');
 1051:    var l           = menuitems.length;
 1052:    var newElement;
 1053:    for(var i=0; i&lt;l; i++)
 1054:    {
 1055:      newElement = d.createElement('menuitem');
 1056:      newElement.setAttribute('id', menuitems&lt;/td&gt;[i]);
 1057:      newElement.setAttribute('label', menuitems&lt;/td&gt;[i]);
 1058:      popup.appendChild(newElement);
 1059:    }
 1060:    return true;
 1061:  }
 1062:  ]]&gt;
 1063:  &lt;/script&gt;
 1064:  &lt;menu label="a menu"&gt;
 1065:    &lt;menupopup id="menupopup"&gt;
 1066:    &lt;/menupopup&gt;
 1067:  &lt;/menu&gt;
 1068:  &lt;spacer flex="1" /&gt;
 1069:  &lt;button id="ctlbutton" class="testButton" label="generate" oncommand="generate( );" /&gt;
 1070:  &lt;/window&gt;
 1071: </pre>
 1072:     <p>The JavaScript function <tt>
 1073:     <!--INDEX generate( ) function --> generate( )</tt> in <a href=
 1074:     "#77028">Example 5-9</a> gets the <tt>menupopup</tt> as the
 1075:     parent element for the new elements, creates five
 1076:     <tt>menuitems</tt> in an array called <tt>menuitems</tt>, and
 1077:     stores five string ID names for those <tt>menuitems</tt>.</p>
 1078:     <p>The variable <i>l</i> is the length of the array. The
 1079:     variable <tt>newElement</tt> is a placeholder for elements
 1080:     created by using the <tt>createElement</tt> method inside of
 1081:     the <tt>for</tt> loop. <tt>generate( )</tt> assigns
 1082:     <tt>newElement</tt> on each iteration of the loop and creates a
 1083:     new <tt>menuitem</tt> each time, providing a way to dynamically
 1084:     generate a list of menu choices based on input data or user
 1085:     feedback. Try this example and experiment with different
 1086:     sources of data, such as a menu of different auto
 1087:     manufacturers, different styles on group of boxes that come
 1088:     from user selection, or tabular data in a tree.</p>
 1089:     <h3><a name="77062"></a> Sharing Data Between Documents</h3>
 1090:     <p>As the scale of your application development increases and
 1091:     your applications grow new windows and components, you may
 1092:     become interested in passing data around and ensuring that the
 1093:     data remains in scope. Misunderstanding that scope often leads
 1094:     to problems when beginning Mozilla applications.</p>
 1095:     <h4><a name="77063"></a> Scope in Mozilla</h4>
 1096:     <p>The general <!--INDEX documents:sharing data:scope --> 
 1097:     <!--INDEX sharing data, scope --> 
 1098:     <!--INDEX scope, sharing data between documents --> rule is
 1099:     that all scripts pulled in by the base XUL document and scripts
 1100:     included in overlays of this document are in the same scope.
 1101:     Therefore, any global variables you declare in any of these
 1102:     scripts can be used by any other scripts in the same scope. The
 1103:     decision to put a class structure or more sophisticated design
 1104:     in place is up to you.</p>
 1105:     <p>The relationship of a parent and child window indicates the
 1106:     importance of storing data in language constructs that can be
 1107:     passed around. This code shows a common way for a parent to
 1108:     pass data to a window it spawns:</p>
 1109: <pre>
 1110: var obj = new Object ( );
 1111: obj.res = "";
 1112: window.openDialog("chrome://xfly/content/foo.xul", 'foo_main',
 1113: "chrome,resizable,scrollbars,dialog=yes,close,modal=yes",
 1114: obj);
 1115: </pre>
 1116:     <h4><a name="77064"></a> Using the window.arguments array</h4>
 1117:     <p>The previous <!--INDEX window.arguments array --> 
 1118:     <!--INDEX arrays, sharing data between documents;window.arguments array -->
 1119:     <!--INDEX documents:sharing data:window.arguments array -->
 1120:     code snippet creates a new JavaScript object, <tt>obj</tt>, and
 1121:     assigns the value of an empty string to that object's
 1122:     <tt>res</tt> property. The object is then passed by reference
 1123:     to the new window as the last parameter of the <tt>openDialog(
 1124:     )</tt> method so it can be manipulated in the scope of the
 1125:     child window:</p>
 1126: <pre>
 1127: function onOk( ) {
 1128: window.arguments[0].res  = "ok";
 1129: return;
 1130: }
 1131: function onCancel( ) {
 1132: window.arguments[0].res  = "cancel";
 1133: return;
 1134: }
 1135: </pre>
 1136:     <p>In that child window, the object is available as an indexed
 1137:     item in the special <tt>window.arguments</tt> array. This array
 1138:     holds a list of the arguments passed to a window when it is
 1139:     created. window.arguments[0] is a reference to the first
 1140:     argument in the <tt>openDialog( )</tt> parameter list that is
 1141:     not a part of the input parameters for that method,
 1142:     window.arguments[1] is the second argument, and so on. Using
 1143:     <tt>window.arguments</tt> is the most common way to pass
 1144:     objects and other data around between documents.</p>
 1145:     <p>When the user clicks a button in the displayed dialog (i.e.,
 1146:     the OK or Cancel button), one of the functions sets a value to
 1147:     the <tt>res</tt> property of the passed-in object. The object
 1148:     is in the scope of the newly created window. When control is
 1149:     passed back to the script that launched the window, the return
 1150:     value can be checked:</p>
 1151: <pre>
 1152: if (obj.res != "ok") {
 1153: dump("User has cancelled the dialog");
 1154: return;
 1155: }
 1156: </pre>
 1157:     <p>In this case, a simple dump statement prints the result, but
 1158:     you can also test the result in your application code and fork
 1159:     accordingly.</p>
 1160:     <h2><a name="77065"></a> XPConnect and Scriptable
 1161:     Components</h2>
 1162:     <p>At the second level of scripting, XPConnect binds JavaScript
 1163:     and the user interface to the application core. Here,
 1164:     JavaScript can access all XPCOM components that implement
 1165:     scriptable libraries and services through a special global
 1166:     object whose methods and properties can be used in JavaScript.
 1167:     Consider these JavaScript snippets from the Mozilla source
 1168:     code:</p>
 1169: <pre>
 1170: // add filters to the file picker
 1171: fp.appendFilters( nsIFilePicker.HTML );
 1172: // display a directory in the file picker
 1173: fp.displayDirectory ( dir );
 1174: // read a line from an open file
 1175: file.readLine(tmpBuf, 1024, didTruncate);
 1176: // create a new directory
 1177: this.fileInst.create( DIRECTORY, parseInt(permissions) );
 1178: retval=OK;
 1179: </pre>
 1180:     <p>The <tt>filepicker</tt>, <tt>file</tt>, and
 1181:     <tt>localfile</tt> components that these JavaScript objects
 1182:     represent are a tiny fraction of the components available via
 1183:     XPConnect to programmers in Mozilla. This section describes how
 1184:     to find these components, create the corresponding JavaScript
 1185:     objects, and use them in your application programming.</p>
 1186:     <h3><a name="77066"></a> What Is XPConnect?</h3>
 1187:     <p>Until now, <!--INDEX XPConnect:overview --> 
 1188:     <!--INDEX scripting:XPConnect, overview --> scripting has
 1189:     referred to scripting the DOM, manipulating various elements in
 1190:     the interface, and using methods available in Mozilla
 1191:     JavaScript files. However, for real applications like the
 1192:     Mozilla browser itself, this may be only the beginning. The UI
 1193:     must be hooked up to the application code and services (i.e.,
 1194:     the application's actual functionality) to be more than just a
 1195:     visual interface. This is where XPConnect and XPCOM come
 1196:     in.</p>
 1197:     <p>Browsing the Web, reading email, and parsing XML files are
 1198:     examples of application-level services in Mozilla. They are
 1199:     part of Mozilla's lower-level functionality. This functionality
 1200:     is usually written and compiled in platform-native code and
 1201:     typically written in C++. This functionality is also most often
 1202:     organized into modules, which take advantage of Mozilla's
 1203:     cross-platform component object model (XPCOM), and are known as
 1204:     <i><!--INDEX XPCOM components --> XPCOM components</i>. The
 1205:     relationship of these components and the application services
 1206:     they provide to the interface is shown in <a href=
 1207:     "#77008">Figure 5-4</a>.</p>
 1208:     <div class="c14">
 1209:       <img src="foo.gif">
 1210:     </div>
 1211:     <p><i>Figure 5-4: <a name="77008"></a></i> <i>How XPConnect
 1212:     fits into the application model</i></p>
 1213:     <p>In Mozilla, XPConnect is the bridge between JavaScript and
 1214:     XPCOM components. The XPConnect technology wraps natively
 1215:     compiled components with JavaScript objects. XPCOM, Mozilla's
 1216:     own cross-platform component technology, is the framework on
 1217:     top of which these scriptable components are built. Using
 1218:     JavaScript and XPConnect, you can create instances of these
 1219:     components and use their methods and properties as you do any
 1220:     regular JavaScript object, as described here. You can access
 1221:     any or all of the functionality in Mozilla in this way.</p>
 1222:     <p><a href="ch08.html#77048">Chapter 8</a> describes more about
 1223:     the XPConnect technology and how it connects components to the
 1224:     interface. It also describes the components themselves and
 1225:     their interfaces, the XPCOM technology, and how you can create
 1226:     your own XPCOM components.</p>
 1227:     <h4><a name="77067"></a> Creating XPCOM objects in script</h4>
 1228:     <p><a href="#77030">Example 5-10</a> demonstrates 
 1229:     <!--INDEX XPCOM:methods:JavaScript implementation --> 
 1230:     <!--INDEX JavaScript:XPCOM objects, 
 1231:     creating --> the creation and use of an 
 1232:     <!--INDEX XPCOM:components --> XPCOM component in JavaScript.
 1233:     In this example, the script instantiates the
 1234:     <tt>filepicker</tt> object and then uses it to display a file
 1235:     picker dialog with all of the file filters selected. To run
 1236:     this example, add the function to your <i>xfly.js</i> file and
 1237:     call it from an event handler on the "New" menu item you added
 1238:     in <a href="ch03.html#77042">Example 3-5</a>. Example 5-10<a
 1239:     name="77030"></a> <i>Scriptable component example</i></p>
 1240: <pre>
 1241:  // chooseApp:  Open file picker and prompt user for application.
 1242:  chooseApp: function( ) {
 1243:    var nsIFilePicker = Components.interfaces.nsIFilePicker;
 1244:    var fp =
 1245:       Components.classes&lt;/td&gt;["@mozilla.org/filepicker;1"].
 1246:           createInstance( nsIFilePicker );
 1247:    fp.init( this.mDialog,
 1248:       this.getString( "chooseAppFilePickerTitle" ),
 1249:       nsIFilePicker.modeOpen );
 1250:    fp.appendFilters( nsIFilePicker.filterAll );
 1251:    if ( fp.show( ) == nsIFilePicker.returnOK &amp;&amp; fp.file ) {
 1252:    this.choseApp   = true;
 1253:    this.chosenApp  = fp.file;
 1254:    // Update dialog.
 1255:    this.updateApplicationName(this.chosenApp.unicodePath);
 1256:  }
 1257: </pre>
 1258:     <p>Note the first two lines in the function and the way they
 1259:     work together to create the <tt>fp</tt> <tt>filepicker</tt>
 1260:     object. The first line in the function assigns the name of the
 1261:     <i>nsFile-picker</i> interface to the <tt>nsIFilePicker</tt>
 1262:     variable in JavaScript. This variable is used in the second
 1263:     line, where the instance is created from the component to
 1264:     specify which interface on that component should be used.
 1265:     Discovering and using library interfaces is an important aspect
 1266:     of XPCOM, where components always implement at least two
 1267:     interfaces.</p>
 1268:     <p>In <a href="#77032">Example 5-11</a>, an HTML file (stored
 1269:     locally, since it wouldn't have the required XPConnect access
 1270:     as a remote file because of security boundaries) loaded in
 1271:     Mozilla instantiates a Mozilla sound component and plays a
 1272:     sound with it. Go ahead and try it. Example 5-11<a name=
 1273:     "77032"></a> <i>Scripting components from HTML</i></p>
 1274: <pre>
 1275:  &lt;html&gt;
 1276:  &lt;head&gt;
 1277:  &lt;title&gt;Sound Service Play Example&lt;/title&gt;
 1278:  &lt;/head&gt;
 1279:  &lt;body&gt;
 1280:  &lt;script&gt;
 1281:    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 1282:    var url = Components.classes&lt;/td&gt;["@mozilla.org/network/standard-
 1283:        url;1"].createInstance( );
 1284:    url = url.QueryInterface(Components.interfaces.nsIURL);
 1285:    url.spec = "resource:/res/samples/test.wav";
 1286:    var sample = Components.classes&lt;/td&gt;["@mozilla.org/sound;1"].createInstance( );
 1287:    sample = sample.QueryInterface(Components.interfaces.nsISound);
 1288:  &lt;/script&gt;
 1289:  &lt;form name="form"&gt;
 1290:    &lt;input type="button" value="Play Sound" onclick="sample.play(url);"&gt;
 1291:  &lt;form&gt;
 1292:  &lt;/body&gt;
 1293:  &lt;/html&gt;
 1294: </pre>
 1295:     <p>As in <a href="#77030">Example 5-10</a>, the classes[ ]
 1296:     array on the special Mozilla <tt>Components</tt> object refers
 1297:     to a particular component-in this case, the <tt>sound</tt>
 1298:     component-by contract ID. All XPCOM objects must have a
 1299:     contract ID that uniquely identifies them with the domain, the
 1300:     component name, and a version number ["@mozilla.org/sound;1"],
 1301:     respectively. See the <a href="ch08.html#77058">"XPCOM
 1302:     Identifiers</a>" section in <a href="ch08.html#77048">Chapter
 1303:     8</a> for more information about this.</p>
 1304:     <h4><a name="77068"></a> Finding components and interfaces</h4>
 1305:     <p>Most <!--INDEX XPCOM:components:locating and viewing -->
 1306:     components are scripted in Mozilla. In fact, the challenge is
 1307:     not to find cases when this scripting occurs (which you can
 1308:     learn by searching LXR for the <tt>Components</tt>), but to
 1309:     find Mozilla components that don't use scriptable components.
 1310:     Finding components and interfaces in Mozilla and seeing how
 1311:     they are used can be useful when writing your own
 1312:     application.</p>
 1313:     <p>The Mozilla <!--INDEX Component Viewer development tool -->
 1314:     Component Viewer is a great tool for discovering components and
 1315:     provides a convenient UI for seeing components and looking at
 1316:     their interfaces from within Mozilla. The Component Viewer can
 1317:     be built as an extension to Mozilla (see "cview" in the
 1318:     extensions directory of the Mozilla source), or it can be
 1319:     downloaded and installed as a separate 
 1320:     <!--INDEX web sites:Component Viewer --> XPI from <i><a href=
 1321:     "http://www.hacksrus.com/~ginda/cview/">http://www.hacksrus.com/~ginda/cview/</a></i>.
 1322:     <a href="appb.html#77021">Appendix B</a> describes the
 1323:     Component Viewer in more detail.</p>
 1324:     <p>Commonly used XPCOM objects in the browser and other Mozilla
 1325:     applications include file objects, RDF services, URL objects,
 1326:     and category managers.</p>
 1327:     <h4><a name="77069"></a> Selecting the appropriate interface
 1328:     from the component</h4>
 1329:     <p>In all cases, 
 1330:     <!--INDEX XPCOM:components:interfaces, selecting --> 
 1331:     <!--INDEX interfaces:XPCOM:components, selecting --> the way to
 1332:     get the object into script is to instantiate it with the
 1333:     special <tt>classes</tt> object and use the <tt>createInstance(
 1334:     )</tt> method on the class to select the interface you want to
 1335:     use. These two steps are often done together, as in the
 1336:     following example, which gets the component with the contract
 1337:     ID <tt>ldap-connection;1</tt>, instantiates an object from the
 1338:     <i>nsILDAPConnection</i> interface, and then calls a method on
 1339:     that object:</p>
 1340: <pre>
 1341: var connection = Components.classes
 1342: ["@mozilla.org/network/ldap-connection;1"].
 1343: createInstance(Components.interfaces.nsILDAPConnection);
 1344: connection.init(queryURL.host, queryURL.port, null,
 1345: generateGetTargetsBoundCallback( ));
 1346: </pre>
 1347:     <p>These two common processes-getting a component and selecting
 1348:     one of its interfaces to assign to an object-can also be
 1349:     separated into two different statements:</p>
 1350: <pre>
 1351: // get the ldap connection component
 1352: var connection = Components.classes
 1353: ["@mozilla.org/network/ldap-connection;1"];
 1354: // create an object from the nsILDAPConnection interface;
 1355: connection.createInstance(Components.interfaces.nsILDAPConnection);
 1356: // call the init( ) method on that object
 1357: connection.init(queryURL.host, queryURL.port, null,
 1358: generateGetTargetsBoundCallback( ));
 1359: </pre>
 1360:     <p>Mozilla constantly uses these processes. Wherever
 1361:     functionality is organized into XPCOM objects (and most of it
 1362:     is), these two statements bring that functionality into
 1363:     JavaScript as high-level and user-friendly JavaScript
 1364:     objects.</p>
 1365:     <h2><a name="77070"></a> JavaScript Application Code</h2>
 1366:     <p>There are 
 1367:     <!--INDEX JavaScript:application programming:overview --> 
 1368:     <!--INDEX application programming, JavaScript:overview --> two
 1369:     ways to use JavaScript in the third, deepest level of
 1370:     application programming. The first is to organize your
 1371:     JavaScript into libraries so your functions can be reused,
 1372:     distributed, and perhaps collaborated upon.</p>
 1373:     <p>The second way is to write a JavaScript component, create a
 1374:     separate interface for that component, and compile it as an
 1375:     XPCOM component whose methods and data can be accessed from
 1376:     XPConnect (using JavaScript). This kind of application
 1377:     programming is described in <a href="ch08.html#77048">Chapter
 1378:     8</a>, which includes examples of creating new interfaces,
 1379:     implementing them in JavaScript or C++, and compiling, testing,
 1380:     and using the resulting component in the Mozilla interface.</p>
 1381:     <p>This section introduces the library organization method of
 1382:     JavaScript application programming. The JSLib code discussed
 1383:     here is a group of JavaScript libraries currently being
 1384:     developed by Mozilla contributors and is especially useful for
 1385:     working with the XPFE and other aspects of the Mozilla
 1386:     application/package programming model. When you include the
 1387:     right source files at the top of your JavaScript and/or XUL
 1388:     file, you can use the functions defined in JSLib libraries as
 1389:     you would use any third-party library or built-in functions.
 1390:     You may even want to contribute to the JSLib project yourself
 1391:     if you think functionality is missing and as your Mozilla
 1392:     programming skills grow.</p>
 1393:     <h3><a name="77071"></a> JavaScript Libraries</h3>
 1394:     <p>The open <!--INDEX JSLib libraries --> 
 1395:     <!--INDEX JavaScript:application programming:JSLib --> 
 1396:     <!--INDEX application programming, JavaScript:JSLib --> source
 1397:     JSLib project makes life easier for developers. The JSLib
 1398:     package implements some of the key XPCOM components just
 1399:     discussed and wraps them in simpler, JavaScript interfaces,
 1400:     which means that you can use the services of common XPCOM
 1401:     components without having to do any of the instantiation,
 1402:     interface selection, or glue code yourself. Collectively, these
 1403:     interfaces are intended to provide a general-purpose library
 1404:     for Mozilla application developers. To understand what JSLib
 1405:     does, consider the following short snippet from the JSLib
 1406:     source file <i>jslib/io/file.js</i>, which implements a
 1407:     <tt>close( )</tt> function for open file objects and provides a
 1408:     handy way to clean up things when you finish editing a file in
 1409:     the filesystem.</p>
 1410: <pre>
 1411: /********************* CLOSE ********************************
 1412: * void close( )                                              *
 1413: *                                                           *
 1414: * void file close                                           *
 1415: * return type void(null)                                    *
 1416: * takes no arguments closes an open file stream and         *
 1417: * deletes member var instances of objects                   *
 1418: *   Ex:                                                     *
 1419: *     var p='/tmp/foo.dat';                                 *
 1420: *     var f=new File(p);                                    *
 1421: *     fopen( );                                              *
 1422: *     f.close( );                                            *
 1423: *                                                           *
 1424: *   outputs: void(null)                                     *
 1425: ************************************************************/
 1426: File.prototype.close = function( )
 1427: {
 1428: /***************** Destroy Instances *********************/
 1429: if(this.mFileChannel)   delete this.mFileChannel;
 1430: if(this.mInputStream)   delete this.mInputStream;
 1431: if(this.mTransport)     delete this.mTransport;
 1432: if(this.mMode)          this.mMode=null;
 1433: if(this.mOutStream) {
 1434: this.mOutStream.close( );
 1435: delete this.mOutStream;
 1436: }
 1437: if(this.mLineBuffer)     this.mLineBuffer=null;
 1438: this.mPosition           = 0;
 1439: /***************** Destroy Instances *********************/
 1440: return;
 1441: }
 1442: </pre>
 1443:     <p>To use the <tt>close</tt> method as it's defined here,
 1444:     import the <i>file.js</i> source file into your JavaScript,
 1445:     create a file object (as shown in the examples below), and call
 1446:     its <tt>close( )</tt> method.</p>
 1447:     <blockquote>
 1448:       <hr>
 1449:       <b>xpcshell</b> 
 1450:       <p>Most <!--INDEX xpcshell --> <!--INDEX shells:xpcshell --> 
 1451:       <!--INDEX JavaScript:xpcshell --> examples in this section
 1452:       are in <i>xpcshell</i>, but using these libraries in your
 1453:       user interface JavaScript is just as easy. You can access
 1454:       these libraries from a XUL file, as the section <a href=
 1455:       "#77077">"Using the DirUtils class</a>," later in this
 1456:       chapter, demonstrates.</p>
 1457:       <p>xpcshell is the command-line interpreter to JavaScript and
 1458:       XPConnect. This shell that uses XPConnect to call and
 1459:       instantiate scriptable XPCOM interfaces. It is used primarily
 1460:       for debugging and testing scripts.</p>
 1461:       <p>To run xpcshell, you need to go to the Mozilla <i>bin</i>
 1462:       directory or have that folder in your PATH. For each
 1463:       platform, enter:</p>
 1464:       <p>Windows:</p>
 1465: <pre>
 1466: xpcshell.exe
 1467: </pre>
 1468:       Unix: 
 1469: <pre>
 1470: ./run-mozilla.sh ./xpcshell
 1471: </pre>
 1472:       To run xpcshell on Unix, you need to supply environment
 1473:       variables that the interpreter needs. You can use the
 1474:       <i>run-mozilla.sh</i> shell script that resides in the
 1475:       Mozilla <i>bin</i> directory. 
 1476: <pre>
 1477: $ ./run-mozilla.sh ./xpcshell
 1478: </pre>
 1479:       To see the available options for xpcshell, type this:
 1480:       <hr>
 1481:     </blockquote>
 1482:     $ ./run-mozilla.sh ./xpcshell --help JavaScript-C 1.5
 1483:     pre-release 4a 2002-03-21 usage: xpcshell [-s] [-w] [-W] [-v
 1484:     version] [-f scriptfile] [scriptfile] [scriptarg...]  The two
 1485:     most important parameters here are -w, which enables warnings
 1486:     output, and -s, which turns on strict mode.
 1487:     <hr>
 1488:     <p>The source files for JSLib are well annotated and easy to
 1489:     read. JSLib provide easy-to-use interfaces for creating
 1490:     instances of components (e.g., File objects), performing
 1491:     necessary error checking, and ensuring proper usage. To use a
 1492:     function like the one just shown, simply include the source
 1493:     file you need in your XUL:</p>
 1494: <pre>
 1495: &lt;script type="application/x-JavaScript"
 1496: src="chrome://jslib/content/jslib.js" /&gt;
 1497: </pre>
 1498:     <p>Then you can include the specific library files you need in
 1499:     your JavaScript code by using the include method:</p>
 1500: <pre>
 1501: include("chrome://jslib/content/io/file.js");
 1502: include("chrome://jslib/content/zip/zip.js");
 1503: </pre>
 1504:     <h4><a name="77072"></a> Installing JSLib</h4>
 1505:     <p>To use the <!--INDEX installation:JSLib --> 
 1506:     <!--INDEX JSLib libraries:installing --> 
 1507:     <!--INDEX JavaScript:application programming:installing 
 1508:     JSLib --> 
 1509:     <!--INDEX application programming, JavaScript:installing JSLib -->
 1510:     JavaScript libraries, install the JSLib package in Mozilla. The
 1511:     package is available as a tarball, a zip file, or as CVS
 1512:     sources. The easiest way to obtain it is to install it from the
 1513:     Web using Mozilla's XPInstall technology, described in <a href=
 1514:     "ch06.html#77063">Chapter 6</a>.</p>
 1515:     <p>Using your Mozilla browser, go to <i><a href=
 1516:     "http://jslib.mozdev.org/installation.html">http://jslib.mozdev.org/installation.html</a></i>
 1517:     and click the installation hyperlink. The link uses XPInstall
 1518:     to install JSLIB and make it available to you in Mozilla. To
 1519:     test whether it is installed properly, type the following code
 1520:     in your shell:</p>
 1521: <pre>
 1522: ./mozilla -chrome chrome://jslib/content/
 1523: </pre>
 1524:     <p>You should see a simple window that says "welcome to
 1525:     jslib."</p>
 1526:     <h4><a name="77073"></a> The JSLib libraries</h4>
 1527:     <p>Currently <!--INDEX classes:JSLib --> 
 1528:     <!--INDEX JSLib libraries:classes --> 
 1529:     <!--INDEX JavaScript:application programming:JSLib classes --> 
 1530:     <!--INDEX application programming, JavaScript:JSLib classes -->
 1531:     available JavaScript functions in the JSLib package are divided
 1532:     into different modules that, in turn, are divided into
 1533:     different classes defined in source files such as
 1534:     <i>file.js</i>, <i>dir.js</i>, and <i>fileUtils.js</i>. <a
 1535:     href="#77010">Table 5-1</a> describes the basic classes in the
 1536:     JSLib package's I/O module and describes how they are used.</p>
 1537:     <p><i>Table 5-1: <a name="77010"></a></i><i>JSLib
 1538:     classes</i></p>
 1539:     <table width="100%" border="1">
 1540:       <tbody>
 1541:         <tr>
 1542:           <td><b>Class / (filename)</b></td>
 1543:           <td><b>Description</b></td>
 1544:         </tr>
 1545:         <tr>
 1546:           <td>File / (<i>file.js</i>)</td>
 1547:           <td>Contains most routines associated with the File
 1548:           object (implementing <tt>nsIFile</tt>). The library is
 1549:           part of the jslib I/O module.</td>
 1550:         </tr>
 1551:         <tr>
 1552:           <td>FileUtils / (<i>fileUtils.js</i>)</td>
 1553:           <td>The chrome registry to local file path conversion,
 1554:           file metadata, etc.</td>
 1555:         </tr>
 1556:         <tr>
 1557:           <td>Dir / (<i>dir.js</i>)</td>
 1558:           <td>Directory creation; variations of directory
 1559:           listings.</td>
 1560:         </tr>
 1561:         <tr>
 1562:           <td>DirUtils / (<i>dirUtils.js</i>)</td>
 1563:           <td>Paths to useful Mozilla directories and files such as
 1564:           <i>chrome</i>, <i>prefs</i>, <i>bookmarks</i>,
 1565:           <i>localstore</i>, etc.</td>
 1566:         </tr>
 1567:       </tbody>
 1568:     </table>
 1569:     <br>
 1570:     <br>
 1571:      
 1572:     <h4><a name="77074"></a> Using the File class</h4>
 1573:     <p>The JSLib <tt>File</tt> <!--INDEX classes:JSLib:File --> 
 1574:     <!--INDEX File class, JSLib --> 
 1575:     <!--INDEX JSLib libraries:File class --> 
 1576:     <!--INDEX JavaScript:application programming:JSLib File class -->
 1577:     <!--INDEX application programming, JavaScript:JSLib File class -->
 1578:     class exposes most local file routines from the <i>nsIFile</i>
 1579:     interface. The <tt>File</tt> class is part of the JSLib I/O
 1580:     module, and is defined in <i>jslib/io/file.js</i>. Here is how
 1581:     you load the library from xpcshell:</p>
 1582: <pre>
 1583: $ ./run-mozilla.sh ./xpcshell -w -s
 1584: js&gt; load('chrome/jslib/jslib.js');
 1585: *********************
 1586: JS_LIB DEBUG IS ON
 1587: *********************
 1588: js&gt;
 1589: </pre>
 1590:     <p>Once JSLib is loaded, you can load the <tt>File</tt> module
 1591:     with an <tt>include</tt> statement:</p>
 1592: <pre>
 1593: js&gt; include(`chrome://jslib/content/io/file.js');
 1594: *** Chrome Registration of package: Checking for contents.rdf at
 1595: resource:/chrome/jslib/
 1596: *** load: filesystem.js OK
 1597: *** load: file.js OK
 1598: true
 1599: js&gt;
 1600: </pre>
 1601:     <p>Note that <i>file.js</i> loads <i>filesystem.js</i> in turn.
 1602:     The class <tt>FileSystem</tt> in <i>filesystem.js</i> is the
 1603:     base class for the <tt>File</tt> object. You can also load
 1604:     <i>file.js</i> by using the top-level construct
 1605:     <tt>JS_LIB_PATH</tt>:</p>
 1606: <pre>
 1607: js&gt; include(JS_LIB_PATH+'io/file.js');
 1608: </pre>
 1609:     <p>Once you have the <i>file.js</i> module loaded, you can
 1610:     create an instance of a <tt>File</tt> object and call methods
 1611:     on it to manipulate the file and path it represents:</p>
 1612: <pre>
 1613: js&gt; var f = new File('/tmp/foo');
 1614: js&gt; f;
 1615: [object Object]
 1616: js&gt; f.help; // listing of everything available to the object
 1617: . . .
 1618: js&gt; f.path;
 1619: /tmp/foo
 1620: js&gt; f.exists( );   // see if /tmp/foo exists
 1621: false
 1622: js&gt; f.create( );   // it doesn't, so create it.
 1623: js&gt; f.exists( );
 1624: true
 1625: js&gt; f.isFile( );   // is it a file?
 1626: true
 1627: js&gt; f.open('w');  // open the file for writing
 1628: true
 1629: js&gt; f.write('this is line #1\n');
 1630: true
 1631: js&gt; f.close( );
 1632: js&gt; f.open( );     // open the file again and
 1633: js&gt; f.read( );     // read back the data
 1634: // you can also use default flag 'r' for reading
 1635: this is line #1
 1636: js&gt; f.close( );
 1637: </pre>
 1638:     <p>You can also assign the contents of the file to a variable
 1639:     for later use, iterative loops through the file contents, or
 1640:     updates to the data:</p>
 1641: <pre>
 1642: js&gt; f.open( );
 1643: true
 1644: js&gt; var contents = f.read( );
 1645: js&gt; f.close( );
 1646: js&gt; print(contents);
 1647: this is line #1
 1648: js&gt;
 1649: // rename the file
 1650: js&gt; f.move(`/tmp/foo.dat');
 1651: foo.dat
 1652: filesystem.js:move successful!
 1653: js&gt; f.path;
 1654: /tmp/foo.dat
 1655: </pre>
 1656:     <p>These examples show some ways the JSLib <tt>File</tt> object
 1657:     can manipulate local files. Using these interfaces can make
 1658:     life a lot easier by letting you focus on creating your Mozilla
 1659:     application without having to implement XPCOM <tt>nsIFile</tt>
 1660:     objects manually from your script.</p>
 1661:     <h4><a name="77075"></a> Using the FileUtils class</h4>
 1662:     <p>To create an <!--INDEX classes:JSLib:FileUtils --> 
 1663:     <!--INDEX FileUtils class, JSLib --> 
 1664:     <!--INDEX JSLib libraries:FileUtils class --> 
 1665:     <!--INDEX JavaScript:application programming:JSLib FileUtils class -->
 1666:     <!--INDEX application programming, JavaScript:JSLib FileUtils class -->
 1667:     instance of the <tt>FileUtils</tt> class, use the
 1668:     <tt>FileUtils</tt> constructor:</p>
 1669: <pre>
 1670: js&gt; var fu = new FileUtils( );
 1671: js&gt; fu;
 1672: [object Object]
 1673: </pre>
 1674:     <p>Then look at the object by calling its <tt>help</tt>
 1675:     method:</p>
 1676: <pre>
 1677: js&gt; fu.help;
 1678: </pre>
 1679:     <p>The difference between using the <i>File</i> and
 1680:     <i>FileUtils</i> interfaces is that methods and properties on
 1681:     the latter are <i>singleton</i> and require a path argument,
 1682:     while the <i>FileUtils</i> utilities are general purpose and
 1683:     not bound to any particular file. The <i>FileUtils</i>
 1684:     interface has several handy I/O utilities for converting,
 1685:     testing, and using URLs, of which this example shows a few:</p>
 1686: <pre>
 1687: js&gt; fu.exists('/tmp');
 1688: true
 1689: // convert a chrome path to a url
 1690: js&gt; fu.chromeToPath('chrome://jslib/content/');
 1691: /usr/src/mozilla/dist/bin/chrome/jslib/jslib.xul
 1692: // convert a file URL path to a local file path
 1693: js&gt; fu.urlToPath('file:///tmp/foo.dat');
 1694: /tmp/foo.dat
 1695: </pre>
 1696:     <p>Most methods on the <tt>FileUtils</tt> objects are identical
 1697:     to the methods found in <i>file.js</i>, except they require a
 1698:     path argument. Another handy method in the <tt>FileUtils</tt>
 1699:     class is <tt>spawn</tt>, which spawns an external executable
 1700:     from the operating system. It's used as follows:</p>
 1701: <pre>
 1702: js&gt; fu.spawn('/usr/X11R6/bin/Eterm');
 1703: </pre>
 1704:     <p>This command spawns a new Eterm with no argument. To open an
 1705:     Eterm with vi, you could also use this code:</p>
 1706: <pre>
 1707: js&gt; fu.spawn('/usr/X11R6/bin/Eterm', ['-e/usr/bin/vi']);
 1708: </pre>
 1709:     <p>Checking to see if three different files exist would take
 1710:     several lines when using the File class, but the
 1711:     <tt>FileUtils</tt> class is optimized for this type of check,
 1712:     as the following listing shows:</p>
 1713: <pre>
 1714: js&gt; var fu=new FileUtils( );
 1715: js&gt; fu.exists('/tmp');
 1716: true
 1717: js&gt; fu.exists('/tmp/foo.dat');
 1718: true
 1719: js&gt; fu.exists('/tmp/foo.baz');
 1720: false
 1721: </pre>
 1722:     <p>You need to initialize the <tt>FileUtils</tt> class only
 1723:     once to use its members and handle local files robustly.</p>
 1724:     <h4><a name="77076"></a> Using the Dir class</h4>
 1725:     <p>The <tt>Dir</tt> <!--INDEX classes:JSLib:Dir --> 
 1726:     <!--INDEX Dir class, JSLib --> 
 1727:     <!--INDEX JSLib libraries:Dir class --> 
 1728:     <!--INDEX JavaScript:application programming:JSLib Dir class -->
 1729:     <!--INDEX application programming, JavaScript:JSLib Dir class -->
 1730:     class is custom-made for working with directory structures on a
 1731:     local filesystem. To create an instance of the <tt>Dir</tt>
 1732:     class, call its constructor and then its <tt>help</tt> method
 1733:     to see the class properties:</p>
 1734: <pre>
 1735: js&gt; var d = new Dir('/tmp');
 1736: js&gt; d.help;
 1737: </pre>
 1738:     <p><tt>Dir</tt> inherits from the same base class as
 1739:     <tt>File</tt>, which is why it looks similar, but it implements
 1740:     methods used specifically for directory manipulation:</p>
 1741: <pre>
 1742: js&gt; d.path;
 1743: /tmp
 1744: js&gt; d.exists( );
 1745: true
 1746: js&gt; d.isDir( );
 1747: true
 1748: </pre>
 1749:     <p>The methods all work like those in the <tt>File</tt> and
 1750:     <tt>FileUtils</tt> classes, so you can append a new directory
 1751:     name to the object, see if it exists, and create it if (it does
 1752:     not) by entering:</p>
 1753: <pre>
 1754: js&gt; d.append('newDir');
 1755: /tmp/newDir
 1756: js&gt; d.path;
 1757: /tmp/newDir
 1758: js&gt; d.exists( );
 1759: false
 1760: js&gt; d.create( );
 1761: js&gt; d.exists( );
 1762: true
 1763: </pre>
 1764:     <h4><a name="77077"></a> Using the DirUtils class</h4>
 1765:     <p>Note that <!--INDEX classes:JSLib:DirUtils --> 
 1766:     <!--INDEX DirUtils class, JSLib --> 
 1767:     <!--INDEX JSLib libraries:DirUtils class --> 
 1768:     <!--INDEX JavaScript:application programming:JSLib DirUtils class -->
 1769:     <!--INDEX application programming, JavaScript:JSLib DirUtils class -->
 1770:     some methods in the <tt>DirUtils</tt> class cannot be called
 1771:     from xpcshell and instead must be called from a XUL window into
 1772:     which the proper JSLib source file was imported. The following
 1773:     XUL file provides two buttons that display information in
 1774:     textboxes about the system directories:</p>
 1775: <pre>
 1776: &lt;?xml version="1.0"?&gt;
 1777: &lt;?xml-stylesheet href="chrome://global/skin" type="text/css"?&gt;
 1778: &lt;window xmlns="<a href=
 1779: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul</a>"
 1780: xmlns:html="<a href=
 1781: "http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>"
 1782: id="dir-utils-window"
 1783: orient="vertical"
 1784: autostretch="never"&gt;
 1785: &lt;script type="application/x-javascript" src="chrome://jslib/content/io/dirUtils.js"/&gt;
 1786: &lt;script&gt;
 1787: var du = new DirUtils( );
 1788: function getChromeDir( ) {
 1789: cd = du.getChromeDir( );
 1790: textfield1 = document.getElementById("tf1");
 1791: textfield1.setAttribute("value", cd);
 1792: }
 1793: function getMozDir( ) {
 1794: md =   du.getMozHomeDir( );
 1795: textfield2 = document.getElementById("tf2");
 1796: textfield2.setAttribute("value", md);
 1797: }
 1798: &lt;/script&gt;
 1799: &lt;box&gt;
 1800: &lt;button id="chrome" onclick="getChromeDir( );" label="chrome" /&gt;
 1801: &lt;textbox id="tf1" value="chrome dir" /&gt;
 1802: &lt;/box&gt;
 1803: &lt;box&gt;
 1804: &lt;button id="moz" onclick="getMozDir( );" label="mozdir" /&gt;
 1805: &lt;textbox id="tf2" value="moz dir" /&gt;
 1806: &lt;/box&gt;
 1807: &lt;/window&gt;
 1808: </pre>
 1809:     <hr>
 1810:     <hr>
 1811:     <a name="260"></a><a href="#b260">[Back]</a> <a name=
 1812:     "77034"></a> This book does not pretend to give a complete
 1813:     overview of JavaScript. You can view the full JavaScript 1.5
 1814:     reference online at <i><a href=
 1815:     "http://developer.netscape.com/docs/manuals/index.html?content=javascript.html">
 1816:     http://developer.netscape.com/docs/manuals/index.html?content=javascript.html</a></i>.
 1817:     
 1818:     <hr>
 1819:     <a name="261"></a><a href="#b261">[Back]</a> <a name=
 1820:     "77035"></a> The third edition of the EMCA-262 EMCAScript
 1821:     Language Specification can be found at <i><a href=
 1822:     "http://www.ecma.ch/ecma1/STAND/ECMA-262.HTM">http://www.ecma.ch/ecma1/STAND/ECMA-262.HTM</a>.</i>
 1823:     
 1824:     <hr>
 1825:     <a name="262"></a><a href="#b262">[Back]</a> <a name=
 1826:     "77036"></a> You can use other DOM methods, but these methods
 1827:     are most commonly used in the XPFE. Mozilla's support for the
 1828:     DOM is so thorough that you can use the W3C specifications as a
 1829:     list of methods and properties available to you in the chrome
 1830:     and in the web content the browser displays. The full W3C
 1831:     activity pages, including links to the specifications
 1832:     implemented by Mozilla, can be found at <i><a href=
 1833:     "http://www.w3.org/DOM/">http://www.w3.org/DOM/</a></i> . 
 1834:     <hr>
 1835:     <br>
 1836:     <br>
 1837:     File a <a href=
 1838:     "http://mozdev.org/bugs/enter_bug.cgi?product=books">Bug</a>
 1839:     for chapter 5. <!-- ?php require(NOTES); ? -->
 1840:     <?php $post_to_list=NO; $author='reviewers@mozdev.org'; $target_page='ch05'; require(NOTES); ?>

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>