ColdFusion 9: Power CFCs and Web Forms

Exclusive offer: get 50% off this eBook here
ColdFusion 9 Developer Tutorial

ColdFusion 9 Developer Tutorial — Save 50%

Create robust professional web applications with ColdFusion

$29.99    $15.00
by John Farrar | August 2010 | Web Development

In this article by John Farrar, author of ColdFusion 9 Developer Tutorial, we will learn to move forward from making our code work to making it interesting to write and reuse. We could call this a "power CFC". Power is doing away with the old practice of copying and pasting code again and again with some minor edits in the pasted code. Here, we will learn to reuse the CFC code to make it much simpler. CFCs are the object-packaging method used in ColdFusion. Database interaction is quite universally wrapped in CFCs. We will look at database interaction as our primary example of power CFCs in the context of working with "web forms". If you are new to development, then these concepts will make it easy for you. Here is an overview of what this article contains:

  • The practice of protecting access to CFC methods
  • The working of web forms
  • Managing multiple products through common forms for listing, editing, and adding data
  • Improving the page flow, also known as work flow
  • Returning messages to the user to know things are working
  • Learning the power of inheritance
  • Simplifying by passing array collections, rather than manual line-by-line passing of the variable values

(For more resources on ColdFusion, see here.)

There used to be long pages of what we called "spaghetti code" because the page would go on and on. You had to follow the conditional logic by going through the page up and down, and then had to understand how things worked. This made writing, updating, and debugging a diffcult task even for highly-skilled developers CFCs allow you to encapsulate some part of the logic of a page inside an object. Encapsulation simply means packaged for reuse inside something. CFCs are the object-packaging method used in ColdFusion.

The practice of protecting access

In CFC methods, there is an attribute called "access".Some methods within a CFC are more examples of reuse. The sample code for _product.cfc is shown here. It is an example of a power CFC. There is a method inside the CFC called setDefaults(). The variable variables.field.names comes from another location in our CFC:

<cffunction name="setDefaults" access="private" output="false">
<cfset var iAttr = 0>
<cfloop list="#listLen(variables.field.names)#" index="iAttr">
<cfscript>
variables.attribute[#listGetAt(variables.field.names,iAttr)#] =
setDefault(variables.field.names,iAttr);
</cfscript>
</cfloop>
</cffunction>

The logic for this would actually be used in more than one place inside the object. When the object is created during the first run, it would call the setDefaults() method and set the defaults. When you use the load method to insert another record inside the CFC, it will run this method. This will become simpler as you use CFCs and methods more often. This is a concept called refactoring, where we take common features and wrap them for reuse. This takes place even inside a CFC. Again, the setDefaults() function is just another method inside the same CFC.

Now, we look at the access attribute in the code example and note that it is set to private. This means that only this object can call the method. One of the benefits to CFCs is making code simpler. The interface to the outside world of the CFC is its methods. We can hide a method from the outside world, and also protect access to the method by setting the access attribute to private. If you want to make sure that only CFCs in the same directory can access these CFC's methods, then you will have to set the attribute to package. This is a value that is rarely used.

The default value for the access attribute is public. This means that any code running on the web server can access the CFC. (Shared hosting companies block one account from being able to see the other accounts on the same server. If you are concerned about your hosting company, then you should either ask them about this issue or move to a dedicated or virtual hosting server.)

The last value for the access attribute is remote. This is actually how you create a number of "cool power" uses of the CFC. There is a technology on the Web called web services. Setting the CFC to remote allows access to the CFC as a web service. You can also connect to this CFC through Flash applications, Flex, or AIR, using the remote access value. This method also allows the CFC to respond to AJAX calls. Now, we will learn to use more of the local power features.

Web forms introduction

Here, we will discuss web forms along with CFCs.

Let us view our web form page. Web forms are the same in ColdFusion as they are in any other HTML scenario. You might even note that there is very little use for web forms until you have a server-side technology such as ColdFusion. This is because when the form is posted, you need some sort of program to handle the data posted back to the server.

<!--- Example: 3_1.cfm --->
<!--- Processing --->
<!--- Content --->
<form action="3_1.cfm" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" name="name" id="idName" value="" /></td>
</tr>
<tr>
<td>Description:</td>
<td><input type="text" name="description" id="idDescription"
value="" /></td>
</tr>
<tr>
<td>Price:</td>
<td><input type="text" name="price" id="idPrice"
value="" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="submit" name="submit" value="submit" /></td>
</tr>
</table>
</form>

First, notice that all of the information on the page is in the content section. Anything that goes from the server to the browser is considered as content. You can fll in and submit the form, and you will observe that all of the form fields get cleared out. This is because this form posts back to the same page. Self-posting forms are a valid method of handling page fow on websites. The reason why nothing seems to be happening is because the server is not doing anything with the data being sent back from the browser. Let us now add <cfdump var="#form#"/> to the bottom of the content, below the form tag, and observe what we get when we post the form:

Now we see another common structure in ColdFusion. It is known as the form structure. There are two types of common structures that send data to the server. The first one is called get and the second one is called post. If you see the code, you will notice that the method of the form is post. The form post setting is the same as coding with the form variable in ColdFusion.

You should also observe that there is one extra field in the form structure that is not shown in the URL structure variable. It is the FIELDNAMES variable. It returns a simple list of the field names that were returned with the form. Let us edit the code and change the form tag attribute to get. Then, refresh the page and click on the submit button:

From the previous screenshot, it is evident that the browser looks at the get or post value of the form, and sends a get or post back to the server. Post is a "form method" belonging to the past and this is why ColdFusion translates posted variables to the form structure. Now change the dump tag to "URL" and observe the results. Fill out the form and submit it again with the new change. This displays the values in our structure as we would expect. This means you can either send URL-type data back to the server, or form-type data with forms.

The advantage of sending form data is that form data can handle a larger volume of data being sent back to the server as compared to a get or URL request. Also, it is worth noting that this style of return prevents the form field values from being exposed in the URL. They can still be accessed, but are just not visible in the URL any more. So the method of choice for forms is post. Change both the method of the form attribute and the value of the cfdump var to form again.

The Description box is not ideal for entering product descriptions. So, we are going to use a text area in its place. Use the following code to accommodate a text area box. You can change the size of form's objects using attributes and styles:

<tr>
<td>Description:</td>
<td>
<textArea name="description" id="idDescription"></textArea>
</td>
</tr>

Here, we see our form looking different. If you fill up the description with more content than the box can hold, it shows the scroll bars appropriately.

Managing our product data

Currently, we have a form that can be used for two purposes. It can be used to enter a new product as well as to edit existing ones. We are going to reuse this form. Reuse is the fastest path to make things easier. However, we must not think that it is the only way to do things. What we should think is that not reusing something requires a reason for doing it differently.

In order to edit an existing product, we will have to create a page that shows the existing product records. Let us create the page:

<!--- Example: product_list.cfm --->
<!--- Processing --->
<cfscript>
objProduct = createObject("component","product").init(dsn="cfb");
rsProducts = objProduct.getRecordset();
</cfscript>
<!--- Content --->
<h3>Select a product to edit.</h3>
<ul>
<cfoutput query="rsProducts">
<li>
<a href="product_edit.cfm?id=#rsProducts.id#">#rsProducts.name#
</li>
</cfoutput>
</ul>

There is no new code here. This is the browser view that we get when we run this page. Here, we will post our edit page. Before you run the code, take the code from 3_1.cfm that we wrote at the beginning of the article and save a copy as product_edit.cfm to make the page work correctly when someone clicks on any of the products:

Now, we will click on a product. Let us manage the Watermelon Plant for now and observe what happens on the next page:

This is our edit page, and we will modify it so that it can get the data when we click through from our list page.

Getting data to our edit page

The current page looks similar to the page where we put the form. To get the data from our database onto the page, we need to do a few things here. First, let us change the action of the form tag to product_edit.cfm. We can modify the processing section of the page frst, which will make things simpler. Add the following code to your product_edit.cfm page:

<!--- Processing --->
<cfparam name="url.id" default="0">
<cfscript>
objProduct = createObject("component","product").init(dsn="cfb");
objProduct.load(url.id);
</cfscript>

We need the default value set so that we do not receive an error message if the page is called without an id. After we set our default, we will see that we have created an object from our CFC object class. This time, we are passing the Data Source Name dsn into the object through the constructor method. This makes our code more portable, and ready for reuse. Once we have an instance, we set the current record using the load method and passing the id of the data record to the method. Let us look at the minor changes that we will make to the content section. We will add the values of the object's protected attributes.

<!--- Content --->
<cfoutput>
<form action="product_edit.cfm" method="post">
<table>
<tr>
<td>Name:</td>
<td>
<input type="text" name="name" id="idName"
value="#objProduct.get('name')#" />
</td>
</tr>
<tr>
<td>Description:</td>
<td>
<textArea name="description" id="idDescription">
#objProduct.get('description')#</textArea>
</td>
</tr>
<tr>
<td>Price:</td>
<td>
<input type="text" name="price" id="idPrice"
value="#objProduct.get('price')#" />
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<input type="submit" name="submit" value="submit" />
</td>
</tr>
</table>
</form>
</cfoutput>

Now, we will refresh the form and see how the results differ:

Doesn't this look better? We can go back to the list page and retrieve an existing product from the edit form.

If we submit back the same form, browsers tend to empty out the form. It should not do that, but the form is not posting the ID of the record back to the server. This can lead to a problem because, if we do not send the ID of the record back, the database will have no idea as to which record's details should be changed. Let us solve these issues first, and then we will learn to use a new tag called the <cfinclude> tag along the way.

The first problem that we are going to solve is where we are calling the page with the ID value in the URL structure; then, if we post the page we will be calling the page with the ID in the form structure. We are going to use a technique that has been widely used for years in the ColdFusion community. We are going to combine the two scopes into a new common structure. We will create a structure called attributes. First we will check if it exists. If it does not, then we will create the structure. After that, we will merge the URL structure, and then the FORM structure into the attributes structure. We will put that code in a common page called request_attributes.cfm, so we can include it on any page we want, reusing the code. Do remember that the form and URL scope always exist.

<!--- request_attributes.cfm --->
<cfscript>
if(NOT isDefined("attributes"))
{
attributes = structNew();
}
structAppend(attributes,url);
structAppend(attributes,form);
</cfscript>

Let us modify our edit page in order to take care of a couple of issues. We need to include the script that we have just created. We will modify the processing section of our edit page as highlighted here:

<!--- Processing --->
<cfinclude template="request_attributes.cfm">
<cfparam name="attributes.id" default="0">
<cfscript>
objProduct = createObject("component","product").init(dsn="cfb");
objProduct.load(attributes.id);
</cfscript>

There is only one more thing we need now: We need our form to store the id value of the record that is being managed. We could just put it in a textbox like the other fields, but the user does not need to know that information. Let us use a hidden input field and add it after our form tag:

<!--- Content --->
<cfoutput>
<form action="product_edit.cfm" method="post">
<input type="hidden" name="id" value="#objProduct.get('id')#">

Refresh the screen, and it will work when we use the form, or when we choose an item from the product list page. We have now created our edit/add page.

ColdFusion 9 Developer Tutorial Create robust professional web applications with ColdFusion
Published: July 2010
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

(For more resources on ColdFusion, see here.)

Saving our data

Now if we could save our data, that would be great. We can see the book site at http://books.sosensible.com if we need to set up this database for practice. Let us look at the two ways in which a record is saved in ColdFusion. The first is how we would save a new record. This is called an INSERT query:

<cfquery datasource="cfb" name="qryInsert">
INSERT INTO product( name , description , price)VALUES(
<cfqueryparam value="#form.name#"> ,
<cfqueryparam value="#form.description#"> ,
<cfqueryparam value="#form.price#">)
</cfquery>

Here, we see the basic code structure of an INSERT query. We can see that the one thing different from the standard queries here is the VALUES section. We have a new ColdFusion tag. The query param tag <cfqueryparam> is used to help make sure that a SQL injection is not used to attack your server. This tag provides additional functionality that is beyond the scope of this example. We just have to remember that for all insert and update queries, we should always use the param tag to protect the data. The queryParam tag is a great tool to block SQL injection attacks. It also automates passing either text or non-text types to SQL for us. That brings us to our UPDATE query:

<cfquery datasource="cfb" name="qryUpdate">
UPDATE product SET name = <cfqueryparam value="#form.name#"> ,
description = <cfqueryparam value="#form.description#"> ,
price = <cfqueryparam value="#form.price#">
WHERE ID = <cfqueryparam value="#form.id#">
</cfquery>

There are a few obvious differences between the two queries beyond what they accomplish. We see an additional item in this query called WHERE. Be careful using updates because you can change data you don't intend to change if set incorrectly. Without this, you would not be able to update an individual record; instead you would update all the records in your query. So it is important to get it right. You never update the ID field of a record either. This means we do not put the ID in the set section of the query.

Note that we use the <queryparam> tag to protect our data integrity. We need to do one more thing, so that our page knows which is the correct query to execute, on any given page call. When a form processing page is called, we need to check if a form was sent. We will do that by looking for submit in the attributes. We do not look into the form value because there can be times you set the method to get. In this case, the values will show up in ColdFusion as URL variables. Here again, we save time by not having to write a whole bunch of extra page logic to handle how information comes to our page. It is another case of admiring code reuse in action.

<cfif structKeyExists(attributes,"submit")>
</cfif>

Now, we need to know if we should be inserting or updating the record. We are using another one of those simpler techniques here. We set the ID field inside the data object to zero if it is a new record. Therefore, if ID equals zero, we will insert the record into our database. Otherwise, we will update the record with a matching ID value. The following is the modified logic that goes into the middle of the previous conditional statement. It goes in the middle because we only want it to run if the page was submitted from a form. If you are doing this, do not forget to include a form button, and name the button by inserting submit for the name attribute of the input tag, as in the following example. We could name it something else, but we have to remember to name the submit button and check for matching attributes:

<cfif structKeyExists(attributes,"submit")>
<cfif attributes.id EQ 0>
<!--- REPLACE WITH INSERT --->
<cfelse>
<!--- REPLACE WITH UPDATE --->
</cfif>
</cfif>

Let us take a look at the processing part of our page:

<!--- Example: product_edit.cfm --->
<!--- Processing --->
<cfinclude template="request_attributes.cfm">
<cfparam name="attributes.id" default="0">
<cfscript>
objProduct = createObject("component","product").init(dsn="cfb");
objProduct.load(attributes.id);
</cfscript>
<cfif structKeyExists(attributes,"submit")>
<cfif attributes.id EQ 0>
<cfquery datasource="cfb" name="qryInsert">
INSERT INTO product( name , description , price) VALUES(
<cfqueryparam value="#attributes.name#"> ,
<cfqueryparam value="#attributes.description#"> ,
<cfqueryparam value="#attributes.price#">)
</cfquery>
<cfelse>
<cfquery datasource="cfb" name="qryUpdate">
UPDATE product SET name = <cfqueryparam value="# attributes.
name#"> ,
description = <cfqueryparam value="# attributes.
description#"> ,
price = <cfqueryparam value="# attributes.price#">
WHERE ID = <cfqueryparam value="# attributes.id#">
</cfquery>
</cfif>
</cfif>

We can see that the page has become much larger. Now, it's time to see it working. Let us modify some data and submit the form back to the server. Modify the description by adding and sweetest member, and then click on the submit button:

Improving page flow

The page was refreshed and showed us an update. But it might not be obvious that an update was made. There is also an issue that it does not appear as if the data was updated because we pulled the data object details before the update was made. Also when we saved our edit page data, it would be nice if we were sent back to the list page. Let us take a look at the redirect tag in ColdFusion, <cflocation>. After an insert or update, the browser will be returned back to the list page. We will also pass a message to the page so that the user will see the updates. First, we will create a message in each of our insert and action code segments. Then, we will push the page back to the list page by passing the message with it:

<cfif structKeyExists(attributes,"submit")>
<cfif attributes.id EQ 0>
<cfquery datasource="cfb" name="qryInsert">
INSERT INTO product( name , description , price ) VALUES(
<cfqueryparam value="#attributes.name#"> ,
<cfqueryparam value="#attributes.description#"> ,
<cfqueryparam value="#attributes.price#"> )
</cfquery>
<cfset returnMessage = "Your product has been added.">
<cfelse>
<cfquery datasource="cfb" name="qryUpdate">
UPDATE product SET name = <cfqueryparam value="#attributes.
name#"> ,
description = <cfqueryparam value="#attributes.
description#"> ,
price = <cfqueryparam value="#attributes.price#">
WHERE ID = <cfqueryparam value="#attributes.id#">
</cfquery>
<cfset returnMessage = "Your product (#attributes.name#) has been
updated.">
</cfif>
<cflocation url="product_list.cfm?message=#returnMessage#">
</cfif>

You can see that we tacked the message to the end location of our target page. We need to add a <cfparam> tag to the calling page, so it will be able to handle calls where the URL variable is not available. Then we can add a little section of the page to show the messages, when they exist. Let us edit the product_list.cfm page as follows:

<!--- Example: product_list.cfm --->
<!--- Processing --->
<cfparam name="url.message" default="">
<cfscript>
objProduct = createObject("component","product").init(dsn="cfb");
rsProducts = objProduct.getRecordset();
</cfscript>
<!--- Content --->
<cfif url.message NEQ "">
<div>
<cfoutput>#url.message#</cfoutput>
<hr />
</div>
</cfif>
<h3>Select a product to edit.</h3>
<ul>
<cfoutput query="rsProducts">
<li>
<a href="product_edit.cfm?id=#rsProducts.id#">#rsProducts.name#
</li>
</cfoutput>
</ul>

This is how the page looks, when we submit our form:

Adding a new record

We have seen the update function work. But, we have not seen the working of our insert function yet. We will modify the code so that we can add a new record. We will have to modify the content section of the product_list.cfm page to complete this function. The previous page is already set up to run:

<!--- Content --->
<cfif url.message NEQ "">
<div>
<cfoutput>#url.message#</cfoutput>
<hr />
</div>
</cfif>
<h3>Select a product to edit.</h3>
<ul>
<li>
<a href="product_edit.cfm">+ New Product
</li>
<cfoutput query="rsProducts">
<li>
<a href="product_edit.cfm?id=#rsProducts.id#">#rsProducts.name#
</li>
</cfoutput>
</ul>

Now, let us observe the page again. Once the page is loaded, and we click on refresh, we will see the following page:

Let us click the + New Product link, and observe what we get. We receive a new form to fill in the details. Let us enter some information and see how a new record is being added:

The following screenshot shows the new record:

In the previous screenshot, we can see that the record has been stored. Now, we are able to fully manage our database table by using the editing functions that we have built into our pages.

Let's look under the hood

Well, since we are focusing on reusing and learning CFCs, it would be great if we could move our logic from our page into the CFC. A constructor is used to get a CFC set up for use. Some CFCs do not require it, while others do. Obviously, this one requires a constructor as it has an attribute that is required, dsn. We have the following:

<cfcomponent output="false" extends="_sdo">
<!--- Constructor Methods --->
<cffunction name="init" access="public" output="false">
<cfargument name="dsn" required="true">
<cfargument name="id" default="0">
<cfscript>
variables.field.name = "id,name,description,price";
variables.field.defaults = "0,'','',0";
variables.field.allowNull = "0,0,0,0";
variables.table = "product";
variables.pKeyField = "id";
variables.pKeyNew = 0;
variables.pKeyType = "autoInteger";
variables.dsn = arguments.dsn;
setDefaults();
</cfscript>
<cfreturn this />
</cffunction>
</cfcomponent>

We can see that all of the extra methods that are being used are actually inside a parent CFC called _sdo.cfc. We are inheriting the methods of the parent CFC. By refactoring our common logic into a common CFC, we can have simpler CFCs for our data objects. This CFC is known as a Simple Data Object. Refer to the book's site at http://books.sosensible.com.

There is an "untapped power" under the hood of our _sdo.cfc. It also has a save() method and a saveStruct() method. If you are manually setting the protected attributes, one at a time, then the save() method will store the data for you. If you are taking a FORM or URL structure that has the values that we want to store, then use the saveStruct() method. This will be similar to the variable names with the fields in your simple data object. Now, store them by passing in one structure variable collection. Let us make some changes to our edit page.

First, we need to remove the last set of query sections that we had added previously and then put some simpler code onto our page. Then, we will move the logic for saving records so that it works with our power CFC. We will then check if a message exists and redirect to the new page once the form is submitted:

<!--- Processing --->
<cfinclude template="request_attributes.cfm">
<cfparam name="attributes.id" default="0">
<cfscript>
myRedirect = "";
objProduct = createObject("component","product").init(dsn="cfb");
if(structKeyExists(attributes,"submit"))
{
result = objProduct.saveStruct(attributes);
myRedirect = "product_list.cfm?message=#result.message#";
}
else
{
objProduct.load(attributes.id);
}
</cfscript>
<cfif myRedirect NEQ "">
<cflocation url="#myRedirect#">
</cfif>

There is a wonderful ORM-like tool that is great for people getting started called Data Manager. ORM means an object-based relational manager for data. It is an alternative to the ORM features in CF9. If you need an ORM-like tool that works backwards compatible with previous versions of ColdFusion, this is a great solution. You can fnd this library listed in the ORM section in the appendix.

Summary

We have reduced the size of our "page down". Also, if we want to set up a function to edit another table, we can use the techniques that have been discussed in this article. We can create a simple data object and child, along with a few parameters that we have seen in product.cfc. This is known as Object Relational Management (ORM) by the development community. It is a very simple example to get started. Now, we can clearly see that when it comes to reuse, this is a much better way to write our code. The following is a summary of our discussion in this article:

  1. We implemented access permissions on CFC methods
  2. We learned to integrate and streamline the workfow of web forms and CFC database processing
  3. We learned to add and edit data through CFCs and web forms
  4. We discovered the power of code reuse through inheritance
  5. We learned to interact with method arguments by using simple variables

Further resources on this subject:


ColdFusion 9 Developer Tutorial Create robust professional web applications with ColdFusion
Published: July 2010
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

About the Author :


John Farrar

John started working with computer programming around 1977. He has had the opportunity to work on projects used by Apple, Blue Cross, Brunswick Recreation, Casio, GVSU, Johnson Controls, Sprint, and many others. This history covers over 30 years of knowledge and experience in the industry.

He started doing web development over 10 years ago. In the early days of the Web ColdFusion stood out to him not just as a way to make web pages into web applications but as a maturing solid platform good for the developer, site owner, and end users. He started at version 4.5 and has been enjoying each version upgrade more and more.

John owns a company called SOSensible. His company does work for large companies but has a special focus on also making sure technology is approachable beyond the enterprise. They have developed a number of Open Source solutions including COOP. COOP is a mix of Custom Tags and CFCs that provides structure while keeping development simpler. It demonstrates his love for the things that make ColdFusion/CFML a delightful language to build websites.

He has spoken at national and regional conferences, online meetings, and area user group meetings. He is also an Adobe User Group manager. John knows that community is a viable and productive tool to build developers and the companies they serve. He has learned much from great resources in the community including bloggers, books, conferences, and resources to great in number to mention here. He blogs at sosensible.com for the community.

Contact John Farrar

Books From Packt


JavaFX 1.2 Application Development Cookbook
JavaFX 1.2 Application Development Cookbook

Ext JS 3.0 Cookbook
Ext JS 3.0 Cookbook

ASP.NET 3.5 Application Architecture and Design
ASP.NET 3.5 Application Architecture and Design

NetBeans Platform 6.9 Developer's Guide
NetBeans Platform 6.9 Developer's Guide

Microsoft Silverlight 4 Data and Services Cookbook
Microsoft Silverlight 4 Data and Services Cookbook

YUI 2.8: Learning the Library
YUI 2.8: Learning the Library

Oracle JRockit: The Definitive Guide
Oracle JRockit: The Definitive Guide

MooTools 1.2 Beginner's Guide
MooTools 1.2 Beginner's Guide


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
q
b
j
z
5
k
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software