Javascript mp3 player

February 12th, 2010 by Marius Hanganu in General, Javascript
flash javascript mp3 player

flash javascript mp3 player

Seems impossible, right? Well, it is :-). The title is a bit misleading. You can’t play mp3 in javascript. Not yet at least. Not without a flash object behind the scenes.

But when it comes to searching a flash mp3 player that has also a customizable skin and javascript integration you won’t have so much luck. The players that allow flash – javascript communication are not skinnable, and the ones that are skinnable, don’t allow communication with javascript.

So after a bit of hacking here’s the final result: http://blog.tremend.ro/wp-content/uploads/2010/02/flash-javascript-mp3-player/test.html

The script is based on this excellent flash mp3 player which involves a hidden flash mp3 player. The main headache is actually not the UI which uses a simple scriptaculous effect for scrolling (just drag and drop the cursor to see it working), but actually making sure the commands from javascript to the flash player work on all browsers.

Another bonus feature added is the ability to jump to different sections of the audio file. The sections are described in a javascript configuration object. An UI for this feature can be easily built using your favorite UI framework.


Codepress extension – syntax highlighting for Velocity

November 19th, 2008 by Marius Hanganu in Javascript

For the impacients, here’s a link on how Velocity syntax highlighting can be done in a browser.

Codepress is an excellent script for syntax highlighting when editing code using the browser. We needed some syntax highlighting for one of the projects at Tremend which included some Velocity code. So after some hacking I came out with a set of regular expressions which should match almost all instructions for a Velocity template.

Of course, the well known problem of matching paranthesis is not solved, so the expressions work only up to three levels of embedded paranthesis, which should be enough for most projects. If more levels of embedded paranthesis are needed, more expressions can be defined.

Here’s how the editor looks like

Velocity in Codepress

Also, velocity is always used in conjunction with other language. Whether it’s about generating HTML, XML or any other piece of text, Velocity is a template engine.

The rules defined in velocity.js are comprising also HTML and JavaScript rules, since the code we’re generating is HTML + JavaScript.


Link flash to html page

October 3rd, 2008 by Marius Hanganu in General, Javascript

This is a small one, but I banged my head several times now before fixing it. Although many have written about it, I decided to write also. More resources are always better than fewer resources :-)

So you’ve created the flash object, correctly set the URL, but when embedding it on the web page on your web server, the link doesn’t work. The solution is quite simple: you need to set allowScriptAccess to sameDomain or even always if sameDomain doesn’t do the job.

And of course, you need to do that for both the object and the embedtag. For example:

1
2
3
4
5
6
7
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" width="295" height="240">
    <param name="allowScriptAccess" value="always" />
    <param name="allowFullScreen" value="false" />
    <param name="movie" value="http://server/flash.swf">
    <param name="quality" value="high">
    <embed src="http://server/flash.swf" quality="high" allowScriptAccess="always" allowFullScreen="false"  pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="295" height="240"></embed>
</object>

Using this parameter, the flash object will now allow linking to the HTML page set in the flash object.

Blogged with Flock

Tags: , , ,


Google Talk in Yahoo Messenger

September 23rd, 2008 by Marius Hanganu in General, Javascript, Tools

Some time ago, when I first saw the Google Talk gadget is being released, I got this crazy idea of embedding the gadget in Yahoo Messenger as a plugin.

Once logged in, the gadget was displayed just fine as a plugin, but if the user was not logged in, he’d be taken to the google login page. So I started hacking and chopping and after half hour I finally got this version working. Click here to download the plugin.

After downloading it, unzip it into the C:\Program Files\Yahoo!\Messenger\Plugin\Test\ContentTab\ folder. UPDATE: after unzipping, rename the folder to test.plugin (thanks Tudor) The final path should look like: C:\Program Files\Yahoo!\Messenger\Plugin\Test\ContentTab\test.yplugin\.

Restart Yahoo Messenger, log-in and select Actions -> Load Test Plug-in, and the Google login page will show up in the plug-ins area. You can select the “remember me” box, so that after restart you will be automatically logged in.

The plugin is quite simple: it contains one simple HTML file with some Google styles, and the HTML code for the login page. On page load – a request is made to http://www.google.com/ig to check if the user is already logged in. This is achieved by parsing the response and checking for “Sign out” string – which means the user is logged in.

Surprisingly, from all the Google pages, the http://www.google.com/ig page was the only one (after 4 or 5 unsuccessful attempts) that could give me an indicator on whether the user is logged in or not.

In case user is logged in, the page is redirected to the URL containing the embeddable flash gadget for Google Talk: http://talkgadget.google.com/talkgadget/popout.

Unfortunately, the administrators of Yahoo Messenger plug-ins at http://gallery.yahoo.com/messenger have rejected the application so far, so in order to use it, every time you start Yahoo Messenger, you need to select “Load Test Plug-in” to load this plugin.

Here are some images with the plugin:
Google Talk in Yahoo Messenger

Google Talk in Yahoo Messenger
And here’s the code for the plugin:

    <!--
<!–
        body,td,div,p,a,font,span {font-family: arial,sans-serif;}
        body {margin-top:2}.c {width:4; height: 4}
        body { background-color:#E8EEFA; }
        A:link {color:#0000cc; }
        A:visited { color:#551a8b; }
        A:active { color:#ff0000; }
        .form-noindent {background-color: #ffffff; border: #C3D9FF 1px solid}
        .gaia.le.lbl { font-family: Arial, Helvetica, sans-serif; font-size: smaller; }
        .gaia.le.fpwd { font-family: Arial, Helvetica, sans-serif; font-size: 70%; }
        .gaia.le.chusr { font-family: Arial, Helvetica, sans-serif; font-size: 70%; }
        .gaia.le.val { font-family: Arial, Helvetica, sans-serif; font-size: smaller; }
        .gaia.le.button { font-family: Arial, Helvetica, sans-serif; font-size: smaller; }
        .gaia.le.rem { font-family: Arial, Helvetica, sans-serif; font-size: smaller; }
        .gaia.captchahtml.desc { font-family: arial, sans-serif; font-size: smaller; }
        .gaia.captchahtml.cmt { font-family: arial, sans-serif; font-size: smaller; font-style: italic; }
        .body { margin-left: 3em;margin-right: 5em;font-family: arial,sans-serif; }
        div.errorbox-good {}
        div.errorbox-bad {}
        div.errormsg { color: red; font-size: smaller; font-family: arial,sans-serif;}
        font.errormsg { color: red; font-size: smaller; font-family: arial,sans-serif;}
        hr {border: 0;background-color:#DDDDDD;height: 1px;width: 100%;text-align: left;margin: 5px;}
         div.errormsg { color: red; font-size: smaller; font-family:arial,sans-serif; }
         font.errormsg { color: red; font-size: smaller; font-family:arial,sans-serif; }
        .gaia.le.lbl { font-family: Arial, Helvetica, sans-serif; font-size: smaller; }
        .gaia.le.fpwd { font-family: Arial, Helvetica, sans-serif; font-size: 70%; }
        .gaia.le.chusr { font-family: Arial, Helvetica, sans-serif; font-size: 70%; }
        .gaia.le.val { font-family: Arial, Helvetica, sans-serif; font-size: smaller; }
        .gaia.le.button { font-family: Arial, Helvetica, sans-serif; font-size: smaller; }
        .gaia.le.rem { font-family: Arial, Helvetica, sans-serif; font-size: smaller; }
        .gaia.captchahtml.desc { font-family: arial, sans-serif; font-size: smaller; }
        .gaia.captchahtml.cmt { font-family: arial, sans-serif; font-size: smaller; font-style: italic; }
    –>
-->
    <script type="text/javascript"><!--mce:0--></script>
<form id="gaia_loginform" action="https://www.google.com/accounts/ServiceLoginAuth" method="post">
<div class="loginBox">
<table id="gaia_table" style="display:none" border="0" cellspacing="0" align="center">
<tbody>
<tr>
<td colspan="2">
                        <span>
                                    <img src="https://www.google.com/accounts/google_transparent.gif" alt="Google" />
                                </span></td>
<td valign="middle">
                                    <span>
                    </span></td>
</tr>
<tr>
<td>
<div>
                            <span class="gaia le lbl">Email:</span></div></td>
<td>
<input id="continue" name="continue" type="hidden" value="http://talkgadget.google.com/talkgadget/popout" />
<input id="service" name="service" type="hidden" value="talk" />
<input id="skipvpage" name="skipvpage" type="hidden" value="true" />
<input name="GALX" type="hidden" value="aa3Jml3aMHk" />
<input id="Email" class="’gaia" name="Email" size="18" type="text" /></td>
</tr>
<tr>
<td></td>
<td align="left"></td>
</tr>
<tr>
<td align="right">
                        <span class="gaia le lbl"> Password: </span></td>
<td>
<input id="Passwd" name="Passwd" size="18" type="password" /></td>
</tr>
<tr>
<td></td>
<td align="left"></td>
</tr>
<tr>
<td align="right" valign="top">
<input id="PersistentCookie" checked="’checked’/" name="PersistentCookie" type="checkbox" value="yes" />
<input name="’rmShown’" type="hidden" value="1?/&gt;
                    &lt;/td&gt;
&lt;td&gt;
                        &lt;span class=" />Remember me on this computer.</td>
</tr>
<tr>
<td></td>
<td align="left">
<input class="gaia le button" name="signIn" type="submit" value="Sign in" /></td>
</tr>
</tbody></table>
</div>
</form>

A href vs onclick – performance test – IE6 vs IE7 vs FF3 vs FF2 vs Chrome vs Opera vs Safari

September 10th, 2008 by Marius Hanganu in General, Javascript

While optimizing some HTML code, I recently came across the question: when rendering anchors, which of the href and onclick are rendered quicker by the browsers. To find out, I created two files, each one with 330.480 anchors, one only with onclick event: onclick=’vc(\’4280220\’)’ and another one only with hrefs: href=’javascript:vc(\’4280220\’)’.

The files have over 17 and 14 megs (the one with hrefs being larger due to more text). Here’s the beginning of each files:

  • href:
        <a class="”td”">-</a>
        <a class="”td”">-</a>
        … 330.478 more
  • onclick:
        <a class="td">-</a>
        <a class="td">-</a>
        … 330.478 more

The “td” class used is necessary since rendering the anchors with href results in underlining the text, thus consuming more CPU power for drawing the page, than the anchors with onclick which do not get underlined.

The environment on which the test was performed is a Windows XP SP2 machine with Multiple IEs for testing on both IE7 and IE6 as well as Firefox 2, Firefox 3, Chrome, Opera and Safari. The rendering time on one browser is not important (which is why I don’t mention hardware indicators), but the actual time comparison between all the browsers. Let’s see the results first:

IE vs FF vs Chrome vs Opera vs SafariWith regards to the main purpose of this test: href vs onclick, it seems that for “small” HTML optimizations, the rendering times are about the same (keep in mind we’re talking about over 330.000 anchors – for several thousands, the difference is imperceptible).

If you however are a hardcore programmer who needs those extra seconds, Internet Explorer is the only one that renders href-s much quicker than onclick-s. All the other browsers render onclick-s faster than href-s. If your website will be accessed by a wide audience of users, given that IE has still the biggest share of the browser market, you may want to render your anchors using href-s.

Apart from the hrefonclick comparison, there are some surprises in this graph. For example FF3, although much faster than FF2 or IE at rendering complex pages and javascript, performs the slowest of all browsers. Opera is the fastest browser, while Firefox 2 does almost the same job as Internet Explorer 7.

IE6 renders the page with href-s incredibly faster, but in the same time, it took more than 5.5 minutes to render the page with onclick event (I eventually killed the process after 5.5 minutes). This may be due to Multiple IEs, so the second result for IE6 is questionable.

And the newest kid in the block, Chrome behaves better than Firefox 3, but much slower than Firefox 2.

While this test may give a slight idea of how fast these browsers performs with this specific test case, it is not relevant for regular HTML pages filled with various elements (div-s, span-s, br-s, p-s, etc). For those pages, as probably everyone has noticed, FF3 renders the page much faster than IE7, or IE6 or FF2.

Blogged with Flock

Tags: , , , , , , , ,


Dynamically embed youtube videos on IE6 using innerHTML

August 6th, 2008 by Marius Hanganu in Javascript

I got this annoying problem today: when trying to automatically populate a DIV with embed code from youtube, nothing happened on IE6. IE7 and FF work fine.

Some googling and I found this good resource about flash embedding techniques. There is one library that fixes the problem, called ufo.js, library which was far too big for my taste (11k uncompressed). So after some trimming, I was a bit surprised to find out that all that was needed to make your innerHTML code work with Internet Explorer 6, was the missing CLASSID attribute for the object tag.

Here is a link to a test page that works well an FF, IE7, IE6, Opera and Safari. There is one small reusable javascript function that fixes the embedded code by adding the CLASSID attribute to the object tag. After that, you can use innerHTML attribute to populate the contents of your HTML elements with embedded video code.

var fixEmbeddedVideo = function(embedCode) {
   if(embedCode &amp;&amp; embedCode.toLowerCase().indexOf(’classid’) == -1) {
      var objPos = embedCode.toLowerCase().indexOf(’object ‘) + ‘object ‘.length;
      return embedCode.substr(0, objPos) + ‘classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000?+ embedCode.substr(objPos);
   } else {
      return embedCode;
   }
}

Blogged with Flock

Tags: , , , , ,


Extjs GridPanel plugin: Ajax row expander

April 22nd, 2008 by Marius Hanganu in Javascript

If you were like me in the need to expand the rows of a Extjs GridPanel not using cached data, but rather dynamically through an Ajax request, then you’ve ended in the right place.

The current Extjs GridPanel plugin – RowExpander, located in the examples of Extjs, is good if you happen to have access to that data when the request for filling the GridPanel takes place. But if your request involves querying lots of tables in the database, then you might want to minimize/optimize the HTTP requests and the SQL queries, hence, your optimized Ajax request to fill GridPanel’s content may not bring those details you might need.

This is a good reason for building a RowExpander that would fill up the bodyContent of the GridPanel’s row using an Ajax request.

The following piece of code creates a class called AjaxRowExpander which extends the original RowExpander provided in the Extjs examples. So if you want to use it, you need to also include the RowExpander.js file.

What it does, is pass an URL in the constructor, URL that will be later used to make the Ajax request. One other thing to notice in the constructor is the disabling of the cache since I needed fresh data every time.

The class overrides the getBodyContent method, in which three things happen:

  1. a temporary body is creating, displaying a “Loading…” message. The temporary body is actually a div that will be later filled with the response from the Ajax request. In order to do that, we need to retrieve the exact same div object later when the response gets back from the server. This is why the div gets an id: ‘tmp’ + record.id, so that it can be uniquely identified later.
  2. second, the Ajax request is made, at an URL obtained by concatenating the URL received in the constructor, and the id of the record. A typical URL may look like: “loadPersonDetails.htm?id=“.
  3. third step (which happens asynchronously) is when the response gets back from the server, and the div created in step 1 is filled with the server’s response
AjaxRowExpander = function(config, previewURL){
    AjaxRowExpander.superclass.constructor.call(this, config, previewURL);
    this.previewURL = previewURL;
    this.enableCaching = false;
}
 
Ext.extend(AjaxRowExpander, Ext.grid.RowExpander, {
    getBodyContent : function(record, index){
        var body =
Loading…

’; Ext.Ajax.request({ url: this.previewURL + record.id, disableCaching: true, success: function(response, options) { Ext.getDom(”articleReportsPreview” + options.objId).innerHTML = response.responseText; }, failure: function(error) { alert(DWRUtil.toDescriptiveString(error, 3)); }, objId: record.id }); return body; }, beforeExpand : function(record, body, rowIndex){ if(this.fireEvent(’beforeexpand’, this, record, body, rowIndex) !== false){ body.innerHTML = this.getBodyContent(record, rowIndex); return true; } else{ return false; } } });

There is another function from the RowExpander which gets overrided, and that is beforeExpand. This was needed since the original beforeExpand method did not eagerly initialized the row’s body.

And here’s an example on how to use it:

var expander = new AjaxRowExpander({}, ‘loadPersonDetails.htm?id=);
var cm = new Ext.grid.ColumnModel([
        expander,
        {header: "Id", dataIndex: 'id', width: 14, align: 'right', defaultSortable: true},
        {header: "Name", dataIndex: 'name', width: 30, sortable: false}
]);
 
var grid = new Ext.grid.GridPanel({
        height: 500,
        width: 635,
        store: store,
        cm: cm,
        sm: sm,
        viewConfig: {
                forceFit:true,
                enableRowBody:true,
                showPreview:false
        },
        plugins: expander
});

Blogged with Flock

Tags: , , , ,


Prototype based script for notifying users for unsaved changes when leaving a page

October 3rd, 2007 by Marius Hanganu in Javascript

It is a common requirement in today’s applications to ask for user’s confirmation when leaving a page – if changes were made in the page. I’ve written this simple prototype based script for checking for modifications. If one does not make any changes to the page – it can leave the page without being asked for confirmation. However, if any change has been made, the user will be asked whether changes will be discarded or not.

The best thing about this script is that it can be applied non-intrusively to any HTML page. No changes to the input fields in the page are required. All you have to do is add the following code to initialize the tracking for changes:

<script>// <![CDATA[
        Event.observe(window, "load", function() {
            initCheckingForModifications();
        });
 
// ]]></script>

The mechanism is simple: track any changes in input fields, textareas or select boxes. Also, monitor every anchor in the page and intercept the onclick event, attaching our own verification before following the URL/javascript in that anchor.

Anchors (a-s) are tracked by removing the href attribute and attaching a handler for the onclick event. This is a very simple method for intercepting clicks on anchors avoiding headaches. Please not that removal of href attribute results in anchors loosing their styles. So do attach your own style to the anchors in the page.

Since requirements can go a long way, this could also serve as a starter for more complex checks.

Following you can find the two files involved: test.htm and checkModifications.js. Here is test.html:

<script src="”js/prototype.js”" type="”text/javascript”"></script><script src="”checkModifications.js”" type="”text/javascript”"></script>

 

 

yahoo
alert

and checkModifications.js

var mustConfirmLeave = false;
function initCheckingForModifications() {
    //var elems = $(formId).elements;
    var inputs = document.getElementsByTagName(”input”);
    for(var i = 0; i &lt; inputs.length; i++) {
        var type = inputs[i].getAttribute(”type”);
        if(type == “checkbox” || type == “radio”) {
            Event.observe(inputs[i], “change”, somethingHasChanged);
        } else {
            Event.observe(inputs[i], “keypress”, somethingHasChanged);
        }
    }
    var textareas = document.getElementsByTagName(”textarea”);
    for(var i = 0; i &lt; textareas.length; i++) {
        Event.observe(textareas[i], “keypress”, somethingHasChanged);
    }
    var selects = document.getElementsByTagName(”select”);
    for(var i = 0; i &lt; selects.length; i++) {
        Event.observe(selects[i], “change”, somethingHasChanged);
    }
 
    // for all a-s - intercept onclick
    var as = document.getElementsByTagName(”a”);
    for(var i = 0; i &lt; as.length; i++) {
        var href = as[i].getAttribute(”href”);
        as[i]._href = href;
        as[i].removeAttribute(”href”);
        Event.observe(as[i], “click”, navigateAway.bindAsEventListener(as[i]));
    }
}
 
function somethingHasChanged(e) {
    if (e.keyCode != Event.KEY_TAB) {
        mustConfirmLeave = true;
    }
}
 
function navigateAway(url) {
    if(checkForModifications()) {
        window.location.href = this._href;//url;
    }
}
 
function checkForModifications() {
    if(mustConfirmLeave) {
        if(confirm(”You’ve made changes in the page. Are you sure you want to leave this page without saving the changes?)) {
            return true;
        } else {
            return false;
        }                           
    }
    return true;
}

Blogged with Flock

Tags: , , ,


Dojo vs Ext.js – How Dojo lost in front of other UI frameworks like Ext js

August 22nd, 2007 by Marius Hanganu in Javascript

I’ve been meaning to write these thoughts for a long time. I have been using Dojo from version 0.2.x (almost two years) and I have promoted Dojo in all the projects I’ve been involved. Long story short – I was and still am – a huge fan of Dojo.

However, in the last few months, I’ve been using Ext js and actually promoted the idea of rewriting an old UI for another project (I hope Martin will share more with us about his Ext js experience).

Both projects are great. Actually dojo is greater than Ext js :-). It has a lot of super-uber-cool stuff in it:

  • superb infrastructure – the dojo.event.connect is the most prominent example; dojo.io.bind is another great functionality
  • i18n and a11y – I’m not even sure if other JS frameworks are implementing this
  • good solid written API – one can easily write its own custom widgets
  • dojo storage and dojo offline – excellent ideas from Brad Neuberg
  • the most ingenious graphics package – uses Canvas for FF and VML for IE making 3d graphics in browser portable between browsers
  • many, many others – just check Dojo’s website and you can find out more

Ext JS is a relatively young framework (compared to dojo) that is best described by Dojo’s own website: “It features a large number of consistent, good looking widgets with an
emphasis on pixel-perfect layout and desktop-like UIs across browsers.
Originally developed to run on top of YUI and later JQuery, EXT now has
it’s own low-level library, removing the need for 3rd party
dependencies. The EXT community is very active and good documentation
is available for the library.

As they put it – Ext js is just a widget library with eye catchy UI. Much less than dojo which has dojo, dijit and dojox. But the true facts here are:

  1. People need widgets. The number of developers wishing to adopt a base framework without a widget hierarchy is ridiculously small compared to the number of users searching for the building blocks of a Web UI – the widgets
  2. A good UI is the first selling point, whether your project it’s open source or not. This is the first and foremost lesson. Alex Russell did not get that. Jack Slocum did. That’s why the Ext js forum are now having their traffic at least triple compared to dojo’s. Dojo’s themes are L.O.U.S.Y. One cannot use them outside the box. On the other hand – Ext JS looks fantastic. The perfect thing for an administrative interface. Perhaps you’ll customize it for a public website, but then again, who’s really using such powerful frameworks for their public website? Try looking for that in their mailing lists. I tried that several times during the last two years and the answers are insignificant. To give an answer to that – almost everyone uses and loves prototype.
  3. The ever lasting table widget. Just look at the number of questions/requests in dojo’s mailing list for a good table widget that supports resizing, pagination, etc. That should’ve been one of the first widgets to develop. Ext js has a marvellous widget here.
  4. Page loading time. Again, this one of Dojo’s ghosts that haunted them until their 0.9 release. Perhaps they’ve fixed it, but until now, it required so much effort if you had your custom widgets, or if you just needed a custom build.

Of all these reasons, I blame number 2 the most. This is a HUGE blocker in adopting a framework for a project. I adopted Dojo with much enthusiasm and I am still waiting for it to become the ultimate framework, since it has such a great potential.

But as a pragmatic programmer, if you have something that gets your job done, you use it. I don’t want to create an awful looking interface and work on “pixel-perfection” another several days. Clients are not paying for customizing Dojo – they’ll just ask – “if you could’ve built the same UI with another library and looked much cooler, why didn’t you do it?”. (we can argue here also about the usability of Dojo’s widgets vs Ext js’ widgets, for I believe Ext js is simpler to use – but this is a different topic).

Dojo had several attempts in pushing for some good looking themes. Unfortunately they did not get that “good looking widgets” and “pixel-perfect” layout. Widgets are still difficult to integrate (I haven’t switched to dojo 0.9 yet), and as I said, developers need widgets. Widgets that work well with other widgets, that are easy to program. Just compare the mail example from Dojo with the RSS feeder written by Jack Slocum.

So the people behind Dojo – excellent developers and arhitects, you must consider some marketing advices. It would rise both Dojo (and Sitepen of course) to the level where it should be – the best JS framework out there. Everyone needs marketing – even open source projects …

technorati tags:, , ,


Javascript advanced tutorial

August 21st, 2007 by Marius Hanganu in Javascript

I prepared a quick Javascript tutorial during the weekend and presented it to my colleagues in Tremend. You can view it better here. Although there are entire presentations or documents dedicated to what may be covered by one single line in this presentation, I tried to summarize some of the Javascript highlights to my colleagues.

Some of the subjects covered were:

  • common mistakes
  • common constructions for a web developer
  • arguments, apply, prototype keywords
  • utility functions with prototype, dojo, ext js, DWR
  • AOP in JS – the simplest way to learn AOP is using Javascript
  • usage of Javascript in Java


AJAX file upload monitoring – monitor your file upload with DWR and commons-fileupload

March 1st, 2007 by Marius Hanganu in Java, General, Javascript

Monitoring your file upload using ajax is a must have for any web 2.0 application. There are already some good examples developed by prosson and carsonm.

The first one looks great, but it is VERY slow. Takes several minutes to upload a file of 40M on localhost on my Core Duo which is Unnaceptable.

Fortunately, the second has the right speed. Actually it has it all, being written with prototype. The version I’m posting on this entry is just a variation to the version posted on mission data blog, using DWR. You can download all sources and dependencies along with Intellij IDEA project here.

The mechanism is simple and works like this:

  1. upload request is made with a dummy target (an embedded invisible iframe)
  2. on the server side, the upload servlet starts monitoring the upload and sets some objects on the session
  3. on the client side, a request for checking progress is made shortly after the upload has been triggered
  4. using DWR this request goes to a DWR proxy on the server side which returns some statistics
  5. when the upload servlet finished the upload, it will signal this to the DWR proxy, so that the next request for progress will be the last one

To integrate this in your own application you must do the following:

1. In web.xml declare the upload servlet and the dwr servlet (if it’s not there already):
    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>ro.tremend.upload.UploadServlet</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>dwr-invoker</servlet-name>
        <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>scriptCompressed</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/Upload</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>dwr-invoker</servlet-name>
        <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>

2. In dwr.xml declare your proxy and a converter for the status object returned to UI:
    <create creator=”new” javascript=”uploadProxy”>
        <param name=”class” value=”ro.tremend.upload.UploadProxy”/>
    </create>

    <convert converter=”bean” match=”ro.tremend.upload.UploadStatus”/>

3. In your jsp or html page add the following:
    <iframe id=’target_upload’ name=’target_upload’ src=” style=’display: none’></iframe>

    <form enctype=”multipart/form-data” name=”form” method=”post” action=”Upload” onsubmit=”return startUploadMonitoring();” target=”target_upload”>
        File to upload: <input id=”importFile” name=”importFile” type=”file”> <br/>
        <input id=”submitButton” type=”submit” value=”Upload”/>
    </form>

    <div id=”uploadStatus”>
        <!– specify width on this uploadProgressBar, otherwise the progress indicator won’t move –>
        <div id=”uploadProgressBar” style=”width:200px;”>
            <div id=”uploadIndicator”></div>
        </div>
        <div id=”uploadPercentage”></div>
    </div>

The iframe is used to swallow the response to the upload request. The form contains the upload fields and the button. The uploadStatus div will hold the progress monitor.

4. In the head section of your jsp or html you will have to add the following:

    <style type=”text/css”>
        #uploadStatus {
            width: 230px;
        }

        #uploadProgressBar {
            height: 14px;
            border: 1px solid #BBB;
            text-align: center;
            display: inline;
            float: left;
        }

        #uploadIndicator {
            height: 10px;
            position: relative;
            margin: 1px;
            padding: 1px;
            background: #9DC0F4;
            width: 0;
            float: left;
        }

        #uploadPercentage {
            width: 20px;
            display: inline;
            float: right;
        }
    </style>

    <script type=’text/javascript’ src=”dwr/util.js”></script>
    <script type=’text/javascript’ src=”dwr/engine.js”></script>
    <script type=”text/javascript” src=”dwr/interface/uploadProxy.js”></script>

    <script type=”text/javascript” language=”JavaScript”>
        var updater = null;

        function checkStatus() {
            uploadProxy.getStatus(function(stat) {
                if (stat.status == 2) {
                    updateProgressBar(100);
                    return;
                }

                if (stat.status == 3) {
                    alert(“An error has occured! ” + stat.message);
                    return;
                }

                if (stat.status == 4) {
                    alert(“An error has occured! ” + stat.message);
                    return;
                }

                // do something with the percentage (nice loading bar, simply show the percentage, etc)
                updateProgressBar(stat.percentComplete);
                window.setTimeout(“checkStatus()”, 500);
            });
        }

        function updateProgressBar(percentage) {
            // make sure you set the width style property for uploadProgressBar, otherwise progress.style.width won’t work
            var progress = document.getElementById(“uploadProgressBar”);
            var indicator = document.getElementById(“uploadIndicator”);
            var maxWidth = parseIntWithPx(progress.style.width) – 4;
            var width = percentage * maxWidth / 100;
            indicator.style.width = width + “px”;
            var perc = document.getElementById(“uploadPercentage”);
            perc.innerHTML = percentage + “%”;
        }

        function parseIntWithPx(str) {
            var strArray = str.split(“p”);
            return parseInt(strArray[0]);
        }

        function startUploadMonitoring() {
            window.setTimeout(“checkStatus()”, 500);
            return true;
        }
</script>

5. You can assign unique identifiers to the set of files you’re uploading. The UploadServlet will make sure when upload finished to make
all files available on the HttpSession object under the key you’ve specified. To do that you’ll have to:
    a) add a new field inside the form:
        <input type=”hidden” name=”uniqueFileIdentifier” value=”1234″/>
    b) in your java code, you can retrieve the files uploaded like this:
        List filesUploaded = (List) session.getAttribute(UploadServlet.FILES_UPLOADED);

    This will return a list of FileItem objects (for more information check the commons file upload documentation).

technorati tags:, , , , ,


Dojo DND – how to cancel drag and drop in dojo

February 27th, 2007 by Marius Hanganu in Javascript

How can one cancel the drag and drop operation in dojo, based on conditions specific to the application? I’ve seen this question asked several times and few if none answers, so I thought I might post my solution here.
The basic idea is to connect to the accepts event from a dojo.dnd.DropTarget using the around advice which lets you control the output of the accepts method (more details here).

Here’s a code snippet:

dojo.event.connect(“around”, myDropTarget, “accepts”, “acceptsDrop”);

Inside your acceptsDrop function you can cancel the DND operation using your own custom conditions:

function acceptsDrop(invocation) {

    var dragObjects = invocation.args[0];

    for (var x = 0; x < dragObjects.length; x++) {

        // if some condition related to dragObjects is true

       if(dragObjects[x].domNode.getAttribute(“someAttribute”) == “someValue”) {

          // returning false will result in cancelling the drag and drop

         return false;

        }

    }

    // returning true will allow the drag and drop operation to continue

    return true;

}

Enjoy


Cursor disappears in Firefox

January 22nd, 2007 by Marius Hanganu in Javascript

Firefox has this annoying problem of the cursor caret becoming invisible sometimes. For example, when showing up a div, the textboxes inside that div don’t have the cursor. You can edit them, but you can’t really know where exactly the cursor is. This can be very frustrating when editing texts in input boxes.

The mouse doesn’t actually dissapear, as one friend cleverly noticed. If you move your div around, you’ll find it blinking somewhere underneath. This has nothing to do whatsoever with z-index, but with div’s scrolling as some people point out .

So basically, to make your caret visible, you’d have to replace your div’s style from something like:

overflow: auto

to

overflow: hidden;

This helped me get rid of the invisible cursor problem. I guess other overflow values will work as well, except for auto.
br />

technorati tags:, , , , , ,


Spreadsheet dojo widget with custom dojo build

September 28th, 2006 by Marius Hanganu in Javascript

After getting a lot of requests on high load time of the spreadsheet widget, I decided to create a custom dojo build.

Here’s a link to the page that uses a custom dojo.js file of 248k.

Note: I started posting entries using Flock‘s embedded blog tool. Editing a post in WordPress was a nightmare. Loses formatting, changes styles, a very painful process overall. But using Flock’s blog tool was a good experience.


Spreadsheet dojo widget

September 25th, 2006 by Marius Hanganu in Javascript

Update3: We’re back to our own hosting.

Update2: Razvan Dragomirescu was kind enough to mirror the spreadsheet widget. Click here to access the demo. Thanks Razvan!

Update: Due to high traffic the demo page might load very slowly. Please have patience.

In the last weeks, I’ve been using my so very little spare time to create a spreadsheet widget based on dojo.

Click here for a demo of the spreadsheet widget. It is based on release 0.3.1 of dojo toolkit. Dojo is an excellent framework. Scores 3rd in this Ajaxian survey. The first two: Prototype and Scriptaculous are lightweight frameworks and certainly don’t offer as much functionality as Dojo. If you make a top of the “heavy” js frameworks, Dojo will definitely take 1st place.

So here’s a status of the widget:

Features

  • Based on Toolbar and TabContainer widgets
  • Keyboard cell navigation
  • Column, row resizing
  • Cell, column and row selection
  • Cell formatting (font, font size, bold, italic, underline, color, background color)
  • Sheet management (rename, delete, new)
  • Functions (22 math functions, 6 string functions)

Status


  • This widget is definitely not final and has several bugs
  • Tested with Firefox and IE only
  • On IE runs slower than Firefox
  • If it would be released as an independent piece of software it would be labelled as 0.7 version

Goals for “1.0” version:


  • Fix all remaining bugs related to column, row resizing and selection
  • Support cell presentation format (dates could be displayed as mm-dd-yyyy, numbers as xx.xx, etc)
  • Support for date functions
  • Post spreadsheet data in JSON format to be saved serverside
  • Improve validation

technorati tags:, , ,


« Previous Entries