<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>appointsolutions.com</title>
	<atom:link href="http://appointsolutions.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://appointsolutions.com</link>
	<description>Rich Mobile and Web Applications</description>
	<lastBuildDate>Sun, 15 Apr 2012 21:55:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.1</generator>
		<item>
		<title>Ludei interviews Appoint:solutions about CocoonJS</title>
		<link>http://appointsolutions.com/2012/03/ludei-interviews-appointsolutions-about-cocoonjs/</link>
		<comments>http://appointsolutions.com/2012/03/ludei-interviews-appointsolutions-about-cocoonjs/#comments</comments>
		<pubDate>Tue, 06 Mar 2012 21:58:42 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://appointsolutions.com/?p=473</guid>
		<description><![CDATA[<p><a href="http://ludei.com/" target="_blank"></a>This week at the <a href="http://www.gdconf.com/" target="_blank">Game Developer Conference</a> in San Francisco, <a href="http://www.ludei.com/" target="_blank">Ludei</a> announced their new flagship product <a title="CocoonJS" href="http://ludei.com/tech/cocoonjs" target="_blank">CocoonJS</a>. The free product will provide HTML5 game developers with an extremely easy to use native wrapper around the game. Besides giving the resulting app a performance and speed increase [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://ludei.com/" target="_blank"><img class="alignright size-full wp-image-478" style="background-color: #000; padding: 20px; border-radius: 10px;" title="ludeilogo" src="http://appointsolutions.com/wp-content/uploads/2012/03/ludeilogo.png" alt="" width="69" height="79" /></a>This week at the <a href="http://www.gdconf.com/" target="_blank">Game Developer Conference</a> in San Francisco, <a href="http://www.ludei.com/" target="_blank">Ludei</a> announced their new flagship product <a title="CocoonJS" href="http://ludei.com/tech/cocoonjs" target="_blank">CocoonJS</a>. The free product will provide HTML5 game developers with an extremely easy to use native wrapper around the game. Besides giving the resulting app a performance and speed increase of up to 1000% it also provides access to device hardware and various service such as ad networks and Facebook. All this without any changes to the HTML5 game sources. Appoint:solutions was one of the first in the world allowed to beta-test the framework . Ludei sent us some questions asking us to talk about our experiences in launching complex applications. You can read the the full interview on <a href="http://blog.ludei.com/interview-with-appoint-solutions/" target="_blank">Ludei&#8217;s blog</a> or right here.</p>
<h3>How did you get started?</h3>
<p><a href="http://appointsolutions.com/wp-content/uploads/2012/03/Icon@2x.png"><img class="alignleft size-full wp-image-438" title="Icon@2x" src="http://appointsolutions.com/wp-content/uploads/2012/03/Icon@2x.png" alt="" width="114" height="114" /></a>After closing some other projects, we started working on a mobile online multiplayer game in November 2011. Until that time most experts agreed that HTML5 was only usable for simple turn-based board games without much movement. We wanted to prove that it was feasible to create a mobile game with fast moving graphics, realtime multiplayer gameplay and interaction, and multichannel sound, all using HTML5 techniques. At the same time, the game should be deployed to iOS, Android and Facebook with a single code-base. Not an easy task, but hey, we do like a challenge.</p>
<h3>What approach did you take?</h3>
<p>To solve the many technical challenges we tried everything from game engines (Custom, ImpactJS, CAAT), server possibilities and deployment strategies. The almost weekly pivots made us a bit nervous (in fact, it was driving Bert crazy) but we decided to continue to anyway. The deployment was the most challenging. We already used PhoneGap for deployment to different app stores. PhoneGap solved some multichannel sound issues in standard HTML5, but to be honest, the solution felt uncomfortable. We also had a number of challenges that were left unsolved:<a href="http://appointsolutions.com/wp-content/uploads/2012/03/html5_logo.jpg"><img class="size-medium wp-image-459 alignright" title="html5_logo" src="http://appointsolutions.com/wp-content/uploads/2012/03/html5_logo-213x300.jpg" alt="" width="128" height="180" /></a></p>
<ul>
<li>Canvas rendering performance was below par. Even after extensive optimization we only achieved around 10 frames per second when we had many movements on screen. For 1990 this would have been great, but for 2012 perhaps not!</li>
<li>Websockets were available, but because the rendering was so intensive on the browser and therefore CPU, packets were being delayed. This caused the realtime game functionality to be broken (we require around 20 packets/second minimum server rate).</li>
<li>Sound was working, but hacked-in at best. We needed to create a separate sound player for browser deployment and a hook into PhoneGap for mobile.</li>
<li>Ad-network integration in a HTML5 based game was buggy at best.</li>
</ul>
<p>That meant: Back to the drawing board…</p>
<h3>So, What did you do next?</h3>
<p>We first tried appMobi’s services and tools, both commercial and open-source. AppMobi’s game developer tools solve a number of problems like rendering speed and multichannel sound (and from what I hear add integration as well). The Websockets however still work from the browser, as well as user input. Therefore as a developer you have 2 separated environments that have to communicate with eachother though serialized calls (EG. directCanvas.execute(“movePointer(“+x+”,”+y+”);”) ). That communication channel is a nasty bottleneck and is not usable for anything that requires fast updates. Aside from that, the game has to be separated into 2 environments for mobile, breaking our write-once-deploy-everywhere requirement.</p>
<h3>So you decided to give CocoonJS a spin?</h3>
<p><a href="http://ludei.com/tech/cocoonjs" target="_blank"><img class="size-full wp-image-436 alignright" title="logo-cocoonjs" src="http://appointsolutions.com/wp-content/uploads/2012/03/logo-cocoonjs.png" alt="" width="129" height="116" /></a>Yes. Through our CAAT experiences I (RB) got into contact with Ibon Tolosana and Ludei. The description of CocoonJS on the site, a seamless environment that runs HTML5 games without any changes, sounded too good to be true. Ibon emphasized however that that was exactly what was being created and told me about some other solutions that would be accompanied by it. He then offered me the chance to beta-test CocoonJS on our own game sources which I happily accepted. The current version of CocoonJS solves all our problems for us. In the most extreme cases we are still getting around 50 frames per second rendering speeds, multichannel sound and Websockets without delay. It also gives us easy ad-network integration and more. The best thing about that is that it hardly requires any work to make all this work in CocoonJS. Almost all HTML5 functionalities like sound, canvas operations, local storage and sockets are supported transparently. We can’t wait to launch our game ‘MonsterMove’ and start work on the next project using CAAT and CocoonJS!</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2012/03/ludei-interviews-appointsolutions-about-cocoonjs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MonsterMove  a HTML5 Multiplayer Mobile &amp; Facebook game</title>
		<link>http://appointsolutions.com/2012/03/monstermove-html5-multiplayer-game/</link>
		<comments>http://appointsolutions.com/2012/03/monstermove-html5-multiplayer-game/#comments</comments>
		<pubDate>Thu, 01 Mar 2012 11:14:36 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[Products]]></category>

		<guid isPermaLink="false">http://appointsolutions.com/?p=422</guid>
		<description><![CDATA[<p>Appoint Solutions has decided to break the conventional thoughts that HTML5 is not yet ready for fast moving realtime online multiplayer games by doing exactly that. The resulting game is called <a title="MonsterMove" href="http://monstermove.co" target="_blank">MonsterMove</a>, created completely with javascript and HTML5 and running on <a title="Ludei" href="http://ludei.com/" target="_blank">Ludei&#8217;s</a> brand new <a title="CocoonJS" href="http://ludei.com/tech/cocoonjs" target="_blank">CocoonJS</a> platform.</p> MonsterMove [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignright size-medium wp-image-459" title="html5_logo" src="http://appointsolutions.com/wp-content/uploads/2012/03/html5_logo-213x300.jpg" alt="" width="77" height="108" />Appoint Solutions has decided to break the conventional thoughts that HTML5 is not yet ready for fast moving realtime online multiplayer games by doing exactly that. The resulting game is called <a title="MonsterMove" href="http://monstermove.co" target="_blank">MonsterMove</a>, created completely with javascript and HTML5 and running on <a title="Ludei" href="http://ludei.com/" target="_blank">Ludei&#8217;s</a> brand new <a title="CocoonJS" href="http://ludei.com/tech/cocoonjs" target="_blank">CocoonJS</a> platform.</p>
<h3>MonsterMove</h3>
<p><a href="http://monstermove.co" target="_blank"><img class="size-full wp-image-438 alignleft" title="Icon@2x" src="http://appointsolutions.com/wp-content/uploads/2012/03/Icon@2x.png" alt="" width="114" height="114" /></a>MonsterMove is an online multiplayer game, that you can play against your friends, right from your iOS or Android mobile phone or on Facebook. The game objective is to avoid the invasion of earth by monsters coming from planet DooVoo. By sliding rows and columns of monsters, you have to make combinations of 3 or more monsters, which will transport the monsters back to their own world. And of course score points!</p>
<p style="text-align: center;"><a href="http://appointsolutions.com/wp-content/uploads/2012/03/game-screen.png"></a><a href="http://monstermove.co" target="_blank"><img class="aligncenter size-full wp-image-449" title="game-screen" src="http://appointsolutions.com/wp-content/uploads/2012/03/game-screen.png" alt="" width="573" height="294" /></a></p>
<p style="text-align: left;">Easy? Think again! You have to outsmart and outplay your opponent. When you move your own rows and columns, you also move those of your opponent. The trick is to score points on your side, but avoid making combinations on the side of your opponent.</p>
<p>If you make big combo&#8217;s you get VooDoo powers from the monsters. You can use VooDoo to either help yourself or sabotage your opponent.</p>
<h3><a title="CocoonJS" href="http://ludei.com/tech/cocoonjs"><img class="alignright size-full wp-image-436" title="logo-cocoonjs" src="http://appointsolutions.com/wp-content/uploads/2012/03/logo-cocoonjs.png" alt="" width="116" height="104" /></a></h3>
<h3>CocoonJS</h3>
<p>CocoonJS is a JavaScript wrapper executed inside the Cocoon platform. It aims to turn JavaScript applications into performance-enhanced native applications by providing a huge performance boost by using OpenGL native binding and other features. The completely transparent functionality and extreme rendering speed increase with CocoonJS are simply amazing.</p>
<p>CocoonJS is currently under private beta. <a title="CocoonJS" href="http://ludei.com/tech/cocoonjs" target="_blank">CocoonJS</a> creators <a title="Ludei" href="http://ludei.com" target="_blank">Ludei</a> have allowed Appoint:solutions to be their technology testers, and as a result we are proud to present one of the first games in the world that offers this level of speed and online interaction. For more news about CocoonJS, look for Ludei at <a title="GDC-12" href="http://www.gdconf.com/" target="_blank">GDC-12</a>.</p>
<h3>Launch</h3>
<p>Launch of the MonsterMove is foreseen on Iphone, Android and Facebook in the first weeks of March 2012. Check back here soon or visit the <a title="MonsterMove" href="http://monstermove.co" target="_blank">MonsterMove site</a> for news about the launch.</p>
<p><img class="alignleft" title="appstore" src="http://appointsolutions.com/wp-content/uploads/2011/10/appstore1.png" alt="" width="300" height="98" /><img class="size-full wp-image-414 alignleft" title="android" src="http://appointsolutions.com/wp-content/uploads/2011/10/android.png" alt="" width="300" height="104" /><img class="size-thumbnail wp-image-442 alignleft" title="facebook-icon" src="http://appointsolutions.com/wp-content/uploads/2012/03/facebook-icon-150x150.png" alt="" width="110" height="110" /><br />
<a href="http://appointsolutions.com/wp-content/uploads/2011/10/android.png"></a></p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2012/03/monstermove-html5-multiplayer-game/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sencha Touch TimePicker form field and Picker sheet</title>
		<link>http://appointsolutions.com/2011/09/sencha-touch-timepicker-form-field-picker-sheet/</link>
		<comments>http://appointsolutions.com/2011/09/sencha-touch-timepicker-form-field-picker-sheet/#comments</comments>
		<pubDate>Fri, 02 Sep 2011 13:32:45 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[Sencha Touch]]></category>

		<guid isPermaLink="false">http://appointsolutions.com/?p=367</guid>
		<description><![CDATA[<p><a href="http://splog.me" target="_blank"></a></p> <p>Currently we are using Sencha Touch as the basis for our new <a href="http://splog.me" target="_blank">splog.me</a> app.The Sencha Touch 1.1 framework includes a very large number of readily available form fields including the DatePicker form field and DatePicker popup sheet itself. Strangely enough, a TimePicker is missing so we decided to build one [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://splog.me" target="_blank"><img class="alignleft size-medium wp-image-385" title="Splog.me" src="http://appointsolutions.com/wp-content/uploads/2011/09/dashboard-155x300.png" alt="" width="155" height="300" /></a></p>
<p>Currently we are using Sencha Touch as the basis for our new <a href="http://splog.me" target="_blank">splog.me</a> app.The Sencha Touch 1.1 framework includes a very large number of readily available form fields including the DatePicker form field and DatePicker popup sheet itself. Strangely enough, a TimePicker is missing so we decided to build one ourselves. The TimePicker below is heavily based on the DatePicker classes and includes a form field and a picker.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3>Ext.ux.form.TimePicker</h3>
<p>To quickly demonstrate, the screenshots below show a TimePicker without any custom configuration and one that only shows a certain timespan and shows every fifth minute.</p>
<p><a href="http://appointsolutions.com/wp-content/uploads/2011/09/Schermafbeelding-2011-09-02-om-15.07.40.png"><img class="size-medium wp-image-368 alignleft" title="TimePicker 1" src="http://appointsolutions.com/wp-content/uploads/2011/09/Schermafbeelding-2011-09-02-om-15.07.40-159x300.png" alt="" width="159" height="300" /></a></p>
<p><a href="http://appointsolutions.com/wp-content/uploads/2011/09/Schermafbeelding-2011-09-02-om-15.08.33.png"><img class="alignleft size-medium wp-image-369" title="TimePicker 2" src="http://appointsolutions.com/wp-content/uploads/2011/09/Schermafbeelding-2011-09-02-om-15.08.33-159x300.png" alt="" width="159" height="300" /></a></p>
<h3 style="clear: both;">Usage</h3>
<p>The TimePicker can be inserted into a form using it&#8217;s xtype &#8216;timepickerfield&#8217; or by creating an instance yourselve.</p>
<pre>[javascript]
	var startTime = new Ext.ux.form.TimePicker({
		name : 'starttime',
		label: 'Start time'
	});
[/javascript]</pre>
<p>Or it can be customized with the starting hour, ending hour and minute interval:</p>
<pre>[javascript]
	var startTime = new Ext.ux.form.TimePicker({
		name : 'starttime',
		label: 'Start time',
		minuteScale: 5,
		hourFrom: 8,
		hourTo: 18,
		value: {
			hour: (new Date().getHours()+1)%24, /* start with the next hour */
			minute: 0
		}
	});
[/javascript]</pre>
<p>Feel free to use the timepicker in your Sencha Touch app and let me know when you do. Always nice to see other great mobile apps. If  you think of any improvements (EG. making it configurable for 24h/ AM PM format), let me know also. The code is displayed below and attached to this post as a tgz archive.</p>
<p>Cheers,<br />
Rob</p>
<h3>Code</h3>
<p><a href="/wp-content/uploads/2011/10/ux.TimePicker.tgz">Download both classes</a></p>
<h3>Ext.ux.Form.TimePicker</h3>
<pre>[javascript]
Ext.ns('Ext.ux.form');
/**
 * @class Ext.ux.form.TimePicker
 * @extends Ext.form.Field
 *
Specialized field which has a button which when pressed, shows a {@link Ext.ux.TimePicker}.

 * @xtype timepickerfield
 */
Ext.ux.form.TimePicker = Ext.extend(Ext.form.Field, {
    ui: 'select',

	/**
     * @cfg {Number} minuteScale
     * List every how many minutes, eg. 5 lists 0, 5, 10, 15, etc. Defaults to 1
     */
    minuteScale: 1, 

	/**
     * @cfg {Number} hourFrom
     * The start hour for the time picker.  Defaults to 0
     */
    hourFrom: 0,

    /**
     * @cfg {Number} hourTo
     * The last hour for the time picker.  Defaults to 23
     */
    hourTo: 23,

	/**
     * @cfg {Object/Ext.ux.TimePicker} picker
     * An object that is used when creating the internal {@link Ext.ux.TimePicker} component or a direct instance of {@link Ext.ux.TimePicker}
     * Defaults to null
     */
    picker: null,

	/**
     * @cfg {Object/Time} value
     * Default value for the field and the internal {@link Ext.ux.TimePicker} component. Accepts an object of 'hour',
     * and 'minute' values, all of which should be numbers, or a Time string.
     *
     * Example: {hour: 18, minute: 15} = 18:15
     */
	/**
     * @cfg {Boolean} destroyPickerOnHide
     * Whether or not to destroy the picker widget on hide. This save memory if it's not used frequently,
     * but increase delay time on the next show due to re-instantiation. Defaults to false
     */
    destroyPickerOnHide: false,

    // @cfg {Number} tabIndex @hide

    // @cfg {Boolean} useMask @hide

    // @private
    initComponent: function() {
        this.addEvents(

			/**
             * @event change
             * Fires when a Time is selected
             * @param {Ext.ux.form.TimePicker} this
             * @param {Time} Time The new Time
             */
            'change'
        );

        this.tabIndex = -1;
        this.useMask = true;

        Ext.form.Text.superclass.initComponent.apply(this, arguments);
    },

	/**
     * Get an instance of the internal Time picker; will create a new instance if not exist.
     * @return {Ext.ux.TimePicker} TimePicker
     */
    getTimePicker: function() {
        if (!this.timePicker) {
            if (this.picker instanceof Ext.ux.TimePicker) {
                this.timePicker = this.picker;
            } else {
                this.timePicker = new Ext.ux.TimePicker(Ext.apply(this.picker || {
					minuteScale: this.minuteScale,
					hourFrom: this.hourFrom,
					hourTo: this.hourTo
				}));
            }

            this.timePicker.setValue(this.value || null);

            this.timePicker.on({
                scope : this,
                change: this.onPickerChange,
                hide  : this.onPickerHide
            });
        }

        return this.timePicker;
    },

    /**
     * @private
     * Listener to the tap event of the mask element. Shows the internal {@link #timePicker} component when the button has been tapped.
     */
    onMaskTap: function() {
        if (Ext.ux.form.TimePicker.superclass.onMaskTap.apply(this, arguments) !== true) {
            return false;
        }

        this.getTimePicker().show();
    },

    /**
     * Called when the picker changes its value
     * @param {Ext.ux.TimePicker} picker The time picker
     * @param {Object} value The new value from the time picker
     * @private
     */
    onPickerChange : function(picker, value) {
        this.setValue(value);
        this.fireEvent('change', this, this.getValue());
    },

    /**
     * Destroys the picker when it is hidden, if
     * {@link Ext.ux.form.icker#destroyPickerOnHide destroyPickerOnHide} is set to true
     * @private
     */
    onPickerHide: function() {
        if (this.destroyPickerOnHide &amp;&amp; this.timePicker) {
            this.timePicker.destroy();
        }
    },

    // inherit docs
    setValue: function(value, animated) {
        if (this.timePicker) {
            this.timePicker.setValue(value, animated);
            this.value = (value != null) ? this.timePicker.getValue() : null;
        } else {
            if (Ext.isObject(value)) {
				var hour = value.hour+"";
				hour = hour.length == 1 ? 0 + hour : hour;
				var minute = value.minute+"";
				minute = minute.length == 1 ? 0 + minute : minute;
				this.value =  hour+":"+minute;
            } else {
                this.value = value;
            }
        }

        if (this.rendered) {
            this.fieldEl.dom.value = this.getValue(true);
        }

        return this;
    },

	/**
     * Returns the value of the field, which will be a {@link Time} unless the format parameter is true.
     * @param {Boolean} format True to format the value with Ext.util.Format.defaultTimeFormat
     */
    getValue: function(format) {
        var value = this.value || null;

		if (Ext.isObject(value)) {
			var hour = value.hour+"";
			hour = hour.length == 1 ? 0 + hour : hour;
			var minute = value.minute+"";
			minute = minute.length == 1 ? 0 + minute : minute;
			return hour+":"+minute;
		}
		return value;
    },

    // @private
    onDestroy: function() {
        if (this.timePicker) {
            this.timePicker.destroy();
        }

        Ext.ux.form.TimePicker.superclass.onDestroy.call(this);
    }
});

Ext.reg('timepickerfield', Ext.ux.form.TimePicker);
[/javascript]</pre>
<h3>Ext.ux.Form.TimePicker</h3>
<pre>[javascript]
Ext.ns('Ext.ux');

/**
 * @class Ext.ux.TimePicker
 * @extends Ext.Picker
 *
 *

A time picker component which shows a TimePicker on the screen. This class extends from {@link Ext.Picker} and {@link Ext.Sheet} so it is a popup.

 *

This component has no required properties.

 *
 *
<h2>Useful Properties</h2>

*
<ul class="list">
 *
<li>{@link #hourFrom}</li>

*
<li>{@link #hourTo}</li>

*
</ul>

*
 *
<h2>Example code:</h2>

*
 *
<pre><code>
var timePicker = new Ext.ux.TimePicker();
timePicker.show();
 * </code></pre>
<p>*<br />
*</p>
<p>you may want to adjust the {@link #hourFrom}, {@link #hourTo} and {@link #minuteScale} properties:<br />
*</p>
<pre><code>
var timePicker = new Ext.ux.TimePicker({
    hourFrom: 8,
    hourTo  : 18,
	minuteScale: 5
});
timePicker.show();
 * </code></pre>
<p>*<br />
* @constructor<br />
* Create a new Timepicker<br />
* @param {Object} config The config object<br />
* @xtype timepicker<br />
*/<br />
Ext.ux.TimePicker = Ext.extend(Ext.Picker, {<br />
/**<br />
* @cfg {Number} minuteScale<br />
* List every how many minutes, eg. 5 lists 0, 5, 10, 15, etc. Defaults to 1<br />
*/</p>
<p>minuteScale: 1,</p>
<p>/**<br />
* @cfg {Number} hourFrom<br />
* The start hour for the time picker.  Defaults to 0<br />
*/<br />
hourFrom: 0,</p>
<p>/**<br />
* @cfg {Number} hourTo<br />
* The last hour for the time picker.  Defaults to 23<br />
*/<br />
hourTo: 23,</p>
<p>/**<br />
* @cfg {String} hourText<br />
* The label to show for the hour column. Defaults to 'Hour'.<br />
*/<br />
hourText: 'Hour',</p>
<p>/**<br />
* @cfg {String} minuteText<br />
* The label to show for the minute column. Defaults to 'Minute'.<br />
*/<br />
minuteText: 'Minute',</p>
<p>/**<br />
* @cfg {Array} slotOrder<br />
* An array of strings that specifies the order of the slots. Defaults to <tt>['hour', 'minute']</tt>.<br />
*/<br />
slotOrder: ['hour', 'minute'],</p>
<p>initComponent: function() {<br />
var hoursFrom = this.hourFrom,<br />
hoursTo = this.hourTo,<br />
hours = [],<br />
minutes = [],<br />
ln, tmp, i, j;</p>
<p>// swap values if user mixes them up.<br />
if (hoursFrom &gt; hoursTo) {<br />
tmp = hoursFrom;<br />
hoursFrom = hoursTo;<br />
hoursTo = tmp;<br />
}</p>
<p>for (i = j = hoursFrom; i &lt;= hoursTo; i++, j++) { 			j = (j+"").length &gt; 1 ? j : "0"+j;<br />
hours.push({<br />
text: j,<br />
value: i<br />
});<br />
}</p>
<p>for (i = j = 0; i &lt;= 59; i = j = i + this.minuteScale) { 			j = (j+"").length &gt; 1 ? j : "0"+j;<br />
minutes.push({<br />
text: j,<br />
value: i<br />
});<br />
}</p>
<p>this.slots = [];</p>
<p>this.slotOrder.forEach(function(item){<br />
this.slots.push(this.createSlot(item, hours, minutes ));<br />
}, this);</p>
<p>Ext.ux.TimePicker.superclass.initComponent.call(this);<br />
},</p>
<p>afterRender: function() {<br />
Ext.ux.TimePicker.superclass.afterRender.apply(this, arguments);</p>
<p>this.setValue(this.value);<br />
},</p>
<p>createSlot: function(name, hours, minutes ){<br />
switch (name) {<br />
case 'hour':<br />
return {<br />
name: name,<br />
align: 'right',<br />
data: hours,<br />
title: this.useTitles ? this.hourText : false,<br />
flex: 5<br />
};<br />
case 'minute':<br />
return {<br />
name: name,<br />
align: 'left',<br />
data: minutes,<br />
title: this.useTitles ? this.minuteText : false,<br />
flex: 5<br />
};<br />
}<br />
},</p>
<p>// @private<br />
onSlotPick: function(slot, value) {</p>
<p>Ext.ux.TimePicker.superclass.onSlotPick.apply(this, arguments);<br />
},</p>
<p>/**<br />
* Gets the current value as a Time object<br />
* @return {hour: x, minute: y} value<br />
*/<br />
getValue: function() {<br />
var value = Ext.ux.TimePicker.superclass.getValue.call(this);</p>
<p>return value;<br />
},</p>
<p>/**<br />
* Sets the values of the TimePicker's slots<br />
* @param {Date/Object} value The value either in a {hour:'value', minute:'value'} format or a String, eg: '18:00'<br />
* @param {Boolean} animated True for animation while setting the values<br />
* @return {Ext.DatePicker} this This DatePicker<br />
*/<br />
setValue: function(value, animated) {<br />
if (Ext.isObject(value)) {<br />
this.value = value;<br />
} else {<br />
var arr = (value+"").split(':');<br />
this.value = {<br />
hour: parseInt(arr[0],10),<br />
minute: parseInt(arr[1],10)<br />
};<br />
}</p>
<p>return Ext.ux.TimePicker.superclass.setValue.call(this, this.value, animated);<br />
}<br />
});</p>
<p>Ext.reg('timepicker', Ext.ux.TimePicker);<br />
[/javascript]</pre>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2011/09/sencha-touch-timepicker-form-field-picker-sheet/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>Mobile User-Experience: Sencha Touch LoadMask delayed</title>
		<link>http://appointsolutions.com/2011/08/sencha-touch-loadmask-delayed-rendering/</link>
		<comments>http://appointsolutions.com/2011/08/sencha-touch-loadmask-delayed-rendering/#comments</comments>
		<pubDate>Tue, 16 Aug 2011 11:16:39 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[Code snippets]]></category>

		<guid isPermaLink="false">http://appointsolutions.com/?p=343</guid>
		<description><![CDATA[<p>The <a title="Sencha Touch" href="http://www.sencha.com/products/touch/">Sencha Touch</a> HTML5 mobile web app framework is great for building cross-platform mobile applications. On current mobile browsers the performance can however be tricky. For our splog.me application we squeeze every bit of performance out of Sencha Touch. This post describes an easy way to make sure a LoadMask in Sencha [...]]]></description>
			<content:encoded><![CDATA[<p>The <a title="Sencha Touch" href="http://www.sencha.com/products/touch/">Sencha Touch</a> HTML5 mobile web app framework is great for building cross-platform mobile applications. On current mobile browsers the performance can however be tricky. For our splog.me application we squeeze every bit of performance out of Sencha Touch. This post describes an easy way to make sure a LoadMask in Sencha Touch is shown before anything else happens, making the perceived user experience better.</p>
<h3>Perceived speed</h3>
<p>There are a number of tricks one can apply to the design of a Sencha Touch application to optimize the performance on mobile devices. Tommy Maintz&#8217;s writeups about <a title="Event delegation" href="http://www.sencha.com/blog/event-delegation-in-sencha-touch/">Event Delegation</a> and  <a title="Optimizing Memory Usage" href="http://www.sencha.com/blog/sencha-touch-optimizing-memory-usage/">Optimizing Memory Usage</a> should be compulsory starting points for anyone who is serious about building a more-than-trivial Sencha Touch based app.</p>
<p><a href="http://appointsolutions.com/wp-content/uploads/2011/08/Schermafbeelding-2011-08-16-om-12.20.51.png"><img class="alignleft size-medium wp-image-344" title="Schermafbeelding 2011-08-16 om 12.20.51" src="http://appointsolutions.com/wp-content/uploads/2011/08/Schermafbeelding-2011-08-16-om-12.20.51-159x300.png" alt="" width="127" height="240" /></a>One thing about the user experience that is often forgotten about is the <strong>perceived</strong> speed of an application. Users of applications, whether they are mobile of internet applications, don&#8217;t mind too much about waiting a second or two for an action to finish, as long as they know what is going on. Nobody likes staring at a blank screen like the one on the left, wondering if anything still is going on.</p>
<h3 style="clear: left;">The load mask</h3>
<p><a href="http://appointsolutions.com/wp-content/uploads/2011/08/Schermafbeelding-2011-08-16-om-12.19.56.png"><img class="alignright size-medium wp-image-348" title="Schermafbeelding 2011-08-16 om 12.19.56" src="http://appointsolutions.com/wp-content/uploads/2011/08/Schermafbeelding-2011-08-16-om-12.19.56-159x300.png" alt="" width="127" height="240" /></a><br />
One of the simplest ways of accomplishing this in Sencha Touch is of course by using a loading mask that shows a spinner and a simple waiting text. The mask should immediately show once the action is initiated and hidden once it finishes. For maximum performance we create a global loadMask that can easily be called through the app namespace.</p>
<pre>[javascript]
myTapHandler: function() {
	MyApp.mask();
	Ext.dispatch({
		controller: 'controllerX',
		action: 'loadComplexPanelIntoCardLayout'
	});
}
[/javascript]</pre>
<p>And in our app.js:</p>
<pre>[javascript]
mask: function() {
	this.views.viewport.loadMask.show();
}
[/javascript]</pre>
<h3>The problem</h3>
<p>The panel&#8217;s afterrender event is used to make a call to &#8216;MyApp.unmask()&#8217; that removes the mask. This setup should show our mask before loading and hide it after the new panel is rendered right? Wrong.</p>
<p>Now this is the part where things start getting a bit annoying. We are calling the show method of the load mask <strong>before</strong> our controller is instructed to start loading a new panel, but the mask is only shown <strong>after</strong> the entire panel has finished rendering. In fact at first you won&#8217;t see the loadMask at all because it is only shown after the panel is rendered, but the panels afterrender event immediately hides it again. If you&#8217;re lucky you might just catch a quick glimpse of it. Spitting through the code you make sure that the entire loadMask code from Sencha Touch is finished before the panel starts loading, then what is the problem???</p>
<p>Sencha Touch is not the problem, the JavaScript engine from your mobile browser is. The engine creates the required elements in the DOM, but rendering of the new elements (the load mask) is deferred until it &#8216;has time&#8217;. It is still busy finishing the rest of your call, creating the new panel and inserting it&#8217;s required elements into the DOM as well. The rendering process is only started after that.</p>
<h3>The solution</h3>
<p>We found a quick and easy solution to this problem: we force the Javascript engine to finish the flow of our first call first, including rendering and starting the Controller action only after that. For this we created a custom dispatch method that defers the call to the controller by 1 microsecond. What this does is that by means of JavaScript&#8217;s setTimeout function, a new function call is scheduled immediately. It will however only begin processing once the process that is currently active has finished, allowing the process to complete the rendering process as well.</p>
<p>in app.js</p>
<pre>[javascript]
dispatch: function(config) {
	new Ext.util.DelayedTask(function(){
		Ext.dispatch(config);
	}).delay(1);
}
[/javascript]</pre>
<p>We can then replace our original dispatch call by:</p>
<pre>[javascript]
myTapHandler: function() {
	MyApp.mask();
	MyApp.dispatch({
		controller: 'controllerX',
		action: 'loadComplexPanelIntoCardLayout'
	});
}
[/javascript]</pre>
<p>And voila, the loadMask show nicely before the controller starts working with no (well ok, 1ms) delay.</p>
<p>Hope this helps others. Let me know what you think or if you&#8217;ve got an even simpler solution</p>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2011/08/sencha-touch-loadmask-delayed-rendering/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Customer Spotlight: ABN AMRO Bank &#8211; AFS</title>
		<link>http://appointsolutions.com/2011/04/abn-amro-luxembourg-afs/</link>
		<comments>http://appointsolutions.com/2011/04/abn-amro-luxembourg-afs/#comments</comments>
		<pubDate>Fri, 29 Apr 2011 10:00:01 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[Customer spotlight]]></category>
		<category><![CDATA[extjs abnamro catalyst perl showcase spotlight client sencha]]></category>

		<guid isPermaLink="false">http://www.robboerman.com/?p=48</guid>
		<description><![CDATA[<p>Before robboerman.com was bootstrapped into appoint:solutions, a complex intranet application was built for ABN AMRO Bank Luxembourg by appoint:solutions co-founder Rob Boerman. At the time acting as client, the concept and functional requirements were designed by Bert Boerman, after he realized that existing off-the-shelf solutions did not come close to covering the requirements that a [...]]]></description>
			<content:encoded><![CDATA[<p>Before robboerman.com was bootstrapped into appoint:solutions, a complex intranet application was built for ABN AMRO Bank Luxembourg by appoint:solutions co-founder Rob Boerman. At the time acting as client, the concept and functional requirements were designed by Bert Boerman, after he realized that existing off-the-shelf solutions did not come close to covering the requirements that a bank has in today&#8217;s regulatory environment.<br />
The application was designed for the documentation and workflow management of alternative investment fund custody and was designed, implemented and delivered within a timeframe of only 3 months and within budget following an interactive development process where the 2 parties had frequent contact and feedback with one another.</p>
<h3><span style="font-size: 21px; line-height: 25px;">ABN AMRO: Alternative Funds</span></h3>
<p>An <a title="Alternative investment" href="http://en.wikipedia.org/wiki/Alternative_investment" target="_blank">alternative investment</a> is an investment other the traditional investments  of stocks, bonds or cash. The investments can include tangible assets such as real estate, private equity, art, wine and antiques and financial assets such as commodities and derivatives. ABN AMRO Luxembourg acts as a custodian to highly specialized alternative investment funds. Without going into detail of all <a title="Custodian bank" href="http://en.wikipedia.org/wiki/Custodian_bank" target="_blank">responsibilities</a> of a custodian bank, roughly the custodian is responsible for safeguarding and supervising the fund&#8217;s assets.</p>
<p>With the responsibilities of a custodian come a great number of internal and external requirements that have to be met. In order to demonstrate that the custodian has properly performed its obligations, it is important that the structure of the funds, all actions by the custodian&#8217;s staff and all required documentation are captured and are always up to date. At any moment the custodian should be able to produce a complete overview of the history of a fund, including all historic changes. This requires a very tight workflow that needs to be supported by specialized software. Most banks rely on excel spreadsheets for this task, which presents some specific risks.</p>
<p>&nbsp;</p>
<h3>Alternative Fund System</h3>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/04/AFS-login1.png"><img class="alignright size-medium wp-image-68" title="AFS login screen" src="http://www.robboerman.com/wp-content/uploads/2011/04/AFS-login1-300x129.png" alt="" width="300" height="129" /></a></p>
<p>The Alternative Fund System has been designed as a intranet application. Aside from the technical requirements there were a number of functional requirements that needed to be covered by the foreseen application:</p>
<ul>
<li>Ease of use</li>
<li>Authentication and authorization</li>
<li>4-eye-control &#8211; input validation</li>
<li>Print-outs</li>
<li>Audit trail</li>
<li>Entity structure</li>
<li>Workflow management</li>
<li>Todo management</li>
<li>Document management</li>
</ul>
<h3>AFS Project</h3>
<p>Because of the tight planning, close cooperation with ABN AMRO was necessary. The project has been executed using agile methodologies. In short iterations of 2-3 weeks the system was designed and implemented. At the start of each iteration, the set of requirements were discussed that were to be developed during that iteration. During the next meeting the results were discussed and evaluated and new requirements for the next iteration fixed. This close cooperation between the client and supplier requires both parties to be actively involved in the development. The result is a software solution that better fits the business requirements since the client is consulted and informed throughout the project, and a much faster development process. Design errors or business requirement mismatches are caught and solved early on in the project.</p>
<p><img class="size-large wp-image-86 aligncenter" title="Schermafbeelding 2011-04-15 om 16.37.38" src="http://www.robboerman.com/wp-content/uploads/2011/04/Schermafbeelding-2011-04-15-om-16.37.381-1024x503.png" alt="" width="595" height="292" /></p>
<h3>Implementation</h3>
<p>Because of the speed of the project the choice was made for the <a title="Catalyst MVC framework" href="http://www.catalystframework.org/" target="_blank">Catalyst Framework</a> as a basis for the server component of the AFS application. Besides the great flexibility, extensibility and performance this MVC framework offers, it also allows the use of the 90.000+ <a title="Comprehensive Perl Archive Network" href="http://www.cpan.org/" target="_blank">CPAN</a> modules directly from the framework.</p>
<p><img class="alignright size-full wp-image-80" title="sencha-logo" src="http://www.robboerman.com/wp-content/uploads/2011/04/sencha-logo1.jpeg" alt="" width="202" height="98" /></p>
<p>It quickly became apparent that the major bulk of complexity was to be expected in the interface of the system. The choice for the interface framework was easily made: <a title="Ext JS framework" href="http://www.sencha.com/products/extjs/" target="_blank">ExtJS</a> by Sencha Inc. The ExtJS framework is currently the most sophisticated Rich Internet Application framework available, by far. Besides an enormous amount of readily available interface widgets and cross-browser functionality, the framework allows developers to create custom interface widgets very easily and quickly and develop the interface in a maintainable Object Oriented fashion.</p>
<p>The client and server components are closely integrated using Ext Direct technology which allows seamless communication between the client and server while optimizing bandwidth usage.</p>
<h3>Features</h3>
<h4>Ease of use</h4>
<p><img class="alignright size-medium wp-image-81" style="border: 1px solid #666;" title="Schermafbeelding 2011-04-15 om 16.20.24" src="http://www.robboerman.com/wp-content/uploads/2011/04/Schermafbeelding-2011-04-15-om-16.20.24-300x275.png" alt="" width="300" height="275" /></p>
<p>The application needed to provide an easy-to-use interface such that extensive training should not be required. The complexity of the underlying investment project should be abstracted away. All information that is not required for a user&#8217;s task should be hidden. The resulting interface can be adjusted on a per-user basis. The user can define which functions and information to show, and how the information should be presented. All user settings are stored on the server so that settings are also available after logging out and back in, or when using the application from another computer.</p>
<p>No matter what information is being worked on, the interface works exactly the same way. This speeds up the learning curve significantly while reducing the risk of human errors.</p>
<p>As the fund structures can become very complex, it is crucial that the fund team members can find any information in the system quickly and easily. All data views and grids have been equipped with server side filters and search functionality.</p>
<p>&nbsp;</p>
<h4>Authentication and authorization layer</h4>
<p>ABN AMRO internal IT can define and administer user accounts and authorization levels. A role-based authorization editor was added to the system allowing IT staff to exactly define the access rights per user and role.</p>
<h4>4-eye-control, event triggers and workflow management</h4>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/04/Schermafbeelding-2011-04-15-om-16.32.31.png"><img class="alignright size-medium wp-image-83" title="Schermafbeelding 2011-04-15 om 16.32.31" src="http://www.robboerman.com/wp-content/uploads/2011/04/Schermafbeelding-2011-04-15-om-16.32.31-300x100.png" alt="" width="300" height="100" /></a>The system provides an easy-to-use 4-eye-control system in which certain user input must be validated by another user. These controls can be set up by the IT staff using a simple 3-step trigger editor. Using this simple procedure, very complex rules can be defined under which an action is triggered in the system. For example:</p>
<ul>
<li>When a <strong>legal entity</strong> of type<strong> SA</strong> is changed, the input has to be validated by a user that has the role<strong> &#8216;Management&#8217;</strong></li>
<li>When a <strong>Bank Account</strong> with currency <strong>USD</strong> is added to a <strong>Legal entity</strong>, <strong>3 documents</strong> must be submitted <strong>and</strong> a <strong>monthly recurring task</strong> is setup to register the end-of-month balance</li>
</ul>
<p>This functionality offers complete 4-eye control and task management in the AFS application. When one action is executed it can be configured to automatically create new Todo items.</p>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/04/Schermafbeelding-2011-05-21-om-14.59.53.png"><img class="alignnone size-full wp-image-235" title="Workflow management" src="http://www.robboerman.com/wp-content/uploads/2011/04/Schermafbeelding-2011-05-21-om-14.59.53.png" alt="" width="603" height="452" /></a></p>
<h4>Audit trail</h4>
<p>All changes into the system by any user are logged, including a change-log of exactly what was changed by whom in the system.</p>
<h4>Entity structure</h4>
<p><img class="alignright size-medium wp-image-89" title="Schermafbeelding 2011-04-15 om 13.53.49" src="http://www.robboerman.com/wp-content/uploads/2011/04/Schermafbeelding-2011-04-15-om-13.53.49-300x199.png" alt="" width="210" height="139" /></p>
<p>The underlying legal structures of the fund are easily made available in the form of a dynamically created organizational chart. The organizational chart is interactive, allowing the user to navigate to a legal structure and all related content with the click of a mouse. At every level in a legal structure the complete substructure can be made visible this way.</p>
<h3>And much, much more</h3>
<p>This project would not have been possible without the close cooperation with ABN AMRO. Because of their timely and accurate feedback on development, a very complex application was created in a very short period of time.  It was fun and exciting to work on a project with a party that knows it&#8217;s business inside out. The result is an application that is very closely matched to the internal work processes.</p>
<p>From a technical viewpoint the application would not have been feasible without the excellent <a title="ExtJS Framework" href="http://www.sencha.com/products/extjs/" target="_blank">ExtJS</a> framework. The framework is an absolute joy to work with, in completeness, extensibility and maintainability and the excellent documentation.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2011/04/abn-amro-luxembourg-afs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>100 Days for KiKa &#8211; Cycling for charity</title>
		<link>http://appointsolutions.com/2011/06/100-days-for-kika-cycling-for-charity/</link>
		<comments>http://appointsolutions.com/2011/06/100-days-for-kika-cycling-for-charity/#comments</comments>
		<pubDate>Tue, 14 Jun 2011 11:14:58 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[Customer spotlight]]></category>

		<guid isPermaLink="false">http://appointsolutions.com/?p=262</guid>
		<description><![CDATA[<p><a title="100 Days for KiKa" href="http://100daysforkika.com" target="_blank"></a>Bicycling 14.000 kilometres around the entire continent of Australia in only 100 days. This grueling journey has just been started by the two dutch men, Reinier van Dieren and Mark van Rijmenam. The goal: to collect as much money as possible for the Dutch charity KiKa that supports research [...]]]></description>
			<content:encoded><![CDATA[<p><a title="100 Days for KiKa" href="http://100daysforkika.com" target="_blank"><img class="alignleft size-full wp-image-264" title="logo-100dagen-100hoog" src="http://appointsolutions.com/wp-content/uploads/2011/06/logo-100dagen-100hoog.png" alt="" width="253" height="100" /></a>Bicycling 14.000 kilometres around the entire continent of Australia in only 100 days. This grueling journey has just been started by the two dutch men, Reinier van Dieren and Mark van Rijmenam. The goal: to collect as much money as possible for the Dutch charity KiKa that supports research into and treatment of children&#8217;s cancer. 100% of all money raised will be donated towards KiKa at the end of the journey. To be able to reach their end-destination on September 17th they will have to cover an average of 175 kilometres per day without any support.</p>
<p>Besides the enormous physical challenges, which the team has been training for for 2.5 years, there were also a number of technical problems to solve. When the team contacted us to ask for help solving those problems we immediately said YES.</p>
<p>The problems that needed a solution were:<br />
- Communicating with followers and the home support team during the trip<br />
- Sharing journals, pictures and movies with the public<br />
- Communicating about the progress along the route</p>
<h3>Brand new website</h3>
<p>The team had a website that had been set up during the preparation for the trip. The website served perfectly for communicating static information about the preparations, the goals and sponsors and partners. For the next phase of the project, the journey itself, other goals however became more important. After trying to integrate all the wishes into the existing site we decided, together with the team, that it would be better to completely rebuild the website using WordPress as a basis. Check the new website <a title="100 Days for KiKa" href="http://100daysforkika.com" target="_blank">here</a>.<br />
<a title="100 Days for KiKa" href="http://100daysforkika.com" target="_blank"><img class="alignleft size-medium wp-image-270" title="New 100 Days for KiKa site" src="http://appointsolutions.com/wp-content/uploads/2011/06/Schermafbeelding-2011-06-14-om-12.51.40-300x183.png" alt="" width="300" height="183" /></a>The new setup would enable the team itself to continuously update the website with all information coming from Australia. It would also enable us to:<br />
- create a more modern look-and-feel with more call-for-action. Thus a completely new website style was created.<br />
- integrate the website with social media<br />
- include secure donations using PayPal<br />
- optimize the site for search engine rankings<br />
- include tracking by Google analytics, set up goals and funnels to measure conversion<br />
- integrate content from YouTube, Picasa and Flickr<br />
- make the site multilingual<br />
- integrate GPS tracking information</p>
<p>&nbsp;</p>
<p><img class="alignleft size-medium wp-image-272" title="Mobile site 1" src="http://appointsolutions.com/wp-content/uploads/2011/06/Schermafbeelding-2011-06-14-om-13.06.15-159x300.png" alt="" width="127" height="240" /></p>
<p><a href="http://appointsolutions.com/wp-content/uploads/2011/06/Schermafbeelding-2011-06-14-om-13.07.06.png"><img class="alignleft size-medium wp-image-273" title="Mobile site 2" src="http://appointsolutions.com/wp-content/uploads/2011/06/Schermafbeelding-2011-06-14-om-13.07.06-159x300.png" alt="" width="127" height="240" /></a>Aside from the site itself, a mobile site was also created. The first initiative was to build a native iphone application but because of the fact that Apple blocks most applications that are used to make donations, the choice was made for a mobile theme for WordPress.</p>
<p>&nbsp;</p>
<h3 style="clear: both;">GPS tracking</h3>
<p><a href="http://appointsolutions.com/wp-content/uploads/2011/06/Schermafbeelding-2011-06-14-om-12.49.27.png"><img class="alignleft size-medium wp-image-269" title="Live tracking of the team's location" src="http://appointsolutions.com/wp-content/uploads/2011/06/Schermafbeelding-2011-06-14-om-12.49.27-300x198.png" alt="" width="300" height="198" /></a> To enable everyone to track the progress of the team a live tracking system was implemented. GPS tracking itself is handled by the RunKeeper IPhone application. The results from the tracks are extracted from the RunKeeper profile using a custom built extractor (exporting is not supported by RunKeeper) on a 5-minute basis. This allows us to track average speed, distance, elevation and calories burnt.</p>
<p><img class="size-full wp-image-293 alignleft" title="Schermafbeelding 2011-06-14 om 14.13.03" src="http://appointsolutions.com/wp-content/uploads/2011/06/Schermafbeelding-2011-06-14-om-14.13.03.png" alt="" width="269" height="228" style="clear: left" /></p>
<p>This data is then transformed into a custom dashboard widget that is visible on all pages and uses a combination of Google&#8217;s Map API and JQuery to show rotating information about the journey in near realtime. The charts on the <a title="Live follow page" href="http://100dagenvoorkika.nl/en/route-2/stats/" target="_blank">live-follow page</a> are created by RunKeeper and have been integrated into the site. When there is network coverage and the team is bicycling, the charts are updated every few seconds.</p>
<h3 style="clear: both;">Other sponsoring</h3>
<p>Appoint:solutions sponsored the complete development of the website and related functionalities and further sponsored:</p>
<p>- a portable but powerful solar panel with integrated battery pack. The solar panel keeps the IPhone&#8217;s battery from draining due to the constant usage of GPS and 3G. Without this solar panel the battery would die within 4 hours of usage while a typical day&#8217;s cycling is 12-13 hours.<br />
- Hosting for the website<br />
- Support for the team by consulting about the possibilities of technology and social media</p>
<p>It was a joy to be able to use our creativity and experience in supporting such a great cause. We wish 100 Days for KiKa all the best and hope to have contributed a small part in realizing their dream of collecting a large sum of money for KiKa. If you like the effort, please support their cause by <a title="100 Days for KiKa" href="http://100dagenvoorkika.nl/en/donate/" target="_blank">donating</a>! It will help create less aggressive treatment for children with cancer, better chances of survival and a better quality of live at a later age.</p>
<p>Good luck guys!</p>
<p>Appoint:solutions</p>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2011/06/100-days-for-kika-cycling-for-charity/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ComboBox editor (remote) and renderer for ExtJS EditorGridPanel</title>
		<link>http://appointsolutions.com/2011/05/combobox-editor-remote-and-renderer-for-extjs-editorgridpanel/</link>
		<comments>http://appointsolutions.com/2011/05/combobox-editor-remote-and-renderer-for-extjs-editorgridpanel/#comments</comments>
		<pubDate>Fri, 20 May 2011 18:46:17 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[editor]]></category>
		<category><![CDATA[editorgridpanel]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[remote]]></category>
		<category><![CDATA[renderer]]></category>
		<category><![CDATA[store]]></category>

		<guid isPermaLink="false">http://www.robboerman.com/?p=197</guid>
		<description><![CDATA[<p>Adding a ComboBox editor to a ExtJS EditorGridPanel column can by tricky if your ComboBox uses a remote store. This post demonstrates using a custom grid column type and associated renderer to solve this problem.</p> ExtJS EditorGridPanel <p>The ExtJS EditorGrid Panel is excellent for editing records from within the grid. The component allows one to [...]]]></description>
			<content:encoded><![CDATA[<p>Adding a ComboBox editor to a ExtJS EditorGridPanel column can by tricky if your ComboBox uses a remote store. This post demonstrates using a custom grid column type and associated renderer to solve this problem.</p>
<h3>ExtJS EditorGridPanel</h3>
<p>The ExtJS EditorGrid Panel is excellent for editing records from within the grid. The component allows one to specify an editor type per column. The ExtJS 3 examples show a nice example of this</p>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-14.04.52.png"><img class="alignnone size-full wp-image-198" title="Schermafbeelding 2011-05-20 om 14.04.52" src="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-14.04.52.png" alt="" width="591" height="269" /></a></p>
<h3>The problem</h3>
<p>The editor works perfectly for columns in which the stored value is also the display value. The EditorGridPanel also allows for a ComboBox to be used as the editor. When the displayField and valueField of the combobox are the same this works ok, but less so for columns that are for instance foreign keys into other models. An example illustrates this:</p>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-13.58.40.png"><img class="alignnone size-full wp-image-201" title="EditorGridPanel with foreign key column" src="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-13.58.40.png" alt="" width="567" height="183" /></a></p>
<p>In this example the Assignee column lists the ID&#8217;s of users. A ComboBox has been specified as the editor. When editing the field, the ComboBox comes into action and correctly lists the Users by name:</p>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-13.58.56.png"><img class="alignnone size-full wp-image-202" title="Schermafbeelding 2011-05-20 om 13.58.56" src="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-13.58.56.png" alt="" width="567" height="181" /></a></p>
<p>After selecting a User the column value is updated with the user&#8217;s id. This allows us to change the column value easily, but of course this is not what we want. Wat we actually want is to display the User&#8217;s name in the grid, hiding the value. At the same time the column&#8217;s raw value should still be the user id:</p>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-14.01.44.png"><img class="alignnone size-full wp-image-203" title="Schermafbeelding 2011-05-20 om 14.01.44" src="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-14.01.44.png" alt="" width="567" height="183" /></a></p>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-14.01.54.png"><img class="alignnone size-full wp-image-204" title="Schermafbeelding 2011-05-20 om 14.01.54" src="http://www.robboerman.com/wp-content/uploads/2011/05/Schermafbeelding-2011-05-20-om-14.01.54.png" alt="" width="567" height="182" /></a></p>
<p>A number of specialized ComboBox renderers have been published in the Sencha forum that allow to do just this. The renderer looks up the corresponding record in the ComboBox&#8217;s store by it&#8217;s valueField and returns the corresponding displayField. For ComboBoxes with local stores that usually works but without hacks this will not fly for ComboBoxes with remote stores such as a JsonStore or DirectStore. This has to do with the fact that at the time the grid is created and the custom renderer is asked for the record&#8217;s displayField the ComboBoxes store has not finished loading yet. The only times this does work is if you get luck and win the race condition&#8230; Not what we want at all in production applications.</p>
<h3>The solution</h3>
<p>I am releasing a custom grid column with associated renderer that solves exactly this problem. The only thing the column configuration needs is the EditorGrid&#8217;s id and an editor that we are specifying anyway. The renderer is setup automatically. The way this works is that the renderer checks if the ComboBox editor&#8217;s store has already been loaded. If this is not the case it sets up a one-time event listener on the ComboBox store&#8217;s &#8216;load&#8217; event. The event handler lets the Editor Grid&#8217;s view refresh, essentially re-calling the columns renderers. This time the store is loaded and the record and displayField can be found.</p>
<p>The following code snipped explains the configuration neccessary:</p>
<pre>[javascript]
/* create the ComboBox editor */
var userCombo = new Ext.ComboBox({
	id: 'myCombo',
	valueField:'id',
	displayField:'name',
	store: ...
});

/* We need a unique id for the grid, either create one yourself or let Ext create it for you */

var gridId = Ext.id();

/* Create the grid */

var todoGrid = new Ext.grid.EditorGridPanel({
	store: myStore,
	id: gridId, 					// Be sure to include the grid id
	cm: new Ext.grid.ColumnModel({
		columns: [{
			id: 'todo',
			header: 'Todo',
			dataIndex: 'todo'
		},{
			id: 'owner',
			width: 150,
			header: 'Assignee',
			dataIndex: 'owner',
			xtype: 'combocolumn', 	// Use the custom column or use the column's render manually
			editor: userCombo,		// The custom column needs a ComboBox editor to be able to render the displayValue, without it just renders value
			gridId: gridId			// Don't forget to specify the grid's id, the columns renderer needs it
		}]
	}),
	clicksToEdit: 1
});
[/javascript]</pre>
<p>And VOILA, an EditorGrid that displays the record by it&#8217;s displayField while still functioning exactly the same and also working with remote stores. The complete custom column specification and renderer is displayed below and also attached as a zip file.</p>
<h3>The code</h3>
<pre>[javascript]
/**
 * @class Ext.ux.grid.ComboColumn
 * @extends Ext.grid.Column
 *
 * A Column definition class which renders a value using a ComboBox editor. The ComboBox is used to
 * convert the column value to a display value using the ComboBox's valueField and displayField.
 * If the ComboBox editor uses a remote store the column definition needs to be passed the grid's id.
 * See the {@Ext.ux.grid.ComboColumn#gridId gridId} config option of {@link Ext.ux.grid.ComboColumn}
 * for more details.
 *
 * @author    Rob Boerman
 * @copyright (c) 2011, by Rob Boerman
 * @date      20. May 2011
 * @version   1.0

Example:

var userCombo = new Ext.ComboBox({
	id: 'myCombo',
	valueField:'id',
	displayField:'name',
	store: ...
});

* We need a unique id for the grid, either create one yourself or let Ext create it for you

var gridId = Ext.id();

* Create the grid

var todoGrid = new Ext.grid.EditorGridPanel({
	store: myStore,
	id: gridId, 					// Be sure to include the grid id
	cm: new Ext.grid.ColumnModel({
		columns: [{
			id: 'todo',
			header: 'Todo',
			dataIndex: 'todo'
		},{
			id: 'owner',
			width: 150,
			header: 'Assignee',
			dataIndex: 'owner',
			xtype: 'combocolumn', 	// Use the custom column or use the column's render manually
			editor: userCombo,		// The custom column needs a ComboBox editor to be able to render the displayValue, without it just renders value
			gridId: gridId			// Don't forget to specify the grid's id, the columns renderer needs it
		}]
	}),
	clicksToEdit: 1
});

 * @license Ext.ux.grid.ComboColumn is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 *

License details: <a href="http://www.gnu.org/licenses/lgpl.html" target="_blank">http://www.gnu.org/licenses/lgpl.html</a>

 *
 * @donate
 *
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
 *
<input name="cmd" type="hidden" value="_donations" />
 *
<input name="business" type="hidden" value="ZRRCZDNRCVVEE" />
 *
<input name="lc" type="hidden" value="NL" />
 *
<input name="item_name" type="hidden" value="robboerman.com" />
 *
<input name="currency_code" type="hidden" value="EUR" />
 *
<input name="bn" type="hidden" value="PP-DonationsBF:btn_donate_LG.gif:NonHosted" />
 *
<input alt="PayPal - The safer, easier way to pay online!" name="submit" src="https://www.paypalobjects.com/WEBSCR-640-20110429-1/en_US/i/btn/btn_donate_LG.gif" type="image" />
 * <img src="https://www.paypalobjects.com/WEBSCR-640-20110429-1/en_US/i/scr/pixel.gif" border="0" alt="" width="1" height="1" />
 * </form>

 */

Ext.ns("Ext.ux.renderer","Ext.ux.grid");

Ext.ux.grid.ComboColumn = Ext.extend(Ext.grid.Column, {

/**
	* @cfg {String} gridId
	*
	* The id of the grid this column is in. This is required to be able to refresh the view once the combo store has loaded
	*/
	gridId: undefined,

    constructor: function(cfg){
        Ext.ux.grid.ComboColumn.superclass.constructor.call(this, cfg);

		// Detect if there is an editor and if it at least extends a combobox, otherwise just treat it as a normal column and render the value itself
		this.renderer = (this.editor &#038;&#038; this.editor.triggerAction) ? Ext.ux.renderer.ComboBoxRenderer(this.editor,this.gridId) : function(value) {return value;};
    }
});

Ext.grid.Column.types['combocolumn'] = Ext.ux.grid.ComboColumn;

/* a renderer that makes a editorgrid panel render the correct value */
Ext.ux.renderer.ComboBoxRenderer = function(combo, gridId) {
	/* Get the displayfield from the store or return the value itself if the record cannot be found */
	var getValue = function(value) {
		var idx = combo.store.find(combo.valueField, value);
		var rec = combo.store.getAt(idx);
		if (rec) {
			return rec.get(combo.displayField);
		}
		return value;
	}

	return function(value) {
		/* If we are trying to load the displayField from a store that is not loaded, add a single listener to the combo store's load event to refresh the grid view */
		if (combo.store.getCount() == 0 &#038;&#038; gridId) {
			combo.store.on(
				'load',
				function() {
					var grid = Ext.getCmp(gridId);
					if (grid) {
						grid.getView().refresh();
					}
				},
				{
					single: true
				}
			);
			return value;
		}

		return getValue(value);
	};
};

[/javascript]
</pre>
<p>Good luck and don&#8217;t forget to share this post if it&#8217;s of use to you, it might also be to others.</p>
<p>Rob</p>
<p><a href="http://www.robboerman.com/wp-content/uploads/2011/05/Ext.ux_.ComboColumn.zip">Ext.ux.ComboColumn source file</a></p>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2011/05/combobox-editor-remote-and-renderer-for-extjs-editorgridpanel/feed/</wfw:commentRss>
		<slash:comments>36</slash:comments>
		</item>
		<item>
		<title>ExtJS RowExpander plugin overriding default getRowClass</title>
		<link>http://appointsolutions.com/2011/05/extjs-rowexpander-plugin-overriding-default-getrowclass/</link>
		<comments>http://appointsolutions.com/2011/05/extjs-rowexpander-plugin-overriding-default-getrowclass/#comments</comments>
		<pubDate>Thu, 05 May 2011 15:16:38 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[extjs]]></category>
		<category><![CDATA[extjs3]]></category>
		<category><![CDATA[grid]]></category>
		<category><![CDATA[override]]></category>
		<category><![CDATA[view]]></category>

		<guid isPermaLink="false">http://www.robboerman.com/?p=189</guid>
		<description><![CDATA[<p>ExtJS (3) is great for OO style interface design but has a couple of strange behaviors. For instance when adding the ExtJS RowExpander plugin to a grid, the plugin overrides the gridView&#8217;s getRowClass function. That&#8217;s not really what you want. For instance, I want to mark certain records in a grid red when they are [...]]]></description>
			<content:encoded><![CDATA[<p>ExtJS (3) is great for OO style interface design but has a couple of strange behaviors. For instance when adding the ExtJS RowExpander plugin to a grid, the plugin overrides the gridView&#8217;s getRowClass function. That&#8217;s not really what you want. For instance, I want to mark certain records in a grid red when they are overdue, and also use the RowExpander plugin. The plugin however throws away my own custom formatting.<br />
Solution is to create a backup copy of the default getRowClass function and call both the plugin function and the original, concatenating the results together with a space</p>
<pre>[javascript]

getRowClass: function(record, rowIndex, p, ds) {
    p.cols = p.cols - 1;
    var content = this.bodyContent[record.id];
    if (!content &#038;&#038; !this.lazyRender) {
        content = this.getBodyContent(record, rowIndex);
    }
    if (content) {
        p.body = content;
    }
    return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
},

/*	If grid already has a getRowClass, don't override it
 *	get the original class and combine it with our class
 */
// @private
combineGetRowClass: function(record, rowIndex, p, ds) {
    var cls = this.originalGetRowClass(record, rowIndex, p, ds) + '"" + this.getRowClass(record, rowIndex, p, ds);
    return cls;
},

init: function(grid) {
    this.grid = grid;

    var view = grid.getView();

    /* create a backup copy of the original, we want to apply both the standard and our custom functions */
    if (view.getRowClass) {
        this.originalGetRowClass = this.clone(view.getRowClass);
        view.getRowClass = this.combineGetRowClass.createDelegate(this);
    } else {
        view.getRowClass = this.getRowClass.createDelegate(this);
    }

    view.enableRowBody = true;

    grid.on('render', this.onRender, this);
    grid.on('destroy', this.onDestroy, this);
},

// @private
clone: function(obj) {
    if (obj == null || typeof(obj) != 'object') return obj;

    var temp = new obj.constructor();
    for (var key in obj)
    temp[key] = clone(obj[key]);

    return temp;
}

[/javascript]</pre>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2011/05/extjs-rowexpander-plugin-overriding-default-getrowclass/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>ExtJS Global eventmanager with destroy catcher</title>
		<link>http://appointsolutions.com/2011/04/global-eventmanager-with-destroy-catcher/</link>
		<comments>http://appointsolutions.com/2011/04/global-eventmanager-with-destroy-catcher/#comments</comments>
		<pubDate>Wed, 27 Apr 2011 21:52:12 +0000</pubDate>
		<dc:creator>Rob Boerman</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[extjs sencha events eventmanager ria destroy]]></category>

		<guid isPermaLink="false">http://www.robboerman.com/?p=135</guid>
		<description><![CDATA[<p>In my applications I like to have a global eventmanager that can be instructed to listen to certain events and call registered event handlers. <a href="http://jonathanjulian.com/" target="_blank">Jonathan Julian</a> proposed an elegant way of creating a global eventmanager by creating an instance of Observable. This works for the most part&#8230; if you don&#8217;t destroy any listeners [...]]]></description>
			<content:encoded><![CDATA[<p>In my applications I like to have a global eventmanager that can be instructed to listen to certain events and call registered event handlers. <a href="http://jonathanjulian.com/" target="_blank">Jonathan Julian</a> proposed an elegant way of creating a global eventmanager by creating an instance of Observable. This works for the most part&#8230; if you don&#8217;t destroy any listeners runtime.</p>
<p>Normally the eventmanager would be created like this <a href="http://www.slideshare.net/jonathanjulian/five-tips-to-improve-your-ext-js-application" target="_blank">this</a>:</p>
<pre>
[javascript]
// Create the global event manager
MyApp.eventManager = new Ext.util.Observable();

// Tell the event manager to which it events it should listen
// and allow subscribers to hook in to
MyApp.eventManager.addEvents(
    'selectnode',
    'someotherevent'
);
[/javascript]
</pre>
<p>The global event manager can then be used by components to fire events on, thus signaling subscribers to the event, or subscribe to certain events themselves.</p>
<pre>
[javascript]
// Somewhere in a tree click handler
// a tree node object is passed to possible event handlers that are listening
MyApp.eventManager.fireEvent('selectnode',node);

...

// A grid somewhere else should refresh it's store on the above event
// Here the MyGrid instance has a 'refreshHandler' function
MyApp.eventManager.on(
    'selectnode', // the event
    myGrid.refreshHandler.createDelegate(myGrid)
);
[/javascript]
</pre>
<p>The above publish-subscribe method works like a charm but sometimes fails. The eventManager is passed a reference to a function of a component, in this case function &#8216;refreshHandler&#8217; of component &#8216;myGrid&#8217;. If the &#8216;myGrid&#8217; component is destroyed (removed from a parent with autoDestroy true), the (now defunct) reference to the handler function still exists in the global eventManager list of subscribers and causes a javascript error to occur when the event is triggered; a function of a non-existent object is called.</p>
<p>The above can be fixed by extending the Observable class. The extended class makes sure that when an object is destroyed (firing it&#8217;s destroy event), any event handlers that might be a part of the object are removed from the list of subscribers.</p>
<pre>
[javascript]
MyApp.eventManager = Ext.extend(Ext.util.Observable, {
	addListener: function(eventName,handler,scope,options) {
		if (scope) {
			scope.on({
				scope:this,
				beforedestroy: function() { //create closure
					this.removeListener(eventName,handler,scope);
				}
			});
		}

		MyApp.eventManager.superclass.addListener.apply(this, arguments);
	}
});
[/javascript]
</pre>
<p>Hope it helps anyone </p>
<p>Rob</p>
]]></content:encoded>
			<wfw:commentRss>http://appointsolutions.com/2011/04/global-eventmanager-with-destroy-catcher/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

