When ‘font-weight: bold’ Is Not Quite Right

Lately I’ve been doing a lot of front-end UI work and in doing so there have been situations where I want to call attention to a piece of text.  Sometimes displaying the text in a BOLD font-weight is just right, but other times that feels more like cracking a nut with a sledge hammer.  CSS has supported a more granular approach to the font-weight attribute for some time but it’s a feature that can be easily forgotten.  In addition to normal, bold, and italics designations, you can use relative terms like “lighter” or “bolder” which apply the weight relative to the parent element. Perhaps more useful is the numeric designations available.  CSS supports font-weight’s of 100 to 900 in even increments of 100.   The catch is that the font you are using needs to support that level of granularity.

I had planned to code out a whole series of examples but I found an article that has already done this so no need to go through that exercise.  Read here to see examples of what this looks like and more details on how it’s applied to fonts that only support the normal and bold font-weights.

CSS Tricks: Font-Weight

Converting HTML Tags to XPage Tags

As xPages becomes a common platform for other modern web frameworks, you may find yourself writing a lot of standard HTML (such as Twitter Boostrap markup) in your Domino Designer source panel.  Here is a convenient reminder that most HTML tags can be converted to xPage tags by adding the xp: prefix.

So,

<div>...</div>

becomes

 

 <xp:div>...</xp:div>

Now it may not be immediately obvious why this would be a useful pattern but xPage tags have with them all the API hooks that are available for the standard xPages controls.  One use case could be to leverage server side logic to selectively render certain parts of your code.   For example you might have some mark up that resembles this:

<div id="show-when-editable" class="row">
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</div>
<div id="hide-when-editable" class="row">
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</div>

If you want to show the first div in edit mode and the second otherwise you could convert the above to the following:

<xp:div styleClass="row">
<xp:this.rendered><![CDATA[#{javascript:document.isEditable()==true}]]></xp:this.rendered>
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</xp:div>
<xp:div styleClass="row">
<xp:this.rendered><![CDATA[#{javascript:document.isEditable()==false}]]></xp:this.rendered>
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</xp:div>

A couple things to note about this type of tag conversion:

–  <xp:div> tags don’t respect normal HTML attributes.  For this reason, you need to also convert class="class-name" to styleClass="class-name"

– To use other attributes you can add an attribute block.  For example, code to add a data-title="My Title" to your tag would look like this:

<xp:this.attrs>
<xp:attr name="data-title">
<xp:this.value><![CDATA[#{javascript:'My Title'}]]></xp:this.value>
</xp:attr>
</xp:this.attrs>

– I haven’t tested this with all legal HTML tags, so there may be some incompatibilities but it does work well with most of the standard building block tags used for construction Boostrap grids and tables.  If you know of other limitations, please feel free to share.

Using a placeholder with Select2 in xPages

Select2 is a fantastic component that provides for a very nice alternative to the classic combo box.  While working with it this week I ran into an issue where I was unable to get the placeholder option to work.  Turns out you need to seed your values array with a null value or else the placeholder won’t display.

Using this select2 initialization code:

//Initialize Select2 Drop Down
$(‘.select2’).select2( {
placeholder: “Select value”,
allowClear: true,
minimumResultsForSearch:-1
})

This works:

var arr = new Array(“”);
var values = @DbColumn()  \\Insert your own code here
var list = arr.concat(values);
return list;

This does not work:

var values = @DbColumn()  \\Insert your own code here
return values;

A subtle but important distinction.  Thanks to Johnny Oldenberger for this tip.  I found this helpful information on one of his blog posts

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).