Skip to main content
Kinetic Community

Data Viewer Sample

This library provides a simple and consistent way to build a search function into a Request service item.  The search.js file contains the main script required to preform the bridge call and data transfer.  The searchConfig.js contains some behavior features that interact with the UI and KD.search.initialize.  KD.search.initialize defines table objects or a list object and initializes them.

 
The Search function features:​
  • Execute a search of the specified Bridge or Simple Data Request using the specified qualification when given the required parameter and returns the requested attributes.

  • Multiple results can be returned as an unordered list with classes as specified by the developer or as a dataTable (see http://datatables.net/ for more information on dataTables).

  • Searches can be run on command (event) or at initialization (of the page)

  • As with dataTables, functions can be run afterinit, before, loadedcallback (After Table/List Load), done (after search is complete), noResults, clickCallback.

  • Results can be set into questions from single or list/table results.

Requirements

  • search.js file 
  • searchConfig.js file 
  • datatables-1.10.7 data tables directory 
  • datatables-responsive-plugin data tables directory 

The library files and examples are available on the Kinetic Community GitHub.

 
Sample Javascript Import requirements 
<!-- Page Javascript -->
<script type="text/javascript" src="<%=bundle.bundlePath()%>libraries/kinetic-search/js/search.js"></script>
<script type="text/javascript" charset="utf8" src="<%=bundle.bundlePath()%>libraries/datatables-1.10.7/media/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" charset="utf8" src="<%=bundle.bundlePath()%>libraries/datatables-responsive-plugin/dataTables.responsive.js"></script>
 
Sample CSS import requirements
<!-- Page Stylesheets -->
<link rel="stylesheet" type="text/css" href="<%=bundle.bundlePath()%>libraries/datatables-1.10.7/media/css/jquery.dataTables.min.css">
<link rel="stylesheet" type="text/css" href="<%=bundle.bundlePath()%>libraries/datatables-responsive-plugin/dataTables.responsive.custom.css">

After adding all of the required files to the catalog's library directory they need to be included as shown above by importing them into the service items. It is best to avoid putting the above import requirements in head.jspf, but to place them into service.jsp (or whatever you are using for the service item JSP is that you are using) instead. Included at this level, the library can then be leveraged by any service item. Then a service item that will use the search function can be made. The next steps would depend on how the search was being leveraged. Three example usages follow. These should outline enough of how to use the library to get started.

Example Usage - Bridge Single, Bridge List

The first example is an "On Behalf Of" functionality. The service item automatically, when opened, loads the logged in user as the Requested for, but gives this user the ability to say this request is for someone different by searching for a different individual. There are several components of this setup (1) the initial search on page load that completes the questions with the logged in user's information, (2) the portion of the page the user interacts with to initiate the search for the new requested for, (3) the search triggered on enter or click (or both) for the new user that will, when a user is found/selected, set that new user as the requested for instead. 

The first thing to do is build the service item so all of the questions are in place.

Example1Questions.png

 An input element will need to be added to page to support the behavior for the search for someone else and to let the user know who the request is for. In this example this is done with div elements within a Dynamic Text field.

<div id="requested_for" class="search-container">
    <div class="search-btn myself active" data-searchconfig="defaultRequestedFor">
        <div class="fa fa-check"></div>
        <div class="myself">Myself</div>
    </div>
    <div class="search-btn someoneelse" data-searchconfig="requestedForTableConfig">
        <div class="fa fa-check"></div>
        <input placeholder="someone else..." style="">
        <div class="fa fa-search" style="cursor: pointer;"></div>
        <div class="searching fa fa-spinner fa-spin fa-lg" style="display: none;"></div>
    </div>
</div>

*The code above will result in the image below with some additional css styling (not in the base kinetic request css code).  The On-Behalf-Of section ends up rendering as below.

KD-Search-img2.jpg

Now, notice the data-searchconfig set up on the div for each div in the above elements. This, as we are about to see, helps define which search is run for which element. When running searches on question events, this determination is done slightly differently, as you will see in one of the other examples.

The next step is to set up the actual searches. This code (below) can be included in the advanced header or a shared JS. In this particular type of case where the functionality is likely to be reused in many service items, a shared JS file that is loaded in the JSP is recommended. The loadPeopleSearch function would, in this case, be called in an on load event of the service item, binding the click events to the elements and running the initial search, since it is a runAtInitialization: true. 


loadPeopleSearch = function() {
    //Append Slide Panel Dive
    $( ".content-slide" ).before("<div class='search-slide'></div>");
    //Bind Events to Search Elements
    
    /*
    Bind Events to Search buttons
    The data attribute of searchconfig must be applied to each button inorder for events to properly work.
    The data attribute is used to indicate which searchConfig is used to search for values.    
    */
    //Return Keypress in input (Not clickable if 'unclickable' class is set.)
    $(document).on('keypress', '.search-container:not(.unclickable) .someoneelse input',function(e) {
        if(e.which == 13) {
            KD.search.executeSearch($(this).closest('.search-btn').data('searchconfig'));
        }
    });
    //Click on search icon (Not clickable if 'unclickable' class is set in before function.)
    $(document).on('click', '.search-container:not(.unclickable) .fa-search', function(){
        KD.search.executeSearch($(this).closest('.search-btn').data('searchconfig'));
    });
    //Click on Myself (Not clickable if 'unclickable' class is set in before function.)
    $(document).on('click', '.search-container:not(.unclickable) .search-btn:not(.active).myself', function(){
        KD.search.executeSearch($(this).closest('.search-btn').data('searchconfig'));
    });
    //Bind events to toggle active class to disable elements while search is performing.
    //Also clears out values when a either myself or someone else is clicked
    //Only buttons which do not have the active or unclickable class applied.
    //Event is bound to all search buttons
    $(document).on('click', '.search-container:not(.unclickable) .search-btn:not(.active)', function(){
        $(this).parent().find('.search-btn').toggleClass(function() {
            var searchBtn = $(this).closest('.search-btn');
            // if the searchconfig data attribute is set on the element and the button is currently active.
            if(searchBtn.data('searchconfig') && searchBtn.hasClass('active') && KD.search.searchConfig[searchBtn.data('searchconfig')]){
                //Loop through each of question elements configured in the column obj and clear it to prep for new values
                $.each(KD.search.searchConfig[searchBtn.data('searchconfig')].columns, function(i,v){
                    if(v.setQstn){
                        KD.utils.Action.setQuestionValue(v.setQstn, "");
                    }
                })
            }
            return "active";
        });
        if(!$(this).hasClass('someoneelse')){
            $('.someoneelse input').val('');
        }
    })
    //Function to toggle unclickable elements and spinning search icon
    //Used in several callbacks of searchConfig Objects
    function toggleUnclickable(o){
        $(o).children().find('.searching').toggle();
        $(o).toggleClass('unclickable');
        $(o).find('input').prop('disabled', function(i, v) { return !v; });
    }
    // Define Table objects or list Object and initialize them.
    KD.search.initialize({
        defaultRequestedFor:{
            //define if this should be run at time of initialization, false if not included
            runAtInitialization: true,
            //type: "BridgeDataTable", "BridgeList", "BridgeGetSingle" or "performSDRTable".  Determines default values to be used and behavior.
            type: "BridgeGetSingle",
            bridgeConfig:{
                model: "Employee Info",
                qualification_mapping: "Login Name",
                //Params to be created and passed to the Bridge.  VALUE MUST BE JQUERY SELECTOR.
                //The only exception to the JQuery selector rule is if there is one parameter and clientManager.userName is used
                //Currently set by js before execution.                
                parameters: {'Login Name': clientManager.userName},
                //CONFIGURE: Bridge Attributes to be returned
                //TODO: can the column configuration be leveraged to retrieve attributes?
                attributes: ["First Name","Last Name","Email","Login Name","Desk Phone", "Mobile Phone", "Office Location"],
            },
            before: function(){
                //Make the search unclickable while the search is running
                toggleUnclickable($('#requested_for'));
            },
            // After Table Load
            loadedcallback: function(){
                //Make the search clickable after search is complete
                toggleUnclickable($('#requested_for'));
            },
            //Define action to take place after Search is complete.
            done: function (){
            },
            noResults: function(){
                alert("No results Found");
                toggleUnclickable($('#requested_for'));
            },
            /*Configure the data to be returned by the Bridge and where to populate the data.
            //columns: Array of Objects.
            //data: Must match the data returned by the Simple Data Request.
            //setQstn: Question Element to be set with the selected value. 
            //Usually questions are set only when defaultRowCallback is used. 
            //This happens automatically when BridgeGetSingle is used
            */
            columns: [
                { data: 'First Name', "setQstn":"ReqFor_First Name"},
                { data: 'Last Name', "setQstn":"ReqFor_Last Name"},
                { data: 'Email', "setQstn":"ReqFor_Email"},
                { data: 'Login Name', "setQstn":"ReqFor_Login ID"},
                { data: 'Desk Phone', "setQstn":"ReqFor_OfficePhone"},
                { data: 'Mobile Phone', "setQstn":"ReqFor_MobileNumber"},
                { data: 'Office Location', "setQstn":"ReqFor_OfficeLoc"},
            ]
        },
        requestedForTableConfig:{
            //type: "BridgeDataTable", "BridgeList", "BridgeGetSingle" or "performSDRTable".  Determines default values to be used and behavior.
            type: "BridgeDataTable",
            bridgeConfig:{
                model: "Employee Info",
                qualification_mapping: "By Lower Full Name or ID",
                //Params to be created and passed to the Bridge.  VALUE MUST BE JQUERY SELECTOR.
                parameters: {'Name': '#requested_for input'},
                //CONFIGURE: Bridge Attributes to be returned
                attributes: ["First Name","Last Name","Email","Login Name","Desk Phone", "Mobile Phone", "Office Location","Street Address","Manager Name","Division","Title"],
            },
            //Where to append the table
            appendTo: $('div.search-slide'),
            //ID to give the table when creating it.
            tableId: 'requestedForTable',
            //After the Table has been created.
            afterInit: function(){ //completeCallback
            },
            before: function(){ //before search
                toggleUnclickable($('#requested_for'));
                $('#requested_for input').val($('#requested_for input').val().toLowerCase());
            },
            // After Table Load
            loadedcallback: function(){
                togglePanel(this);
            },
            //Define action to take place after SDR is complete.
            done: function (){
            },
            noResults: function(){
                alert("No results Found");
                toggleUnclickable($('#requested_for'));
            },
            //Bind a click event to tr, td, etc.
            clickCallback: function(){
                //set up function for click event on select icon
                $('table').on( "click", 'tr td.select.requestedForTableConfig', {value:this}, function(event){
                    defaultRowCallback(this);  //use default fow callback
                    //then display full name in requested for input field
                    $('#requested_for input').val(KD.utils.Action.getQuestionValue('ReqFor_First Name')+ ' ' + KD.utils.Action.getQuestionValue('ReqFor_Last Name'));
                    toggleUnclickable($('#requested_for'));   //allow search function
                    togglePanel(event.data.value);   //close search table
                })
            },
            /*Configure the data to be displayed in the table and set into Question Values
            //This is a modified object used by Datatables.net.  setQstn has been added to it.
            //columns: Array of Objects.
            //data: Must match the data returned by the Simple Data Request or Bridge.
            //title: Display name used in the table header.
            //setQstn: Question Element to be set with the selected value. (Only when defaultRowCallback is used)
            //className: Used with DataTables Responsive Plugin.
            //date: true or false. Should be true if the data returned is a date or date/time value. default is false if not included
            //moment: format for date (required if date is included) Uses formats for http://momentjs.com/docs/, ex. "M/D/YYYY"
            //width: number of pixels, can be used to specify column width. Column will auto fit if not included
            //className: classes to add to table column/list item
            */
            columns: [
                { data: 'select', "title":"SELECT",    orderable: false, width: 60,            className: "requestedForTableConfig select" },
                { data: 'Login Name', "title":"LOGIN ID", "setQstn":"ReqFor_Login ID",        className: "control" },
                { data: 'First Name', "title":"FIRST", "setQstn":"ReqFor_First Name",        className: "min-tablet control-additional" },
                { data: 'Last Name', "title":"LAST", "setQstn":"ReqFor_Last Name",            className: "min-tablet control-additional" },
                { data: 'Division', "title":"DIVISION",             className: "min-tablet control-additional" },
                { data: 'Title', "title":"TITLE",             className: "min-tablet control-additional" },
                    
                { data: 'Desk Phone', "title":"OFFICE PHONE", "setQstn":"ReqFor_OfficePhone",    className: "none" },
                { data: 'Mobile Phone', "title":"MOBILE PHONE", "setQstn":"ReqFor_MobileNumber",    className: "none" },
                { data: 'Email', "title":"EMAIL", "setQstn":"ReqFor_Email",                    className: "none" },
                { data: 'Street Address', "title":"STREET ADDRESS",                 className: "none" },
                { data: 'Office Location', "title":"OFFICE LOCATION", "setQstn":"ReqFor_OfficeLoc",    className: "none" },
                { data: 'Manager Name', "title":"MANAGER NAME",     className: "none" }
            ]
        },
        
        
    });
}

 This used the requestedForTableConfig object. Do not be concerned if this table does not match your color scheme. etc. The style of the resulting table is entirely dependent on the theme. It is not provided by the library. The resulting customer experience for the styles in the theme used in this particular example look like this. When the page is initially loaded the customer, Don Demo would see this:

File:10_Kinetic_Request/Resources/Libraries/KD.search.searchConfig/Example1PageLoad.png

And then he can choose to search for "Peter":

Example1ToSearch.png

And he would see this table, where he can expand the individual he is interested in (in this case, one of the Brian Petersons) to make sure that he is choosing the right person, and then select that row.

File:10_Kinetic_Request/Resources/Libraries/KD.search.searchConfig/Example1SearchResults.png

Which would make the service item look like this:

Example1Searched.png

Example Usage - Simple Data Request Table

The second example leverages questions, rather than additional elements inside a text element, and KD events, rather than jQuery events, to trigger a search on a Last Name to fill out Requested for information. Note that questions and events like this can be used in any scenario (not just the On-Behalf-Of scenario like these), you do not have to use a Simple Data Request if you are using questions. This example just happens to use them together.

In this example, the components that need to be set up are essentially the same. The steps are (1) create the questions to receive the data, (2) create the questions to do the search (in this case they are the same), (3) set up the code for the table (the load search function), and (4) set up the necessary supporting events. In this particular case, the questions are the same set as in the example above:

SDRSearchQuestions.png

But this time instead of a text element and jQuery events to do the search, an event on Requested For Last Name that will trigger the search.

SDRSearchEvent1.png

Where the custom code in the event is:


  var lastName = KD.utils.Action.getQuestionValue('ReqFor_Last Name');
  var lastNameElm = KD.utils.Util.getElementObject('ReqFor_Last Name');
  var lastNameId = null;
  if (lastNameElm && lastNameElm.id) {
    lastNameId = lastNameElm.id.substr(7);
    var sdrparams = '' + lastNameId + '=' + lastName;    
    CallLastNameSDR( clientAction.actionId,sdrparams,'Search by Last Name');
    KD.search.executeSearch('requestedForSDRTableConfig');
  }

And the simple data request looks up against the people source for the system by last name. Notice the direct call to KD.search.executeSearch('requestedForSDRTableConfig'); rather than something like KD.search.executeSearch($(this).closest('.search-btn').data('searchconfig')); This is because, from this event we will only and always be directly calling this search.

We do still, however, need to define 'requestedForSDRTableConfig'. This can be done in the advanced header. 

function CallLastNameSDR(ActionID,Params,Name) {

KD.search.initialize({
        requestedForSDRTableConfig:{
            //type: "BridgeDataTable" or "BridgeList".  Determines default values to be used and behavior.
            type: "performSDRTable",
            sdrConfig:{
                SDRId: ActionID,
                params: Params,
                sdrName: Name
            },
            //Where to append the table
            appendTo: $('div.search-slide'),
            //ID to give the table when creating it.
            tableId: 'requestedForTable',
            //After the Table has been created.
            afterInit: function(){ //completeCallback
            },
            before: function(){ //before search
                toggleUnclickable($('#requested_for'));
                $('#requested_for input').val($('#requested_for input').val().toLowerCase());
            },
            // After Table Load
            loadedcallback: function(){
                togglePanel(this);
            },
            //Define action to take place after SDR is complete.
            done: function (){
            },
            noResults: function(){
                alert('No results Found');
                toggleUnclickable($('#requested_for'));
            },
            //Bind a click event to tr, td, etc.
            clickCallback: function(){
                $('table').on( 'click', 'tr td.select.requestedForTableConfig', {value:this}, function(event){
                    defaultRowCallback(this);
                    $('#requested_for input').val(KD.utils.Action.getQuestionValue('ReqFor_First Name')+ ' ' + KD.utils.Action.getQuestionValue('ReqFor_Last Name'));
                    toggleUnclickable($('#requested_for'));
                    togglePanel(event.data.value);
                })
            },
            /*Configure the data to be displayed in the table and set into Question Values
                        */
            columns: [
                { data: 'select', "title":"SELECT",    orderable: false, width: 60,            className: "requestedForTableConfig select" },
                { data: 'AR Login', "title":"LOGIN ID", "setQstn":"ReqFor_Login ID",        className: "control" },
                { data: 'First Name', "title":"FIRST", "setQstn":"ReqFor_First Name",        className: "min-tablet control-additional" },
                { data: 'Last Name', "title":"LAST", "setQstn":"ReqFor_Last Name",            className: "min-tablet control-additional" },
                { data: 'Department', "title":"DEPT",             className: "min-tablet control-additional" },
                    
                { data: 'Phone Number', "title":"OFFICE PHONE", "setQstn":"ReqFor_OfficePhone",    className: "none" },
                { data: 'Email', "title":"EMAIL", "setQstn":"ReqFor_Email",                    className: "none" },
                { data: 'AddrLine1', "title":"STREET ADDRESS",                 className: "none" },
                { data: 'Create Date', "title":"Created On", "setQstn":"ReqFor_OfficeLoc",    className: "none" },
                { data: 'Supervisor Name', "title":"MANAGER NAME",     className: "none" }
            ]
        }
        });
}

Because SDR searches need the Action ID from the SDR, they need to call the function with the action ID each time, not just the KD.search.executeSearch('requestedForSDRTableConfig');

So, for this particular example, in this particular theme, the customer experience would be like this:

The user would put in at least part of the Requested for Last Name in said field and press enter.

SDRSearchToSearch.png

Then the user would select from the table, expanding  the individual he is interested in (in this case, one of the Brian Petersons) to make sure that he is choosing the right person, and then select that row.

SDRSearchResults.png

Then the page looks as follows.

SDRSearched.png

Example Usage - Bridge Request List

The last example for this article will be how to use a bridge request to display the results as a list rather than as a table.  This is very similar to the first listed example, except in type of the search, which is "BridgeList" and in the resulting formatting of the display, which will be an unordered list. This unordered list can then use classes and styles to be formated however necessary. The example described here will be a user search where the customer experience looks like this:

ListSearch.png

This is initially working off of a question, which brings up another div (a search container), so the search has to be added to the question layer. This was done with the following code.

// Add Business Tester Person Search to question layer
        $('div.questionLayer[label="<wbr/>Business Tester"]').append(
                   $('<div>').addClass("col-sm-<wbr/>12").append(
                $('<a>').attr({href:"<wbr/>javascript:void(0);", id:"change-business-tester"}).<wbr/>addClass("color-secondary").<wbr/>text("Add User"),
                $('<a>').attr({href:"<wbr/>javascript:void(0);", id:"clear-business-tester"}).<wbr/>addClass("color-secondary pull-right").text("Clear").on(<wbr/>'click',function(){
                    KD.utils.Util.<wbr/>setQuestionValue('Business Tester','');
                    KD.utils.Util.<wbr/>setQuestionValue('Business Tester Name','');
                    KD.utils.Util.<wbr/>setQuestionValue('Business Tester Id','');
                    KD.utils.Util.<wbr/>setQuestionValue('Business Tester Id Temp','');
                }),
                $('<div>').addClass("input-<wbr/>group search-container col-sm-12").attr("style","<wbr/>display:none;").append(
                    $('<input>').attr({id:"<wbr/>business_tester",name:"<wbr/>business_tester",placeholder:"<wbr/>Search for..."}).addClass("form-<wbr/>control"),
                    $('<span>').addClass("input-<wbr/>group-btn").append(
                        $('<button>').addClass("btn btn-secondary business_tester").attr({"data-<wbr/>searchconfig":"<wbr/>businessTesterTableConfig"}).<wbr/>append(
                            $('<i>').addClass("fa fa-search")
                        )
                    )
                ),
                $('<div>').addClass('col-sm-<wbr/>12').attr('id','<wbr/>businessTesterTableDiv')
            )
        );

The call to the library and other set up was then included in a file on the server. This file also included other common search definitions, like requested for, which have been stripped out for clarity of viewing--to make it clear what code belongs to this specific example. Check the comments for details.

if (typeof KINETIC == "undefined") {
    KINETIC = {};
}
if (typeof KINETIC.serviceitems == "undefined") {
    KINETIC.serviceitems = {};
}
if (! KINETIC.serviceitems.Helper){
    KINETIC.serviceitems.Helper = new function(){
        this.clearSearch = function(tableConfig) {
            if (KD.search.searchConfig[tableConfig]) {
                $.each(KD.search.searchConfig[tableConfig].columns, function(i,v){
                    if(v.setQstn){
                        KD.utils.Action.setQuestionValue(v.setQstn, "");
                    }
                });
            }
        };
        
        this.loadSearch = function(evt, form) {
            if (clientManager.submitType === "ReviewRequest") { return false; }
            
            //Append Slide Panel Dive
            $( "header" ).before(
                $('<div>').addClass('search-slide background-white container').css({
                    'display': 'none',
                    'position': 'absolute',
                    'top': 0,
                    'bottom': 0,
                    'left': 0,
                    'height': '100%',
                    'width': '100%',
                    'z-index': '1000'
                })
            )
            //Bind Events to Search Elements
            
            // Bind change user
            $('body').on('click', 'a#change-business-tester', function(){
                KINETIC.serviceitems.Helper.clearSearch('businessTesterTableConfig');
                $(this).siblings('div.search-container').show();
                $(this).hide();
            });

            /*
            Bind Events to Search buttons
            The data attribute of searchconfig must be applied to each button inorder for events to properly work.
            The data attribute is used to indicate which searchConfig is used to search for values.    
            */
            //Return Keypress in input (Not clickable if 'unclickable' class is set.)
            $('#business_tester').on('keypress',function(e) {
                if(e.which == 13) {
                    e.preventDefault();
                    //$('button.business_tester').trigger('click');
                    KD.search.executeSearch($(this).siblings('span').children('button').attr('data-searchconfig'));
                    e.stopPropagation();
                }
            });
            
            
            //Click on search icon (Not clickable if 'unclickable' class is set in before function.)
            $('button.business_tester').on('click', function(e){
                e.preventDefault();
                KD.search.executeSearch($(this).attr('data-searchconfig'));
            });
            
            //Function to toggle unclickable elements and spinning search icon
            //Used in several callbacks of searchConfig Objects
            function toggleUnclickable(o){
                $(o).toggleClass('loading');
                $(o).toggleClass('unclickable');
                $(o).children('input').prop('disabled', function(i, v) { return !v; });
            }
            // Define Table objects or list Object and initialize them.
            KD.search.initialize({
                
                businessTesterTableConfig:{
                    //type. Determines default values to be used and behavior.
                    type: "BridgeList",
                    bridgeConfig:{
                        templateId: BUNDLE.config.commonTemplateId,
                        model: "Person",
                        qualification_mapping: "By Full Name",
                        //Params to be created and passed to the Bridge.  VALUE MUST BE JQUERY SELECTOR.
                        //parameters: {'Email': 'input#requested_for','First Name': 'input#requested_for','Last Name': 'input#requested_for'},
                        parameters: {'Full Name': 'input#business_tester'},
                        //Add sort meta because we never know if there is an unlimited length field which will throw and error
                        matadata: {'order': [encodeURIComponent('<%=attribute["Last Name"]%>:ASC'),encodeURIComponent('<%=attribute["First Name"]%>:ASC'),encodeURIComponent('<%=attribute["Email"]%>:ASC')]},
                        //CONFIGURE: Bridge Attributes to be returned
                        attributes: ["First Name","Last Name", "Full Name", "Email","Login Id","Work Phone Number", "Department", "Office Location", "Site Country"],
                    },
                    //Where to append the table
                    appendTo: $('div#businessTesterTableDiv'),
                    //ID to give the table when creating it.
                    tableId: 'businessTesterTable',
                    //After the Table has been created.
                    afterInit: function(){ //completeCallback
                    },
                    before: function(){ //before search
                        toggleUnclickable($('#business_tester').closest('div.input-group.search-container'));
                        $('div#businessTesterTableDiv').show();
                    },
                    // After Table Load
                    loadedcallback: function(){
                        //togglePanel(this);
                    },
                    //Define action to take place after SDR is complete.
                    done: function (){
                    },
                    noResults: function(){
                        alert(BUNDLE.localize("No results found"));
                        toggleUnclickable($('#business_tester').closest('div.input-group.search-container'));
                    },
                    //Bind a click event to tr, td, etc.
                    clickCallback: function(){
                        this.appendTo.on( "click", 'li', {value:this}, function(event){
                            defaultListCallback(this);
                            var testers = KD.utils.Action.getQuestionValue('Business Tester').split(",").filter(function(v){return v!==""});
                            testers.push(KD.utils.Action.getQuestionValue('Business Tester Name'));
                            KD.utils.Action.setQuestionValue('Business Tester', testers.join(","));
                            var ids = KD.utils.Action.getQuestionValue('Business Tester Id').split(",").filter(function(v){return v!==""});
                            ids.push(KD.utils.Action.getQuestionValue('Business Tester Id Temp'));
                            KD.utils.Action.setQuestionValue('Business Tester Id', ids.join(","));
                            toggleUnclickable($('#business_tester').closest('div.input-group.search-container'));
                            //togglePanel(event.data.value);
                            $('#business_tester').closest('div.search-container').hide();
                            $('a#change-business-tester').show();
                            $('input#business_tester').val("");
                        })
                    },
                    /*Configure the data to be displayed in the table and set into Question Values
                    //This is a modified object used by Datatables.net.  setQstn has been added to it.
                    //columns: Array of Objects.
                    //data: Must match the data returned by the Simple Data Request.
                    //title: Display name used in the table header.
                    //setQstn: Question Element to be set with the selected value. (Only when defaultRowCallback is used)
                    //className: Used with DataTables Responsive Plugin.
                    */
                    columns: [
                        { data: 'Login Id', "setQstn":"Business Tester Id Temp",    className: "hidden", visible: false },
                        { data: 'Full Name', "setQstn":"Business Tester Name", className: "col-sm-12" },
                        { data: 'Email', className: "col-sm-12" }
                    ]
                },
            });
        }
    }
}
YAHOO.util.Event.addListener(window, "load", KINETIC.serviceitems.Helper.loadSearch, document.getElementById("pageQuestionsForm"), true);