Wednesday, May 29, 2013

Performing a case-insensitive search on a tree in apex

Someone mailed me recently about my previous posting about the apex tree, wondering how to implement a case-insensitive search. Indeed, the default search behaviour is case-sensitive. You might know how to do some toUppercase() magics, but how and where would you implement this?
I thought this question deserved some spotlight, so here we are. I do want to point out that my solution here is specific to the tree and the methods I used in my previous post. Other than that, it is still useful and can be used outside the context of the tree too.
Now, basically when you call the search function of jsTree, it will perform this search on the titles of the nodes in the tree with a jQuery pseudo-selector. By default this is the ":contains" selector, which will look for an occurence of a given string in the element. And this is not case insensitive.
Wait, pseudo-what? Don't fret, you've most likely encountered one of these beasts already. Some examples: ":first-child",":last-child",":hover",":visited",":enabled",... You can find some more info on them here (http://css-tricks.com/pseudo-class-selectors/) or just google them!
When you call the search on the tree, I have simply used search('somestring'). "search" however has another parameter!
search(needle [, compare_function])
Don't be deceived however by the name of the parameter. You do not actually pass on a function. The docs say this:

Optional argument. The jQuery function to be used for comparing titles to the string - defaults to "contains".

What is actually used is not a function per se, but a jQuery pseudo class selector. That is great actually. Pseudo classes can be created to extend the basic functionality of jQuery after all! This allows us to create a pseudo-class which performs the compare we require, and then pass this along to the tree search function.
You can find excellent documentation on how to create a pseudo-class by googling it of course, but this document helped me a lot: http://www.jameswiseman.com/blog/2010/04/19/creating-a-jquery-custom-selector/
I added the below code to my "treeOnload" function, so that jQuery is extended at the correct time. This means that once the document has finished loading, jQuery will be extended, and the custom selector will be available.
$.extend($.expr[':'], {
    ciContains: function(elem, i, match) {
        return $(elem).text().toUpperCase().indexOf(match[3].toUpperCase()) >= 0;
    }
});
In short, this will create a new pseudo-class "ciContains", which will look for the occurence of a given string in the element text while ignoring case. "match[3]" is the input text if you're wondering.
This selector can now be used anywhere on this page! For example, search for "tEsT" in each "td" element:
$("td:ciContains('tEsT')")
To get the search in the tree to work case-insensitive simply add "ciContains" as the second parameter to the search function.
For example:
$.tree.reference(l$Tree).search("d","ciContains");
I also built this feature into my tree demo page. By using the "case sensitive?" checkbox you can search either case sensitive or insensitive. One caveat though is using the same search string with different sensitivity. For example, searching the tree for "oliv" with no case sensitivity will highlight the "OLIVIA" node. Toggling the checkbox and searching again so that the search would be case sensitive will still highlight the "OLIVIA" node! This is because of how jstree performs a check on the provided search value. If the value is identical to the previous value, the already highlighted nodes will remain highlighted!

Tree demo page