Re: [qooxdoo-devel] qooxdoo-devel Digest, Vol 36, Issue 103
Brought to you by:
ecker,
martinwittemann
From: Josef S. <jos...@mr...> - 2009-05-31 09:22:51
|
Hi Please remove me from the mailing list. Thanks Joe ________________________________ Von: qoo...@li... [mailto:qoo...@li...] Gesendet: So 31.05.2009 01:20 An: qoo...@li... Betreff: qooxdoo-devel Digest, Vol 36, Issue 103 Send qooxdoo-devel mailing list submissions to qoo...@li... To subscribe or unsubscribe via the World Wide Web, visit https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel or, via email, send a message with subject or body 'help' to qoo...@li... You can reach the person managing the list at qoo...@li... When replying, please edit your Subject line so it is more specific than "Re: Contents of qooxdoo-devel digest..." Today's Topics: 1. Generator changes - /tmp may not be a good choice on *ix systems (Derrell Lipman) 2. Re: Generator changes - /tmp may not be a good choice on *ix systems (thron7) 3. Re: Using Simulator to access tables by row/column (Mr. Hericus) ---------------------------------------------------------------------- Message: 1 Date: Sat, 30 May 2009 09:40:55 -0400 From: Derrell Lipman <der...@un...> Subject: [qooxdoo-devel] Generator changes - /tmp may not be a good choice on *ix systems To: qooxdoo Development <qoo...@li...> Message-ID: <cdc...@ma...> Content-Type: text/plain; charset="iso-8859-1" >From the weekly news: > Cache Stuff > The default path of the cache folder has been changed, it's now in a system-dependent temp directory. The system- > dependent temp directory will be something like /tmp on *ix systems Most Linux distributions either clear out /tmp each time the computer boots, or mount /tmp on a virtual file system (e.g. tmpfs) that is recreated on each boot. I believe this means that on a laptop that is rebooted a few times each day, an application in development would require a complete rebuild following each boot -- a very long process, of course, with any substantial application. The concept of putting a single cache someplace on the machine for general use is fabulous. Putting them in TMPDIR is less desirable, and changing TMPDIR in order to put cache files elsewhere is no good since most applications use TMPDIR as a place to put their temporary files, and those files should, in fact, be deleted if they still exist, upon the next boot. I would suggest using a different environment variable variable name, e.g. QOOXDOO_CACHE_DIR, with a fallback to TMPDIR. If QOOXDOO_CACHE_DIR is not found in the environment, a warning should be issued (but not in -q "quiet" mode) because falling back to TMPDIR will cause the problem described above on each boot, causing a poor user experience of long build times. Maybe a suggestion of how to add it to .profile could be provided. Derrell -------------- next part -------------- An HTML attachment was scrubbed... ------------------------------ Message: 2 Date: Sat, 30 May 2009 22:55:45 +0200 (CEST) From: "thron7" <tho...@1u...> Subject: Re: [qooxdoo-devel] Generator changes - /tmp may not be a good choice on *ix systems To: "qooxdoo Development" <qoo...@li...> Message-ID: <472...@im...> Content-Type: text/plain;charset=iso-8859-1 Hi Derrell, thanks for your thoughts. > Most Linux distributions either clear out /tmp each time the computer > boots, > or mount /tmp on a virtual file system (e.g. tmpfs) that is recreated on > each boot. I believe this means that on a laptop that is rebooted a few > times each day, an application in development would require a complete > rebuild following each boot -- a very long process, of course, with any > substantial application. Mh, I was not aware of that. I have been working with Linux the past 10 years and never had my /tmp been cleared automatically. But then, I'm not using one of those modern desktop versions of Linux. And actually, since 2 years I hardly reboot, I only hibernate. - But of course, I'm interested in other people's experiences. > I > would suggest using a different environment variable variable name, e.g. > QOOXDOO_CACHE_DIR, with a fallback to TMPDIR. I'm not sure I completely understand you here. For one thing, we are not evaluating environment variables. Technically, we use the gettempdir() function of one of Python's standard modules, tempfile. This in turn does evaluate environment variables, but also applies a lot of platform logic, so we don't have to worry about it. Since we are running But more importantly, the TMPDIR *is* the fallback already. Just set your CACHE macro to some other path and you're set. If you don't like the TMPDIR location, just override it. Config macros are our way of tailoring the system, and I wouldn't want to add another mechanism, like environment variables, without compelling need. - If you are running multiple applications on your machine, and want to maintain a central setting, just create some "site.json" and include it in the individual config.json's. But sure enough, while this should help you individually, the fallback setting should be sensible for most people, so if a lot of people have the issues you describe we should re-consider the default. > If QOOXDOO_CACHE_DIR is not > found in the environment, a warning should be issued (but not in -q > "quiet" > mode) because falling back to TMPDIR will cause the problem described > above > on each boot, causing a poor user experience of long build times. Maybe a > suggestion of how to add it to .profile could be provided. As I said, the solution is as close as your config.json. As for the poor user experience due to automatically erased /tmp directories, I would be interested to learn who else has this issue?! Thomas ------------------------------ Message: 3 Date: Sat, 30 May 2009 18:19:41 -0500 From: "Mr. Hericus" <mr...@he...> Subject: Re: [qooxdoo-devel] Using Simulator to access tables by row/column To: mr...@he..., qooxdoo Development <qoo...@li...> Message-ID: <4A2...@he...> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Hi Daniel, Some of the functions in that last message didn't work properly. Apologies for that. Here's an update where everything is working. I've also added some new functions: qxTableGetValue - This will return the text in the table that is at the given row, column position qxObjectExecFunction - This allows you to use locators to find a qooxdoo object, and then pass in the name of the function you would like executed on the located object. This is very handy for doing object.getValue(), etc. with locators instead of hand-coded javascript. QxSelenium.java updates: /** This will return the number of rows in the qooxdoo table that is referenced * by the given locator string. Normal locator rules apply. */ public int qxTableGetRows(String locator) { String ret = this.commandProcessor.doCommand("getQxTableRows", new String[] {locator,}); if(ret.startsWith("OK,")){ return Integer.parseInt(ret.substring(3)); } else { throw new SeleniumException("Error returned from getQxTableRows: " + ret); } } /** This will return the number of columns in the qooxdoo table that is referenced * by the given locator string. Normal locator rules apply. */ public int qxTableGetCols(String locator) { String ret = this.commandProcessor.doCommand("getQxTableCols", new String[] {locator,}); if(ret.startsWith("OK,")){ return Integer.parseInt(ret.substring(3)); } else { throw new SeleniumException("Error returned from getQxTableCols: " + ret); } } /** This will return the text in the table that is at the given row, column position * */ public String qxTableGetValue(String locator, int row, int col){ String opts = "row=" + row + ",col=" + col; String ret = this.commandProcessor.doCommand("getQxTableValue", new String[] {locator, opts, }); if(ret.startsWith("OK,")){ return ret.substring(3); } else { throw new SeleniumException("Error returned from getQxTableValue: " + ret); } } /** This will use the locator to find the qooxdoo object at the given position in the * document and then execute the named function on that object and return the results. * */ public String qxObjectExecFucntion(String locator, String functionName){ String ret = this.commandProcessor.doCommand("getQxObjectFunction", new String[] {locator, functionName, }); if(ret.startsWith("OK,")){ return ret.substring(3); } else { throw new SeleniumException("Error returned from getQxObjectFunction: " + ret); } } /** This will click on the qooxdoo table that is referenced * by the given locator string. The click will be located over the given row * and column of the table. */ public String qxTableClick(String locator, int row, int col) { String opts = "row=" + row + ",col=" + col; return this.commandProcessor.doCommand("qxTableClick", new String[] {locator, opts, }); } /** This will click on the qooxdoo table that is referenced * by the given locator string. The click will be located over the given row * and column of the table. */ public String qxTableClick(String locator, int row, int col, String opts) { String opts1 = "row=" + row + ",col=" + col + "," + opts; return this.commandProcessor.doCommand("qxTableClick", new String[] {locator, opts1, }); } /** This will right-click on the qooxdoo table that is referenced * by the given locator string. The click will be located over the given row * and column of the table. */ public String qxTableContextMenu(String locator, int row, int col) { String opts = "row=" + row + ",col=" + col + ",button=right"; return this.commandProcessor.doCommand("qxTableClick", new String[] {locator, opts, }); } /** This will right-click on the qooxdoo table that is referenced * by the given locator string. The click will be located over the given row * and column of the table. */ public String qxTableContextMenu(String locator, int row, int col, String opts) { String opts1 = "row=" + row + ",col=" + col + ",button=right," + opts; return this.commandProcessor.doCommand("qxTableClick", new String[] {locator, opts1, }); } /** This will double-click on the qooxdoo table that is referenced * by the given locator string. The click will be located over the given row * and column of the table. */ public String qxTableDoubleClick(String locator, int row, int col) { String opts = "row=" + row + ",col=" + col + ",double=true"; return this.commandProcessor.doCommand("qxTableClick", new String[] {locator, opts, }); } /** This will double-click on the qooxdoo table that is referenced * by the given locator string. The click will be located over the given row * and column of the table. */ public String qxTableDoubleClick(String locator, int row, int col, String opts) { String opts1 = "row=" + row + ",col=" + col + ",double=true," + opts; return this.commandProcessor.doCommand("qxTableClick", new String[] {locator, opts1, }); } user-extensions-qooxdoo.js updates: /** Returns the qx global object so that we can use qx functionality * */ Selenium.prototype.getQxGlobalObject = function () { if (this.page()._globalQxObject) { return qx = this.page()._globalQxObject; } else { throw new SeleniumError("Qxh Locator: Need global qx object to search by attribute"); } }; /** * Utility function to do {object instanceof qxclass} comparisons. * Since the qx. namespace is not directly available, we have to go through * some extra steps. * <p> * Use quotes around qxclass when you pass it in. * * @param object {var} The object to check * @parame qxclass {var} The string name of the qx class type to compare against * @return returns true of object instanceof qxclass, false if not. */ Selenium.prototype.isQxInstanceOf = function (object, qxclass) { var qx = this.getQxGlobalObject(); var myClass = qx.Class.getByName(qxclass); LOG.debug("isQxInstanceOf checking (" + object.classname + ") against class (" + qxclass + ")"); try { if (object instanceof myClass) { return true; } } catch (e) { if (object.classname === qxclass) { return true; } } return false; }; /** * Uses the standard qx locators to find a table, and then returns the number of rows * from the table model. * * @type member * @param locator {var} an element locator * @return {var} The number of rows in the table. */ Selenium.prototype.getQxTableRows = function(locator) { var element = this.page().findElement(locator); if (!element) { throw new SeleniumError("No such object: " + locator) } var qx = this.getQxGlobalObject(); // this.page().findElement() returns the html element. // we also need the real object to work with. var qxObject = qx.ui.core.Widget.getWidgetByElement( element ); if(qxObject){ if(!this.isQxInstanceOf(qxObject, "qx.ui.table.Table")){ throw new SeleniumError("Object is not a qx Table: " + locator); } } else { throw new SeleniumError("Object is not a qx Table: " + locator); } return String(qxObject.getTableModel().getRowCount()); }; /** * Uses the standard qx locators to find a table, and then returns the number of columns * from the table model. * * @type member * @param locator {var} an element locator * @return {var} The number of columns in the table. */ Selenium.prototype.getQxTableCols = function(locator) { var element = this.page().findElement(locator); if (!element) { throw new SeleniumError("No such object: " + locator) } var qx = this.getQxGlobalObject(); // this.page().findElement() returns the html element. // we also need the real object to work with. var qxObject = qx.ui.core.Widget.getWidgetByElement( element ); if(qxObject){ if(!this.isQxInstanceOf(qxObject, "qx.ui.table.Table")){ throw new SeleniumError("Object is not a qx Table: " + locator); } } else { throw new SeleniumError("Object is not a qx Table: " + locator); } return String(qxObject.getTableModel().getColumnCount()); }; /** * Uses the standard qx locators to find a qooxdoo object, and then executes * the given function of that object. If the object does not contain the referenced * function, then an exception will be thrown. * * @type member * @param locator {var} an element locator * @param functionName {var} A text string that should identify the function to be executed. * @return {var} The return value from the function. */ Selenium.prototype.getQxObjectFunction = function(locator, functionName) { var element = this.page().findElement(locator); if (!element) { throw new SeleniumError("No such object: " + locator) } var qx = this.getQxGlobalObject(); // this.page().findElement() returns the html element. // we also need the real object to work with. var qxObject = qx.ui.core.Widget.getWidgetByElement( element ); if(qxObject){ if(qxObject[functionName]){ return qxObject[functionName](); } else { throw new SeleniumError("Object does not have function (" + functionName + "), " + locator); } } else { throw new SeleniumError("Object is not a qooxdoo object: " + locator); } }; /** * Uses the standard qx locators to find a table, and then returns the text * found in the cell at row, column position. * * @type member * @param locator {var} an element locator * @param eventParams {var} A text string that should contain "row=Y,col=X" * @return {var} The text found at the given table cell. */ Selenium.prototype.getQxTableValue = function(locator, eventParams) { var element = this.page().findElement(locator); if (!element) { throw new SeleniumError("No such object: " + locator) } var qx = this.getQxGlobalObject(); // this.page().findElement() returns the html element. // we also need the real object to work with. var qxObject = qx.ui.core.Widget.getWidgetByElement( element ); if(qxObject){ if(!this.isQxInstanceOf(qxObject, "qx.ui.table.Table")){ throw new SeleniumError("Object is not a qx Table: " + locator); } } else { throw new SeleniumError("Object is not a qx Table: " + locator); } var additionalParamsForClick = {}; if (eventParams && eventParams !== "") { var paramPairs = eventParams.split(","); for ( var i = 0; i < paramPairs.length; i++) { var onePair = paramPairs[i]; var nameAndValue = onePair.split("="); // rz: using String.trim from htmlutils.js of selenium to get rid of // whitespace var name = new String(nameAndValue[0]).trim(); var value = new String(nameAndValue[1]).trim(); additionalParamsForClick[name] = value; } } var row = Number(additionalParamsForClick["row"]); var col = Number(additionalParamsForClick["col"]); LOG.debug("Targeting Row(" + row + ") Column(" + col + ")"); return String(qxObject.getTableModel().getValue(col, row)); }; /** * Uses the standard qx locators to find a table, and then processes a click * on the table at the given row/column position. Note, your locator should * only find the table itself, and not the clipper child of the table. We'll * add the extra Composite/Scroller/Clipper to the locator as required. * * <p> * mousedown, mouseup will be fired instead of only click * additionaly to doQxClick the x-/y-coordinates of located element will be determined. * TODO: implement it like doFooAt, where additional coordinates will be added to the element-coords * <p> * eventParams example: button=left|right|middle, clientX=300, shiftKey=true * for a full list of properties see "function Selenium.prototype.qx.triggerMouseEventQx" * * @type member * @param locator {var} an element locator * @param row {var} table row to click * @param col {var} table column to click * @param eventParams {var} additional parameter for the mouse-event to set. e.g. clientX. * if no eventParams are set, defaults will be: left mousebutton, all keys false and all coordinates 0 * @return {void} */ Selenium.prototype.doQxTableClick = function(locator, eventParams) { var element = this.page().findElement(locator); if (!element) { throw new SeleniumError("No such object: " + locator) } var qx = this.getQxGlobalObject(); // this.page().findElement() returns the html element. // we also need the real object to work with. var qxObject = qx.ui.core.Widget.getWidgetByElement( element ); if(qxObject){ if(!this.isQxInstanceOf(qxObject, "qx.ui.table.Table")){ throw new SeleniumError("Object is not a qx Table: " + locator); } } else { throw new SeleniumError("Object is not a qx Table: " + locator); } // Now add the extra components to the locator to find the clipper itself. // This is the real object that we want to click on. element = this.page().findElement(locator + "/qx.ui.container.Composite/qx.ui.table.pane.Scroller/qx.ui.table.pane.Clipper" ); if (!element) { throw new SeleniumError("Could not find clipper child of the table"); } // Get the coordinates of the table: var coordsXY = getClientXY(element); LOG.debug("computed coords: X=" + coordsXY[0] + " Y=" + coordsXY[1]); var additionalParamsForClick = {}; if (eventParams && eventParams !== "") { var paramPairs = eventParams.split(","); for ( var i = 0; i < paramPairs.length; i++) { var onePair = paramPairs[i]; var nameAndValue = onePair.split("="); // rz: using String.trim from htmlutils.js of selenium to get rid of // whitespace var name = new String(nameAndValue[0]).trim(); var value = new String(nameAndValue[1]).trim(); additionalParamsForClick[name] = value; } } var row = Number(additionalParamsForClick["row"]); var col = Number(additionalParamsForClick["col"]); LOG.debug("Targeting Row(" + row + ") Column(" + col + ")"); var doContextMenu = false; var doDoubleClick = false; if(additionalParamsForClick["button"] && additionalParamsForClick["button"] === "right" ){ doContextMenu = true; } if(additionalParamsForClick["double"] && additionalParamsForClick["double"] === "true" ){ doDoubleClick = true; } // Focus the table row/column so that it can receive the click events //qxObject.setFocusedCell(col, row, true); // Adjust our row number to match the rows that are currently visible: var first_row = qxObject.getPaneScroller(0).getTablePane().getFirstVisibleRow(); var row_count = qxObject.getPaneScroller(0).getTablePane().getVisibleRowCount(); LOG.debug("qxTable firstVisibleRow(" + first_row + "), visibleRowCount(" + row_count + ")"); // If our target row is below or beyond the set of rows visible, then scroll // it into view: if( row < first_row || row >= (first_row + row_count)){ qxObject.setFocusedCell(col, row, true); // now it should be in the viewport first_row = qxObject.getPaneScroller(0).getTablePane().getFirstVisibleRow(); row_count = qxObject.getPaneScroller(0).getTablePane().getVisibleRowCount(); LOG.debug("qxTable firstVisibleRow(" + first_row + "), visibleRowCount(" + row_count + ")"); } // Adjust our "row" coordinate to be relative to the viewport: row = row - first_row; // Add in table height plus row height to get to the right row: LOG.debug("Table Header Height = " + qxObject.getHeaderCellHeight() ); LOG.debug("Table Row Height = " + qxObject.getRowHeight() ); coordsXY[1] = coordsXY[1] + ( row * qxObject.getRowHeight() ); // Add in the column widths for each column: for(var i = 0; i < col; i++){ LOG.debug("Column (" + i + ") Width = (" + qxObject.getTableColumnModel().getColumnWidth( i ) + ")" ); coordsXY[0] = coordsXY[0] + qxObject.getTableColumnModel().getColumnWidth( i ); } LOG.debug("updated coords: X=" + coordsXY[0] + " Y=" + coordsXY[1]); coordsXY[0] = coordsXY[0] + 5; coordsXY[1] = coordsXY[1] + 5; LOG.debug("final coords: X=" + coordsXY[0] + " Y=" + coordsXY[1]); // TODO: very dirty no checking, maybe refactoring needed to get doQxClick // and doQxClickAt to work smoothly together. var newEventParamString = eventParams + ",clientX=" + coordsXY[0] + ",clientY=" + coordsXY[1]; LOG.debug("newEventParamString=" + newEventParamString); // Always do a standard click to focus the cell this.clickElementQx(element, newEventParamString); // If requested, also do a context menu request: if(doContextMenu){ // subtract out the original element position: var origcoordsXY = getClientXY(element); coordsXY[0] = coordsXY[0] - origcoordsXY[0]; coordsXY[1] = coordsXY[1] - origcoordsXY[1]; LOG.debug("Calling selenium doContextMenuAt with X,Y=" + coordsXY[0] + "," + coordsXY[1]); this.doContextMenuAt(locator + "/qx.ui.container.Composite/qx.ui.table.pane.Scroller/qx.ui.table.pane.Clipper", coordsXY[0] + "," + coordsXY[1] ); } // If requested, also do a double-click request: if(doDoubleClick){ // subtract out the original element position: var origcoordsXY = getClientXY(element); coordsXY[0] = coordsXY[0] - origcoordsXY[0]; coordsXY[1] = coordsXY[1] - origcoordsXY[1]; LOG.debug("Calling selenium doDoubleClickAt with X,Y=" + coordsXY[0] + "," + coordsXY[1]); this.doDoubleClickAt(locator + "/qx.ui.container.Composite/qx.ui.table.pane.Scroller/qx.ui.table.pane.Clipper", coordsXY[0] + "," + coordsXY[1] ); } }; Sincerely, Mr. Hericus mr...@he... http://www.hericus.com/ Mr. Hericus wrote: > Hi Daniel, > > I tried your patch and it didn't raise the context menu for me > properly. I saw the contextmenu event added to the mouseover, > mousedown, etc. series of events, but the context menu still didn't come > up for me. > > <shrug> I was 80% of the way through some enhancements to add > click/contextmenu/double-click at row/cell positions in the table > anyway, so I went ahead and finished what I had started. If you would > like to add these updates to the simulator contribution, please feel free. > > I'm using junit for all of our testing, and this is one of the things > that I love about the Simulator is that it has support for running the > tests from junit, and driving the qooxdoo app :-) > QxSelenium.java class updates: > > > > Sincerely, > > Mr. Hericus > mr...@he... > http://www.hericus.com/ > > > Daniel Wagner wrote: > >> Hi again, >> >> right clicking now works as it should, by adding the button=right >> parameter to the click command. qooxdoo uses the mouse coordinates to >> place menus, so use qxClickAt if you want the menu to appear where it >> should be. >> >> As for table clicking, I don't know anything about how the table worked >> in qooxdoo 0.7, so I can't even tell if clicking a specific cell was >> ever possible with the Simulator. In 0.8, the table uses HTML divs for >> its cells, so it's not possible to write a qxh=... locator string that >> finds a specific cell. If you issue a qxClickAt command, Selenium uses >> the screen coordinates of the table's clipper widget for the mouse >> event, which explains why you could only select the top left corner. >> >> The qxClick command, however, allows you to specify the coordinates >> yourself: >> >> selenium.qxClick("qxh=qx.ui.table.Table/qx.ui.container.Composite/qx.ui.table.pane.Scroller/qx.ui.table.pane.Clipper", >> "clientX=350,clientY=270") >> >> This would click the table cell at viewport coordinates 350:270. >> >> >> Obviously, it would be nicer if you could just specify a cell in the >> locator without having to worry about table scrolling or screen >> coordinates. I've filed a bug report[1] for this. >> >> [1]http://bugzilla.qooxdoo.org/show_bug.cgi?id=2435 >> >> >> Regards, >> Daniel >> >> >> Daniel Wagner schrieb: >> >> >>> Hi, >>> >>> it looks like this is a bug in the Simulator contrib. I'll have to take >>> a closer look at how clicking tables is supposed to work, but in a quick >>> test case I could only click the top left corner of the table itself. >>> I'll check it out right now and reply with my findings. >>> >>> As for the right click, that's definitely a bug. I opened a report for >>> it[1]. On the bright side, I already found a fix that works in FF and >>> Opera, still have to test it in IE. >>> >>> I assume you're using the user-extensions-qooxdoo.js in the Simulator >>> trunk, right? >>> >>> [1]http://bugzilla.qooxdoo.org/show_bug.cgi?id=2432 >>> >>> Regards, >>> Daniel >>> >>> Mr. Hericus schrieb: >>> >>> >>>> Hi All, >>>> >>>> I've been working with the Simulator project for a bit now, and I really >>>> like the way it works and integrates into our existing testing >>>> environment. I've been struggling a bit, though, in getting the >>>> simulator to work well with tables (treevirtual will come next). What >>>> I'd like to be able to do is to click, double-click, and right-click >>>> (context menu), at any row/column position (row only would suffice) in >>>> the table. >>>> >>>> I've read everything I can find on the qooxdoo wiki, nabble for the >>>> mailing list archives, and general google. I haven't been able to find >>>> much. There are some tantalizing clues, such as: >>>> http://qooxdoo.org/contrib/project/simulator/qooxdoo-tests-with-selenium#clicking_in_tables >>>> but there's nothing there other than the advice to "use qxClickAt". >>>> qxClickAt doesn't work for me, and doesn't provide double-click or >>>> right-click. >>>> >>>> The core selenium interface does provide double-click and >>>> right-click/context menu functionality. Is this the way that I need to >>>> go? How do I get row/column positioning correct? >>>> >>>> I've read through the whole source of the user-extensions.js file >>>> provided by the Simulator package, and have even updated a few bits of >>>> it because of problems with the debug messages when used with our >>>> application. I'm more than happy to extend this interface to provide >>>> more direct support for qooxdoo tables, but I don't want to go down that >>>> path if I'm just missing something obvious. I don't think I am, but >>>> there you go. The collective knowledge of this group is much more >>>> powerful than my own :-) >>>> >>>> Any help or guidance would be much appreciated, and thanks in general >>>> for the Simulator project. It's very useful :-) >>>> >>>> Sincerely, >>>> >>>> Mr. Hericus >>>> mr...@he... >>>> http://www.hericus.com/ >>>> >>>> >>>> >>>> >>>> ------------------------------------------------------------------------------ >>>> Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT >>>> is a gathering of tech-side developers & brand creativity professionals. Meet >>>> the minds behind Google Creative Lab, Visual Complexity, Processing, & >>>> iPhoneDevCamp as they present alongside digital heavyweights like Barbarian >>>> Group, R/GA, & Big Spaceship. http://p.sf.net/sfu/creativitycat-com >>>> _______________________________________________ >>>> qooxdoo-devel mailing list >>>> qoo...@li... >>>> https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel >>>> >>>> >>>> >>>> >>> ------------------------------------------------------------------------------ >>> Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT >>> is a gathering of tech-side developers & brand creativity professionals. Meet >>> the minds behind Google Creative Lab, Visual Complexity, Processing, & >>> iPhoneDevCamp as they present alongside digital heavyweights like Barbarian >>> Group, R/GA, & Big Spaceship. http://p.sf.net/sfu/creativitycat-com >>> _______________________________________________ >>> qooxdoo-devel mailing list >>> qoo...@li... >>> https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel >>> >>> >>> >>> >> ------------------------------------------------------------------------------ >> Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT >> is a gathering of tech-side developers & brand creativity professionals. Meet >> the minds behind Google Creative Lab, Visual Complexity, Processing, & >> iPhoneDevCamp as they present alongside digital heavyweights like Barbarian >> Group, R/GA, & Big Spaceship. http://p.sf.net/sfu/creativitycat-com >> _______________________________________________ >> qooxdoo-devel mailing list >> qoo...@li... >> https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel >> >> >> > > > > ------------------------------------------------------------------------------ > Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT > is a gathering of tech-side developers & brand creativity professionals. Meet > the minds behind Google Creative Lab, Visual Complexity, Processing, & > iPhoneDevCamp as they present alongside digital heavyweights like Barbarian > Group, R/GA, & Big Spaceship. http://p.sf.net/sfu/creativitycat-com > _______________________________________________ > qooxdoo-devel mailing list > qoo...@li... > https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel > > ------------------------------ ------------------------------------------------------------------------------ Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT is a gathering of tech-side developers & brand creativity professionals. Meet the minds behind Google Creative Lab, Visual Complexity, Processing, & iPhoneDevCamp as they present alongside digital heavyweights like Barbarian Group, R/GA, & Big Spaceship. http://p.sf.net/sfu/creativitycat-com ------------------------------ _______________________________________________ qooxdoo-devel mailing list qoo...@li... https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel End of qooxdoo-devel Digest, Vol 36, Issue 103 ********************************************** |