Usingnamespace – Blog

C#, XUL, Javascript, C++ …

Affichage des posts dans Javascript

Unfortunately, there are no “clean” way to limit the maximum number of characters (or more generally apply constraints) in edit mode of a tree, unlike textbox elements.

Here is the trick : the oninput event of a tree is only fired when you are editing :

Short tree example (limiting max number of characters to 10):

<tree hidecolumnpicker="true" minwidth="100px" height="300" editable="true" flex="1" 
 oninput="event.originalTarget.value = event.originalTarget.value.substring(0,10);">
  <treecols>
    <treecol primary="true" flex="1"/>
  </treecols>
  <treechildren context="">
    <treeitem>
      <treerow>
        <treecell label="joe@somewhere.com"/>
      </treerow>
    </treeitem>
    <treeitem>
      <treerow>
        <treecell label="mel@whereever.com"/>
      </treerow>
    </treeitem>
  </treechildren>
</tree>

I am pleased to share one creation of mine :) .

The following widget is a XBL binding based on the XUL’s XNL templates technology.
https://developer.mozilla.org/en/XUL/Template_Guide/XML_Templates

If you need to display xml data into a xul menulist -combobox- or in a radiogroup -radiobox-, without having to implement an XML parser, here is the solution :

xml-combobox
xml-radiobox

<xml-combobox id="myid" datasources="chrome://application/pathtoxmlfile" expr=""/>
<xml-radiobox id="myid" datasources="chrome://application/pathtoxmlfile" expr=""/>

Attributes :

datasources : URL of an XML document, either a local file or a remote web site.

expr : the expr attribute is a very simple XPath expression which simply retrieves the root elements of your query within the datasource.

label :

  • starting with “!” : an XPath expression corresponding to the node you want to use as a label.
  • starting with “?” : a property of the node corresponding to the expr result, you want to use as a label.
  • Otherwise just a text used as a label for all the generated lines (can be empty).



value :

  • starting with “!” : an XPath expression corresponding to the node you want to use as a value.
  • starting with “?” : a property of the node corresponding to the expr result, you want to use as a value.
  • Otherwise just a text used as a value for all the generated lines (can be empty).



image :

  • starting with “!” : an XPath expression corresponding to the node you want to use as an image.
  • starting with “?” : a property of the node corresponding to the expr result, you want to use as an image.
  • Otherwise just a text used as an image for all the generated lines (can be empty).



imageBaseFolder : if the attribute image is used, the base folder where to look the images for. Useful if the image query result is just a file name, or a relative path. Must contain the final “/” or “\”. Can be left empty.

orient : (xml-radiobox only) : “horizontal” or “vertical”.

onselect: occurs when selection changed.

Examples :

Example 1 : simple example using the properties of a XML node

<people>
  <person name="Napoleon Bonaparte" id="NB"/>
  <person name="Cleopatra" id="Cl"/>
  <person name="Julius Caesar" id="JC"/>
  <person name="Ferdinand Magellan" id="FM"/>
  <person name="Laura Secord" id="LS"/>
</people>
<xml-combobox id="myid" datasources="chrome://application/pathtoxmlfile" expr="person/" label="?name" value="?id"/>

Example 2 : using a node property for the id and a XPath from the targeted node for the label (here the text contained in the subnode “name”)

<people>
  <person id="NB">
    <name>Napoleon Bonaparte</name>
    <gender>Male</gender>
  </person>
  <person id="Cl">
    <name>Cleopatra</name>
    <gender>Female</gender>
  </person>
  <person id="JC">
    <name>Julius Caesar</name>
    <gender>Male</gender>
  </person>
  <person id="FM">
    <name>Ferdinand Magellan</name>
    <gender>Male</gender>
  </person>
  <person id="LS">
    <name>Laura Secord</name>
    <gender>Female</gender>
  </person>
</people>
<xml-radiobox id="myid" datasources="chrome://application/pathtoxmlfile" expr="person/" label="!./name/text()" value="?id" orient="horizontal"/>

Example 3 : same as example 2 but with a xml-combobox and using the image property and imageBaseFolder to specify where the widget should look the images for

<people>
  <person id="NB">
    <name>Napoleon Bonaparte</name>
    <gender>Male</gender>
    <picture>nap.png</picture>
  </person>
  <person id="Cl">
    <name>Cleopatra</name>
    <gender>Female</gender>
    <picture>cleo.png</picture>
  </person>
  <person id="JC">
    <name>Julius Caesar</name>
    <gender>Male</gender>
    <picture>julius.png</picture>
  </person>
  <person id="FM">
    <name>Ferdinand Magellan</name>
    <gender>Male</gender>
    <picture>magellan.png</picture>
  </person>
  <person id="LS">
    <name>Laura Secord</name>
    <gender>Female</gender>
    <picture>secord.png</picture>
  </person>
</people>
<xml-combobox id="myid" datasources="chrome://application/pathtoxmlfile" expr="person/" label="!./name/text()" value="?id" imageBaseFolder="chrome://application/skin/pic_small/" image="!./picture/text()"/>

And like all the XBL widgets, the bindings need to be done in a CSS :

xml-combobox{
	-moz-binding: url("chrome://application/content/bindings/xml-combobox.xml#xml-combobox") !important;
}
 
xml-radiobox{
	-moz-binding: url("chrome://application/content/bindings/xml-radiobox.xml#xml-radiobox") !important;
}

Here are some useful functions I wrote or found on the web.
Suggestions are welcome :)

Chrome url to file path :

function chromeToPath (aChromePath) {
 
   if (!aChromePath) || !(/^chrome:/.test(aChromePath))))
      return; //not a chrome url
   var rv;
 
   var ios = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces["nsIIOService"]);
   var uri = ios.newURI(aPath, "UTF-8", null);
   var cr = Components.classes['@mozilla.org/chrome/chrome-registry;1'].getService(Components.interfaces["nsIChromeRegistry"]);
   rv = cr.convertChromeURL(uri).spec;
 
   if (/^file:/.test(rv))
      rv = this.urlToPath(rv);
   else
      rv = this.urlToPath("file://"+rv);
 
   return rv;
}

URL to file path :

function urlToPath (aURL) {
 
   if (!aURL || !/^file:/.test(aURL))
      return ;
   var rv;
   var ph = Components.classes["@mozilla.org/network/protocol;1?name=file"]
        .createInstance(Components.interfaces.nsIFileProtocolHandler);
    rv = ph.getFileFromURLSpec(aPath).path;
    return rv;
}

Get a nsILocalFile’s URL :

function pathToUrl (aFile) {
   //aFile must be an instance of nsILocalFile
   var rv;
   var ph = Components.classes["@mozilla.org/network/protocol;1?name=file"]
        .createInstance(Components.interfaces.nsIFileProtocolHandler);
    rv = ph.getURLSpecFromFile(aFile);
    return rv;
}

Check if file exists :

function fileExists(aPath){
  try {
    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
    var file = Components.classes["@mozilla.org/file/local;1"]
                         .createInstance(Components.interfaces.nsILocalFile);
    file.initWithPath(aPath);
    return file.exists();
  } catch(ex) {
    return false;
  }
}

Quit function :

function quit (aForceQuit) {
  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
  var appStartup = Components.classes['@mozilla.org/toolkit/app-startup;1'].
    getService(Components.interfaces.nsIAppStartup);
 
  // eAttemptQuit will try to close each XUL window, but the XUL window can cancel the quit
  // process if there is unsaved data. eForceQuit will quit no matter what.
  var quitSeverity = aForceQuit ? Components.interfaces.nsIAppStartup.eForceQuit :
                                  Components.interfaces.nsIAppStartup.eAttemptQuit;
  appStartup.quit(quitSeverity);
}