Archive for the ‘javascript’ Category

Create a custom dojo build

Sunday, July 25th, 2010

When you develop an application with dojo, you can pretty quickly end-up with tens of http calls to load all dojo.require() files on demand. In my case, for a simple script relying on dijit.Dialog & dijit.Tooltip, firebug reporting more than 25 http calls for individual files. In production, this is not optimal at all.

So we will try to use dojo utilities to create our custom dojo file containing all (and only) the required dojo code into a single file. Problem is that there is scarce help on this matter on the Internet and official documentation is not correct!

Here are the steps:

  1. download a dojo- SDK (or “source-release”) (http://dojotoolkit.org)  (the uncompressed code is not enough), it contains data required to create dojo builds
  2. ensure java 1.4.2 or later is set up on your computer
  3. unzip dojo sdk file and go to: cd <dojo_folder>/util/buildscripts
  4. now create a new file that you will save wherever you want and call it “myapp.profile.js
    In this file we will put the following content:

    dependencies = {
    layers:
    [
    //This layer is used to discard modules
    //from the dojo package.

    {
    name: "dojo.js",
    customBase: true,
    discard: true,
    dependencies:
    [
    "dojo._base"
    ]

    },

    //This layer is used to discard modules
    //from the dijit package.

    //Custom layer mydojo.js which
    // includes our custom Dojo artifacts
    {
    //place the file under dojoRootDir
    name: “../../mydojo.js”,
    layerDependencies:
    [
    "dojo.js",
    ],
    dependencies:
    [
    //modules to be included from the
    //custom project. You can probably
    //list a single module here which in
    //turn declares all the required
    //modules as opposed to listing all the
    //modules individually. Please take a
    //look at dojoRootDir/dojo/_base.js
    //file for an example.
    // "dojo._base"
    "dijit.Dialog",
    "dijit.Tooltip"

    ]
    }
    ],

    prefixes: [
    ["dijit", "../dijit"],
    ["dojox", "../dojox"]
    ]

    }

  5. customize this file as you wish, basically simply list all the module you need where I put dijit.Dialog & dijit.Tooltip. Do not worry about modules required by these two modules, they will be automatically inserted by the build script!
  6. now run the following command:
    ./build.sh profileFile=<path_to_myapp.profile.js> action=clean,release releaseName=cusomizedDojo
  7. wait a few minutes (maybe more!)
  8. when the script has finished running, go up to dojo-root folder, a new folder named ‘release’ has been created, your dojo code is in there! The file you’re interested into is at the root of this folder and is named ‘mydojo.js’ or whatever name you put in your profile file.
  9. That’s it!

One-step further

At this point, we have generated a mydojo.js file containing all shrinked code for our dojo modules. Problem is that it does not contain dojo.js itself! It tried to include ‘dojo._base’ as a required module but it did not work. What I did was simply to open dojo.js in a word editor, copy its content and paste it at the top of mydojo.js file.

Now you have all your dojo-related js code in a single file! Exactly what we wanted.
If we insert this file in our code, replacing regular dojo.js file, we can see in firebug that no-more http calls are made!

Important notice

In my case, a folder named ‘nls’ was generated. It seems that this folder content cannot be incorporated into the single js file by build-script => I left it on its own in my /js folder and a single http call is made to retrieve the content of appropriate language file. A single extra http-call seems acceptable to me so  I left it this way.

Illustration

Before:

After:

Woooow! this is far better.

sources

issues with IE when cloning file input node

Monday, August 3rd, 2009

I wrote a js script in which I needed to clone an <input type=”file”> node.

As usual, IE spoiled the party. It appears that when you clone an <input type=”file”> node (through “node.cloneNode(true);” or “dojo.clone(node);”), the value property is not cloned. Therefore you end-up with an almost useless cloned node.

For security reasons, .value property of such node is not writable with js (only readable), this part I understand fully, but why on earth can’t IE  set it on the cloned node???
FF does it pretty well…

Only solution I came up with was to attach input node into a form node, upload it and then immediately re-attach to its previous form node. This is a pretty ugly solution which did not work fully for me since both uploads were asynchronous and I could not know where to reattach my input node before hand. => too  bad for IE users, they won’t benefit my nice features. FF users will.

sources

cannot set a name on hidden node in IE

Tuesday, July 28th, 2009

I tried to create an hidden input node with dojo using dojo.create(‘input’, {type:’hidden’, name:’foo’, value:’bar’}).

It worked fine in FF, but when looking at IE7, created node always had no name.
After multiple attempts, the only workaround I found was to create an outer node and fill it with hidden node converted as string, using .innerHTML and then retrieve it back using .firstChild property of outer node.

Here is the example:

var dummy = dojo.create(‘div’, {innerHTML: ‘<input type=”hidden” name=”foo” value=”bar”/>’});
var myInput = dummy.firstChild;

By doing this, when myInput got inserted into DOM, it kept its name property.

how to debug/keep-an-eye on all your http traffic

Tuesday, July 28th, 2009

While looking for a way to track xhr/http request in IE, I found out fiddler (www.fiddler2.com).

This tool is simply fantastic, it allows you to keep an eye on all your http requests!!!! ALL of them.
Very useful to check if a file is still being uploaded, to track xhr requests and so on…

For privacy concerns, make sure to use it while debugging only (it acts as a proxy, god knows what they do with proxied data).

sources

.indexOf on arrays with IE

Monday, July 27th, 2009

IE (at least version 7) has no implementation of ‘indexOf’ function on arrays.

Good news is that dojo has implemented a function just for this, and guess what, it’s dojo.indexOf().

Here is the signature of the function:

dojo.indexOf(/* array */haystack, /* mixed */ needle, /* integer? */fromIndex, /* boolean? */findLast);

sources

how to set minimum Heigth in dijit.form.Textarea

Thursday, July 2nd, 2009

[UPDATE]: rather than using below code, simply use css min-height property. Styling with ‘height’ does not work since dojo changes it dynamically, but min-height works great (too bad for IE6 users, but it’s time to update guys!).

**************************************

With v.1.3.1, dojo comes with a really nice feature on textarea form. They automatically expand while user is writting.

While this feature is great, it’s pittyful that for now, no option exists to prevent box resizing upon creation.
Therefore your textarea looks like a single line <input type=”text”> node and user simply has no idea that he can write a lot of stuff in it.

I subclassed the dijit to add a ‘skipResizeUponCreation’ option as defined below.
That way your textarea looks like a multi-line box and user knows he can write multiple line content.

/*
* Extends dijit.form.textarea to conditionally prevent height resizing upon instanciation
*/
dojo.provide('fenv.widgets.Textarea');
dojo.require('dijit.form.Textarea');

dojo.declare(
 'fenv.widgets.Textarea',
 dijit.form.Textarea,
 {
  skipResizeUponCreate: null,
  _rdFirstCall: true,

  resize: function(){
   // summary:
   //   skip call of parent resize() if called upon creation and this.skipResizeUponCreate is true
    if (!this._rdFirstCall || !this.skipResizeUponCreate) {
     this._rdFirstCall = false;
    return this.inherited(arguments);
   }
  }
 }
);

Screenshot
Textarea has not been resized upon instanciation, keeps its ‘expand-ad-you-type’ feature and user is aware of it being a multiline box.
=> best of both worlds.

Dont forget to use dojo.registerModulePath(); to link fenv.widgets (or whatever name you give him) to its url path.

sources

dojo + php = multi-file upload + progress bar

Wednesday, July 1st, 2009

In a previous post (simple multifile upload with dojo), I explained how to have multifile upload thanks to dojo.

In this post we will simply add the progress bar to our uploader (=> you might find useful to read the previous post first).
For now, flash server file served with dojo for testing looks like it’s not working properly (or at least that I don’t use it properly) => for this post, I will focus on JS multi-file uploader (you will have to select files one by one, sorry).

server side requirements

By default, php does not permit to access file-upload progress information. We need to activate dedicated php module for this. In our case, we will use the APC (alternative php cache) module.

  1. install the module (sudo apt-get install php-apc)
  2. modify php.ini (sudo vi /etc/php5/apache2/php.ini) to add the following line: “apc.rfc1867 = 1″ (without double quotes, everything in a dedicated single line)
    note: by default, even after installation of apc module, apc.rfc1867 option is turned off, so we need to activate it using php.ini otherwise we won’t be able to access file-upload progress information
  3. restart apache (sudo /etc/init.d/apache2 restart)

That’s it.

Our server should be up and running.
To ensure everything is ok, display a single php file with “<?php phpinfo();” as content and ensure apc.rfc1867 option is turned on as below:
apc.rfc1867

Javascript

Let’s have fun now.

The idea here is to capitalize on great work done by mike wilcox and its dojox.form.FileUploader widget, and to add our progress bar with a non-flash solution and to use dijit.ProgressBar as well to avoid the trouble of creating our own progress bar.

You can see a live example right here: progress multifile dojo uploader
note1: no design has been done, simply click on the text and choose the file to upload. Repeat the selection step to add another file for upload, then click on upload button. – seee at the end of the post for more details
note2: I did not display currently uploaded size, nor total size nor average speed. I could have done it, but as you will see at the end of the post, I am not entirely satisified with current implementation => I will do it on next implementation

Here is the js code:

dojo.require('dijit.ProgressBar');
dojo.require("dojox.form.FileUploader");

dojo.addOnLoad(function() {

/**** create progress bar ****/
var progressHandlerH = {
bar: new dijit.ProgressBar({}, 'progressBarH'),
_apcNode:null,
_lastXhr:null,
addApcNode: function() {
// summary:
//		add hidden node necessary by apc module for php-servers to be able to return upload-progress information
// note:
// 		only one node is necessary, even if multiple file upload
if (!this._apcNode || !this._apcNode.parentNode) {
this._apcNode = dojo.create('input', {type:'hidden', name:'APC_UPLOAD_PROGRESS', value:getUniqueId()}, uploaderH._formNode, 'first');
}
},
query4Progress: function() {
// summary:
//		query server for info upon upload progress
// note:
// 		- this._apcNode.parentNode is automatically cleared by dojox.form.FileUploader => will stop refresh upon onComplete()
//		- xhrGet.fired = -1 when query has not completed (=> avoid triggering a new xhr request while previous one is not finished)
if (uploaderH.fileList.length && this._apcNode.parentNode) {
if (!this._lastXhr || -1 != this._lastXhr.fired) {
this._lastXhr = dojo.xhrGet({
url: 'progressStatus.php',
content:{apc:this._apcNode.value},
handleAs:'json',
}).addCallback(dojo.hitch(this, function(data) {
if (100 != this.bar.progress && data.total) {
this.bar.update({progress: (data.current/data.total * 100)});
}
return data;
})).addErrback(function(data) {console.error(data);});
}
clearTimeout(this.TO);
this.TO = setTimeout(dojo.hitch(this, 'query4Progress'), 1000);
}
},
TO: null
},
getUniqueId = function() {
// summary:
//		return a unique random id based on microtime + random string (required by apc field)
var universe = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', id = new Date().getTime() + '_';
for (var i = 0; i<10; i++) {
id = id + universe.charAt(Math.max(0, Math.floor(Math.random() * universe.length) - 1));
}
return id;
};

/**** FOR JS/HTML UPLOADER ****/
var uploaderH = new dojox.form.FileUploader({
isDebug:true,
deferredUploading:true,
uploadUrl:'receiveFile.php',
uploadOnChange:false,
progressWidgetId:progressHandlerH.bar.id,
force:'html',
fileListId:'attachmentListH'
},
"addFilesH"
), uploadButton = dojo.byId('uploadFilesH');

dojo.connect(uploadButton, 'onclick', uploaderH, 'upload');
dojo.connect(uploadButton, 'onclick', progressHandlerH, 'query4Progress');
dojo.connect(uploaderH, 'onChange', progressHandlerH, 'addApcNode');

});

How does it work?

Basically we simply add an hidden field into the form generated by dojox.form.FileUploader dijit. This field has a specific name “APC_UPLOAD_PROGRESS” and a unique value. If we want to be able to access file-upload progress information on server side, this is required by php (it must be placed before <file> elements => we placed it first).

Then, we simply launched an ajax-request (dojo.getXhr()) that query at regular interval our server (progressStatus.php), asking for data upon current upload process.

Here is our server side file, it calls a single function (apc_fetch()) and return data into json format:

<?php
//phpinfo();

// THIS IS AN EXAMPLE
// you will obviously need to do more server side work than I am doing here to check and move your upload.

// JSON.php is available in dojo svn checkout
require(dirname(__FILE__). "/JSON.php");
$json = new Services_JSON();

$data = null;
if (isset($_GET['apc']) && ($apc_id = $_GET['apc'])) {
$data = apc_fetch('upload_'.$apc_id);
unset($data['temp_filename']);
}

// yeah, seems you have to wrap iframeIO stuff in textareas?
$stats = $json->encode($data);

echo $stats;

Limitations

This is great, but honestly I’m a bit disappointed. If you try it with multiple files, progress-bar is still acurate but it looks like php does not allow you to access the size of each file prior to having fully uploaded it.
The only information you have is the total size of all files to uploaded, but not this info for each individual file.

What’s the consequence? It’s that you cannot show a progress bar on a per-file basis. You can still display the name of the file currently uploaded (this is returned by php server script) but you won’t know what percentage of that file has been uploaded until it is finished. => you can find out this value for the ultimate file only.

This is why I will write another post where I take a slightly different approach. Multifile-upload will still be possible, but in the background each file to be uploaded will have it’s own <form> and a chain of uploads will be triggered.
(basically we will generate a dojox.form.FileUploader for each file to be uploaded). I will try to have a nicer css too.

I hope this info helped you run your own progress-bar file-uploader with dojo & php.
Any comment is welcomed (spams are not considered comments though :-) )

sources

dojo: ‘load’ function on xhrGet not called

Tuesday, June 30th, 2009

I had hard time finding this out, I will put it here.

My ‘load’ function set on xhrGet() object was not called at all upon successful ajax query. Query was performed but load function was simply ignored (no error, nothing).

I also directly registered another function through .addCallback() method, and same result. Plainly ignored!

After loosing half of my hair, I tried to remove ‘handleAs’ option too, and finally it worked. Both functions were triggered!

‘handleAs’ was set to ‘json’ and returned code is wrapped into textarea node as explained in js documentation (this is what caused below error, json data must be wrapped into <textarea> node when generated into an iframe, not through ajax requests).
Then I added an error callback (.addErrback()) logging provided response and finally an error message got displayed:

SyntaxError: missing } in XML expression message=missing } in XML expression

Conclusion

Always attach an Errback function, even if it’s just to log the response.

multi-file upload with dojo

Tuesday, June 30th, 2009

If you use dojo, you might want a nice file-uploader.

Good news is that such a widget exits! Bad news is that it’s not very easy to use, at least for now.
I will try here to gather what I’ve learnt while using it (or at least while trying to make it work). Maybe someone will find it useful.

Before we start

As of now (dojo 1.3.1), I’m using trunk version of dojox.fileUploader (it has more features and greater flexibility than version available in dojo 1.3.1).
=> you will need to download trunk version too (http://archive.dojotoolkit.org/nightly/) if you want example to work (and we cannot use cdn for nows, too bad).

Quickstart

We will use dojox.fileUploader widget.

First we will do a basic file-upload form. No progress bar, just a plain file-upload.

Currently it works as follow: the node you pass through when creating the widget is what will be the upload button.
If flash is detected, flash uploader is automatically instanciated (with its nice multi-file selection) otherwise regular js uploader is created (you can force either of them by providing ‘force’ option with value set to ‘html’ or ‘flash’).

So what’s important:

  • provided node at instanciation (will be upload button)
  • ‘fileListId’ option (id of node/div that will store list of selected files for upload)
  • ‘uploadUrl’ option (make it point to your server-side script)
  • have an upload button and connect it to your widget upload function upon click event

Here is all the js you need:

dojo.require("dojox.form.FileUploader");

dojo.addOnLoad(function() {
 var uploader = new dojox.form.FileUploader({
   isDebug:true,
   deferredUploading:true,
   uploadUrl:'ReceiveFile.php',
   uploadOnChange:false,
   force:'',
   fileListId:'attachmentList'
  },
  "addFiles"
 );

 dojo.connect(dojo.byId('uploadFiles'), 'onclick', uploader, 'upload');
});

Here is a live example, with both flash / non-flash version of the uploader: simple multi-file uploader with dojo

note1: to remove a file from the selection, simply click on it
note2: as server-side script, I’ve used script dojox/form/resources/RecieveFile.php (which name I corrected due to typo and adding a dump into text file to ensure data were received on server side).

Well, big thanks to mike wilcox for this great widget.

Progress bar

If you want to have a nice progress bar on your multi-file uploader (when working in js mode since flash mode is dealt by flash already), read on the next post: dojo + multifile uploader + progressbar

sources

Browser detection with dojo

Tuesday, June 30th, 2009

Browser detection in dojo is pretty easy, simply use dojo.isXXX() properties.

ex: dojo.isFF; dojo.isIE; dojo.isChrome; dojo.isOpera;

Let’s get it more interesting. You’re targeting a specific version of IE? then use:

if (dojo.isIE > 6) {
 // code to execute for ie7 and more
}

sources