I am very proud to announce the beta release of Ganttzilla, online project plan documents viewer with versioning, sharing and publishing capability. Our team has been working hard last 3 months to create user friendly and reliable web solution to view MPP, MPX, XML and Planner project plans.
Well, what's interesting about the project? First of all, the idea. There are situations in our professional career when IT project plan needs to be submitted to higher management for discussion and approval, but people who are interested in viewing it cannot open the document since they do not have desktop software with corresponding capability. In this case Ganttzilla may help. You simply create document in Ganttzilla workspace, upload the document file and view the Gantt! Each document has several versions, so if you upload new document file under the same document it is treated as new version of this document and called document revision. Each document revision has its own sharing and publishing settings. Sharing is a secure way to share document version - invitations for viewing are sent to users explicitly specified by document author. All collaborators may post comments. Publishing differs from sharing in the way how the security is being applied: published document may be viewed by any user of the Internet by navigating to document version URL.
Ganttzilla supports viewing of Microsoft Project 98, 2000, 2003 and 2007 formats and also Planner files.
Technically, Ganttzilla is built on top of Grails framework. We are the developers with Java background and found Grails and Groovy to be mature for the project like this one. The bad thing about Grails is that several bugs we've identified were severe enough and made us spend some time to find a resolution or a workaround for the problem, but on the other hand we are very pleased with development productivity that Grails give us and we will eventually stay with Grails for Ganttzilla and will choose it for our future projects.
It would be much harder to implement Ganttzilla without MPXJ library which provides capability for reading and writing Microsoft Project and Planner files. MySQL is used as database management system. Jetty is our development and production application server. On the client side we stick to jQuery.
What's coming up next? Well, we are going to make preparations for Professional account support in Ganttzilla and look forward to implement export of project Gantt into image files soon. Stay tuned!
пятница, 15 мая 2009 г.
вторник, 15 июля 2008 г.
Defining and Changing Image Size in JavaScript
How can one define the size of the image in JavaScript?
I've come up with the approach of handling onload event for image element like this:
imageElement.onload = function() {
var width = imageElement.width;
var height = imageElement.height;
...
}
Once image is loaded browser knows for sure the width and height of this image and developer can reference actual values from width and height properties of image DOM element.
By changing image height or width it will be proportionally resized. BUT it is important to remember that corresponding image element properties are changed only when image is displayed!
Let's say there is an image 10 pixels long and 20 pixels high. JavaScript code sets width to be 50 pixels, thus demanding height to be increased 5 times, resulting in 100 pixels. If image was visible at the moment width value was overriden, height property will be updated as well, with 100 pixels. And if image was invisible, height property is not updated and left as it is before image si actually displayed!
I've come up with the approach of handling onload event for image element like this:
imageElement.onload = function() {
var width = imageElement.width;
var height = imageElement.height;
...
}
Once image is loaded browser knows for sure the width and height of this image and developer can reference actual values from width and height properties of image DOM element.
By changing image height or width it will be proportionally resized. BUT it is important to remember that corresponding image element properties are changed only when image is displayed!
Let's say there is an image 10 pixels long and 20 pixels high. JavaScript code sets width to be 50 pixels, thus demanding height to be increased 5 times, resulting in 100 pixels. If image was visible at the moment width value was overriden, height property will be updated as well, with 100 pixels. And if image was invisible, height property is not updated and left as it is before image si actually displayed!
четверг, 10 июля 2008 г.
Attaching/deattaching DOM elements in IE
Today I've faced an interesting issue with attaching and deattaching DOM elements in Internet Explorer.
The issue was around the creation of so called Thumbnail Bar component: visual representation of photo strip which supports scrolling with buttons:
All thumbnails to be displayed are loaded and stored in the array. Initially each thumbnail was represented by separate object, defined as follows:
ThumbnailBar.prototype.Thumbnail = function(imageAttachment) {
this.imageAttachment = imageAttachment;
this.containerElement = document.createElement("div");
this.containerElement.className = "eyestride-Thumbnail";
this.imageElement = document.createElement("img");
this.imageElement.src = imageAttachment.image.thumbnailFile.url;
this.containerElement.appendChild(imageElement);
}
So, thumbnail instance held a reference to container element and then used this reference to attach thumbnail container to Thumbnail Bar's image container. Each time Thumbnail Bar needs to be refreshed, inner HTML of imageContainerElement is first cleared:
this.imageContainerElement.innerHTML = "";
...
for (var i = startPosition; i < endPosition; ++i) {
this.imageContainerElement.appendChild(this.thumbnails[i].domElement);
}
Code works fine in Mozilla Firefox but refuses to work properly in Internet Explorer. In Internet Explorer appending of child div element (see containerElement in Thumbnail) actually works, but image is appended only once. So, if container element of Thumbnail was removed by clearing innerHTML of imageContainerElement the link between div and img is lost: div is appended without its child elements.
In order to fix this I've stored two references in Thumbnail: one for div and another for img, then added a method which performs attaching of thumbnail to specific container, in such a way:
EyeStride.UI.Viewer.ThumbnailBar.prototype.Thumbnail = function(imageAttachment) {
this.imageAttachment = imageAttachment;
this.containerElement = document.createElement("div");
this.containerElement.className = "eyestride-Thumbnail";
this.imageElement = document.createElement("img");
this.imageElement.src = imageAttachment.image.thumbnailFile.url;
this.attachTo = function(domElement) {
domElement.appendChild(this.containerElement);
this.containerElement.appendChild(this.imageElement);
}
}
...
for (var i = startPosition; i < endPosition; ++i) {
this.thumbnails[i].attachTo(this.imageContainerElement);
}
Another solution for the problem is to clear the contents of imageContainerElement not by setting empty string for innerHTML property, but using removeChild method to remove elements from the image container, one by one. This solution works in both browsers too.
The issue was around the creation of so called Thumbnail Bar component: visual representation of photo strip which supports scrolling with buttons:
All thumbnails to be displayed are loaded and stored in the array. Initially each thumbnail was represented by separate object, defined as follows:
ThumbnailBar.prototype.Thumbnail = function(imageAttachment) {
this.imageAttachment = imageAttachment;
this.containerElement = document.createElement("div");
this.containerElement.className = "eyestride-Thumbnail";
this.imageElement = document.createElement("img");
this.imageElement.src = imageAttachment.image.thumbnailFile.url;
this.containerElement.appendChild(imageElement);
}
So, thumbnail instance held a reference to container element and then used this reference to attach thumbnail container to Thumbnail Bar's image container. Each time Thumbnail Bar needs to be refreshed, inner HTML of imageContainerElement is first cleared:
this.imageContainerElement.innerHTML = "";
...
for (var i = startPosition; i < endPosition; ++i) {
this.imageContainerElement.appendChild(this.thumbnails[i].domElement);
}
Code works fine in Mozilla Firefox but refuses to work properly in Internet Explorer. In Internet Explorer appending of child div element (see containerElement in Thumbnail) actually works, but image is appended only once. So, if container element of Thumbnail was removed by clearing innerHTML of imageContainerElement the link between div and img is lost: div is appended without its child elements.
In order to fix this I've stored two references in Thumbnail: one for div and another for img, then added a method which performs attaching of thumbnail to specific container, in such a way:
EyeStride.UI.Viewer.ThumbnailBar.prototype.Thumbnail = function(imageAttachment) {
this.imageAttachment = imageAttachment;
this.containerElement = document.createElement("div");
this.containerElement.className = "eyestride-Thumbnail";
this.imageElement = document.createElement("img");
this.imageElement.src = imageAttachment.image.thumbnailFile.url;
this.attachTo = function(domElement) {
domElement.appendChild(this.containerElement);
this.containerElement.appendChild(this.imageElement);
}
}
...
for (var i = startPosition; i < endPosition; ++i) {
this.thumbnails[i].attachTo(this.imageContainerElement);
}
Another solution for the problem is to clear the contents of imageContainerElement not by setting empty string for innerHTML property, but using removeChild method to remove elements from the image container, one by one. This solution works in both browsers too.
четверг, 3 июля 2008 г.
Redirect Browser to Custom URL with HTTP POST Method
Recently I've investigated how to redirect browser to custom URL using HTTP POST method.
This problem is not new, many people looked for the solution. The one I've come up with my colleague, Yury Povhanych, is a bit different from others. But inly in details, not in idea.
In order to be more specific let me describe the case in more detail. Now our company is building application for Facebook. Well, there are two ways to build applications for Facebook:
Now, we want to get use of Facebook's friend selection system, i.e. use similar UI to select friends and send them message or request. There are specific controls which help to achieve this goal, but they are FBML-based, so they are of no real value in our case. Basically, Facebook developed a solution for this situation. They have a page called Multi Friend Selector. This page is hosted on Facebook. In order to use the page, it should be fed with parameters. Some of these parameters are messages to be displayed on the selector itself, others represent application API key and call signature. All these staff may be the part of URL, but this is BAD for at least two reasons:
First thing I wanted to know is whether I can achieve such redirection by using only server-side capabilities. In Java EE stack I've found two methods which deal with redirection: request.forward and response.sendRedirect() but both of them has nothing to do with POST. First one is used to forward the request within the same servlet container while sendRedirect can be used to perform GET based redirection to any URL, both internal and external.
Well, there is no way to do it in pure server-side. It is because of redirection nature, which is implemented in the following way. When server wants to say browser "User is boring, send him to xxx site", it means that HTTP response status code must be set to 302 (which means "Moved Temporarily") and URL of "xxx site" must be specified under "Location" header entry in that response. "Location" is just a string. It doesn't have some POST-specific format. Pure URL, nothing more. Browser should open "xxx site" and will use GET method in order to achieve that.
Solution for the problem is easy: use DOM forms, on the client side. Forms may be given a method to be used for request sending, including POST. Forms may be created dynamically, like any other DOM element. So, what you need to do, is:
Digr2iD.Controller.PostRedirector = {
go: function(url, parameters, target) {
if (!url) {
return;
}
var form = document.createElement("form");
form.id = "org_flushpongle_digr2id_js_controller_postredirector_form";
form.name = form.id;
form.action = url;
form.method = "post";
if (!target) {
target = "_self";
}
form.target = target;
document.body.appendChild(form);
for (var parameterName in parameters) {
var input = document.createElement("input");
input.name = parameterName;
input.value = parameters[parameterName];
input.type = "hidden";
form.appendChild(input);
}
form.submit();
document.body.removeChild(form);
}
};
This problem is not new, many people looked for the solution. The one I've come up with my colleague, Yury Povhanych, is a bit different from others. But inly in details, not in idea.
In order to be more specific let me describe the case in more detail. Now our company is building application for Facebook. Well, there are two ways to build applications for Facebook:
- use FBML and Facebook-specific JavaScript
- implement IFrame-based application
Now, we want to get use of Facebook's friend selection system, i.e. use similar UI to select friends and send them message or request. There are specific controls which help to achieve this goal, but they are FBML-based, so they are of no real value in our case. Basically, Facebook developed a solution for this situation. They have a page called Multi Friend Selector. This page is hosted on Facebook. In order to use the page, it should be fed with parameters. Some of these parameters are messages to be displayed on the selector itself, others represent application API key and call signature. All these staff may be the part of URL, but this is BAD for at least two reasons:
- URL may become long, thus it may be handled badly by some browsers
- URL is bloated with data that should not be seen so easily by the user
First thing I wanted to know is whether I can achieve such redirection by using only server-side capabilities. In Java EE stack I've found two methods which deal with redirection: request.forward and response.sendRedirect() but both of them has nothing to do with POST. First one is used to forward the request within the same servlet container while sendRedirect can be used to perform GET based redirection to any URL, both internal and external.
Well, there is no way to do it in pure server-side. It is because of redirection nature, which is implemented in the following way. When server wants to say browser "User is boring, send him to xxx site", it means that HTTP response status code must be set to 302 (which means "Moved Temporarily") and URL of "xxx site" must be specified under "Location" header entry in that response. "Location" is just a string. It doesn't have some POST-specific format. Pure URL, nothing more. Browser should open "xxx site" and will use GET method in order to achieve that.
Solution for the problem is easy: use DOM forms, on the client side. Forms may be given a method to be used for request sending, including POST. Forms may be created dynamically, like any other DOM element. So, what you need to do, is:
- Create form
- Set URL to send request to in action parameter
- Set HTTP method in method parameter
- Optionally specify target
- Insert hidden input elements inside the form, give them the name and values which correspond to parameters to be sent via POST
- Make form the part of DOM by appending it to one of the DOM nodes
- Submit the form
- Destroy the form by
Digr2iD.Controller.PostRedirector = {
go: function(url, parameters, target) {
if (!url) {
return;
}
var form = document.createElement("form");
form.id = "org_flushpongle_digr2id_js_controller_postredirector_form";
form.name = form.id;
form.action = url;
form.method = "post";
if (!target) {
target = "_self";
}
form.target = target;
document.body.appendChild(form);
for (var parameterName in parameters) {
var input = document.createElement("input");
input.name = parameterName;
input.value = parameters[parameterName];
input.type = "hidden";
form.appendChild(input);
}
form.submit();
document.body.removeChild(form);
}
};
понедельник, 30 июня 2008 г.
New Line at End of File - What For?
When composing code style rules for my Digr2iD project, I've asked myself a question: "Why there is a demand to insert new line at end of source file?". Yeah, such a dumb silly question. But why?
Certain compilers emit warnings when dealing with files without line at end. I've also found this rule as a check module in CheckStyle configuration. Moreover, vi and emacs support these conventions: vi inserts line at the end automatically, emacs needs require-final-newline variable to be set with appropriate value.
Theory behind this rule is the following: "source file is a text file, each text file comprises zero or more lines, each line comprises zero or more line characters terminated by a newline character". So unterminated text line is not a proper text line. Well, it's ok but what's the practical side of this?
Careful reading of this post and some thinking made me understand why it is important to have a new line at end of source file:
Certain compilers emit warnings when dealing with files without line at end. I've also found this rule as a check module in CheckStyle configuration. Moreover, vi and emacs support these conventions: vi inserts line at the end automatically, emacs needs require-final-newline variable to be set with appropriate value.
Theory behind this rule is the following: "source file is a text file, each text file comprises zero or more lines, each line comprises zero or more line characters terminated by a newline character". So unterminated text line is not a proper text line. Well, it's ok but what's the practical side of this?
Careful reading of this post and some thinking made me understand why it is important to have a new line at end of source file:
- When source files are merged, there is a potential to have unterminated line of one source file inproperly united with the first line of another source file. For example if you are writing in C or C++ and include the header file which contains unterminated line with #define syntax, expect #define directive to be appended with the contents which goes right after #include statement. You may receive compilation error, or, even worse, don't receive it :) and be frustrated by strange program behaviour. As far as many header files end up with preprocessor directives probability of receiving a headache for a newbie is high.
- It complicates pattern-based search. If you are looking for a piece of text that appears at the end of line, match result should not include the snippet which appears at the end of the file, which doesn't have new line as the last symbol. For example if you are trying to determine the number of lines in the file(s) using regular expression based search by simply couting number of CR or LF or CR-LF symbols, you will have a problem because unterminated line is not a line.
Maven 2 - Site Plugin Specifics
Recently I've tried to use CheckStyle plugin for Maven 2 and had the following problem.
I've followed basic instructions on plugin setup here, but when executing mvn site the following error appeared on the screen:
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Error parsing site descriptor
Embedded error: expected = after attribute name (position: START_DOCUMENT seen ...mitations\r\nunder the License.\r\n-->\r\n\r\n
Now let's look at how I've defined the name of the module in pom.xml:
<name>"flushpongle" Framework </name>
Soon it became obvious that the cause of the problem is double quotes which are present in the module name. Module name is taken 'as is' for the attribute value inside the root element of project site descriptor XML. After removing the double quotes plugin started working normally.
Afterwards I've identified that it wasn't the problem of CheckStyle plugin, it was the problem of Site plugin, because after removing reporting section and keeping double quotes characters in the module name I've got the same error as before. So I decided to open an item in Codehaus JIRA of Maven 2 Site plugin.
I've followed basic instructions on plugin setup here, but when executing mvn site the following error appeared on the screen:
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Error parsing site descriptor
Embedded error: expected = after attribute name (position: START_DOCUMENT seen ...mitations\r\nunder the License.\r\n-->\r\n\r\n
Now let's look at how I've defined the name of the module in pom.xml:
<name>
Soon it became obvious that the cause of the problem is double quotes which are present in the module name. Module name is taken 'as is' for the attribute value inside the root element of project site descriptor XML. After removing the double quotes plugin started working normally.
Afterwards I've identified that it wasn't the problem of CheckStyle plugin, it was the problem of Site plugin, because after removing reporting section and keeping double quotes characters in the module name I've got the same error as before. So I decided to open an item in Codehaus JIRA of Maven 2 Site plugin.
воскресенье, 15 июня 2008 г.
Notes on Using BaseCamp
Several months ago we were sticked to pure project management tools, like Microsoft Project. Yes, it is great to see Gantt and organize tasks in a hierarchy, but, you know, we, managers and engineers in Shift Labs, need some collaboration features. For sure, it's not just about Shift Labs :), but other IT and non-IT companies who manage projects.
Collaboration matters. Let's say I want to assign task to a person, which will be notified via email and, once it will be finished, some kind of check box marking or drag'n'drop operation will occur and, voola, we have some task history, some progress! Discussing project issues via email is normal, but, it's better to handle messaging in one place, not throughout email boxes of involved engineers. And we need wiki. And we need bug tracking. Documents? Yes, diagrams, screenshots, designs, etc. It is great to have one tool for that, not many. It is awesome to have native support of project components/modules and releases. Calendar and events are also essential. And it should be web-based of course, no Microsoft Project + Groove alternative is accepted.
So, we started using BaseCamp. What I personally liked in BaseCamp from the very first moments was its clean and neat interface. Easy-to-use one. Pages are not heavy and overwhelming. But that's the minor thing ...
Now about tasks and organizing the work. Each project may have many to-do items, grouped in to-do lists. The first bad thing for me: no hierarchies! I would like to create tasks and sub-tasks and then sub-tasks again, but not this time. To-do item itself is a very "tiny" entity: you can give it a name, assign a person (ONLY ONE, by the way) responsible for resolution and ... that's it! No due dates, no multiple assignments. To-do item doesn't have description. To-do list has it instead. Milestones live outside the to-do lists and to-dos, but one can associate to-do list and milestone. This specific made me create to-do lists and milestones with the same name and then associate the two.
Lack of product version support in BaseCamp became pain very soon. In order to handle the staff for our Pre Alpha, Alpha and Beta versions of the product, we simply created separate projects. An alternative approach would be to create one project, but in this case to-do page is polluted with lots of items and becomes unusable. Besides you have no intuitive and normal way to find out which item belongs to which version.
BaseCamp provides possibility to create writeboards. Writeboard is very similar to wiki article, but more limited in features then basic wiki. Article versions and version comparison are supported. Users may post comments to the writeboard. Available formatting options are not great but sufficient for non-complex articles and formatting guide is always at hand. By the way, it would be great to associate writeboard to a to-do item or to-do list, but BaseCamp doesn't support that. Good news is that you can import the writeboard created earlier, specifying the URL and password.
I found messaging system in BaseCamp sufficient for our needs. User specifies the title of the message, writes the body and optionally extended body, assign the category of the message. It is possible to attach files and notify subset of the people on the project. Ability to associate message to a milestone is a good feature and I found it useful. Besides, one can preview the mesage before sending it.
Chat is one of the features which we didn't try yet. Somehow we used to "skype chat" each other instead or just use aural communication. Still, I am sure that it is great feature. I would even say it is a mistake that we passed it through ...
Document management is easy and supports versioning. However, absence of project versions also affected the way we managed documents. There were documents generic for the project and specific to the version of the project. Not to lose generic documentation we created separate project for handling them.
So, BaseCamp is a good tool to use for small agile projects. It is really easy and user-friendly. Still, task management is too primitive. No native support for project versions, multiple assignments, task due dates and events makes the tool insufficient for use very soon. At least in my case.
Collaboration matters. Let's say I want to assign task to a person, which will be notified via email and, once it will be finished, some kind of check box marking or drag'n'drop operation will occur and, voola, we have some task history, some progress! Discussing project issues via email is normal, but, it's better to handle messaging in one place, not throughout email boxes of involved engineers. And we need wiki. And we need bug tracking. Documents? Yes, diagrams, screenshots, designs, etc. It is great to have one tool for that, not many. It is awesome to have native support of project components/modules and releases. Calendar and events are also essential. And it should be web-based of course, no Microsoft Project + Groove alternative is accepted.
So, we started using BaseCamp. What I personally liked in BaseCamp from the very first moments was its clean and neat interface. Easy-to-use one. Pages are not heavy and overwhelming. But that's the minor thing ...
Now about tasks and organizing the work. Each project may have many to-do items, grouped in to-do lists. The first bad thing for me: no hierarchies! I would like to create tasks and sub-tasks and then sub-tasks again, but not this time. To-do item itself is a very "tiny" entity: you can give it a name, assign a person (ONLY ONE, by the way) responsible for resolution and ... that's it! No due dates, no multiple assignments. To-do item doesn't have description. To-do list has it instead. Milestones live outside the to-do lists and to-dos, but one can associate to-do list and milestone. This specific made me create to-do lists and milestones with the same name and then associate the two.
Lack of product version support in BaseCamp became pain very soon. In order to handle the staff for our Pre Alpha, Alpha and Beta versions of the product, we simply created separate projects. An alternative approach would be to create one project, but in this case to-do page is polluted with lots of items and becomes unusable. Besides you have no intuitive and normal way to find out which item belongs to which version.
BaseCamp provides possibility to create writeboards. Writeboard is very similar to wiki article, but more limited in features then basic wiki. Article versions and version comparison are supported. Users may post comments to the writeboard. Available formatting options are not great but sufficient for non-complex articles and formatting guide is always at hand. By the way, it would be great to associate writeboard to a to-do item or to-do list, but BaseCamp doesn't support that. Good news is that you can import the writeboard created earlier, specifying the URL and password.
I found messaging system in BaseCamp sufficient for our needs. User specifies the title of the message, writes the body and optionally extended body, assign the category of the message. It is possible to attach files and notify subset of the people on the project. Ability to associate message to a milestone is a good feature and I found it useful. Besides, one can preview the mesage before sending it.
Chat is one of the features which we didn't try yet. Somehow we used to "skype chat" each other instead or just use aural communication. Still, I am sure that it is great feature. I would even say it is a mistake that we passed it through ...
Document management is easy and supports versioning. However, absence of project versions also affected the way we managed documents. There were documents generic for the project and specific to the version of the project. Not to lose generic documentation we created separate project for handling them.
So, BaseCamp is a good tool to use for small agile projects. It is really easy and user-friendly. Still, task management is too primitive. No native support for project versions, multiple assignments, task due dates and events makes the tool insufficient for use very soon. At least in my case.
Ярлыки:
collaboration,
project-management
Подписаться на:
Сообщения (Atom)