These code snippets have come mainly from the mailing list. If you find a mistake in any of the snippets, or have an improvement, or have a snippet of your own, please Login to this wiki and edit these pages.
The Demo Browser has a large number of simple examples. You can use the online version or if you’ve downloaded and built the SDK, you can use your local copy in application/demobrowser/build subdirectory of your SDK installation.
Sometimes you want to see the HTML created by qooxdoo to solve layout problems or track down bugs in qooxdoo.
In Firefox you can use the Firebug extension. “Inspect Element” allows you to click on any part of the page and see the XML and CSS that generated the element.
Otherwise this link will work in all browsers to show the XML tree for the current page:
javascript:if (window.document.body.outerHTML != undefined){'<xmp>'+window.document.body.outerHTML+'</xmp>'} else if (document.getElementsByTagName("html")[0].innerHTML != undefined){'<xmp>'+document.getElementsByTagName("html")[0].innerHTML+'</xmp>'} else if (window.document.documentElement.outerHTML != undefined){'<xmp>'+window.document.documentElement.outerHTML+'</xmp>'} else { alert('Your browser does not support this functionality') };
There is also a simpler form for IE that will open up the XML in a new window:
javascript:void(window.open("javascript:'<xmp>'+opener.window.document.documentElement.outerHTML+'</xmp>'"));
You can create a shortcut for this on the toolbar.
The basic programming model of qooxdoo suggests that you develop your application in its source version, and once you’re satisfied create the build version of it, which is then deployed on a web server. qooxdoo’s build versions of an application are self-contained, they encompass all script files, resources like images and style sheets, and any helper files that are necessary for the application. You can safely copy the build directory to the document forrest of a web server, or zip it up in an archive and send it by mail; the recipient will be able to unpack it and run the application without flaws.
In contrast, the source version is run off of the file system most of the time (i.e. opening it with the file: protocol in your browser). The source script just references source code and resources with relative paths, wherever they happen to be on your file system. This usually doesn’t lend itself well to being run from a web server. Even if you include the source directory of your application in an server-accessible path (somewhere down from its DocumentRoot or one of the defined Aliases), chances are that the source script references files which are outside the document scope of the web server.
So if you find yourself in the situation where you need to run a source version of your app from a web server, mind the following hints:
A simple way to achieve this is to map the DocumentRoot or an Alias to a directory in your file system that is a common parent to all involved qooxdoo components of your app.
Firefox 3 will by default employ a strict same-origin-policy concerning file URIs, ie. URIs loaded with the %file://% protocol. This can lead to errors when you run the source version of your app from the file system, particularly when your app uses parts. In order to relax this strict policy for file URIs, enter about:config in the location bar of your browser and apply the following setting:
security.fileuri.strict_origin_policy : false
Here is the solution:
var win = new qx.ui.window.Window();
// first solution
win.addListener("resize", function(){
this.center();
}, win);
// second solution
win.addListener("resize", win.center, win);
this.getRoot().add(win);
win.open();
This solution works even if we don’t know the real size of the window, because it depends on its content.
Before the window is shown and know its real size, we place it at the center. We use the resize event instead of the appear event to prevent any flickering, because when using the appear event the window is already visible and then moved to the center. With the resize you can center the window right after the inserting in the DOM (the widget resizes) and avoid any flickering.
Here is the solution:
var win = new qx.ui.window.Window();
win.setLayout(new qx.ui.layout.Canvas);
var field = new qx.ui.form.TextField;
win.add(field)
field.focus();
this.getRoot().add(win);
win.open();
Setting the focus at the textfield widget is done in a post-process, so you do not have to use any event listener methods to achieve this.
How do I ensure that the correct “this” is referred to in an event handler? Say you have an event-handler within a custom widget which looks like this:
_someHandler : function(e) {
alert(this);
}
and then later within the same class definition, register a handler with another class instance:
var anotherWidget = new AnotherWidget();
anotherWidget.addListener("changeSomething", this._someHandler);
When the handler gets triggered by a “changeSomething” event, the alert of the handler is being called. However, there is a problem in that ‘this’ now refers to an object of class AnotherWidget and not to the instance of MyWidget. To solve this problem, use:
anotherWidget.addListener("changeSomething", this._someHandler, this);
To set a transparent color for any widget do the following:
// text color
myWidget.setTextColor("transparent");
// background color
myWidget.setBackgroundColor("transparent");
As the transparent color is part of every color theme in qooxdoo, you set this color by simply use this string.
Storing any arbitrary value in a qooxdoo object.
You can store arbitrary user-defined data in any qooxdoo object using the setUserData and getUserData methods. These are guaranteed not to conflict with qooxdoo or javascript properties of the object. Note that as qooxdoo events are derived from qx.event.type.Event which extends qx.core.Object, you can store user-defined data in events as well.
For example:
MyObject.setUserData("MyData", "123");
MyObject.debug("MyData = " + MyObject.getUserData("MyData"));
Modal windows are windows which have to be closed (e.g. via it’s buttons like “OK” or “Cancel”) before any other UI element can be used. In qooxdoo a special blocker element is used to prevent user actions on other elements than the open modal window. The blocker element can be styled (e.g. it can have an semi-transparent background) to accent that the window is a modal one. The blocker is included in every root widget (qx.ui.root.Application, qx.ui.root.Inline, qx.ui.root.Page) and in qx.ui.window.Desktop.
this.getApplicationRoot().set({
blockerColor: '#bfbfbf',
blockerOpacity: 1.2
});
If you want to use this feature not inside a widget based object but inside a qx.application.Standalone, use this.getRoot() instead of this.getApplication.Root().
This short snippet also applies if just want to add a flash movie to your qooxdoo application.
var doc = this.getRoot();
var win = new qx.ui.window.Window("Window");
win.setLayout(new qx.ui.layout.Canvas());
doc.add(win, {top: 20, left: 20});
var layout = new qx.ui.layout.Basic();
var container = new qx.ui.container.Composite(layout);
container.set({ width: 400, height: 400 });
win.add(container);
win.addListener("appear", function()
{
var domElement = container.getContentElement().getDomElement();
var flash = qx.bom.Flash.create(domElement, FLASH_URL, "flashMovie");
});
win.open();
As default behaviour the cell editors of the table widget are stop the editing mode whenever the user clicks at any other cell. Anyway sometimes the users want to be able to stop the editing whenever the value has changed, e.g. if they pick another item out of the list of a combobox. To achieve this you can add the following to the cell editor classes
// this snippet targets the ComboBox cell editor
// this approach should also work for the other cell editors
createCellEditor : function(cellInfo)
{
...
cellEditor.addListener("changeValue", function()
{
cellInfo.table.stopEditing();
}, this);
...
}
To enable drag and drop features at virtual widgets you currently have to manipulate framework methods directly. The issues with drag and drop in virtual widgets will be addressed with the Bug #1215
// patch the "supportsDrop" method
qx.ui.core.Widget.prototype.supportsDrop = function(dragCache)
{
var supportsDropMethod = this.getSupportsDropMethod();
if (supportsDropMethod !== null) {
return supportsDropMethod.call(this, dragCache);
}
return true;
};
// patch the "getDropTarget" method
qx.event.handler.DragAndDropHandler.prototype.getDropTarget = qx.core.Variant.select("qx.client",
{
"gecko" : function(e)
{
var vCurrent = e.getTarget();
// if (vCurrent == this.__dragCache.sourceWidget) {
// vCurrent = qx.event.handler.EventHandler.getTargetObject(qx.html.ElementFromPoint.getElementFromPoint(e.getPageX(), e.getPageY()));
// } else {
vCurrent = qx.event.handler.EventHandler.getTargetObject(null, vCurrent);
// }
while (vCurrent != null)
{
if (!vCurrent.supportsDrop(this.__dragCache)) {
return null;
}
if (this.supportsDrop(vCurrent)) {
return vCurrent;
}
vCurrent = vCurrent.getParent();
}
return null;
},
"default" : function(e)
{
var vCurrent = e.getTarget();
while (vCurrent != null)
{
if (!vCurrent.supportsDrop(this.__dragCache)) {
return null;
}
if (this.supportsDrop(vCurrent)) {
return vCurrent;
}
vCurrent = vCurrent.getParent();
}
return null;
}
}),
I have found this useful for testing with Selenium. If you have a native DOM element and want to find out which qooxdoo widget it is, use the following code, (I only tried it in qooxdoo 1.2).
getQooxdooClassName: function (domElement)
{
if (!qx) return; // this is not a qooxdoo frame
if (domElement.$$hash)
{
var qxWrapper = qx.core.ObjectRegistry.__registry[domElement.$$hash];
if (qxWrapper.__attribValues && qxWrapper.__attribValues["$$widget"])
{
var wid = qxWrapper.__attribValues["$$widget"]; // widgetId
var widget = qx.core.ObjectRegistry.__registry[wid];
return widget.classname
}
}
// the domElement has no qooxdoo counterpart - returns `undefined`
};
Contributed by Farid Elyahyaoui
Suppose you like to display a contextual help inside a toolTip widget by requesting the help contents dynamically with the help of a XMLHttp request. This little snippet could be a good entry point.
this._help = new qx.ui.basic.Image("icon/16/actions/help-contents.png");
this.getRoot().add(this._help);
this._helpToolTip = new qx.ui.tooltip.ToolTip('the <b>initial html</b> code');
this._helpToolTip.set({ rich: true, showTimeout: 200 });
this._help.setToolTip(this._helpToolTip);
// only get the help content once
this._help.addListenerOnce("mouseover", this.onHelpMouseOver, this);
this.onHelpMouseOver = function(e)
{
var req = new qx.io.remote.Request("path/to/help.txt");
req.addListener("completed", this.onHelpRequestCompleted, this);
req.send();
};
this.onHelpRequestCompleted = function(e)
{
var content = e.getContent();
this._helpToolTip.setLabel(content);
};
By default a Desktop widget does not display scrollbars if a wiget get positioned (partly) outside the visible area of the Desktop. If you want to have scrollbars, you have to configure the Manager of the Desktop:
var windowManager = new qx.ui.window.Manager().set({
allowShrinkX : false,
allowShrinkY : false
});
var desktop = new qx.ui.window.Desktop(windowManager);
Consider the following setup: A low-level widget which tries to listen to key input events at a e.g. native input element. If you develop your low-level application with extending the qx.application.Simple framework class everything is fine and you’re done. However, if you choose to develop a stand-alone low-level widget/application which does not extend the simple application class you have to activate the focus handler for yourself.
if (qx.Class.isDefined("qx.event.handler.Focus"))
{
qx.event.Registration.getManager(window).getHandler(qx.event.handler.Focus);
}
You probably think: so why do you need to do this?
The reason is that the focus handler is not created at startup rather at the first key events dispatched by the user actions. Since the focus handler is not available at the time the user focusses the input element at the first time it cannot set this element as the active one and does not delegate the events to this element. The events are fired at the BODY element (which is the fallback if no element is active).
Note
This snippet is about low-level functionality when adding listener to e.g. input elements. The high-level textfield widget does provide the input event for monitoring the value changes.
Suppose you like to get informed when the user types into a certain input element you probably dealing with the question: should I use the keypress or the keyup event listener?
These code snippet should help you with your decision:
var inputEl = document.getElementById("input");
// suppose the user is inserting the value "a"
// into the empty input element
inputEl.addListener("keypress", function(e){
// "this" refers to the input element
this.debug(this.value);
// -> value == ""
});
inputEl.addListener("keyup", function(e){
// "this" refers to the input element
this.debug(this.value);
// -> value == "a"
});
The interesting thing is that the keypress event is fired before the input element receives the value, so you can’t use the keypress event to check for the correct value. You have access to the inserted character by e.getKeyIdentifier() but you can’t know where the character is inserted.
The keyUp event on the other hand does get you the right value because this event is fired after the value is inserted. Drawback for the keyup listener: if the user holds the key only one event is fired at the end.
Note
As of r19372, the actions suggested in this snippet are no longer required. The mutex %__loadRowCountRequestRunning% has been added within qx.ui.table.model.Remote to prevent multiple concurrent calls to the user’s _loadRowCount() method. To revert to the original behavior, set the remote model’s property blockConcurrentLoadRowCount to false.
This snippet is assuming you’ve already read the article about Using the remote table model.
Normally the remote table model does fire several requests when starting up to retrieve the information about the row count. Since several table components need this value they are requesting this value on their own resulting in multiple requests to the backend (as long as the value is retrieved and stored).
To omit this behaviour you can only allow one request for the row count to be fired and blocking all other requests.
members : {
__loadRowCountRequestRunning : false,
// overloaded - called whenever the table requests the row count
_loadRowCount : function()
{
if (!this.__loadRowCountRequestRunning)
{
// Call the backend service (example) - using XmlHttp
var url = "http://localhost/services/getTableCount.php";
var req = new qx.io.remote.Request(url, "GET", "application/json");
// Add listener
req.addListener("completed", this._onRowCountCompleted, this);
// send request
req.send();
// setting the flag
this.__loadRowCountRequestRunning = true;
}
},
// Listener for request of "_loadRowCount" method
_onRowCountCompleted : function(response)
{
// Resetting the flag
this.__loadRowCountRequestRunning = false;
var result = response.getContent();
if (result != null)
{
// Apply it to the model - the method "_onRowCountLoaded" has to be called
this._onRowCountLoaded(result);
}
}
}
It should be pretty straightforward to integrate qooxdoo with free map software. Here are some pointers that should get you started for integrating with ...
If you plan to use a BOM application inside a frameset you have to be aware of some IE-specific behaviour. All versions of IE do fire the ready event before the listener can be attached to the window object. However below is a solution to deal with this behaviour.
qx.event.Registration.addListener(window, "ready", function() { alert(1); });
if (qx && qx.event && qx.event.Registration)
{
var manager = qx.event.Registration.getManager(window);
var handler = manager.findHandler(window, "ready");
if (handler.isApplicationReady()) {
alert("application ready");
}
}
If you try to detect a caps lock you can use for example this algorithm, but keep care the algorithm doesn’t work with umlauts etc.
Here the qooxdoo equivalent:
var textField = new qx.ui.form.TextField();
var capsLock = new qx.ui.form.CheckBox("Caps lock");
var doc = this.getRoot();
doc.add(capsLock , {left: 100, top: 25});
doc.add(textField, {left: 100, top: 50});
textField.addListener("keyinput", function(e) {
var charCode = e.getCharCode();
var shiftPressed = e.isShiftPressed();
// Keep care the algorithm doesn't work with umlauts etc.
if (((charCode >= 65 && charCode <= 90) && !shiftPressed) ||
((charCode >= 97 && charCode <= 122) && shiftPressed)) {
capsLock.setValue(true);
} else {
capsLock.setValue(false);
}
});
Suppose you have a client-detection at your site and you want to serve your visitors a client-specific version of your application. To achieve this goal you can use the powerful generator. You only have to create a custom configuration and you’re done.
{
// normal skeleton configuration
// left out for simplicity
/* the "jobs" section is the interesting part */
"jobs" :
{
"build-script" :
{
/* adding the variants */
"variants" :
{
"qx.client" : [ "gecko", "mshtml", "webkit", "opera" ]
},
"compile-options" :
{
"paths" :
{
/* overwrite "file" entry to get client-specific file names */
"file" : "${BUILD_PATH}/script/${APPLICATION}-{qx.client}.js"
}
}
}
}
}
You know that Memory Management is an important task and you would like to check your application against potential memory leaks? Then read on :)
The best way to achieve this is to create a new job by extend the existing source job. This lets you easily switch between your normal development and a special version of your application to track down memory issues.
{
"jobs" :
{
// existing jobs ...
"source-disposerDebug" :
{
"desc" : "source version with 'qx.disposerDebugLevel' for destruct support",
"extend" : [ "source" ],
"settings" :
{
"qx.disposerDebugLevel" : "1"
}
}
}
}
That’s all.
If you like you can add the source-disposerDebug to your export list to make this job public. If you run ./generate.py ? this job will show up in the list with the given description.
When you generated your application with the source-disposerDebug job all you have to run is
qx.core.ObjectRegistry.shutdown();
at the Firebug console. This starts the destruct mechanism of your application and you can analyze the given messages to improve your application. Usually, there is not much to see because we can not check for some of the critical stuff. So be sure to read Memory Management documentation.
This explains how to enable a gzipped qooxdoo.js without having this possibility directly built in to your webserver.
If you have php at the server, you can write in your html file:
<script type="text/javascript" src="<<path>>/qooxdoo.php"></script>
Then you create a file called qooxdoo.php with this content:
<?php
/**
* @author Oliver Vogel <o.vogel@muv.com>
* @since 05.03.2006
*/
$encodings = array();
if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
{
// Get all available encodings
$encodings = explode(',', strtolower(preg_replace("/\s+/", "", $_SERVER['HTTP_ACCEPT_ENCODING'])));
// Check for gzip header
if (in_array('gzip', $encodings))
{
// found: send the zip-ed file
header("Content-Encoding: gzip");
echo file_get_contents(getenv('DOCUMENT_ROOT') . '<<path>>/qooxdoo.js.gz');
die;
}
}
// Encoding not found or gzip not accepted -> send "normal" file
echo file_get_contents(getenv('DOCUMENT_ROOT') . '<<path>>/qooxdoo.js');
die;
?>
This page checks if the browser supports gzip. If this is true, the server sends the gzip file to the client. This solution needs no gzip-support at the server-side!
Also, if you are writing your own webserver it is trivial to include this feature directly.
I know, it is NOT JavaScript but maybe it is a good idea to add this to the qooxdoo distribution (and it may be a good idea if one with Python or Perl or other experience ports this script to another server-side programming language).
See separate document.
If you want to have a different class as the main class of your application, this is what you have to do:
(version 0.8.3+)
In the global let section of your config file, add the “APPLICATION_MAIN_CLASS” macro:
{
"let" : {
"APPLICATION_MAIN_CLASS" : "<namespace>.<ClassName>",
...
}
}
(version <0.8.3)
You have to tweak two keys in your configuration:
- you have to override the include key of the compile jobs
- you have to override the qx.application setting
In a GUI skeleton you could achieve this like so:
{
...
"jobs" : {
"common" : {
"=include" : ["${QXTHEME}", "<namespace>.<ClassName>"],
"settings" : { "qx.application" : "<namespace>.<ClassName>"}
}
}
}
The = in front of the include key is important, since you need to overrride the whole list of included names.
At times you might need to incorporate code into your qooxdoo application that for some reason cannot be clad in qooxdoo class code, e.g. because it is code you don’t maintain yourself or which is used across several projects.
As of today, there is no complete integration of foreign code into a qooxdoo application. But here are some hints:
- You can compress and optimize non-qooxdoo code using the tool/bin/compile.py frontend of the compiler. compile.py works on individual files. Use compile.py --help to familiarize yourself with the options. You have to capture the output into a file.
- You can use the :ref:`copy-files <pages/tool/generator_config_ref#copy-files>` config key, to copy JS files between source and build version.
- To integrate the code in your application, you can use <script> tags in your index.html. In your qooxdoo class code you can then access the classes and functions provided by the foreign JS code module.
- Have a look at the code of the Playground application that uses CodeMirror code.
Increasingly, people use complex name spaces in their applications, e.g. following the Java style with name spaces like org.myorg.webclient.utils. See this separate document for more details on using complex name spaces.
You can create a local version of the Apiviewer application by running :ref:`generate.py api <pages/tool/generator_default_jobs#api>` in your application. By default, though, only your own application classes and the framework classes are taken into account and displayed in the generated Apiviewer. If you are using additional qooxdoo libraries and/or contributions in your application (which requires you to list them in the libraries job in your config), and want them included in a local Apiviewer, you have to overwrite the API_INCLUDE macro, to get the lib classes documented in Apiviewer. Add this to your config.json’s let section:
API_INCLUDE : ["qx.*","${APPLICATION}.*", "lib1.*", "contrib2.*"]
The first two, "qx.*" and "${APPLICATION}", should always be in; then, add the name spaces of libs/contribs as desired, to have the data in the generated Apiviewer.
If you are using the default settings, the cache path for your generator runs is under a system-wide TMP directory. The path to this TMP directory is system-dependend (e.g. under Linux, it is usually /tmp, and on some Windows version, it might be under C:TEMP). To find out which path is used on your particular system, use the following shell command:
python -c "import tempfile; print tempfile.gettempdir()"