Tuesday, May 8, 2012

Dojo Resources

I have been asked in the past week by multiple people what are some good resources to use when working with the Dojo Toolkit. The following is far from an all inclusive list, but they are the resources I use when working with the Dojo Toolkit. Please feel free to post other Dojo resources in the comments.

The Dojo Module

I find myself using Dojo's Reference Guide when working with functions in the dojo module. These tend to be non ui widgets that are utilities. These areas include but are not limited to:

  • dojo.xhrGet, xhrPost
  • dojo.Deferred
  • dojo.data.ItemFileReadStore
  • dojo.store.Memory
  • dojo.connect, dojo.subscribe
  • dojo.query

The Dijit Module

The Dijit Theme Tester provides a good catalog to browse the UI widgets in the dijit namespace. I have the source code on my development machine and when I am looking to modify the behavior of a widget, I look directly in the source code. The Reference Guide gives some basic examples for the widgets. The tests are also a good source options and configuration examples. I don't usually load a test in the browser, I just browse the tests to see the examples.

The DojoX Module

The code that exists in the DojoX module is experimental and not guaranteed to maintain its API from release to release. Code stays here because of a number of reasons including not being properly documented. Lack of documentation can make using a piece of code difficult to use. Using code in this module almost always requires diving into the source code to make it work.

Miscellaneous

  • jsFiddle is a place where you can create and share snippets.
  • I also have a "sandbox" web site set up on my development machine where I can create html files to quickly demo different scenarios.
  • Dojo has a mailing list
  • Stack Overflow
  • Google Chrome - the debugger is fantastic.

Monday, April 16, 2012

Preventing a CSS Jungle

The problem using CSS in a large project is that over time the amount of CSS grows. When a rule is defined generically, like .shadedData, then it is difficult to know every place it is used. And when a developer doesn't know where it is used, modifying the style is done by adding a new rule .shadedData2. Even rules that aren't as generic as this can suffer the same fate. A CSS garden quickly becomes a jungle.

I recently had to add support for multiple themes to a product. The product is going to be OEM'd and needs to represent different companies' brands. The product had a lot of CSS that had become overgrown. In this post, I am going to describe the changes I made to the CSS infrastructure to support multiple themes and to also help the development team better maintain the CSS rules. And even if a project only needs a single theme, I think the following infrastructure is still beneficial to a project.

File Structure
themes
  common
    images
    layout
      header.css
    pages
      account.css
    widgets
      dialog.css
      button.css
    common.css

  oemA
    images
    layout
      header.css
    pages
      account.css
    widgets
      dialog.css
      button.css
    oemA.css

  oemB
    images
    layout
      header.css
    pages
      account.css      
    widgets
      dialog.css
      button.css
    oemB.css

The common theme contains general layout rules that will be used for all themes. There are no hard and fast rules for what belongs in the common theme, but it should be anything that should be used in all OEM themes, including heights, widths, paddings, and margins. Generally the common theme wouldn't contain any color definitions. However, with the work I just recently completed, there was part of the application that used a grey color and did not change based on the OEM, so I put it in the common theme. Again, this can be more of an art and you should do what feels right for you. You can always move things later.

The oemA and oemB directories contain css rules for particular OEMs. Only one of these themes should be used at a time in combination with the common theme. The css rules in these files are specific to the OEM and are generally the color scheme. The product I was recently working on is a business application and we weren't looking to make any major changes to the UI. The colors and in some cases the fonts were changed.

This is a good place to talk about CSS preprocessors. SASS and LESS are two examples of such preprocessors. Preprocessors allow you to define variables (colors, borders, etc) and then use the variables throughout the stylesheets. At first glance, it would appear that these preprocessors would eliminate the need to have oemA and oemB defined. One could just have the a single set of CSS rules defined and apply a different set of variables for each OEM. However, it was my experience that the colors don't translate one for one across themes. For example, in the original theme there were tabs that used the primary color of the theme. When implementing an additional theme, the tabs looked better using one of the secondary colors. It is my opinion that the CSS preprocessor provides value when used within a theme, but should not be used to provide variables across themes.

In the file structure above, the images directory contains images for the particular theme. I don't believe it is a good practice to reference images in a different theme. However, one exception is referencing images in the common theme. The common theme could contain a set of icons and for a particular OEM theme and you might want to override an image with one of those icons. Again, there are no hard and fast rules; this is an art.

The layout directory contains files that define CSS rules for the framework of the web application. These include things like headers, footers and sidebars. The pages directory is for specific page layouts within the application. The widgets directory is for widgets that are used throughout the application. Buttons and dialogs are two examples of such widgets.

The CSS
.themeName .pageOrWidget .theSpecificCssRule

What makes this concept work is nesting CSS selectors. Each CSS rule declaration should begin with the theme that it belongs to. The theme name should be added to the CSS of the body element and by doing so, you now control whether or not a theme is applied to the page. This is how Dojo applies themes.

Each CSS rule declaration should further restrict its use by specifying the page or widget that it is being used for as the second CSS selector. Each combination of the first two CSS selectors should equate to its own file in the file structure above. Any CSS selectors after the first two are specific to the need of the rule.

The Account Page Example
<body class="common oemB">
  <div class="logo"></div>
  <div class="accountPage">
    <div class="button">
      <span class="buttonText">OK</span>
    </div>
    <div class="accountSummary">...</div>
  </div>
</body>
/* common/widgets/button.css */
.common .button .buttonText {
 font-size: 1.25em;
}

/* oemA/widgets/button.css */
.oemA .button .buttonText {
 color: red;
}

/* oemB/widgets/button.css */
.oemB .button .buttonText {
 color: green;
}

Looking at this example, I would expect the logo to be defined in header.css, the button and buttonText to be defined in the button.css and accountSummary in the account.css

Compressing CSS

While defining CSS rules across multiple files is good for maintenance, it's horrible for production. So as part of the build process, the CSS files should be compressed and combined into fewer. I generally make one file per theme and the server side code that emits the html knows what stylesheets to include and what css classes to add to the body element.

The General CSS Declaration
.color { color: red; }

<div class="button">
    <span class="red">OK</span>
</div>

Avoid the general approach. What happens if we want to make the color blue? We have to modify the code that creates the widget to apply the blue class. What about multiple themes that each use a distinct color? The widget now needs to have knowledge of the current theme. The purpose of CSS is to take this responsibility away from the widget code. By using the general approach, the widget is again responsible for specific styling logic.

The widget code should be thought of as defining the theme "interface" using CSS classes. The CSS rules are the implementations of the "interface". The class names that make up the "interface" should describe their purpose (.buttonText) but not a specific implementation (.red).

CSS Grouping
.oemA .button .buttonText,
.oemA .button .buttonText.important,
.oemA .anotherWidget .widgetText {
 color: red;
}

CSS grouping provides the ability to not repeat yourself. While we as developers always try to adhere to the DRY principle, it has been my experience that overusing CSS grouping leads to poor maintainability.

Using the example above, where would the CSS rule reside in the file structure that we previously defined? You could make a common file beneath the widgets directory for rules like this. The problem with this approach is that over time a lot of widget rules would migrate to the common file and when you need to make modifications to a widget, it will require you to look in multiple files. Worse yet, if this rule lives in the anotherWidget file, looking for all button CSS now requires you to look throughout the jungle, thus defeating the purpose of having a separate file per widget.

/* oemA/widgets/button.css */
.oemA .button .buttonText,
.oemA .button .buttonText.important {
 color: @red; /* @red is less syntax */
}

/* oemA/widgets/anotherWidget .css */
.oemA .anotherWidget .widgetText {
 color: @red; /* @red is less syntax */
}

I think a better approach is to limit the use of CSS grouping to rules that belong in a single file and use a CSS preprocessor to provide common definition across multiple files within a single theme. This allows for the definition of red to be defined once and allows the CSS rules for a specific widget to live in a single file.

Wrapping Up

I have mentioned this a couple times throughout the post, but it bears repeating - using CSS is very much an art. I have tried to define a set of best practices around CSS that allows CSS to do what it was designed to do and still be maintainable across a large project that over time will inevitably have to be modified and even refactored.

Wednesday, April 11, 2012

A JSON Serialization Engine - Part II


In part I, I introduced the JsonStoreEngine and described how the developer uses the engine to serialize Java objects into JSON and can customize how the engine serializes certain objects. In this post, I am going to show more of the inner workings of the JsonStoreEngine.

Under the hood, the JsonStoreEngine uses Jackson for the JSON serialization. The JsonStoreEngine provides the logic to emit the flat list of items using the reference object notation to represent object references.

A Simple Example
public class OrderStatus {
    
    public String getIdentifier();
    
    public String getName();
    
    public String getDisplayOrder();
}

public class OrderStatusJsonConverter 
 extends AbstractTypedJsonConverter<OrderStatus> {

    public String determineIdentifier(OrderStatus obj) {
        return String.format("orderStatus::%s", obj.getIdentifier()); 
    }
    
    protected String onDetermineType(OrderStatus obj);
        return "orderStatus";
    }
    
    protected void onWriteObjectProperties(
      JsonSerializationContext context, OrderStatus obj) 
      throws IOException;
      
        context.writeProperty("name", obj.getName());
        context.writeProperty("order", obj.getDisplayOrder());
    }
}
The above example is the most basic. An OrderStatus object has no references to other objects and the engine serializes the primitive properties. This example produces the following output:
[{ id: 'orderStatus::1', _type: 'orderStatus', 
    name: 'Pending', order: 10 
}]

A Bigger Example
Here is an example where the engine serializes object references.
public class Order {
    
    public String getIdentifier();

    public String getOrderNumber();
    
    public Customer getCustomer();
    
    public OrderStatus getOrderStatus();

    public List getOrderLineItems();
}

public class OrderJsonConverter 
 extends AbstractTypedJsonConverter<Order> {

    public String determineIdentifier(Order obj) {
        return String.format("order::%s", obj.getIdentifier()); 
    }
    
    protected String onDetermineType(Order obj);
        return "order";
    }
    
    protected void onWriteObjectProperties(
      JsonSerializationContext context, Order obj) 
      throws IOException;
      
        context.writeProperty("orderNumber", obj.getOrderNumber());
        context.writeProperty("customer", obj.getCustomer());
        context.writeProperty("orderStatus", obj.getOrderStatus());
        context.writeProperty("lineItems", obj.getOrderLineItems());
    }
}
And the output produced:
[{ id: 'order::1', _type: 'order', orderNumber: 1234, 
    customer: { _reference: 'customer::1' }, 
    orderStatus: { _reference: 'orderStatus::1' },
    lineItems:[
        { _reference: 'orderLine::1' }, 
        { _reference: 'orderLine::2' }
    ]
},
{ id: 'customer::1', _type: 'customer', 
    name: 'Anderson, Jose'
},
{ id: 'orderStatus::1', _type: 'orderStatus', 
    name: 'Pending', order: 10 
},
{ id: 'orderLine::1', _type: 'orderLine', 
    order: { _reference: 'order::1' }, 
    price: 1.07, quantity: 10, 
    product: { _reference: 'product::1' } 
},
{ id: 'orderLine::2', _type: 'orderLine', 
    order: { _reference: 'order::1' }, 
    price: 4.48, quantity: 1, 
    product: { _reference: 'product::2' } 
}]

Where the Engine Does the Work
context.writeProperty("orderStatus", obj.getOrderStatus());
When the converter asks to write the order status, the engine intercepts and emits the reference object. The engine then registers the object for deferred serialization, only if the object hasn't already been serialized or registered for deferred serialization. Once the serialization of the original object is complete, the engine then serializes the objects that had been deferred.

Using the JsonStoreEngine and the ViewModelStore, we now have a framework in place to ship a complex model of data that is represented by Java objects on the server to javascript objects that can be consumed on the web client and used as a view model. The complexity of the data model can include references to other objects, circular object references and self references and also gives us control to specify how certain objects are serialized.

This will be a core component to building a Dojo View to plug into Spring's Web MVC architecture. The code for the serialization engine is checked into the evince framework repository on GitHub. There is also some more documentation on how to customize the serialization and conversion process.

Monday, April 9, 2012

A JSON Serialization Engine - Part I


In this previous post, I describe a specific JSON format that I want to consume on the client side using Dojo. In this post, I am going to show how to take a Java object and create that JSON format using the JsonStoreEngine.

The JsonStoreEngine is something I have written, has a simple API and works as a black box. The developer passes a Java object into the engine and the engine gives a custom formatted JSON structure back. The JsonStoreEngine also provides flexibility, giving the developer the ability to customize and control how particular objects are converted to JSON.

This version of the JsonStoreEngine is one way. It does not support deserialization. My primary use case for this engine is to serialize an object graph into a Dojo store, so that I can use the data on the client side to build the web page. When communicating back with the server, I only send a limited set of data using the standard HTTP post mechanism.

The Data

This custom JSON format is flat list of items where references to other items use a special notation. The following is an example of that structure:
var dataArr = [
  { id: 'customer::1', _type: 'customer', 
    name: 'Anderson, Jose',
    address: { _reference: 'address::1' } 
  },
  { id: 'address::1', _type: 'address', 
    address1: '2272 Stratford Drive', 
    address2: 'Honolulu, HI 96814' 
  },
  { id: 'customer::2', _type: 'customer', 
    name: 'Gomez, Marcus',
    address: { _reference: 'address::2' } 
  },
  { id: 'address::2', _type: 'address', 
    address1: '2718 College Avenue', 
    address2: 'Dayton, OH 45459' 
  },

  // ... other customers ...

  { id: 'product::1', _type: 'product', 
    name: 'Ball Original Classic Pectin Small Batch', 
    price: 1.07 
  },
  { id: 'product::2', _type: 'product', 
    name: 'Ball 4-Pk 16 Oz. Wide Mouth Class Canning Jars with Lids', 
    price: 4.48 
  },

  // ... other products ...

  { id: 'orderStatus::1', _type: 'orderStatus', 
    name: 'Pending', displayOrder: 10 
  },
  { id: 'orderStatus::2', _type: 'orderStatus', 
    name: 'Back Ordered', displayOrder: 30 
  },
  { id: 'orderStatus::3', _type: 'orderStatus', 
    name: 'Shipped', displayOrder: 20 
  },
  { id: 'order::1', _type: 'order', orderNumber: 1234, 
    customer: { _reference: 'customer::1' }, 
    orderStatus: { _reference: 'orderStatus::1' },
    lineItems:[
        { _reference: 'orderLine::1' }, 
        { _reference: 'orderLine::2' }
    ]
  },
  { id: 'orderLine::1', _type: 'orderLine', 
    order: { _reference: 'order::1' }, 
    price: 1.07, quantity: 10, 
    product: { _reference: 'product::1' } 
  },
  { id: 'orderLine::2', _type: 'orderLine', 
    order: { _reference: 'order::1' }, 
    price: 4.48, quantity: 1, 
    product: { _reference: 'product::2' } 
  },

  // ... other orders ...
];
The API

The API consists of two methods. The developer can call serialize only passing the object to be serialized. The JSON will be returned as a String to the developer. Alternatively, the developer can also pass an OutputStream. Doing so will write the JSON to that stream and nothing will be returned.
public String serialize(Object model)

public void serialize(OutputStream out, Object model) 
    throws IOException
Customizing the Conversion

The default converter is the PojoConverter. In the example below, a second PojoConverter is registered to handle the OrderLine class. Now anytime an OrderLine object is serialized, this converter will be used. This converter is configured to only serialize two fields: product and price. So looking at the data example above, the order field of an OrderLine object would not be serialized.
JsonStoreEngine engine = new JsonStoreEngine();
  
PojoConverter productConverter = new PojoConverter();
productConverter.setIncludeFieldsByDefault(false);
productConverter.setOverriddenFields(
  new String[] { "product", "price" });
engine.getLookupMap().put(OrderLine.class, productConverter);
The developer can also create a custom implementation of the JsonConverter interface. This custom implementation can then be registered in the same manner as the as the PojoConverter above.

The code for the serialization engine is checked into the evince framework repository on GitHub. In part II, I will look into the black box and go over the details of how the engine works.

Monday, March 12, 2012

Dojo: Implementing a ViewModelStore

In the 1.6 release of Dojo, a new Store API was released with the intentions of improving on the design of the Data API. While I agree with most of the new Store API design, there is one feature of the Data API that I make significant use of and it was not carried forward into the Store API. The missing feature is the data structure that is used to initialize the store. In this post I will discuss why I find it important and create a store that uses the legacy structure and also implements the new Store API.

I use a data store as a page view model providing the needed data to the widgets on the page. The data has many references and I want to be able to query the store for any object within it. The legacy Data API uses a flat list of items. Any references to other items are identified using a "_reference" attribute. When the store initializes, it will replace the "_reference" object with the actual object. This allows for a normalized model with all objects queryable in the store.

The Data

For this example, the view model contains multiple orders. I want to be able to create a combobox with all of the customers and a combobox with all possible order statuses. The orders that are shown should match the customer and order status selected in the comboboxes.

var dataArr = [
  { id: 'customer::1', _type: 'customer', 
    name: 'Anderson, Jose',
    address: { _reference: 'address::1' } 
  },
  { id: 'address::1', _type: 'address', 
    address1: '2272 Stratford Drive', 
    address2: 'Honolulu, HI 96814' 
  },
  { id: 'customer::2', _type: 'customer', 
    name: 'Gomez, Marcus',
    address: { _reference: 'address::2' } 
  },
  { id: 'address::2', _type: 'address', 
    address1: '2718 College Avenue', 
    address2: 'Dayton, OH 45459' 
  },

  // ... other customers ...

  { id: 'product::1', _type: 'product', 
    name: 'Ball Original Classic Pectin Small Batch', 
    price: 1.07 
  },
  { id: 'product::2', _type: 'product', 
    name: 'Ball 4-Pk 16 Oz. Wide Mouth Class Canning Jars with Lids', 
    price: 4.48 
  },

  // ... other products ...

  { id: 'orderStatus::1', _type: 'orderStatus', 
    name: 'Pending', displayOrder: 10 
  },
  { id: 'orderStatus::2', _type: 'orderStatus', 
    name: 'Back Ordered', displayOrder: 30 
  },
  { id: 'orderStatus::3', _type: 'orderStatus', 
    name: 'Shipped', displayOrder: 20 
  },
  { id: 'order::1', _type: 'order', orderNumber: 1234, 
    customer: { _reference: 'customer::1' }, 
    orderStatus: { _reference: 'orderStatus::1' },
    lineItems:[
        { _reference: 'orderLine::1' }, 
        { _reference: 'orderLine::2' }
    ]
  },
  { id: 'orderLine::1', _type: 'orderLine', 
    order: { _reference: 'order::1' }, 
    price: 1.07, quantity: 10, 
    product: { _reference: 'product::1' } 
  },
  { id: 'orderLine::2', _type: 'orderLine', 
    order: { _reference: 'order::1' }, 
    price: 4.48, quantity: 1, 
    product: { _reference: 'product::2' } 
  },

  // ... other orders ...
];

The ViewModelStore

The ViewModelStore extends the MemoryStore and the core component is the implementation of setData. Here, each item in the array is registered. After an item is registered and if previous objects contain a reference to the object, a setter function will be called and the reference to the newly registered object will be set. Using the data above as an example, after registering the address object, a previously registered setter function will be called to set the address property of the customer to this address object.
setData: function(data){
    ...
    this.index = {};
    
    // when a reference to an item has not been registered,  
    // a setter function is added and will be called when 
    // the item is registered. 
    this._referenceSetters = {};
    
    // loop through the flat list of items
    dojo.forEach(this.data, function(itm, idx) {
        
        var itmId = itm[this.idProperty]; 
        this.index[itmId] = idx;
        
        for(var prop in itm) {
            this._registerItem(itm, prop); 
        }
        
        // process the queue of deferred setters for this object.
        if (this._referenceSetters[itmId]) {
            dojo.forEach(this._referenceSetters[itmId], 
              function(fnSet) { fnSet(itm); }
            );
            this._referenceSetters[itmId] = null;
        }
        
    }, this);
    
    delete this._referenceSetters;
}
Registering an item determines if it is an array or not. If so, it will call the inspectValue function for each item of the array, otherwise it is called for the item itself. The inspectValue function determines if it is a reference to another object in the data. If it isn't, then there is nothing more to do. If it is a reference and the referenced item has already been registered, then that item replaces the object identified by the reference. If the item has not been registered, then a function to set the value is registered to be called when the reference is registered.
_inspectValue: function(val, fnSetter) {
    
    var refProperty = this.referenceProperty || '_reference';
    
    // not a 'referece', so return the value.
    if (!val || !dojo.isObject(val) || !val[refProperty]) {
        fnSetter(val);
        return; 
    }
    
    var refId = val[refProperty];
    var refItm = this.get(refId);
    
    // the referenced item has already been loaded, return it. 
    if (refItm) {
        fnSetter(refItm); 
        return;
    }
    
    //we don't have the reference yet, qo queue a deferred setter that 
    //will set the value once the item is registered.
    if (! this._referenceSetters[refId]) {
        this._referenceSetters[refId] = [];
    }
    this._referenceSetters[refId].push(fnSetter); 
},

Using the ViewModelStore

The code snippet below creates two comboboxes and populates them using the data from the store. The onChange events are then wired to a function that queries the store using the filters specified in the comboboxes and renders the orders. If a new status is to be added later, it simply has to be put into the view model and the page will handle it accordingly.
var customer = new ComboBox({
    store:  store,
    query:  { _type: 'customer' },
    labelAttr: 'name'
}, dojo.byId('customerNode'));
customer.startup();

var status = new ComboBox({
    store:  store,
    query:  { _type: 'orderStatus' },
    labelAttr: 'name'
}, dojo.byId('statusNode'));
status.startup();

var fnDisplayOrders = function() {
    
    var contentNode = dojo.byId("content");
    var displayedOrder = false;
    domConstruct.empty(contentNode);
    
    var result = store.query({ 
        _type:          'order',
        customer:       customer.get('item'),
        orderStatus:    status.get('item'),
    });
    
    result.forEach(function(order) {
        displayedOrder = true;
        var div = dojo.create('div', {}, contentNode);
        dojo.addClass(div, 'order');
        
        var header = dojo.create('div', {}, div);
        dojo.addClass(header, 'orderHeader');
        header.innerHTML = dojo.replace(
          'Order #: {orderNumber}<br/>{customer.firstName}
          {customer.lastName}<br/>{customer.address.address1}
          <br/>{customer.address.address2}',
          order );
        
        ... more rendering logic ...
    });
    
};

dojo.connect(customer, 'onChange', fnDisplayOrders);
dojo.connect(status, 'onChange', fnDisplayOrders);
The complete example can be found at https://gist.github.com/2010137

Monday, March 5, 2012

Dojo Configurations

When using the Dojo Toolkit in an application, I setup the application so that one of three different configurations can be used.  There is a production, an uncompressed, and a debug configuration. Each of these configurations can be used to load the Dojo Toolkit and any custom javascript.  

The production configuration uses the output of the Dojo build. The javascript files are minified and combined into a single file. This is the default configuration and used by default. Since this code is minified, it is very hard to debug.

As part of the Dojo build process, a second set of files are created. These files end in .uncompressed.js. The uncompressed configuration uses these files. It is still a single file for javascript and loads fast, but the javascript code is human readable. This makes it easier to step through code using a browser's javascript debugger. While it is easy to debug, it still requires a build cycle to deploy changes.

The third configuration is the debug configuration. This configuration points directly at the source code and does not use the build process. This is noticeably slower when loading pages because the browser is loading many files but this configuration doesn't require a build to see changes made to source.

When an application is deployed, the debug configuration is not made available. The production configuration is the default and having the uncompressed available allows a developer to troubleshoot a problem on an end user's machine. The debug configuration is valuable when developing a significant piece of javascript and you do not want to be continually building to see your changes.


Implementation

I use a query string parameter to specify a different configuration. This requires server side code to interpret the query string value and swap out a configuration. You may find it useful for the server code to place the value in the session. Doing so allows yout to navigate to differen
t pages without continuing to modify the query string.

A simplified folder structure is:

static
    dojo-release
        dojo
            dojo.js
            dojo.js.uncompressed.js
        myDojoCode
            custom-dojo.js
            custom-dojo.js.uncompressed.js
    dojo-source*
        dojo
            dojo.js
        myDojoCode
            [a bunch of custom javascript files]

* only exists on development machines


The HTML output when using the production configuration would look something like:

<script type="text/javascript" src="/static/dojo-release/dojo/dojo.js" djConfig="parseOnLoad: false"></script>
<script type="text/javascript" src="/static/dojo-release/myDojoCode/custom-dojo.js"></script>

The uncompressed like this:

<script type="text/javascript" src="/static/dojo-release/dojo/dojo.js.uncompressed.js" djConfig="parseOnLoad: false"></script>
<script type="text/javascript" src="/static/dojo-release/myDojoCode/ custom-dojo.js.uncompressed.js"></script>

The debug like this:

<script type="text/javascript" src="/static/dojo-source/dojo/dojo.js" djConfig="parseOnLoad: false, debug: 'true'"></script>

dojo.js is the core dojo kernal and custom-dojo.js contains the auxiliary javascript code in a single file.  The source of this code exists in multiple files in the source folder.  In the debug configuration, we do not include an all encompassing file because we want Dojo to load the individual source files.

Wednesday, February 29, 2012

Welcome to the Swinging Code Blog

My name is Craig Swing. I write code for a living and this is my blog.

My primary goal for 2012 is to increase my professional web presence. For the first time that I remember, LinkedIn says that my profile is 100% complete. And now I start my development blog.

The goal of this blog is to improve my technical writing, share resources, architectural ideas, and coding examples that I find useful. Most of my current technical interests include the Java open source community, including Hibernate, Spring and the Dojo Toolkit, but I also have experience using the Microsoft stack as well.

As part of launching this blog, I am also launching an open source project called evince. The evince framework will be a collection of code that I have found useful in building web applications on a Java open source stack. The evince framework will be licensed using The Apache Software License, 2.0. The code will be hosted on GitHub and the feature set will include the following:

  • A Dojo view implementation for Spring Web MVC 
  • A JSON serialization engine to support serializing a collection POJOs into a json structure that can be used to instantiate a Dojo data store. 
  • Support for creating a "stored procedure" in a java class using the JPA Criteria API.