<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
	<channel>
		<title>Codestore Activity Log</title>
		<description>Latest ten updates to codestore, be they blogs or articles.</description>
		<language>en-gb</language>
		<link>http://www.codestore.net</link>
		<lastBuildDate>Thu, 11 Apr 2013 03:06:33 -0500</lastBuildDate>
		<atom:link href="http://www.codestore.net/store.nsf/rss.xml" rel="self" type="application/rss+xml" />

		<item>
			<title>Mapping Document Collections To Views | Blog</title>
			<pubDate>Thu, 11 Apr 2013 03:06:33 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>It's now a couple of months since I wrote about a <a href="http://www.codestore.net/store.nsf/unid/BLOG-20130204-1459">Super Useful Set of LotusScript Wrapper Classes</a>, which I've written about on and off since February. </p> <p>Today I want to share a few more extensions I've made to them, which help massively when it comes to getting documents from views.</p> <p>Imagine the following use scenario:</p> <p>&nbsp;</p><pre class="code2"><span class="TPkeyword1">Dim </span>factory <span class="TPkeyword1">As New </span>CustomerFactory<span class="TPbracket">()</span>
<span class="TPkeyword1">Dim </span>coll <span class="TPkeyword1">As </span>CustomerCollection

<span class="TPcomment">'Let's get a collection from a view</span>
<span class="TPkeyword1">Set </span>coll <span class="TPoperator">= </span>factory.GetAll<span class="TPbracket">()</span>

<span class="TPcomment">'Or you could to this</span>
<span class="TPkeyword1">Set </span>coll <span class="TPoperator">= </span>factory.GetAllByID<span class="TPbracket">()</span>

<span class="TPcomment">'Or you could to this</span>
<span class="TPkeyword1"><br>Set </span>coll <span class="TPoperator">= </span>factory.GetAllByCountry<span class="TPbracket">()</span>

<span class="TPcomment">'Or you could to this</span>
<span class="TPkeyword1">Set </span>coll <span class="TPoperator">= </span>factory.GetCustomersMatching<span class="TPbracket">(</span><span class="TPstring">"foo"</span><span class="TPbracket">)</span></pre>
<p>&nbsp;</p>
<p>This code utilises the following functions in the CustomerFactory class:</p>
<p>&nbsp;</p><pre class="code2"><span class="TPkeyword1">Class </span>CustomerFactory <span class="TPkeyword1">As </span>DocumentFactory

  <span class="TPkeyword1">Private Function </span>FromView<span class="TPbracket">(</span>ViewName <span class="TPkeyword1">As String</span><span class="TPbracket">) </span><span class="TPkeyword1">As </span>CustomerCollection
    <span class="TPkeyword1">Set </span>FromView <span class="TPoperator">= </span><span class="TPkeyword1">New </span>CustomerCollection<span class="TPbracket">(</span>GetViewNavigator<span class="TPbracket">(</span>ViewName<span class="TPbracket">))</span>
  <span class="TPkeyword1">End Function</span>

  <span class="TPkeyword1">Function </span>GetAll<span class="TPbracket">() </span><span class="TPkeyword1">As </span>CustomerCollection
    <span class="TPkeyword1">Set </span>GetAll <span class="TPoperator">= </span>FromView<span class="TPbracket">(</span><span class="TPstring">"AllCustomers"</span><span class="TPbracket">)</span>
  <span class="TPkeyword1">End Function</span>
  
  <span class="TPkeyword1">Function </span>GetAllByCity<span class="TPbracket">() </span><span class="TPkeyword1">As </span>CustomerCollection
    <span class="TPkeyword1">Set </span>GetAllBySite <span class="TPoperator">= </span>FromView<span class="TPbracket">(</span><span class="TPstring">"CustomersByCity"</span><span class="TPbracket">)</span>
  <span class="TPkeyword1">End Function</span>

  <span class="TPkeyword1">Function </span>GetAllByCountry<span class="TPbracket">() </span><span class="TPkeyword1">As </span>CustomerCollection
    <span class="TPkeyword1">Set </span>GetAllBySite <span class="TPoperator">= </span>FromView<span class="TPbracket">(</span><span class="TPstring">"CustomersByCountry"</span><span class="TPbracket">)</span>
  <span class="TPkeyword1">End Function</span>
  
  <span class="TPkeyword1">Function </span>GetAllByStatus<span class="TPbracket">() </span><span class="TPkeyword1">As </span>CustomerCollection
    <span class="TPkeyword1">Set </span>GetAllByStatus <span class="TPoperator">= </span>FromView<span class="TPbracket">(</span><span class="TPstring">"CustomersByStatus"</span><span class="TPbracket">)</span>
  <span class="TPkeyword1">End Function</span>

  <span class="TPkeyword1">Function </span>GetCustomerByID<span class="TPbracket">( </span>id <span class="TPkeyword1">As String </span><span class="TPbracket">) </span><span class="TPkeyword1">As </span>Customer
    <span class="TPkeyword1">Set </span>GetCustomerByID <span class="TPoperator">= </span><span class="TPkeyword1">New </span>Customer<span class="TPbracket">( </span>GetDocumentByKey<span class="TPbracket">( </span><span class="TPstring">"CustomersByID"</span>, id <span class="TPbracket">) )</span>
  <span class="TPkeyword1">End Function</span>
  
  <span class="TPkeyword1">Function </span>GetCustomersMatching<span class="TPbracket">(</span>query <span class="TPkeyword1">As String</span><span class="TPbracket">) </span><span class="TPkeyword1">As </span>CustomerCollection
    <span class="TPkeyword1">Set </span>GetCustomersMatching<span class="TPoperator">= </span><span class="TPkeyword1">New </span>CustomerCollection<span class="TPbracket">(</span>GetViewEntriesByKey<span class="TPbracket">(</span><span class="TPstring">"(LU-CustomersByAllKeys)"</span>, query, <span class="TPkeyword2">False</span><span class="TPbracket">))</span>
  <span class="TPkeyword1">End Function</span>
<span class="TPkeyword1">End Class</span></pre>
<p>&nbsp;</p>
<p>In turn the above code relies on a few additions to the <em>base</em> DocumentFactory class:</p>
<p>&nbsp;</p><pre class="code2"><span class="TPkeyword1">Class </span>DocumentFactory
  <span class="TPkeyword1">Function </span>GetViewEntriesByKey<span class="TPbracket">(</span>ViewName <span class="TPkeyword1">As String</span>, SearchTerm <span class="TPkeyword1">As Variant</span>, ExactMatch <span class="TPkeyword1">As Boolean</span><span class="TPbracket">) </span><span class="TPkeyword1">As </span><span class="TPkeyword3">NotesViewEntryCollection</span>
    <span class="TPkeyword1">Set </span>GetViewEntriesByKey <span class="TPoperator">= </span>GetView<span class="TPbracket">(</span>ViewName<span class="TPbracket">)</span>.Getallentriesbykey<span class="TPbracket">(</span>SearchTerm, Exactmatch<span class="TPbracket">)</span>
  <span class="TPkeyword1">End Function</span>

  <span class="TPkeyword1">Function </span>GetViewNavigator<span class="TPbracket">(</span>ViewName <span class="TPkeyword1">As String</span><span class="TPbracket">) </span><span class="TPkeyword1">As </span><span class="TPkeyword3">NotesViewNavigator</span>
    <span class="TPkeyword1">Set </span>GetViewNavigator <span class="TPoperator">= </span>GetView<span class="TPbracket">(</span>ViewName<span class="TPbracket">)</span>.Createviewnav<span class="TPbracket">()</span>
  <span class="TPkeyword1">End Function</span>

  <span class="TPkeyword1">Function </span>GetAllViewEntries<span class="TPbracket">(</span>ViewName <span class="TPkeyword1">As String</span><span class="TPbracket">) </span><span class="TPkeyword1">As </span><span class="TPkeyword3">NotesViewEntryCollection</span>
    <span class="TPkeyword1">Set </span>GetViewNavigator <span class="TPoperator">= </span>GetView<span class="TPbracket">(</span>ViewName<span class="TPbracket">)</span>.AllEntries
  <span class="TPkeyword1">End Function</span>
  
  <span class="TPkeyword1">Function </span>GetDocumentByKey<span class="TPbracket">(</span>ViewName <span class="TPkeyword1">As String</span>, Lookup <span class="TPkeyword1">As Variant</span><span class="TPbracket">) </span><span class="TPkeyword1">As </span><span class="TPkeyword3">NotesDocument</span>
    <span class="TPkeyword1">Set </span>GetDocumentByKey <span class="TPoperator">= </span>GetView<span class="TPbracket">(</span>ViewName<span class="TPbracket">)</span>.Getdocumentbykey<span class="TPbracket">( </span>Lookup, <span class="TPkeyword2">True </span><span class="TPbracket">)</span>
  <span class="TPkeyword1">End Function</span>
<span class="TPkeyword1">End Class</span></pre>
<p>&nbsp;</p>
<p>What all this does is make it a lot less tiresome to get a handle on document(s) relating to a particular business object (in this case documents based on the "Customer" form). The base DocumentCollection class now has helper functions to get all view entries or simply a view navigator object. It also has a GetDocumentByKey method which defaults the ExactMatch option to True, because it always is, thus saving us passing it in to the call.</p>
<h4>Going Forward</h4>
<p>I've also been using these classes extensively in a new Domino database I've been lucky enough to create from scratch and have been working on lately. As I've gone along I've been tweaking and extending the classes and now feel they're at a solid point ready for wider use.</p>
<p>I don't want to bang my own drum too much, but they're <strong>amazing</strong>. You maybe have to use them to see why, but, once you do, there's no going back. If this were ten years ago they'd be almost revolutionary.</p>
<p>Talking of going back; will I ever!? Will I ever be asked to create a <strong>new</strong> Domino-based web app from scratch? Never say never and all that, but I have a feeling I won't. Which makes it a shame that I developed such a rich and powerful way of working with Notes objects via LotusScript so late in my days with Notes.</p>
<p>Either way, if you're still working with and creating new Notes apps I implore you to give these classes a go. You'll love them.</p>
<p>My plan is to scrub them all a bit, document them better and then share on Github, along with a demo database. Assuming there's interest?</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130411-0306?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130411-0306</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130411-0306</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130411-0306?Open#comments</comments>
			<slash:comments>11</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130411-0306</wfw:commentRss>
		</item>
		<item>
			<title>Making Google Apps For Business Behave Like a Mail Client | Blog</title>
			<pubDate>Wed, 3 Apr 2013 05:11:17 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>So, I mentioned that I've moved to a new PC and, for the first time in years, I didn't install a dedicated Mail client/app. Instead I'm using direct web access to Google Apps For Business (posh name for paid-for Gmail).</p> <p>The one thing I never liked about using Email in a browser is that I constantly close and re-open the browser. Thus, in effect, closing my email client, which I then have to remember to re-open.</p> <p>Now though I've made it seem like a real app. You can do this using Chrome's "application shortcuts". Here's how.</p> <p>First I <a href="http://support.google.com/a/bin/answer.py?hl=en-uk&amp;hlrm=en&amp;answer=47283">setup a CNAME record</a> in my DNS so that mail.mydomain.com points to Gmail. Then I visited the new URL in Chrome and, from the Options menu I chose Tools -&gt; "Create application shortcuts...." which brings up this option dialog:</p> <p><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/CDE760B1A0683F6686257B420037F3ED/$file/image_ed00c82d-15bb-4d86-9d28-4a8bc7564e43.png" width="409" height="310"></p> <p>When I clicked Create it added a shortcut to my Taskbar, as below.</p> <p><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/4EF95C0B4218BADB86257B420037F441/$file/image_4b42ba3f-a946-498c-aee8-2e8ef8841038.png" width="232" height="67"></p> <p>As you can see it looks just like a real application. It even tells me how many unreads there are.</p> <p>The app runs in a chrome-less version of Chrome, as below:</p> <p><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/E655DF647AF2F64786257B420037F64E/$file/image_4f4cee4a-19f9-43b8-abbb-842617b6866f.png" width="1431" height="1030"></p> <p>I can now open and close my actual Chrome browser as many times as I like and my "mail client" stays open.</p> <p>Did you notice the custom logo being used? <a href="http://support.google.com/a/bin/answer.py?hl=en&amp;answer=96474">Here's how</a>.</p> <p>You can also <a href="http://support.google.com/mail/answer/1075549?hl=en">configure notifications</a> to make it even more like a true mail client experience, where new email alerts appear on the bottom right of your screen.</p> <p><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/D3A51CF64705DC9286257B420037F6BF/$file/image_281043ca-437e-4f84-ab16-9fd020a482b1.png" width="329" height="190"></p>       <p>All in all it makes for a nice experience. Now if only I could shake the un-nerving sense that I could lose all my mail at any point...</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130403-0511?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130403-0511</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130403-0511</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130403-0511?Open#comments</comments>
			<slash:comments>6</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130403-0511</wfw:commentRss>
		</item>
		<item>
			<title>Documenting Your LotusScript Classes | Blog</title>
			<pubDate>Wed, 27 Mar 2013 03:46:41 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Just a quick LotusScript tip:</p> <p>Hopefully you've been inspired by my recent adventures in creating custom LotusScript classes. If so, you'll have noticed that, when creating your own properties and methods in these classes that they get their own comments added in.</p> <p>For example, if you type "property get foo as string" and then press return, you'll see something like this:</p><pre class="code2"><span class="TPcomment">%REM
        Property Get Foo
        Description: Comments for Property Get
%END REM</span>
<span class="TPkeyword1">Property Get </span>Foo <span class="TPkeyword1">As String</span>

<span class="TPkeyword1">End Property</span>
</pre>
<p>Domino Design auto-adds the comments above the property for you. </p>
<p>It's easy to see this as an annoyance and find yourself taking the comments out to keep your code tidy. However, I've forced myself to stop doing that and now make a point of adding in a meaningful comment. Like so: </p><pre class="code2"><p><span class="TPcomment">%REM
        Property Get Action
        Description: Value of the "Action" field, which is used by the
        workflow associated with most documents
        
        Value is only available when editing/saving
        (not stored on document)!
%END REM</span>
<span class="TPkeyword1">Property Get </span>Action <span class="TPkeyword1">As String</span>
        Action <span class="TPoperator">= </span>GetFieldValue<span class="TPbracket">(</span><span class="TPstring">"Action"</span><span class="TPbracket">)</span>
<span class="TPkeyword1">End Property</span></p></pre>
<p>The benefit of doing so is manifold. Not only does it remind yourself what your intentions were but also, more importantly, future maintainers of the code.</p>
<p>But the real tip here is that, if you comment code using the standard way then, when you hover the mouse over a call to that property in use in your code you get to see the "documentation" inline. As below:</p>
<p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/464C4175A6E0D17286257B3B0030384B/$file/image_3.png" width="533" height="247"></p>
<p>That alone makes it worth the time to document properly. Even if just for your own benefit as a quick reminder of what everything is.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130327-0346?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130327-0346</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130327-0346</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130327-0346?Open#comments</comments>
			<slash:comments>7</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130327-0346</wfw:commentRss>
		</item>
		<item>
			<title>The Perfect Desk Quest - 2013 Update | Blog</title>
			<pubDate>Tue, 26 Mar 2013 07:12:37 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>It must be time for another update on how my desk is evolving towards the elusive "perfect desk". <a href="http://www.codestore.net/store.nsf/unid/BLOG-20100422-0421">Previous</a> <a href="http://www.codestore.net/store.nsf/unid/BLOG-20110706-0336">updates</a> <a href="http://www.codestore.net/store.nsf/unid/BLOG-20120404-0325">here</a>.</p> <p>Here's my desk now, following a recent overhaul:</p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_3377" border="0" alt="IMG_3377" src="http://www.codestore.net/store.nsf/rsrc/D533285F0DA873C486257B3A0043127E/$file/IMG_3377_1.jpg" width="605" height="403"></p> <p>The two main differences from before: The Lenovo T400 laptop and its docking station have gone and the left-most monitor has moved positions to be on the right.</p> <p>My desk needs to not only keep up with the way I work, but also the changes of the times. I decided it was time I got "with the times" and "embrace the cloud" and all that stuff. </p> <p>For years my main work PC has been a laptop. For the obvious reasons, such as being able to work anywhere, being able to hide it when I go on holiday etc. It used to be that I'd undock the T400 each night and take it indoors of a night time. Then I bought the Yoga 13, which fast became my "house laptop" and the T400 was confined to the office.&nbsp; The T400 was getting on for <a href="http://www.codestore.net/store.nsf/unid/BLOG-20090924-0342">being 3 years old</a> and had the noisiest damned fan in its docking station! It's days were numbered!!</p> <p>While using a laptop as my main PC I have always missed having a dual monitor setup, which laptops can't deal with. Not easily anyway.</p> <p>So, I've bought a <a href="http://www.lenovo.com/products/us/workstation/thinkstation-e-series.html">Lenovo E31 SFF</a> and stuck 32GB of RAM in it. This is now my main work PC and drives the two monitors to the right. I've missed having dual monitors! </p> <p>Using the recently-discovered <a href="http://technet.microsoft.com/en-gb/library/ee256062(v=ws.10).aspx">Hyper-V Manager tool</a> for Windows 7 means I don't need the monitor attached directly to server. As you can see above the second monitor is showing a Domino server running in a VM on the server. It makes day-to-day Domino development so much easier having ready access to the server console.</p> <h4>No Email Client</h4> <p>The cloud now means I'm not tied to a single machine. When I do go "on the road" I can easily take everything with me.</p> <p>For the first time in as long as I can remember using a PC with email I'm not going to install a Mail app. I've used various email clients over the years: Opera, Outlook Express, Lotus Notes, Thunderbird, Postbox, Mail.app. Now I'm just going to use Gmail.com online. Got to admit I don't like the idea that all messages aren't backed up on my own PCs. But hey. Brave new world and all that.</p> <p>The only non-cloud-stored data I work with are Notes database. I tried using Dropbox to store my Notes Data folder, but, unsurprisingly, it didn't work. It probably could work if you made sure to only have Notes running on one PC at a time.</p> <p>Moving from one PC to another this time was a lot less painful than it has been in the past. Still took a couple of hours. Not the full day of time it used to though.</p> <p>Who knows what the 2014 update will bring. I just hope I'm still lucky enough to be working from my own little home-office, so that it matters either way.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130326-0712?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130326-0712</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130326-0712</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130326-0712?Open#comments</comments>
			<slash:comments>11</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130326-0712</wfw:commentRss>
		</item>
		<item>
			<title>Updating Glyphicons in Bootstrap | Blog</title>
			<pubDate>Thu, 21 Mar 2013 04:57:41 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Using Bootstrap, whenever I want to an icon to an element, I simply write HTML like this:</p>  <pre>&lt;a class=&quot;btn&quot; href=&quot;foo&quot;&gt;&lt;i class=&quot;icon icon-plus&quot;&gt;&lt;/i&gt; Add New&lt;/a&gt;</pre>

<p>And I get something like this:</p>

<p><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/2BCB9C297DAF97F886257B350078AED9/$file/image_3.png" width="161" height="70" /></p>

<p>The choice of icons is <a href="http://twitter.github.com/bootstrap/base-css.html#icons">documented here</a> and the icons themselves are based on <a href="http://glyphicons.com/">Glyphicons</a> &quot;halflings&quot;. </p>

<p>All is good.</p>

<p>However, recently I found myself wanting to use an icon to show that a document had some attachments. Naturally, I wanted a paperclip. Just like a magnifying glass denotes searching, a paperclip denotes a file attachment. Who knows why!</p>

<p>To my surprise the available set of icons listed on the Bootstrap site didn't include a paperclip. Really? But on further investigation it became apparent that the current version of Glyphicons <em>did</em> include a paperclip!</p>

<p>Here are the icons currently supplied with Bootstrap. It's a single flat image, which is used as a <a href="http://alistapart.com/article/sprites">CSS Sprite</a>:</p>

<p><img src="http://hse.abf.local/apps/projects/ABF/hse/audits.nsf/img/glyphicons-halflings-old.png" /></p>

<p>&#160;</p>

<p>And here's the image which represents the <em>current</em> release of Glypicons:</p>

<p>&#160;</p>

<p><img alt="GLYPHICONS Halflings" src="http://glyphicons.com/wp-content/themes/glyphicons/images/glyphicons_halflings.png" /></p>

<p>&#160;</p>

<p>Notice the difference? </p>

<p>There are a few very slight differences. But the main difference is that the latter image has an <strong>extra row</strong> of icons. Not only that but one of them is a paperclip! Get in!!</p>

<p>Luckily (for me) the makers of Glypicons add new icons to the end of the grid, without changing the positions of the existing icons. This meant I could simply replace the image currently used by my copy of Bootstrap with the latest Glyphicons release. Without breaking the existing CSS/icons!</p>

<p>Once the new image was in place all I had to do was work out the grid size used, which, it turns out is 24 by 24 pixels. Before long I'd extended the available Bootstraps icons by adding the following CSS:</p>

<pre>.icon-attachment {
  background-position: -24px -168px;
}</pre>

<p>Job done. I love Bootstrap!</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130321-1657?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130321-1657</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130321-1657</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130321-1657?Open#comments</comments>
			<slash:comments>7</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130321-1657</wfw:commentRss>
		</item>
		<item>
			<title>Easy Access to Sub-Collections of the DocumentWrapper Class | Blog</title>
			<pubDate>Wed, 13 Mar 2013 05:20:27 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>My favourite feature of the Wrapper Classes I keep going on about is the ready access they can give to any given class's related sub-classes.</p>  <p>For example, let's say you have an object based on the Customer class and you want to find all their Invoices. Wouldn't it be nice to write code like this in a WQO agent:</p>  <pre class="code2"><span class="TPkeyword1">Dim </span>customer <span class="TPkeyword1">As </span>Customer, invoice <span class="TPkeyword1">As </span>Invoice
<span class="TPkeyword1">Set </span>customer <span class="TPoperator">= </span><span class="TPkeyword1">New </span>Customer<span class="TPbracket">(</span>web.document<span class="TPbracket">)</span>

<span class="TPkeyword1">If </span>customer.Invoices.Count <span class="TPoperator">&gt; </span><span class="TPnumber">0 </span><span class="TPkeyword1">Then</span>
  <span class="TPkeyword1">Set </span>invoice <span class="TPoperator">= </span>customer.Invoices.getFirst<span class="TPbracket">()</span>
  
  <span class="TPkeyword1">While Not </span>invoice <span class="TPkeyword1">Is </span><span class="TPkeyword2">Nothing </span>
    <span class="TPcomment">'Do what you like here</span>
    
    <span class="TPkeyword1">Set </span>invoice <span class="TPoperator">= </span>customer.Invoices.getNext<span class="TPbracket">()</span>
  <span class="TPkeyword1">Wend</span>
<span class="TPkeyword1">End If</span></pre>

<p>When you start using code like this in your day-to-day LotusScripting that's when it all starts to gel and there's no going back.</p>

<p>Imagine also that you wanted to know how many other invoices the customer for any given invoice has. You can do it like this:</p>

<pre class="code2"><span class="TPkeyword1">Print </span><span class="TPstring">&quot;This invoice's customer has &quot; </span><span class="TPoperator">+ </span><span class="TPkeyword1">Cstr</span><span class="TPbracket">(</span>invoice.Customer.Invoices.Count <span class="TPoperator">- </span><span class="TPnumber">1</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">&quot; other invoices&quot;</span></pre>

<p>Cool, no?</p>

<h4>Adding Collection Properties</h4>

<p>But how do we add this &quot;Invoices&quot; property? Easy. Just do something like this in the Invoice class:</p>

<pre class="code2"><span class="TPkeyword1">Class </span>Customer <span class="TPkeyword1">as </span>DocumentWrapper
 <span class="TPkeyword1">Private </span>Invoices_ <span class="TPkeyword1">As </span>InvoiceCollection
 <span class="TPkeyword1">Private </span>Factory_ <span class="TPkeyword1">As </span>InvoiceFactory

 <span class="TPkeyword1">Property Get </span>Invoices <span class="TPkeyword1">As </span>InvoiceCollection
  <span class="TPcomment">'lazy load the invoices!</span>
  <span class="TPkeyword1">If </span>Invoices_ <span class="TPkeyword1">Is </span><span class="TPkeyword2">Nothing </span><span class="TPkeyword1">Then</span>
   <span class="TPkeyword1">Set </span>Factory_ <span class="TPoperator">= </span><span class="TPkeyword1">new </span>InvoiceFactory<span class="TPbracket">()</span>
   <span class="TPkeyword1">Set </span>Invoices_ <span class="TPoperator">= </span>Factory_.GetInvoicesForCustomer<span class="TPbracket">(</span><span class="TPkeyword1">Me</span><span class="TPbracket">)</span>
  <span class="TPkeyword1">End If</span>

  Invoices <span class="TPoperator">= </span>Invoices_
 <span class="TPkeyword1">End Property</span>
<span class="TPkeyword1">End Class</span></pre>

<p>Noticed we've defined a class-level InvoiceCollection and InvoiceFactory (we need to do this so they stay in scope). We then &quot;lazy load&quot; them the InvoiceCollection when it's first requested.</p>

<p>Here we're only going one level of sub-class deep. But you could go as many levels deep as you needed. </p>

<p>In the example above I've called a method called GetInvoicesForCustomer(). How this works depends on how your data is structured. Maybe the invoices are child documents of the Customer. Maybe they're just tied together by an &quot;id&quot; field. Either way, it doesn't really matter.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130313-0520?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130313-0520</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130313-0520</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130313-0520?Open#comments</comments>
			<slash:comments>4</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130313-0520</wfw:commentRss>
		</item>
		<item>
			<title>Creating New Documents Based on the DocumentWrapper Class | Blog</title>
			<pubDate>Mon, 11 Mar 2013 08:19:17 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>So far, we've seen <a href="http://www.codestore.net/store.nsf/unid/BLOG-20130308-0328">how to &quot;wrap&quot;</a> an <strong>existing</strong> document inside a custom class, which is based on the DocumentWrapper subclass. But the document we wrap doesn't have to exist. We can use the DocumentWrapper classes to handle creation of new documents.</p>  <p>Imagine that, anywhere in your LotusScript, you could write this:</p>  <pre class="code2"><span class="TPkeyword1">Set </span>invoice <span class="TPoperator">= </span><span class="TPkeyword1">New </span>Invoice<span class="TPbracket">(</span>web.<span class="TPkeyword2">database</span>.CreateDocument<span class="TPbracket">)</span>
invoice.Number <span class="TPoperator">= </span>factory.GetNextInvoiceNumber<span class="TPbracket">()</span>
invoice.Value <span class="TPoperator">= </span><span class="TPnumber">12345.98</span>
invoice.Save<span class="TPbracket">()</span></pre>

<p>Wouldn't that be cool!?</p>

<p>It's quite simple to do. To help take some of the pain out creating new documents of a certain type you can use the &quot;New()&quot; method of the derived class to check if it's a new document being created. If it is new then we can &quot;initialise&quot; it by adding some of the key fields and values. Such as the &quot;Form&quot; field.</p>

<p>So, for the Invoice class the New() method would look like this:</p>

<pre class="code2"><span class="TPkeyword1">Class </span>Invoice <span class="TPkeyword1">As </span>DocumentWrapper
 <span class="TPkeyword1">Sub New</span><span class="TPbracket">(</span>doc <span class="TPkeyword1">As </span><span class="TPkeyword3">NotesDocument</span><span class="TPbracket">)</span>, DocumentWrapper<span class="TPbracket">(</span>doc<span class="TPbracket">) </span>
    <span class="TPkeyword1">If </span>doc.IsNewNote <span class="TPkeyword1">Then </span>
         SetFieldValue <span class="TPstring">&quot;Form&quot;</span>, <span class="TPstring">&quot;Invoice&quot; </span>
         AllowedEditors <span class="TPoperator">=</span> web.user.Canonical
         AllowedReaders <span class="TPoperator">= </span><span class="TPkeyword1">Split</span><span class="TPbracket">(</span><span class="TPstring">&quot;*/ACME;&#91;Administrators&#93;&quot;</span>, <span class="TPstring">&quot;;&quot;</span><span class="TPbracket">)</span>
        <span class="TPcomment">'Any other fields this form *needs* to have!?</span>
    <span class="TPkeyword1">End If </span>
 <span class="TPkeyword1">End Sub</span>

 <span class="TPkeyword1">Property Set </span>Value <span class="TPkeyword1">As Variant</span>
  SetFieldValue <span class="TPstring">&quot;Value&quot;</span>, <span class="TPkeyword1">CCur</span><span class="TPbracket">(</span>Value<span class="TPbracket">)</span>
 <span class="TPkeyword1">End Property</span>

 <span class="TPkeyword1">Property Set </span>Number <span class="TPkeyword1">As String</span>
  SetFieldValue <span class="TPstring">&quot;Number&quot;</span>, Number
 <span class="TPkeyword1">End Property</span>

<span class="TPkeyword1">End Class</span></pre>

<p>So far the derived classes I've shown only had &quot;getter&quot; properties, which returned field values. But the above two snippets of code demonstrate the user of &quot;setter&quot; properties also.</p>

<p>Notice the AllowedEditors and AllowedReaders properties. These are a new addition to the base DocumentWrapper class, which we can use to add Names-type fields. In turn they rely on a new AddNamesField method, like so:</p>

<pre class="code2"><span class="TPkeyword1">Property Set </span>AllowedEditors <span class="TPkeyword1">As Variant</span>
 AddNamesField <span class="TPstring">&quot;DocAuthors&quot;</span>, AllowedEditors, <span class="TPkeyword2">AUTHORS </span>
<span class="TPkeyword1">End Property</span>
        
<span class="TPkeyword1">Property Set </span>AllowedReaders <span class="TPkeyword1">As Variant</span>
 AddNamesField <span class="TPstring">&quot;DocReaders&quot;</span>, AllowedReaders, <span class="TPkeyword2">READERS</span>
<span class="TPkeyword1">End Property</span>

<span class="TPkeyword1"><br />Sub </span>AddNamesField<span class="TPbracket">(</span>FieldName <span class="TPkeyword1">As String</span>, UserNames <span class="TPkeyword1">As Variant</span>, FieldType <span class="TPkeyword1">As Integer</span><span class="TPbracket">)</span>
  <span class="TPkeyword1">Dim </span>item <span class="TPkeyword1">As New </span><span class="TPkeyword3">NotesItem</span><span class="TPbracket">(</span>document_, FieldName, UserNames, FieldType<span class="TPbracket">)</span>
<span class="TPkeyword1">End Sub</span></pre>

<p>All document creation logic for each business class/model can now be encapsulated in one place. Then, no matter where in your code you find yourself creating new instances from, you don't need to worry about what the form name is or what fields are needed.</p>

<p>As I keep saying: the more I use and extend these three classes the more I wonder how I got by without them...</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130311-0819?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130311-0819</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130311-0819</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130311-0819?Open#comments</comments>
			<slash:comments>1</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130311-0819</wfw:commentRss>
		</item>
		<item>
			<title>Extending The LotusScript Wrapper Classes | Blog</title>
			<pubDate>Fri, 8 Mar 2013 03:28:40 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>The <a href="http://www.codestore.net/store.nsf/unid/BLOG-20130204-1459">wrapper classes</a> I blogged about in February went down a lot better than I thought they might.</p>  <p>So, taking it further. Let's extend the DocumentWrapper class some more. Since first writing about them&#160; on here I've been busy using and extending them as I go. Here are a few examples of some of the properties I've added</p>  <h4>Simple Helper Properties</h4>  <p>Some very simple properties: WebLink, UNID and IsNew.</p>  <pre class="code2"><span class="TPkeyword1">Property Get </span>WebLink <span class="TPkeyword1">As String</span>
  WebLink <span class="TPoperator">= </span><span class="TPstring">&quot;/&quot; </span><span class="TPoperator">+ </span><span class="TPkeyword1">Replace</span><span class="TPbracket">(</span>web.<span class="TPkeyword2">database</span>.Filepath, <span class="TPstring">&quot;\&quot;</span>, <span class="TPstring">&quot;/&quot;</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">&quot;/0/&quot; </span><span class="TPoperator">+ </span>UNID <span class="TPoperator">+ </span><span class="TPstring">&quot;?OpenDocument&quot;</span>
<span class="TPkeyword1">End Property</span>
        
<span class="TPkeyword1">Property Get </span>UNID <span class="TPkeyword1">As String</span>
  UNID <span class="TPoperator">= </span>Document.Universalid
<span class="TPkeyword1">End Property</span>
        
<span class="TPkeyword1">Property Get </span>IsNew <span class="TPkeyword1">As Boolean</span>
  IsNew <span class="TPoperator">= </span>document.Isnewnote
<span class="TPkeyword1">End Property</span></pre>

<p>These don't do anything that can't already be done. They just make it simpler and quicker to code.</p>

<h4>Logging Document Actions</h4>

<p>Properties are all well and good. But they're just borne of laziness really. Let's add a method or two, to make it really useful. How about adding a new method to the base class called LogAction() which you might use like this:</p>

<pre>invoice.LogAction<span class="TPbracket">(</span><span class="TPstring">&quot;Invoice Approved&quot;</span><span class="TPbracket">)</span></pre>

<p>And the method might look like this (it assumes all your Forms have a standard field called &quot;ActionLog&quot;):</p>

<pre class="code2"><span class="TPkeyword1">Sub </span>LogAction<span class="TPbracket">( </span>Action <span class="TPkeyword1">As String </span><span class="TPbracket">)</span>
  <span class="TPkeyword1">Dim </span>item <span class="TPkeyword1">as </span><span class="TPkeyword3">NotesItem</span>
  <span class="TPkeyword1">Set </span>item <span class="TPoperator">= </span>document.GetFirstItem<span class="TPbracket">( </span><span class="TPstring">&quot;ActionLog&quot; </span><span class="TPbracket">)</span>
  <span class="TPkeyword1">Call </span>item.AppendToTextList<span class="TPbracket">( </span>Action <span class="TPoperator">+ </span><span class="TPstring">&quot; on &quot; </span><span class="TPoperator">+ </span><span class="TPkeyword1">Format</span><span class="TPbracket">(</span><span class="TPkeyword1">Now</span>, <span class="TPstring">&quot;dd mmm yy&quot;</span><span class="TPbracket">) </span><span class="TPoperator">+</span>_
   <span class="TPstring">&quot; at &quot; </span><span class="TPoperator">+ </span><span class="TPkeyword1">Format</span><span class="TPbracket">(</span><span class="TPkeyword1">Now</span>, <span class="TPstring">&quot;hh:nn:ss&quot;</span><span class="TPbracket">) </span><span class="TPoperator">+ </span><span class="TPstring">&quot; by &quot; </span><span class="TPoperator">+ </span>web.User.Abbreviated <span class="TPbracket">)</span>
<span class="TPkeyword1">End Sub</span></pre>

<p>The web.user part assumes you're already using <a href="http://www.codestore.net/store.nsf/unid/BLOG-20080211">my WebSession Class</a>.</p>

<h4>Deleting Documents</h4>

<p>If you never actually delete documents, but merely flag them as deleted then you can give all objects based on the DocumentWrapper class a Delete() method, like so:</p>

<pre class="code2"><span class="TPkeyword1">Sub</span> MarkDeleted<span class="TPbracket">()</span>
 <span class="TPkeyword1">Call </span>SetFieldValue<span class="TPbracket">(</span><span class="TPstring">&quot;Delete&quot;</span>, <span class="TPstring">&quot;1&quot;</span><span class="TPbracket">)</span>
 <span class="TPkeyword1">Call </span>LogAction<span class="TPbracket">(</span><span class="TPstring">&quot;Document Deleted&quot;</span><span class="TPbracket">)</span>
<span class="TPkeyword1">End Sub</span></pre>

<p>You can then add an IsDeleted property to the DocumentWrapper class, like so:</p>

<pre class="code2"><span class="TPkeyword1">Property </span>IsDeleted <span class="TPkeyword1">As Boolean</span>
  IsDeleted <span class="TPoperator">= </span><span class="TPbracket">(</span>GetFieldValue<span class="TPbracket">(</span><span class="TPstring">&quot;Deleted&quot;</span><span class="TPbracket">)</span><span class="TPoperator">=</span><span class="TPstring">&quot;1&quot;</span><span class="TPbracket">)</span>
<span class="TPkeyword1">End If</span></pre>

<p>At this point you might consider moving some of the field name and their values to &quot;static strings&quot; at the top of the class, then changes the corresponding methods, like this:</p>

<pre class="code2"><span class="TPkeyword1">Const </span>DELETED_FIELD_NAME <span class="TPoperator">= </span><span class="TPstring">&quot;Deleted&quot;</span>
<span class="TPkeyword1">Const </span>DELETED_FIELD_VALUE <span class="TPoperator">= </span><span class="TPstring">&quot;1&quot;</span>

<span class="TPkeyword1">Sub</span> MarkDeleted<span class="TPbracket">()</span>
  <span class="TPkeyword1">Call </span>SetFieldValue<span class="TPbracket">(</span>DELETED_FIELD_NAME, DELETED_FIELD_VALUE<span class="TPbracket">)</span>
<span class="TPkeyword1">End Sub</span>

<span class="TPkeyword1">Property </span>IsDeleted <span class="TPkeyword1">As Boolean</span>
  IsDeleted <span class="TPoperator">= </span><span class="TPbracket">(</span>GetFieldValue<span class="TPbracket">(</span>DELETED_FIELD_NAME<span class="TPbracket">) </span><span class="TPoperator">= </span>DELETED_FIELD_VALUE<span class="TPbracket">)</span>
<span class="TPkeyword1">End If</span></pre>

<p>Naturally you might want to add an UnDelete() method too...</p>

<h4>Extending Derived Classes</h4>

<p>So far I've talked about extending the <strong>base</strong> DocumentWrapper class. Everything we add there is available to all classes derived from it. But, obviously, you can also add properties and methods to the derived classes. For example, the Invoice class might want an Approve() method. This could either simply flag it as approved or perform some more complex workflow logic, passing it between approvers.</p>

<pre class="code2"><span class="TPkeyword1">Class </span>Invoice <span class="TPkeyword1">As </span>DocumentWrapper
 <span class="TPkeyword1">Sub </span>Approve<span class="TPbracket">()</span>
  <span class="TPcomment">'Do whatever </span>
  LogAction<span class="TPbracket">(</span><span class="TPstring">&quot;Approved&quot;</span><span class="TPbracket">)</span>
 <span class="TPkeyword1">End Sub</span>
 
 <span class="TPkeyword1">Sub </span>AddItem<span class="TPbracket">(</span>Description <span class="TPkeyword1">as String</span>, Value <span class="TPkeyword1">as Currency</span><span class="TPbracket">)</span>
  <span class="TPcomment">'Add to the relevant fields</span>
 <span class="TPkeyword1">End Sub</span>
<span class="TPkeyword1">End Class</span></pre>

<p>Notice I added a call to LogAction() inside the Approve() method. That way you never forget to log the approval no matter where you call the Approve() method from.</p>

<p>The possibilities are almost endless. </p>

<h4>Summary</h4>

<p>These wrapper classes have completely changed the way I code my Domino web apps! My dream has always been to be a &quot;proper coder&quot;. I'd love to be able to work completely in Visual Studio writing C#. Unless you've done that you won't appreciate what a pleasure it can be. I get a buzz from writing my own classes and then using them. This is a buzz I've never really had while coding for Domino. Until now. These wrapper classes will have to do for the time being.</p>

<p>Next week I'll talk even more about them and show how to handle collections of documents that belong to other documents. While I go, I'm working on a demo app that brings it all together.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130308-0328?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130308-0328</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130308-0328</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130308-0328?Open#comments</comments>
			<slash:comments>15</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130308-0328</wfw:commentRss>
		</item>
		<item>
			<title>How to Do Infinite Scrolling in Domino | Blog</title>
			<pubDate>Wed, 6 Mar 2013 02:06:30 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Last week I <a href="http://www.codestore.net/store.nsf/unid/BLOG-20130221-0252">linked to a Domino-based infinite-scrolling view</a> demo and promised I'd write up how I did it. Even though the reaction to the demo was mixed I'm a man of my word, so here goes.</p>  <p>It always surprises (worries!) me when I'm asked to either share a downloadable copy of a demo or write a how-to about it. Particularly when it's something that's all but completely web-based. The code is there to see; just a click away.</p>  <p>That said, in this case, unless you're familiar with jQuery and/or jQuery plugins it might not make much sense and warrants some explaining, so here goes.</p>  <p>First bit of code you see is at the very bottom of the <a href="http://www.codestore.net/apps/invoices.nsf/Scroller?ReadForm&amp;view=InvoicesByDate">scrolling view page</a> and looks like this:</p>  <pre class="code2"><span class="TPcomment">//Setup the &quot;infinite&quot; scrolling view</span>
$<span class="TPbracket">(</span><span class="TPkeyword4">document</span><span class="TPbracket">)</span>.ready<span class="TPbracket">(</span><span class="TPkeyword1">function</span><span class="TPbracket">(){</span>
    $<span class="TPbracket">(</span><span class="TPchar">'#invoices'</span><span class="TPbracket">)</span>.infiniteScroller<span class="TPbracket">({</span>
        URL<span class="TPoperator">: </span><span class="TPstring">&quot;(getInvoices)?OpenAgent&quot;</span><span class="TPoperator">,</span>
        viewName<span class="TPoperator">: </span><span class="TPstring">&quot;InvoicesByDate&quot;</span><span class="TPoperator">,</span>
        emptyArraySize<span class="TPoperator">:</span><span class="TPnumber">1</span><span class="TPoperator">,</span>
        pageSize<span class="TPoperator">: </span><span class="TPnumber">40</span><span class="TPoperator">,</span>
        itemTemplate<span class="TPoperator">: </span><span class="TPstring">&quot;&lt;tr&gt;&lt;td&gt;&lt;%=Document.Created.Formatted%&gt;&lt;td&gt;&lt;%=Title%&gt;&lt;/td&gt;&lt;td&gt;&lt;%=Customer.FullNameReversed%&gt;&lt;/td&gt;&lt;td style=\&quot;text-align: right;\&quot;&gt;&lt;%=Value.Formatted%&gt;&lt;/td&gt;&quot;</span><span class="TPoperator">+</span>
        <span class="TPstring">&quot;&lt;td&gt;&lt;span class=\&quot;label &lt;%=Status.LabelClass%&gt;\&quot;&gt;&lt;%=Status.Description%&gt;&lt;/span&gt;&lt;/td&gt;&quot;</span><span class="TPoperator">+</span>
        <span class="TPstring">&quot;&lt;td&gt;&lt;a class=\&quot;btn btn-link\&quot; href=\&quot;0/&lt;%=Document.UNID%&gt;?Open\&quot;&gt;View &amp;raquo;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&quot;</span><span class="TPoperator">,</span>
        endTemplate<span class="TPoperator">: </span><span class="TPstring">&quot;&lt;tr&gt;&lt;td colspan=\&quot;6\&quot; style=\&quot;text-align:center\&quot;&gt;&lt;strong&gt;There are no more invoices to load!&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&quot;</span>
    <span class="TPbracket">})</span>;
<span class="TPbracket">})</span>;</pre>

<p>You may recognise the .ready() bit as being jQuery's way of having the enclosed code run as soon as the document is done loading/rendered.</p>

<p>But what about that <strong>$().infiniteScroller()</strong> bit!? Well, that's a call to the jQuery plugin part that I wrote. That might sound difficult, but it's not. </p>

<p>In essence you can define a jQuery plugin, which I've done in the <a href="http://www.codestore.net/apps/invoices.nsf/js/Application-0.1.js">&quot;Application.js&quot; file</a>, like this:</p>

<pre class="code2"><span class="TPbracket">(</span><span class="TPkeyword1">function </span><span class="TPbracket">(</span>$<span class="TPbracket">) {</span>
    <span class="TPkeyword1">var </span>Scroller <span class="TPoperator">= </span><span class="TPkeyword1">function </span><span class="TPbracket">(</span>element<span class="TPoperator">, </span><span class="TPkeyword4">options</span><span class="TPbracket">) {</span>
        <span class="TPkeyword1">this</span>.$element <span class="TPoperator">= </span>$<span class="TPbracket">(</span>element<span class="TPbracket">)</span>;
        <span class="TPkeyword1">this</span>.<span class="TPkeyword4">options  </span><span class="TPoperator">= </span><span class="TPkeyword4">options</span>;

        <span class="TPcomment">//Do what you like here.</span>
        <span class="TPcomment">//Remember that this.$element always points to</span>
        <span class="TPcomment">//the HTML element we &quot;invoked&quot; infiniteScroller on!</span>

    <span class="TPbracket">}</span>

    <strong>$.fn.infiniteScroller</strong> <span class="TPoperator">= </span><span class="TPkeyword1">function </span><span class="TPbracket">(</span><span class="TPkeyword4">options</span><span class="TPbracket">) {</span>
            <span class="TPkeyword6">return </span><span class="TPkeyword1">new </span>Scroller<span class="TPbracket">(</span><span class="TPkeyword1">this</span><span class="TPoperator">, </span><span class="TPkeyword4">options</span><span class="TPbracket">)</span>;
    <span class="TPbracket">}</span>;

<span class="TPbracket">})(</span><span class="TPkeyword4">window</span>.jQuery<span class="TPbracket">)</span>;</pre>

<p>Notice the part in bold! This is where we extend jQuery's $() selector with our own method name. Simple, but so powerful.</p>

<p>Obviously I've missed out most of the <em>actual</em> code above. All that the missing code does is call an Agent via Ajax and append the resulting data as HTML &lt;tr&gt; elements on to this.$element. </p>

<p>The only tricky part is handling the auto-scrolling. To do this we bind an event handler to the windows onScroll event when the page has loaded, like so:</p>

<pre class="code2">$<span class="TPbracket">(</span><span class="TPkeyword4">window</span><span class="TPbracket">)</span>.<span class="TPkeyword2">bind</span><span class="TPbracket">(</span><span class="TPchar">'scroll'</span><span class="TPoperator">, </span><span class="TPbracket">{</span>scroller<span class="TPoperator">: </span><span class="TPkeyword1">this</span><span class="TPbracket">}</span><span class="TPoperator">, </span><span class="TPkeyword1">this</span>.scrollHandler<span class="TPbracket">)</span>;</pre>

<p>This adds the Scroller's scrollHandler method as a listener and passes a reference to the current Scroller in to the event's data. Which we can use in the scrollHandler method, like so:</p>

<pre class="code2">Scroller.<span class="TPkeyword2">prototype</span>.scrollHandler <span class="TPoperator">=  </span><span class="TPkeyword1">function</span><span class="TPbracket">(</span><span class="TPkeyword4">event</span><span class="TPbracket">){</span>
    <span class="TPkeyword1">var </span>$this <span class="TPoperator">= </span><span class="TPkeyword4">event</span>.<span class="TPkeyword4">data</span>.scroller; 
        
    <span class="TPcomment">//Are we at (or as near) the bottom?</span>
    <span class="TPkeyword1">if</span><span class="TPbracket">( </span>$<span class="TPbracket">(</span><span class="TPkeyword4">window</span><span class="TPbracket">)</span>.<span class="TPkeyword4">scrollTop</span><span class="TPbracket">() </span><span class="TPoperator">+ </span>$<span class="TPbracket">(</span><span class="TPkeyword4">window</span><span class="TPbracket">)</span>.<span class="TPkeyword5">height</span><span class="TPbracket">() </span><span class="TPoperator">&gt; </span>$<span class="TPbracket">(</span><span class="TPkeyword4">document</span><span class="TPbracket">)</span>.<span class="TPkeyword5">height</span><span class="TPbracket">() </span><span class="TPoperator">- </span>$this.<span class="TPkeyword4">options</span>.pixelBuffer <span class="TPbracket">) {</span>
        $this.currentPage<span class="TPoperator">++</span>;
        $this.loadData<span class="TPbracket">()</span>;
    <span class="TPbracket">}  </span>
<span class="TPbracket">}</span>;</pre>

<p>Fairly simple, no? It just tests to see if you're at the bottom of the page. Using the &quot;pixelBuffer&quot; option you can make it so they don't need to be right at the bottom, if, for reason, scrolling needs to happen before they get to the bottom. You could even pre-empt them getting there by loading when they're getting close to the bottom and they'd never need to wait.</p>

<h4>What About the Backend?</h4>

<p>Back on the server things are actually quite simple. Perhaps you thought that wasn't the case?</p>

<p>To get it to work the first thing I did was extend the <a href="http://www.codestore.net/store.nsf/unid/BLOG-20130204-1459">DocumentCollection wrapper class</a> I talked about a few weeks back by adding a getNth() method to it.</p>

<p>Now, all my Agent needs to do is this:</p>

<pre class="code2"><p><span class="TPkeyword1">Dim </span>factory <span class="TPkeyword1">As New </span>InvoiceFactory
<span class="TPkeyword1">Dim </span>invoices <span class="TPkeyword1">As </span>InvoiceCollection
<span class="TPkeyword1">Dim </span>invoice <span class="TPkeyword1">As </span>Invoice
        
<span class="TPkeyword1">Dim </span>start <span class="TPkeyword1">As Long</span>, count <span class="TPkeyword1">As Integer</span>, i <span class="TPkeyword1">As Integer</span>
<span class="TPkeyword1">Dim </span>View <span class="TPkeyword1">As String</span>
        
start <span class="TPoperator">= </span><span class="TPkeyword1">CLng</span><span class="TPbracket">(</span>web.query<span class="TPbracket">(</span><span class="TPstring">&quot;start&quot;</span>, <span class="TPstring">&quot;1&quot;</span><span class="TPbracket">))</span>
count <span class="TPoperator">= </span><span class="TPkeyword1">CInt</span><span class="TPbracket">(</span>web.query<span class="TPbracket">(</span><span class="TPstring">&quot;count&quot;</span>, <span class="TPstring">&quot;40&quot;</span><span class="TPbracket">))</span>
view <span class="TPoperator">= </span>web.query<span class="TPbracket">(</span><span class="TPstring">&quot;view&quot;</span>, <span class="TPstring">&quot;InvoicesByTitle&quot;</span><span class="TPbracket">)</span>
        
<span class="TPkeyword1">Set </span>invoices <span class="TPoperator">= </span>factory.GetAllInvoices<span class="TPbracket">(</span>view<span class="TPbracket">)</span>

<span class="TPcomment">'Invoice to start at</span>
<span class="TPkeyword1">Set </span>invoice <span class="TPoperator">= </span>invoices.getNth<span class="TPbracket">(</span>start<span class="TPbracket">)</span>
        
<span class="TPkeyword1">Print </span><span class="TPstring">&quot;content-type: &quot; </span><span class="TPoperator">+ </span>JSON_CONTENT_TYPE
        
<span class="TPkeyword1">Print </span><span class="TPstring">&quot;&#91;&quot;</span>
        
<span class="TPkeyword1">While Not </span>invoice <span class="TPkeyword1">Is </span><span class="TPkeyword2">Nothing </span><span class="TPkeyword1">And </span>i<span class="TPoperator">&lt;</span>count

        <span class="TPkeyword1">Print </span>invoice.AsJSON <span class="TPoperator">+ </span><span class="TPstring">|,|</span>
                
        i <span class="TPoperator">= </span>i <span class="TPoperator">+ </span><span class="TPnumber">1</span>
        <span class="TPkeyword1">Set </span>invoice <span class="TPoperator">= </span>invoices.getNext<span class="TPbracket">(</span><span class="TPbracket">)</span>
<span class="TPkeyword1">Wend</span>

<span class="TPkeyword1">Print </span><span class="TPstring">|null&#93;| </span><span class="TPcomment">'NASTY, NASTY hack to avoid dropping last , from array. Yack!</span></p></pre>

<p>Note that the &quot;web&quot; object referred to above is based on the <a href="http://www.codestore.net/store.nsf/unid/BLOG-20070815">WebSession class</a> which has been a mainstay of my Domino development for the past 5 or 6 years.</p>

<p>The more I use these new &quot;wrapper classes&quot; in LotusScript the more I kick myself for not having thought of them earlier on in my career. Although I am now using them on a daily basis and they're saving me time I keep thinking of how much time they could have saved over the years.</p>

<p>Having said that, just last week, for the first time in ages, I pressed Ctrl+N in Domino Designer and started a new Domino project from scratch (who says Notes is dead!?). The wrapper classes are an integral part of this new project.</p>

<p>Talking of the wrapper classes. I'll be updating and talking about them shortly. I've made significant changes to them and I think you'll like.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130306-0206?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130306-0206</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130306-0206</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130306-0206?Open#comments</comments>
			<slash:comments>8</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130306-0206</wfw:commentRss>
		</item>
		<item>
			<title>Infinite Scrolling Views in Domino | Blog</title>
			<pubDate>Thu, 21 Feb 2013 02:52:05 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>On Tuesday I was <a href="http://www.codestore.net/store.nsf/unid/BLOG-20130219-0406">talking about</a> whether it does a user an disservice to simply give them a selection of &quot;Form X by Field Y&quot;-type Notes views when building for the web.</p>  <p>I then (over-)promised to show some alternatives. Hopefully you're not expecting anything revolutionary, coz that they ain't. Just something different. Extra tools for the toolbox.</p>  <h4>Infinite Scrolling</h4>  <p>The first alternative is to use &quot;infinite scrolling&quot;. This is something you'll be used to if you use Twitter or Pinterest. The pages load the most recent content and, when you scroll to the end of that list, it loads more. And so on, until there's no more to load.</p>  <p>The screenshot below is of <a href="http://www.codestore.net/apps/invoices.nsf/Scroller?ReadForm&amp;view=InvoicesByDate">this Domino-based &quot;infinite scrolling&quot; view</a>. </p>  <p><a href="http://www.codestore.net/apps/invoices.nsf/Scroller?ReadForm&amp;view=InvoicesByDate"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.codestore.net/store.nsf/rsrc/86CAF65FA270335C86257B190030C6FE/$file/image_3.png" width="594" height="420" /></a></p>  <p>Combined with the fact some of the column headers are sortable/clickable it makes the &quot;Invoices by X&quot; links in the nav bar redundant, as it does page navigation.</p>  <p>What surprised me was how &quot;simple&quot; it was to implement. At first I'd gone looking for ready-built jQuery plugins to do it, but they all weighed in at +20kb and were either overkill or not particularly suited to Domino (nothing ever is!).</p>  <p>So I wrote my own jQuery plugin to do the job, using about 20 - 30 lines of code. I'll talk more next post on the code involved. For now I want to discuss the pros and cons of these types of view.</p>  <h4>Advantages</h4>  <p>Hmm. What are the advantages? I'm not really sure. They're nice, I guess. Saves the user having to press the &quot;Next Page&quot; link each time they get to the bottom. Other than that...</p>  <h4>Disadvantages</h4>  <p>Oddly, I can think of plenty of these, even though I'm suggesting they're use as an alternative to the normal way of doing things.</p>  <ul>   <li>What I find most annoying about these views is that the scrollbar jumps out of each if, like me, you use the mouse to click the scrollbar and pull it down to scroll. Once more content loads the scrollbar puller moves and you have to move your mouse back up to grab hold of it again.</li>    <li>Once the first page loads you can't use the scrollbar's size as a visual indicator to work out how much content there actually is.</li>    <li>It breaks the back button (although there are ways round that)</li>    <li>It blocks access to the page's footer area, as you can never quite get to it until all the content has loaded! In the example above, there isn't anything in the footer, but if you do have something like FAQ or Contact Us links you don't want to prevent people ever being able to scroll down to them!</li> </ul>  <p>As with everything and as I always say, it's a case of &quot;horses for courses&quot;. Hopefully when I show the code in the next post you'll see these auto-scrolling views aren't as hard to implement as you might have thought.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20130221-0252?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20130221-0252</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20130221-0252</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20130221-0252?Open#comments</comments>
			<slash:comments>13</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20130221-0252</wfw:commentRss>
		</item>


	</channel>
</rss> 
