coder and technology lover
Davide Zanotti
This user hasn't shared any biographical information
Homepage: http://www.daveoncode.com
Posts by Davide Zanotti
Image background fallback using <img/> tag error handlers
Aug 20th
In the project I’m working on, we make large use of external assets like images, coming from several providers. Unfortunately these links are often broken, resulting in “empty squares” which are unpleasant for the user. The best thing to do, since we don’t have the full control over these resources, is to display the classic “image not available” pic, but this is not simple as writing an if statement such:
1 2 3 4 5 | if (record.image) { <img src="record.image" /> } else { <img src="imageNotAvailableLink" /> } |
because in our case “record.image” is defined although its path is unreachable. So, we have to try to load the image and replace it at runtime when we know that the src is broken. How can we catch that exception? We just need to listen to onerror and onabort img events, so we can simply write this smart img tag:
1 | <img src="providerSrc" onerror="this.src=fallbackSrc" onabort="this.src=fallbackSrc" /> |
The this key inside an event attribute, refers to the dom node itself, so we can switch an attribute like src with a little piece of inline JavaScript.
Anyway in my scenario the problem is bigger than this example, because thumbs images are rendered as divs background in order to make a sort of polaroid effect and, at the same time, to solve a potential issue related to exceeding image dimensions or a stretched one.
Fortunately the solution I found, is not so far from the above, the only difference is that I used an invisible img tag located inside the div and when it catch the error rather than change its src it changes its parent background:
1 2 3 | <div style="background:url(providerSrc)"> <img style="display:none" src="providerSrc" onerror="this.parentNode.style.backgroundImage='url(fallbackSrc)'" /> </div> |
Of course in my final implementation I replaced the piece of inline js assignment with an utility function call that does the job and also trace a debug message to the console, displaying the path of the broken image… I’m pretty satisfied about my work :^)
iPhone applications’ localization
May 15th
Well, it’s quite a lot since my first approach to Objective C, Cocoa and iPhone development and now I’m starting to move my first concrete steps into this beautiful world. Today I would like to face an important aspect of iPhone development (and application development generally), localization! Fortunately the excellent Cocoa framework and Objective C ecosystem make internalization process easy, fast and clean, thus localize an application into several languages can be accomplished with a minimum effort by developers.
Fundamentally there are three main steps to get a multi language app in action. The first is to create a special folder with the extension .lproj under application’s root for each language we want/can support. So, if we suppose to support english, italian and spanish languages, we will create those folders: en.lproj, it.lproj, es.lproj. “en”, “it” and “es” represent the ISO 639-1 language designation (the same used in domain suffixes), it’s also possible to adopt the ISO 639-2 convention and the folders would be renamed as “eng”, “ita” and “spa”, anyway first approach is the preferred and widely adopted. If you want to know all ISO 639-1 and ISO 639-2 designators, I suggest you this page: http://www.loc.gov/standards/iso639-2/php/code_list.php.
Once we create all the necessary .lproj folders (using Finder or Terminal) we can switch to our beloved Xcode and create all the necessary resources that will keep the different localized strings. We must now select the logical “Resources” folder and add a new file by choosing “String file” under Mac OS X “Resource“, then click next and naming this file “Localizable.strings“, finally save it under previous created folders (we must repeat this operation for each folder).
Those .strings file are just simple text file, where we can specify keys and values as the following example:
// en.lproj
“hello” = “Hello”;
// it.lproj
“hello” = “Ciao”;
// sp.lproj
“hello” = “Hola”;
The basic approach is to use the english word as the key and assign it different values based on the localization file, but we can be more cryptic and use our custom conventions, for example by assigning incremental keys like: “k1″, “k2″, “k3″ and so on. As last step, we can automatically read the right localized string version at runtime (based on user’s language setting) by using macro (NSLocalizedString, NSLocalizedStringFromTable, NSLocalizedStringFromTableInBundle, NSLocalizedStringWithDefaultValue) or class methods available in Apple’s framework. The simplest way to do that is to use the function NSLocalizedString(), which accepts two arguments: an NSString representing the key we are looking for and a second optional argument which has the mere purpose of serving as note for developers by trying to describe string’s context (*), for this reason this argument will be “nil” the most of times:
1 2 | // set myLabel's text using NSLocalizedString() myLabel.text = NSLocalizedString(@"hello", nil); |
Although this approach is very simple, it’s not recommendable in my opinion, because it doesn’t provide a way to handle missing keys and if a key can’t be found, it will print that key literally (and in case of a not human readable key like “k-15″, it would be very annoying for the user). Thus, a far better option is to use the NSLocalizedStringWithDefaultValue(), which accepts 5 arguments: an NSString for the key, an NSString for the table, an NSBundle reference, an NSString for a fallback string’s value, and an optional NSString for a comment (like NSLocalizedString()). The table argument refers to the name of .strings file (without extension), in fact we are not limited to “Localizable.strings” but we can create as many .strings files we need (“Menu.strings”, “InfoPanel.strings”…). “Localizable.strings” is only a conventional name used to specify a default file for localization that can be loaded without specify its name (NSLocalizedString() can read only that file). The bundle is a reference to the NSBundle where the .strings file is located (usually the main bundle).
1 2 | // set myLabel's text using NSLocalizedStringWithDefaultValue() myLabel.text = NSLocalizedStringWithDefaultValue(@"MyKey", @"MyStringFile", [NSBundle mainBundle], @"Missing key", nil); |
It’s also possible to use bundle’s method localizedStringForKey:value:table, which accepts an NSString as key, a second NSString as fallback value, and a third NSString for the table (or nil if you are lazy and want to use “Localizable.strings” without specifying it) :
1 2 | NSBundle *bundle = [NSBundle mainBundle]; myLabel.text = [bundle localizedStringForKey:@"MyKey" value:@"Missing key :(" table:nil]; |
An important point to understand is how localization process works, that is which steps are involved at runtime on the device. As far I know (by doing several experiment using iPhone simulator), the process is the following:
1. code is executed and a request for a localized string is found
2. the system try to find the .strings file of the current locale (according to user language settings). The NSString represenitng current locale can be found using:
1 | [[NSLocale currentLocale] localeIdentifier]; |
3. If the file is found, it will search for the requested key and it will return it’s value if found or a nil otherwise. If .strings file can’t be found, the system will try to find others .strings file sequentially, using preferredLanguages‘s NSArray:
1 | NSArray *preferredLanguages = [NSLocale preferredLanguages]; |
and it will stops as soon a file is found, by returning key’s value if found or nil otherwise.
So, considering the simple “hello” localization example into english, italian and spanish, our @”hello” key will be successfully translated if we use one of the three locales (en, it, sp) and it will be printed in english for extra locales (like german, french and so on).
Finally it’s also possible to generate strings file automatically from the source code by using Terminal’s command genstrings, see here for details: http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man1/genstrings.1.html
* the comment parameter is also used to automatically generate a comment (/* comment */) in .strings files using genstrings
Learning shell scripting while trying to fix Snow Leopard local network issue :)
Apr 8th
Mac OS X is a great system, but this doesn’t mean it’s perfect and without issues. One thing is not working as it would (in Snow Leopard 10.6.X), it’s the automatic discovering and mounting of local machines connected to the same network. This is especially true if the network includes a lot of Windows computers. Theoretically those machines should be automatically discovered and displayed in Finder under “SHARED” label, but often this is not the case. Anyway in such cases is fortunately possible to connect to a particular device from Finder (once we know its IP address) by choosing “Connect to server…” (cmd+K) and by using the Samba protocol (ie: smb://ip-address). This is however very annoying, because we have to know each ip we want to connect to and type several addresses, so I spent some time to find a way to make this process automatic (because I didn’t find a solution to the original OS X issue) and I realized a shell script which tries to do the job the Leopard should do :)
I’m really a newbie when it comes to shell scripting (and networking too :P) and my script doesn’t work perfectly, anyway it does an acceptable work the most of times and although it doesn’t really solve the problem, it could help a lot.
The script is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #!/bin/bash # get user name declare currentuser=$(whoami) # get mac name declare macname=$(scutil --get ComputerName) # save arp command result declare ipstring=$(arp -a) # split ipstring into an array by using a regex to match ip addresses declare -a iplist=($(echo $ipstring | grep -o -E "([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})")) # display addresses found echo "${#iplist[*]} ip addresses found" # switch directory to user dir cd /Users/$currentuser # for each ip in list try to mount it using smbfs for ip in ${iplist[@]} do if [ ! -d link-to-$ip ] then echo "creating directory link-to-$ip under /Users/$currentuser" mkdir link-to-$ip echo "trying to mount //$macname:@$ip/Public" mount -t smbfs //$macname:@$ip/Public /Users/$currentuser/link-to-$ip else echo 'folder "link-to-$ip" already exists... skip' fi done # complete exit 0 |
Any comment and suggestion is really appreciated! Thanks
Handling doubleclick on Flex components
Apr 7th
Handling a common event like a double click is not so simple as we may assume when it comes to Flex programming.
In fact there are several critical aspects related to this event, first of all we have to set the attribute doubleClickEnabled to true in order to make our components double clickable, and this sounds pretty silly to me. Why should I enable an object to receive an event which is so common and “natural”? This is incoherent from a software design perspective end totally unexpected by the user. Anyway this is not a real problem, because once we know about it we have just to use a setter, but double click is somehow buggy in the framework and its broadcasting can be blocked mysteriously when using certain components. I faced this issue by trying to handle a double click on a DataGrid which makes use of custom item renderers and I tested that by double clicking on the “special cells” (those rendered by my own renderers) the MouseEvent.DOUBLE_CLICK is not dispatched/cacthed. To solve the problem I had to draw an invisible rectangle using the underlying Actionscript graphics API! This is my workaround:
1 2 3 | this.graphics.beginFill(0xffffff, 0); this.graphics.drawRect(0, 0, this.parent.width, this.parent.height); this.graphics.endFill(); |
Finally, it seems that double click is completely ignored by Firefox 3.6 on Mac OS X, but fortunately this seems to been fixed in the last release (3.6.3)
Get hours, minutes and seconds from a number without maths operations
Feb 5th
Today I’m gonna show you a secret ninja technique to extract hours, minutes and seconds from a number (representing an amount of seconds). In my example I will show an Actionscript code, but this can be implemented in JavaScript and maybe other languages too.
So, the scenario is the following: we have a number representing seconds and we want to know how many hours this amount of seconds contains, how many minutes and how many remaining seconds. We know that a minute is composed by 60 seconds and an hour by 60 minutes and we could write a series of maths operations in order to accomplish our objective, but there is a way far simple and fast: use the Date class!
Date class already implements all the methods we need:
- getHours()
- getMinutes()
- getSeconds()
So, in order to take advantage of these useful methods, all we have to do is initialize a “fake” date using the amount of seconds we want to “split” into hours, minutes and seconds. The Date class has several OPTIONAL arguments that can be specified during its initializations, these are:
- year
- month
- date (day number)
- hours
- minutes
- seconds
- milliseconds
Because theme all are optional, we can create a date object by specifying only the know arguments (in our case seconds) and by assigning null or zero to the others:
1 | var date:Date = new Date(null, null, null, 0, 0, 9137); |
Then by calling getHours, getMinutes and getSeconds we will obtain what we expect:
1 2 3 | trace("hours: ", date.getHours()); // 2 hours trace("minutes: ", date.getMinutes()); // 32 minutes trace("seconds: ", date.getSeconds()); // 17 seconds |
Not sure about the result? Let’s test it:
60 * 60 * 2 = 7200; (seconds contained in 2 hours)
32 * 60 = 1920; (seconds contained in 32 minutes)
7200 + 1920 + 17 = 9137; (original seconds!)
Debugging PhoneGap applications using Xcode console
Jan 12th
When I started to play whit PhoneGap, my greatest issue was: “how can I debug my code?”, I use often tools such FireBug and JavaScript debugger included in Internet Explorer 8 (which is the first good thing IE has to offers!) but write and test my code on iPhone simulator is completely different. Fortunately PhoneGap offers a way to access to Xcode console and print messages by choosing among three different levels: log, warn and error. In order to print a message, we have to use the debug object, which has scope window (it is a global object), in this way:
1 2 3 4 5 | debug.log("my log message"); // or debug.warn("my warning message"); // or debug.error("my error message"); |
To open Xcode console you have to choose “Run -> Console” from the toolbar (or CMD+SHIFT+R) and after a “Build and Run” (CMD+Enter), you will see your message appear in the console.
Testing applications while developing using PhoneGap and Xcode, is an intense activity, because errors are not automatically notified (like in FireBug or similar), so it’s really important to make use of try/catch/finally blocks and logging calls:
1 2 3 4 5 6 7 8 9 10 11 12 13 | try { mycommand.execute(); } catch (e) { debug.error("Error using mycommand: " + e.message); } finally { // do something smart here :^) } |
Finally, in order to avoid problems related to Xcode cache, I suggest to always clean the cache by running “Build -> Clean” from the toolbar (or CMD+SHIFT+K) before doing a new build and eventually remove the application folder under “/Users/{your-name}/Library/Application Support/iPhone Simulator/User/Applications/{app-number}”.
How to solve CSS conflicts using jQTouch
Jan 2nd
In these days I’m developing my first iPhone application, using frameworks like PhoneGap and jQTouch. This aims to be a powerful app, not a mere widget, so I’m writing a lot of code and I’m using several libraries and components in order to create a really native-like application.
Unfortunately, due to a bad CSS approach, I faced an issue with the excellent SpinningWheel component which won’t never be displayed unless you open and modify jQTouch CSS. The fact is that, jQTouch takes the body and use it as main container to hold all application’s pages and components and by default it hides all nodes inside the body except the current displayed content (active page/section) and this is a problem, because SpinningWheel create the ui object into the body and it won’t be displayed.
Fortunately the solution is simple, we just need a wrapper, an html element which will holds all jQTouch stuff in place of body, so in my html I wrapped all inside this:
1 2 3 4 5 | <body> <div id="jqtouch-wrapper"> <!-- content --> </div> </body> |
Then I changed all jQTouch CSS, to reflect this approach (you can see the wrapper as a sandbox):
1 2 3 4 5 6 7 8 9 10 11 | div#jqtouch-wrapper > * { -webkit-backface-visibility: hidden; -webkit-box-sizing: border-box; display: none; position: absolute; left: 0; width: 100%; -webkit-transform: translate3d(0,0,0) rotate(0) scale(1); min-height: 420px !important; } /* ...and so on*/ |
…and finally I changed jQuery selectors inside JavaScript code:
1 2 | $body = $('#jqtouch-wrapper'); // ...and so on |
It’s also possible to solve the conflict by changing SpinningWheel code in order to put the dom node inside active jQTouch pages, but the problem of potential incompatibilities will remains. A CSS declaration, except reset statements and other rare cases, should NEVER refers directly to a whole set of nodes (ie: ul { color: red; }), but to a node type contained by a wrapper/sandbox (ie: #foo ul {color: red; }), thus conflicts will be avoided.
Google Closure’s idiocies: Ajax can’t be synchronous :(
Dec 31st
While I love Closure tools and the possibility to write a better organized and OOP based JavaScript, I’m facing some weakness and inexplicable choices in the library. The last discover is that is impossible to make synchronous ajax call using the provided classes, since I didn’t find nothing about synch/asynch options by reading API reference, I took a look to the source (xhrio.js), and I noticed this:
1 | this.xhr_.open(method, url, true); // Always async! |
that true should be a parametric value which developers can set to true or false. Why forcing it to true???
I’m really disappointed :(
“Not authorized” error due to Safari “private browsing” mode! :P
Dec 27th
This is just a quick post to share my misadventure with Safari and the “private
browsing” mode. I’m working on a small JavaScript library which has the goal to abstract SQLite database api and allow users to create table, insert, update and delete
records easily… my code seems to work very well, but this morning during some
tests, I got the “not authorized” exception (error code n°1) on every
transaction. After hours of debugging I realized that my code was ok, but I forgot
to disable the “private browsing” mode in Safari!
So, bear in mind, if you want to play with database api and other client-related store capabilities (such cookies), remember to disable that option in Safari ;)
Extending Eclipse using JavaScript and Monkey Script engine
Dec 16th
I was wondering how to wrap a string with quotes in Eclipse by using a shortcut, then I realized that there is not such command, so I started thinking for a solution and initially I created an Aptana’s snippet, but I was not satisfied, because I want to have an handy shortcut to invoke my snippet. By googling, I discovered Eclipse Monkey Script engine, that is an extremely powerful tool for every JavaScript developer who wants to extend Eclipse features by writing few lines of JavaScript code. In order to use the js engine, you must have the package org.eclipse.eclipsemonkey (which is installed by default by Aptana) installed. Unfortunately I can’t find a complete and exhaustive reference for this project, which seems forgotten by authors, so I learnt what I know reading different posts.
Basically, Monkey Script allow developers to access editor’s instance, get selected test, get document content, edit it and update it. Moreover is possible to print text to consol, read and create files on the filesystem… and finally the scripts created will be accessible from Eclipse menu (under “Scripts”) and invokable through user defined shortcut… really nice!
So, backing to my experiment, I created this monkey script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /* * Key: M1+M2+C * Menu: Custom Scripts > Wrap with double quotes * DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript * Kudos: Davide Zanotti */ function main() { // no editor... exit if (typeof editors.activeEditor == "undefined") { return alert("No active editor"); } // get a reference to the editor in use var editor = editors.activeEditor; // beginning of text selection var startOffset = editor.selectionRange.startingOffset; // end of text selection var endOffset = editor.selectionRange.endingOffset; // selected text var selection = editor.source.substring(startOffset, endOffset); try { // surround selection with quotes editor.applyEdit(startOffset, endOffset - startOffset, '"' + selection + '"'); } catch (e) { alert("Error " + e.code + ": " + e.message); } }; |
It’s important to notice some points, first you MUST write an “header” using the comment syntax “/* */” into which you MUST declare at least 2 parameter: the “Menu” path (you can nest several menu items by writing several “>”) and the “DOM” package (which allows you to access specific objects like “editors“). You can write several js functions, but you must provide a main() function, which is called automatically by Eclipse once you launch your script, otherwise you can call the main function as you like but you must then provide the “OnLoad” param in the header, specifying which function will be called.
To define a shortcut you can use the “Key” parameter and define your own keys combination by choosing among the four modifiers: M1, M2, M3, M4, that are a platform-independent way of representing keys (these stand for ALT, COMMAND, CTRL, and SHIFT). The strange (at least to me) parameter “Kudos” is used to declare the author of the monkey script.
To test my little script or create your own, you have simply to create a new project into Eclipse by calling it as you like (ie: “custom-extensions”), then create a “scripts” folder into which you will save your .js files… that’s all, try yourself and enjoy :)
This is how it looks:

eclipse monkey script menu
ps: I’m going to explore the Monkey Script API in order to write more complex and useful extensions :P
Recent comments