Starting with PSC Tomorrow

Tomorrow I begin working with a new employer for the first time in nearly 13 years.  I’m very excited to join the talented group of developers at PSC as a Senior Consultant.  The past few years have been challenging as my previous employer (cereal manufacturer Malt-O-Meal/MOM Brands/Post Consumer Brands) has undergone a lot of change, both corporately and within the IT department.  Moving away from daily application support and the ongoing maintenance of legacy applications and to more dynamic project work will be both challenging and exciting.  I anticipate this will ultimately result in better content for this space, both in terms of quantity and quality.  I’ve been the beneficiary of many hours of shared knowledge from this community and look forward to doing more of that myself going forward.  Time to give back a little more.

MWLUG Recap

After attending 10 conferences as simply an attendee, the recently concluded MWLUG conference in Atlanta gave me my first opportunity to take a turn at speaking.  It was a great experience preparing and presenting for the first time and I couldn’t be happier that I took the chance to step out of my comfort zone and try something different.  My topic “Navigating the Jungle of Web Development” was well attended and for that I thank all the attendees that gave it a chance.  I certainly hope it was worth your time.     I have posted the information from my session here on my new conferences page.

In addition to speaking the conference gave me the opportunity to catch up with friends and make some new ones.  Richard Moy and his team did a wonderful job as usual planning and producing this great event.  Highlight sessions for me were watching Howard Greenberg and Brad Balassaitis talk in different sessions about xPages performance, Mark Roden talking about Web Sockets, Kathy Brown and Julian Robichaux talk about some interesting Java uses, Steve Zavocki and Dwain Wuerfel sharing an interesting DB2 to xPages integration project and of course Louis Richardson speaking at lunch on Thursday about the importance of social interactions in people.

As good as the sessions were, the social aspect of the conference is what makes it so special.  We are all lucky to be a part of such a wonderful community of technology professionals.   Until next time!

jQuery Formatted xPage from JSON Source

In my last post I detailed a technique I used to hack the design of the xPages Extension Library DataView control to enhance a printable page of our corporate directory. In this post I’ll show an alternative method that does not use any xPage controls.  In this example all the heavy lifting is done using javascript and jQuery to parse out a JSON formatted Notes view. To format the printable display I used another javascript library, Dynatable.js, which is very nice table formatting library for jQuery.
To start off I added the following HTML to my xpage by editing the source directly.
<body>
<div>
<div id="location-template">
<table>
<thead>
<th class="dynatable-head" data-dynatable-column="details">Details</th>
</thead>
<tbody class="sitebody"></tbody>
</table>
</div>
<div id="list-template">
<table>
<thead>
<th class="nameHdr dynatable-head" data-dynatable-column="name1">Name</th>
<th class="nbrHdr dynatable-head" data-dynatable-column="ext1">Ext</th>
<th class="nameHdr dynatable-head" data-dynatable-column="name2">Name</th>
<th class="nbrHdr dynatable-head" data-dynatable-column="ext2">Ext</th>
<th class="nameHdr dynatable-head" data-dynatable-column="name3">Name</th>
<th class="nbrHdr dynatable-head" data-dynatable-column="ext3">Ext</th>
<th class="nameHdr dynatable-head" data-dynatable-column="name4">Name</th>
<th class="nbrHdr dynatable-head" data-dynatable-column="ext4">Ext</th>
</thead>
<tbody class="listbody"></tbody>
</table>
</div>
</div>
<div class="list-content"></div>
</body></div>
<div dir="ltr">
In this code I have specified two table templates: location-template for the location header information on my print list and list-template for the phone number details (name and number).  These div’s will display on my xPage by default but I will hide them at the end by adding some CSS styling. It’s basically straight forward table html with the important stuff happening on the table column definitions.  Each column contains the dynatable-head class and the data-dynatable-column attribute.  Those are for the Dynatable.js use as we will see later.
My data source is just a simple notes view categorized for my purposes by location and then phone number type (conference room, production phone, individual, etc…).  Very much the same view used in the example in my previous post.  As you know you can render a view in JSON format by using the specific url parameters for JSON. So my view URL becomes something like this where ?openview is replaced with
?readviewentries&outputformat=JSON.
I placed all my code in the onClientLoad event but you could just as easily break it out into script libraries.   To start I need to use the jQuery.getJSON()function to read in the source data.  When successful, the callback function is executed.  That callback function is using the jQuery.each() function to iterate through each JSON entry, calling a callback function itself for each entry in the JSON object.  It is that inner function that will house all the processing necessary to generate the desired output.
$.getJSON("xPrintListCat?readviewentries&count=5000&outputformat=JSON",function(data) {
  $.each(data.viewentry, function(index, obj) {
    .
    .
    << output generation code goes here >>
    .
    .
  })
})
As I iterate through the entries in the view, I will want to handle them differently depending on the type of row.  If it’s the top level category I will know I have a new location to process.  If I have a second level category I have a new type and if I have a third level category I will have a name and number to process.  To determine where I am at for any given entry it’s helpful to look at what the browser sees. Adding a console.log(data) between the .getJSON and .each functions I can see the entire representation of the view as seen by the browser.
View DOM Explaination
So for each entry, I can calculate the level (1,2 or 3) of categorization and the position (exact location in the view hierarchy).
var pos = data.viewentry[index]["@position"]
var level = data.viewentry[index].entrydata.length;
The logic is that if I have a level 1 entry, I’m going to capture the location in a variable and add a horizontal rule to the main content area, the DIV with a class of “list-content”.  If I have a level 2 entry, I need to check to make sure it is a phone number category and not the level 2 entry for the location details.  To do this I’m using a regular expression to match on the position.  If the position is in the format of n.1 I know that I’m at the first level 2 category for that location. This is due to my view sorting.
if (pos.match("^[0-9]+\.1") == null) {
  //Not in the format of n.1, process as if a phone number category
}
Since each phone category is going to correspond to its own html table, I need to know how many rows are in that table before I start building the data array so that I can apply the data in order to achieve alphabetic sorting down each column and then to the next rather than alphabetic sorting across the rows.  To calculate the number of rows for each table I use the @descendants parameter of the view entry and then apply the Math.ceil function to the number of descendants.
entries = data.viewentry[index]["@descendants"];
rows = Math.ceil(entries/4);    //where 4 is the number of columns
Finally all the action happens if I have a level 3 entry.  Basic logic is that if I have a level 3 entry for the location details I will create a simple one row table for the location details and add it to the list-content DIV.  If I have a level 3 entry for a phone category I will add that entry to the data array for the proper column.  When I get to the last entry for that category I render a new table to the list-content DIV and then apply the data array to that table.
var template = $($("#list-template").html())
//clone the template and add to the DOM
listContent.append(template.clone().attr('id',"loc-" + location + "-" + type))
//get the table that was just added
var myTable = $('#loc-' + location + "-" + type)
//add table header 
myTable.prepend("<caption class=\"typeHeader\">" + phoneType + "</caption>")
//render table to the DOM using Dynatable.js
myTable.dynatable( {
  dataset : {
  records : finalData},
  features : {
  paginate : false,
  sort : false,
  search : false,
  recordCount : false}
});
After iterating through each entry I finish with some clean up CSS adding some classes to the table and hiding the templates.
Here is the finished product in action.  A button at the bottom of the page will reveal the entire code.
CONCLUSION
This technique acts very much like an xAgent in principal where you start with a blank xPage as your canvas and write data to it as you go through your processing.  It may not ultimately be the best solution for this particular use case but it demonstrates how data can be sourced from JSON, manipulated, and then rendered to the DOM using CSJS and jQuery.  The use of the external Dynatable.js library is interesting too and my use of it in this example barely scratches the surface of what that library is capable of producing.  Your mileage may vary but I found this scaled up just fine for our use case of about 2,000 documents in the source view.

Hacking the xPages Data View CSS to Enhance Display

A few weeks ago I was tasked with creating a new printable version of our corporate directory for use in production areas where phones were not located near workstations.  Until this point this list had been maintained manually.

After looking around at a few options I settled on the Data View control from the xPages Extension Library.  This control allows one to easily set the number of columns, which in my case was important because they wanted specifically a four column display.

Our corporate directory has three data types in play here:  location documents, individual person documents and documents for all other non-individual phone numbers.  First I set up the source view categorized first by location and secondly by the phone category.   It looked something like this (the data below has been changed for privacy).

Source View in Notes

Source View

Connecting that data source up to the data view control yielded initially a display that was far from what I was seeking.

Unformatted Data View

With some CSS overrides and jQuery DOM manipulation I was able to convert that to this:

Data View Formatted

Ahhh, much better.  Below is the custom CSS that I used.  The first six are just custom classes I used to style the site headers, category headers as well as the detail sections.  Basic stuff.   Then I used two classes that were already included in the xPages extension library theme I was using.  Adding them to my custom CSS allowed me to override some margins and spacing. Finally the last two classes are used by the jQuery code to enhance the breaks between locations.

/* Custom Classes */
.catSite {
font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif;
font-size: 18px;
color: #365F91;
}
.catCategory {
font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif;
font-weight: Bold;
font-size: 14px;
color: #365F91;
padding-top: 8px;
}
.siteDetail {
font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif;
font-size: 12px;
color: #365F91;
}
.nbrDetail {
font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif;
font-size: 12px;
}
.nameCell {
width: 165px;
border-bottom: 1px solid #ddd;
}
.nbrCell {
width: 40px;
border-bottom: 1px solid #ddd;
text-align: center;
}
/* Overrides to Existing Classes */
.lotusTable td {
padding: 0px;
border-top: 0px;
}
.lotusTable th {
padding-top: 0px;
border-top: 0px;
}
/* Type Selector Formatting */
h2 {
font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif;
font-size: 24px;
padding-left: 10px;
}
hr {
background: #8C8C8C;
clear: both;
float: none;
width: 100%;
height: 1px;
margin: 0 0 0.6em;
border: none;
}

The jQuery code I used is run from the onPageLoad event.

//jQuery magic to enhance formatting by manipulating DOM
//Removes some extra white space at the top of the page
$("thead").remove();
//Remove Top Level Categorization
$(".catLocation").text("");
//Replace + with horizontal rule for each site
//this sets each site off from the previous
var s1 = $(".catSite");
$.each(s1, function( index, value ) {
  var s2 = $(this).text()
  $(this).html(s2.replace("+","<br><hr>"))
});
//remove the nameCell and nbrCell class from the site class elements to eliminate extra underline
//replace the address delimiter tilde from view data with break tag to start a new line for each address data
var d1 = $(".siteDetail");
var d2 = d1.parent()
d2.removeClass("nameCell")
d2.next('td').removeClass("nbrCell")
$.each(d1, function( index, value ) {
  var d3 = $(this).text()
  $(this).html(d3.replace(/\~/g,'<br>'))
});
//add horizontal rule at the end of the list
//$("table").append("<BR><HR>")

Really not very much code to achieve a nice result.   Of course, there is still the overhead of the extension library code and the CSS that is loaded there is large and cumbersome to override in places.  If you wish for more styling flexibility there are other options.   In my next post (hopefully next week) I will demonstrate how to use a JSON feed from a Notes view to write out the same data without using the Data View control (hint: it also involves jQuery and a special jQuery plugin).

A Stoplight Status Component Using Bootstrap

I had a request to create a “stoplight” status section in a recent application.  The purpose of this section would be for the users to register a red, yellow, green status to a number of categories on a product development information form.

Using a combination of Twitter Bootstrap and jQuery I was able to create a nice looking component that provided the exact functionality they were hoping to achieve.

Here is a link to the demo page where you can see this in action.

Button Code

<div>
<a id="cat_1_btn" class="cat1" href="javascript:statusToggle('cat1')" role="button">
Strategic Support</a>
<a id="cat_2_btn" class="cat2" href="javascript:statusToggle('cat2')" role="button">
Competitive Landscape</a>
<a id="cat_3_btn" class="cat3" href="javascript:statusToggle('cat3')" role="button">
Product Development</a>
</div>

statusToggle Code

All the below function does is to check the current CSS class of the button and toggle it to the next one.  Very simple.

function statusToggle(category) { 
 if ($("." + category).hasClass("btn btn-success btn-xs btn-custom")) { 
   $("." + category).removeClass("btn btn-success btn-xs btn-custom")
   $("." + category).addClass("btn btn-danger btn-xs btn-custom")
   $("." + category + "Status").val("danger")
 } else if ($("." + category).hasClass("btn btn-danger btn-xs btn-custom")) { 
   $("." + category).removeClass("btn btn-danger btn-xs btn-custom")
   $("." + category).addClass("btn btn-warning btn-xs btn-custom")
   $("." + category + "Status").val("warning")
 } else if ($("." + category).hasClass("btn btn-warning btn-xs btn-custom")) { 
   $("." + category).removeClass("btn btn-warning btn-xs btn-custom")
   $("." + category).addClass("btn btn-success btn-xs btn-custom")
   $("." + category + "Status").val("success")
 }
};

The CSS classes set by the above code is standard Twitter Bootstrap CSS. The final btn-custom class only serves to add some margins and bold weight to the button text.  Nothing special there.

Interestingly enough, the above code was initially implemented in an Oracle APEX application.   Since it was all javascript and CSS I was able to easily port the code over to a sample xPages application without changing really anything.

Using Twitter Bootstrap to Create a Mobile Schedule for a Youth Sports League

This baseball season I am the coordinator for my son’s second grade league.  I decided to create an app for all the parents that would include field information, schedules and coaches contact information.  Using Twitter Bootstrap makes this really easy because most of the components are responsive and will work with any size screen.

If you take a look at the demo application I have setup you can see that while it works best on a mobile phone device, it can be viewed just as clearly in a large screen web browser.

The magic in the schedule and teams pages is that I used a repeat control bound to a Notes view and then wrote a SSJS function to return HTML with the bootstrap tags to achieve the desired look.   Simple and effective.

Repeat Control Code

<div class="panel-body">
<xp:repeat id="repeat1" value="#{view1}" var="row" rows="300">
<xp:text escape="false" id="computedField1"
value="#{javascript:return buildScheduleTable()}">
</xp:text>
</xp:repeat>
</div>

SSJS Code

function buildScheduleTable() {
var tHTML = "";
tHTML += "
<div class=\"panel panel-primary\">
<table class=\"table\">"
tHTML += "
<tbody>"
tHTML += "
<tr>"
tHTML += "
<div class=\"panel-heading\"><b>"
tHTML += row.getColumnValue("event_dt")
tHTML += "</b></div>
"
tHTML += "
<div clas=\"panel-body\">"
tHTML += "
<td>"
if (row.getColumnValue("event_status") == "Canceled") {
tHTML += "<span class=\"label label-danger\">CANCELED</span>
"
} else if (row.getColumnValue("event_type") == "Practice") {
tHTML += "<span class=\"label label-default\">Practices</span>
"
} else if (row.getColumnValue("event_type") == "Game") {
tHTML += "<span class=\"label label-warning\">Games</span>
"
} else {
tHTML += "<span class=\"label label-danger\">Other</span>
"
}
tHTML += "<b>Cherryview #1 </b>" + row.getColumnValue("location1")
tHTML += "
<b>Cherryview #2 </b>" + row.getColumnValue("location2")
tHTML += "
<b>Village Creek </b>" + row.getColumnValue("location3")
if (row.getColumnValue("location4").trim() != "" ){
tHTML += "
<b>Oak Shores </b>" + row.getColumnValue("location4")
}
tHTML += "</td>
"
tHTML += "</div>
"
tHTML += "</tr>
"
tHTML += "</tbody>
"
tHTML += "</table>
</div>
"
return tHTML
}

Message me if you’d like a copy of the demo application NSF.