Wednesday, August 29, 2007

Ajax-like live-search in the client

This one was harder than I initially had hoped, and it is still just a proof of concept at best.






>> Download Demo Application

I built this demo on top of Jake's Fake Names application. The "autocomplete" performs a DbLookup based on the value of the field the user types in. Then a HTML (Pass Through) table with links is created from the result. Since the Notes Client is sooo dynamic when it comes to HTML, I just have to restart the universe reload the frameset to refresh the generated table of results.

I couldn't get this to work in a useful manner when not in a frameset, because I can't figure out how to set the pointer to the end of the query-field when not in a frameset. This results in the pointer always going at the start of the query-field when "reloading" the form.

E.g.:
User types: abb
Field value: abb
Refresh/lookup
User types: ie
Field value: ieabb

If you're downloading the demo:
All elements modified/created for the demo contains "DontPanic Demo" in the comments-column.

The first lookup may take some time, as the view-index has to be created in the lookup-view.

If you want to try out fixing the autocomplete when not in a frameset, I've made a non-frameset form, "AutoComplete". If you find a way to set the pointer at the end of the query-field, please let me know how, and I will fix the demo-application.

I spent WAY(!) more time than planned to make this demo (work), so please don't hate me for not cleaning up unused code, documenting code and design elements, etc.

The file-size of the demo-application is 6.66 MB according to Windows Explorer, so beware modem users and superstitious people!

Update: I originally called this post "Ajax-like Autocomplete in the client", but the functionality is most like live-search. Sorry about that.

Tuesday, August 28, 2007

Highlighting fields on "Entering" with CSS in the Notes Client

Since I have vacation now, I have more time to try out different stuff i've been wondering about.

This is one of those things.






>> Download Demo Application

For some months now, I've wanted to look at possibilities of dynamic usage of CSS in the Notes Client. It is somewhat possible, but you have to close/reopen the document you're working on (+mess with SaveOptions), and you need a subform/stylesheet per state.

Unless someone finds out how to set the id of an element dynamically, this example, with one state per field will probably require too many design-elements to be put into a real-life system.

What I have to do for this demo, as previously stated, is to have one CSS-file/subform per field (since each field-focus is a different state) in the form. This because I only want to highlight the field when it has focus. To do this without a dynamic parent class or id, seems impossible.

Inline CSS for Notes WYSIWYG tables doesn't work. Putting a Pass-Through-HTML element around Notes WYSIWYG elemements doesn't work. With WYSIWYG, I mean Create->Field/Table/Button/etc.

It seems Notes first renders one, then the other. E.g. first render P-T-HTML, then WYSIWYG. This makes it impossible to wrap WYSIWYG elements with other elements with dynamic id's and classes.

My example:


The name-field has focus -> load css for field1 (computed subform containing resource=Environment( "fieldName" ).

Every CSS-file has this structure:
#nameOfField{
/* where #nameOfField is a TD that contains the input element */
background-color: #ffd;
/*
Strange that Notes has different shorthand order than the W3C spec.
Notes, border: color style width;
W3C, border: width style color;
*/
border: #555 solid 1px;
}

What I want:


field1 gets focus. Set environment variable. Containing element of a part of the form has computed "class=\"" + @Environment("name") + "\"".

In form:
<div class=<computed value>>
##WYSIWYG BLOCK##
</div>

In CSS:

.field1 #field1 {/* CSS for td containing field1, when it has focus */}
.field2 #field2 {/* CSS for td containing field2, when it has focus */}


With a dynamically named parent element, I should be able to put all CSS into one file.

If any of you find out more elegant ways of doing this particular functionality, please let me know.

Good CSS-in-Notes resource I stumbled upon (german):
Description of possibilities/limitations of CSS in the Notes-Client

Partially translated by Google (I don't speak german)

Sunday, August 12, 2007

Sexifying the client with DHTML

I don't know why, but I've been really motivated program/test out different ideas this weekend. This is another product of my brainstorming.






Full size

I tried putting this video up on Google Video, but it got so compressed that it was impossible to see what was going on. This means that the front page currently is about 3-5MB heavy.. Sorry..

As I've stated a couple of times before, I'm not an expert programmer, and I'm quite lazy. Because of that, my demos are never production ready (stability, speed, etc). This demo is FAR from production ready. It is a proof of concept.

What I do in the demo is passing javascript commands ( e.g. javascript:doStuff() ) to a web-document inside a "Microsoft Web Browser" control. The commands are coded per field, "entering".

I use mootools' Fx.Scroll class to get the smooth scrolling.

Current issues with the OLE object:

  • I get OLE Automation Error when I try to resize the control (works with the debugger on)

  • Passing browser.navigate( url ), where browser is the browser object variable, is slow when not running on localhost (I only tried on one external server)

Maybe there is a faster way/more efficient object (mozilla, webkit, etc) to use.

To run the demo yourself (only tested in Windows XP):

  1. Download the Demo-db zip file

  2. Extract the db onto your hard-drive, and copy it to where you want to run it from

  3. Alter the postopen (in the member form) url to fit with the location you put the db. Make sure the HTTP-task is running

  4. Open the member form in the Client


Let me know if it doesn't work..

Fun stuff with the FieldListener class

BIIG flash.. Sorry, but don't know how to get it smaller.







In the demo above, I have a form with a "Microsoft Web Browser" object. If the "search" field contains the words wiki, amazon or amazonuk, I use the searchengine at the site to search for the rest of the parameters.

The search-sub:

Sub search
Dim ws As New NotesUIWorkspace
Dim arrAction As Variant
Dim sstr As String, baseURL As String

arrAction = Split(ws.CurrentDocument.FieldGetText( "search" ), " ")


Dim browser As Variant
Set browser = ws.CurrentDocument.GetObject("Microsoft Web Browser")
If browser.width <> 950 Then
browser.width = 950
browser.height = 700
End If

sstr = Replace( Implode(arrAction, "+"), arrAction(0)+"+", "")
Select Case Lcase( arrAction(0) )
Case "wiki"
baseURL = "http://en.wikipedia.org/wiki/Special:Search?search="

Case "amazonuk"
baseURL = "http://www.amazon.co.uk/s/026-5318277-1707602?&field-keywords="

Case "amazon"
baseURL = "http://www.amazon.com/s/026-5318277-1707602?&field-keywords="

Case Else
baseURL = arrAction(0)
sstr = ""
End Select

Call browser.navigate( baseurl + sstr )
End Sub

Saturday, August 11, 2007

FieldListener class for NotesUI - search on enter, etc.

To redeem myself from my previous SNTT post, I did a little brainstorming, to find something worthy for SNTT. I hope this one is..

A while back Chris Blatnick posted a method to listen for 'Enter', and search based on JavaScript in the Notes client.

Lotus did a bad implementation of setInterval in the Notes client (several actions by the user can cause the interval to run "forever" -> high CPU usage). Because of that, the method is probably not an option for most people.

The FieldListener class I made for this demo, uses the NotesTimer, and On Event Alarm to do the same. Since you're working with LS, you can add whatever functionality you want in response to something the user writes.

The downside with NotesTimer is that you can only specify whole seconds (Integer) as the intervals. Worst-case scenario, there is a delay of one second between a phrase being typed and the action execution.

Demo (the button calls the same method as the fieldListener, onEnter):






To implement the FieldListener-object on a form:
Globals in the form:

Option Public
Use "FieldListener"


Declarations:

Dim fieldListenerObject As FieldListener


In the QueryOpen event:

Sub Queryopen(Source As Notesuidocument, Mode As Integer,_
Isnewdoc As Variant, Continue As Variant)
Dim interval As Integer
interval = 1
Set fieldListenerObject = New FieldListener( Source, "fieldName",_
"substring(Chr(10) without quotes=enter)", interval, "actionToExecute" )
End Sub


I'm running two paralell "listeners" on one field in the demo, one for "johnny", and one for Chr(10) (Enter).

Download the application to see the implementation of the demo. Johnny can speak, but my screen capture software (free) cannot listen (there is a setting for it, but it doesn't work on my machine).

Friday, August 10, 2007

Strange bug(?) - Pass Me as a parameter works...sometimes

When I try to pass Me (As Variant) into a method that takes one parameter (getHĂ„ndbok in the pic), it works, when I try to pass Me into a sub that takes three parameters(getUtsnitt in the pic), this happens:
Error

I have to create a variant and assign Me to it to make it work (replace Me with obj). Bug?

The Subs being called:



Thursday, August 9, 2007

Updating the posts containing code-snippets

I'll be going through the blog, and use Joe Litton's online "Format Your LotusScript" (which is made from Julian Robichaux' ls2html) to convert the LS code snippets from flat text to HTML with the "correct" colors.

I've thought about it for some time, but never got around to it. A colleague of mine pointed out the bad readability (real word?) of the code (to which I responded that he should go get his eyes checked).

Guess it's about time.

Wednesday, August 8, 2007

Dynamic LS-methods - Allow fluid number of/types of parameters

The easier / better way, use Lists... :)

Thanks Dwight and Sean.


My example is quite simple, but with some data-type testing/object testing and decent error-handling, I think you could expand this "idea" into more advanced Subs/Functions.

Instead of setting the method to accept a fixed amount of parameters, set it to accept one object. In my example, I use a Class that allows you to add different objects (through a variant-array).

There is (at least) one weakness with this approach. You can't add arrays to the Argument-object, as (from what I've read) an array can't contain another array. If it's a string array, you could implode the array, add a symbol at the front of the string to identify it as an array, and split it in the receiving function.

Screenshots of test-agent:
Dim s As New NotesSession
Dim args As New Arguments

Dim doc As NotesDocument
Set doc = s.CurrentDatabase.CreateDocument
doc.subject = "Some mostly harmless planet"

Call args.add( "Tommy Valand" )
Call argumentsAsObject( args )


Call args.add( doc )
Call argumentsAsObject( args )


Call args.add( "Always pack your towel!" )
Call argumentsAsObject( args )


The Arguments-class
The argumentsAsObject-sub
The test-agent
Code in a text-file

The Arguments class:

'argsdemo scriptlib
Class Arguments
Private counter As Integer
Private args() As Variant

Public Sub add( var As Variant)
Redim Preserve args( counter )
If Isobject ( var ) Then
Set args( counter ) = var
Else
args( counter ) = var
End If

counter = counter + 1
End Sub

Public Function getNth( nth As Integer ) As Variant

If Isobject ( args(nth) ) Then
Set getNth = args(nth)
Else
getNth = args(nth)
End If
End Function

Public Function getAll As Variant
getAll = args
End Function

Public Function length As Integer
length = Ubound( args ) + 1
End Function
End Class


Sub that take one parameter (As Arguments):

'accept an Arguments-object
Sub argumentsAsObject( args As Arguments )
Select Case args.length
Case 1
Msgbox "Name: " + args.getNth(0)

Case 2
Msgbox "Name: " + args.getNth(0) + Chr(13) +_
"Address: " + args.getNth(1).subject(0)

Case Else
Dim strTemp As String
Dim counter As Integer
Forall item In args.getAll
'35 - Product object (I know it's a NotesDocument)
If Datatype( item ) = 35 Then
strTemp = strTemp + "Item at index " +_
Cstr( counter ) + ": doc[" + item.subject(0) + "]" + Chr(13)
Else
strTemp = strTemp + "Item at index " +_
Cstr( counter ) + ": " + item + Chr(13)
End If

counter = counter + 1
End Forall
Msgbox strTemp
End Select
End Sub


Full code for the test-agent:

'options
Option Public
Option Declare
Use "argsdemo"

Sub Initialize
Dim s As New NotesSession
Dim args As New Arguments

Dim doc As NotesDocument
Set doc = s.CurrentDatabase.CreateDocument
doc.subject = "Some mostly harmless planet"

Call args.add( "Tommy Valand" )
Call argumentsAsObject( args )

Call args.add( doc )
Call argumentsAsObject( args )

Call args.add( "Always pack your towel!" )
Call argumentsAsObject( args )
End Sub


If you have an easier way to allow this kind of flexibility please let me know.