Skip to content

Sample Functions

This page is a collection of sample javascript functions that could be used in your Leap applications.

Note: these functions were collected from the community and you may need to modify them when implementing in your Leap application.

Date Field Functions

Add hours to timestamp

Add number of hours to a timestamp

Function:

// dateObj1 - The date field (i.e. BO.F_Date)
// numOfHours - The number of hours to add
app.getSharedData().addHoursToDate = function(dateObj1, numOfHours) {
    var d1 = dateObj1.getValue(); //get first date

    var d1_milli = d1.getTime();
    return new Date(Math.ceil(d1_milli + (1000 * 60 * 60 * numOfHours)));
}

Add days to date

Add number of days to a date.

Usage:

Place the function in the Settings...Events...Custom actions. In the field where you enter the date or the number of days to add you place:

app.getSharedData().addDaysToDate(BO.F_Date, 90);

You can either hard-code the number of days to add or have the value come from a field.

Function:

// dateObj1 - The date field (i.e. BO.F_Date)
// numOfDays - The number of days to add
app.getSharedData().addDaysToDate = function(dateObj1, numOfDays) {
    var d1 = dateObj1.getValue(); //get first date

    var d1_milli = d1.getTime();
    return new Date(Math.ceil(d1_milli + (1000 * 60 * 60 * 24 * numOfDays)));
}

Add working days to date

This function will return a new date with the number of specified working days added, it will not include weekends when determining the final date. You will need to set that returned date into your desired field.

A n additional function was added to determine if a day was a registered holiday.

Usage:

Place the function in the Settings...Events...Custom actions. In the field where you enter the date or the number of days to add.

You can either hard-code the number of days to add:

app.getSharedData().addWorkingDaysToDate(BO.F_Date, 90);

or have the value come from a field.

app.getSharedData().addWorkingDaysToDate(BO.F_Date, BO.F_Number.getValue());

To set another field with the result:

BO.F_newDate.setValue(app.getSharedData().addWorkingDaysToDate(BO.F_Date, 90));

Function:

app.getSharedData().isHoliday = function(d) {
  var r = false;
  var holidays = ["01-01","02-18","04-19","05-20","08-05","09-02"]; //set your holidays

  //check if any of the days is a holiday
  for(var holiday in holidays) {
    var h = new Date(get(holidays, holiday)+"-2020");

    if(d.getMonth() == h.getMonth() && d.getDay() == h.getDay()) {
      r = true;
    }
  }
  return r;
}

// dateObj1 - The date field (i.e. BO.F_Date)
// numOfDays - The number of working days to add
app.getSharedData().addWorkingDaysToDate = function(dateObj1, numOfDays) {

  var sd = dateObj1.getValue(); //get first date
  var ed = dateObj1.getValue();
  var counter = numOfDays;
  var millisecondsPerDay = 86400 * 1000;
  while (counter > 0) {
    ed = new Date(Math.ceil(ed.getTime() + millisecondsPerDay));
    var theDay = ed.getDay();

    // don't count sat and sunday and holidays
    if(theDay !== 0 && theDay !==6 && !app.getSharedData().isHoliday(ed)) {
        counter--;
    }   
  }

  return ed;   
}

Calculate age from birth date

Calculates the age from a specified birth date.

Usage:

Place function in Settings...Events...Custom Actions

BO.F_Number.setValue(app.getSharedData().getAgeFromBirthDate(BOA.getValue(), true)); 
BO.F_SingleLine.setValue(app.getSharedData().getAgeFromBirthDate(BOA.getValue(), false));

Function:

// birthDay - the date to calculate the age from
app.getSharedData().getAgeFromBirthDate = function(birthDay) {   
  // 1557600000 is 24 * 3600 * 365.25 * 1000 Which is the length of a year
  // the length of a year is 365 days and 6 hours which is 0.25 day.
  // double tilde converts float to integer
  return ~~((Date.now() - birthDay) / (31557600000));
}

Calculate working days between days

Returns the number of calendar days between the two dates specified. The function expects to receive date objects, not strings.

Usage:

Copy the function to the Settings...Events...onStart section of your application.

const holidays = ["01-01-2024","02-18-2024","04-19-2024","05-20-2024","08-05-2024","09-02-2024", "12-25-2024"];
app.getSharedData().includesHoliday = function (startDt, endDt, holidays) {

  var r = false;

  for(var holiday in holidays) {
    var h = new Date(get(holidays, holiday));

    if(h.getTime() >= startDt.getTime() && h.getTime() <= endDt.getTime()) {
      r = true;
    }
  }
  return r;
}

// includeWeekends - Pass true if you want to include weekends otherwise false.
app.getSharedData().workingDaysBetweenDates = function(startDt, endDt, includeWeekends) {
  // Validate input
  if (endDt < startDt)
    return 0;

  // Calculate days between dates
  var millisecondsPerDay = 86400 * 1000; // Day in milliseconds
  startDt.setHours(0,0,0,1);  // Start just after midnight
  endDt.setHours(23,59,59,999);  // End just before midnight
  var diff = endDt.getTime() - startDt.getTime();  // Milliseconds between datetime objects    
  var days = Math.ceil(diff / millisecondsPerDay);   

  if(!includeWeekends) {
    // Subtract two weekend days for every week in between
    var weeks = Math.floor(days / 7);
    var days = days - (weeks * 2);

    // Handle special cases
    var startDay = startDt.getDay();
    var endDay = endDt.getDay();

    // Remove weekend not previously removed.   
    if (startDay - endDay > 1) {       
      days = days - 2;     
    }

    // Remove start day if span starts on Sunday but ends before Saturday
    if (startDay === 0 && endDay !== 6) {
      days = days - 1;
    }

    // Remove end day if span ends on Saturday but starts after Sunday
    if (endDay === 6 && startDay !== 0) {
      days = days - 1; 
    }

    //subtract for any holiday included in the span
    if(app.getSharedData().includesHoliday(startDt, endDt)) {
      days = days - 1;
    }
  }
  return days;
}

Compare date fields

Given two dates the function will validate that they are numOfDays days apart. If the dateObj2 is not numOfDays days greater than dateObj1 then dateObj2 will be set invalid with the specified errorMsg.

Usage:

Copy the function to the Settings...Events...onStart section of your application.

// dateObj1 - the first date field (e.g. BO.F_Date1)
// dateObj2 - the second date field (e.g. BO.F_Date2)
// numOfDays - the number of days that D2 must be grater than D1
// errorMsg - The error message to display for D2 when invalid
app.getSharedData().compareDatesByDays = function(dateObj1, dateObj2, numOfDays, errorMsg) {

var d1 = dateObj1.getValue(); //get first date
var d2 = dateObj2.getValue(); //get sec date
var diff = 0;

//convert to milliseconds
var d1_milli = d1.getTime();
var d2_milli = d2.getTime();

//get difference and convert back to days
var diff_milli = d2_milli - d1_milli;
var nDays = Math.ceil(diff_milli / 1000 / 60 / 60 / 24);

 //if the diff is less then numOfDays set the d2 field invalid
 if(nDays < numOfDays) {
    dateObj2.setValid(false, errorMsg);
  } else {
    dateObj2.setValid(true, "");
  }
}

Then in both of your date fields place this in the onItemChange event to call the function (make sure you replace F_Date2 and F_Date3 with the IDs of your date fields):

app.getSharedData().compareDatesByDays(BO.F_Date2, BO.F_Date3, 14, "The date must be 14 days past D1.");

Convert AD date to Buddhist date

Convert a standard AD date into a Buddhist Date

Usage:

Place in onLoad or beforeSubmit event to convert a date. The code would be something like:

BO.F_DATETIME.setValue(app.getSharedData().convertToBuddhistDate(new Date()));

Function:

// dateAD: the date to convert
app.getSharedData().convertToBuddhistDate = function(dateAD){
    var dateBE = new Date(dateAD);
    dateBE.setFullYear(dateAD.getFullYear() + 543);
    return dateBE;
}

Get Days in Month

Given a date it will return the number of days in that month.

Function:

app.getSharedData().daysInThisMonth = function( date ) {

  var calendarObj = new Array();

  calendarObj.push({month: 0, days: 31});
  calendarObj.push({month: 1, days: 28});
  calendarObj.push({month: 2, days: 31});
  calendarObj.push({month: 3, days: 30});
  calendarObj.push({month: 4, days: 31});
  calendarObj.push({month: 5, days: 30});
  calendarObj.push({month: 6, days: 31});
  calendarObj.push({month: 7, days: 31});
  calendarObj.push({month: 8, days: 30});
  calendarObj.push({month: 9, days: 31});
  calendarObj.push({month: 10, days: 30});
  calendarObj.push({month: 11, days: 31});

  // Get the "non-Leap Year" number of days in the current month.
  var month = date.getMonth(); // Returns [0-11]
  var days = get( calendarObj, month ).days;

  // Add one day to February, if this is a Leap Year.
  if( month === "1" && app.getSharedData().isLeapYear( date ) ) {
    days++;
  }

  return days;
}

Get week number from date

Given the BO of a date field this function will return the week number the specified date is part of.

Usage:

Place the function in the onLoad event of the form where you want to use it. Then in the onItemChange event of the date field where you want to get the week number.

BO.F_weekNumber.setValue(app.getSharedData().getWeekNumberFromDate(BOA.getValue()));

Function:

// theDt - the business object of the date field (i.e. BOA, BO.F_Date1)
app.getSharedData().getWeekNumberFromDate = function(theDt) {
  if(theDt !== "") {

    // Create a copy of this date object
    var t = new Date(theDt);

    // ISO week date weeks start on monday so correct the day number
    var dayNr = (t.getDay() + 6) % 7;

    // Set the target to the thursday of this week so the target date is in the right year
    t.setDate(t.getDate() - dayNr + 3);

    // ISO 8601 states that week 1 is the week with january 4th in it
    var jan4 = new Date(t.getFullYear(), 0, 4);

    // Number of days between target date and january 4th
    var dayDiff = (t - jan4) / 86400000;

    // Calculate week number: Week 1 (january 4th) plus the number of weeks between target date and january 4th
    var weekNr = 1 + Math.ceil(dayDiff / 7);
    return weekNr;
  } else {
    return "";
  }
}

Subtract days from date

Subtract number of days from a date.

Usage: You might want to calculate a new date based on one that a user has entered by placing this code in the onItemChange event of the date field:

BO.F_Date2.setValue(app.getSharedData().subtractDaysFromDate(BO.F_Date1, 15));

Function:

// dateObj1 - The date field (i.e. BO.F_Date)
// numOfDays - The number of days to subtract
app.getSharedData().subtractDaysFromDate = function(dateObj1, numOfDays) {
  var d1 = dateObj1.getValue(); //get first date
  var d1_milli = d1.getTime();
  return new Date(Math.ceil(d1_milli - (1000 * 60 * 60 * 24 * numOfDays)));
}

Subtract working days from date

Subtract number of working days (not incl Sat or Sun) from a date.

Usage: You might want to calculate a new date based on one that a user has entered by placing this code in the onItemChange event of the date field:

BO.F_Date2.setValue(app.getSharedData().subtractWorkingDaysFromDate(BO.F_Date1, 15));

Function:

// dateObj1 - The date field (i.e. BO.F_Date)
// numOfDays - The number of working days to subtract
app.getSharedData().subtractWorkingDaysFromDate = function(dateObj1, numOfDays) {
  var d1 = dateObj1.getValue(); //get first date
  var d2 = dateObj1.getValue();
  var counter = numOfDays;
  var millisecondsPerDay = 86400 * 1000;
  while (counter > 0) {
    d2 = new Date(Math.ceil(d2.getTime() - millisecondsPerDay));
    var theDay = d2.getDay();
    // don't count sat and sunday
    if(theDay !== 0 && theDay !==6) {
      counter--;
    }
  }
  return d2;    
}

Verify entered date is after today

This function will set the date field to invalid if the entered date is not after today.

Usage:

Copy the code to the Settings...Events...onStart section of your application.

// dateObj1 - the business object of the date field (i.e. BOA, BO.F_Date1)
// errorMsg - The message to display to the user when the field is set to invalid
app.getSharedData().isDateAfterToday = function(dateObj1, errorMsg) {

  //get todays date, including the current time
  var today = new Date();       
  var d = dateObj1.getValue(); //get date from passed object

  //set the time of the date to just before midnight
  d.setSeconds(59);
  d.setMinutes(59);
  d.setHours(23);

  //D1 MUST be less then today
  if(d < today) {
    dateObj1.setValid(false, errorMsg);
  } else {
    dateObj1.setValid(true, "");
  }
}

You can use this function in the date field itself or elsewhere. You could use this in the onItemChange event of the date field by:

app.getSharedData().isDateAfterToday(BOA, "The date must be after today.");

Or you could use this in a different event within a different item

app.getSharedData().isDateAfterToday(BO.F_Date1, "The date must be after today.");

Verify entered date is before today

This function will set the date field to invalid if the entered date is not after today.

Usage:

Copy the function to the Settings...Events...onStart section of your application.

// dateObj1 - the business object of the date field (i.e. BOA, BO.F_Date1)
// errorMsg - The message to display to the user when the field is set to invalid
app.getSharedData().isDateAfterToday = function(dateObj1, errorMsg) {
  //get todays date, including the current time
  var today = new Date();       
  var d = dateObj1.getValue(); //get date from passed object

  //set the time of the date to just before midnight
  d.setSeconds(59);
  d.setMinutes(59);
  d.setHours(23);

  //D1 MUST be less then today
  if(d < today) {
    dateObj1.setValid(false, errorMsg);
  } else {
    dateObj1.setValid(true, "");
  }
}

You can use this function in the date field itself or elsewhere. You could use this in the onItemChange event of the date field by:

app.getSharedData().isDateAfterToday(BOA, "The date must be after today.");

Or you could use this in a different event within a different item:

 app.getSharedData().isDateAfterToday(BO.F_Date1, "The date must be after today.");

List Item Functions

Get select many item title

Returns the displayed title of the select many selection. You pass it the value.

Usage:

Then to use it you would call it in the onItemChange of the dropdown:

var selectedItemTitle = app.getSharedData().getCheckListItemTitles(item, BOA.getValue());
Then you can do whatever you want with the selectedItemTitle, like set it into another field:
BO.F_SingleLine.setValue(selectedItemTitle);

Function:

app.getSharedData().getCheckListItemTitles = function(theList, compareValue) {
  //split the compareValue into all the selected item
  var selChecks = compareValue.split("__#__");

  //for each one, get the title from the list and create a new concatenation
  var opts = theList.getOptions();
  var retVal = "";
  for(var h=0;h<selChecks.length;h++) {
    var v = get(selChecks, h);

    //loop all the items and return if you find the compareValue
    for(var i=0;i<opts.length;i++) {               
      var theItemTitle = get(get(opts,i), 'title');
      var theItemValue = get(get(opts,i), 'value');

      if(theItemValue === v) {
        if (retVal !== "") {
          retVal += "__#__";
        }
        retVal += theItemTitle;
          break;
      }
    }
  }
  return retVal;
}

Get dropdown title

When you are using an ID for the saved value of a drop down and you need to get at the actual title.

Usage:

Copy the code to the Settings...Events...onStart section of your application.

Then to use it you would call it in the onItemChange of the dropdown:

 var selectedItemTitle = app.getSharedData().getDropdownItemTitle(item, BOA.getValue());
 ```

Then you can do whatever you want with the selectedItemTitle, like set it into another field:
```javascript
BO.F_SingleLine.setValue(selectedItemTitle);

Function:

app.getSharedData().getDropdownItemTitle = function(theList, compareValue) {
  var opts = theList.getOptions();
  var retVal = "";

  //loop all the items and return if you find the compareValue
  for(var i=0;i<opts.length;i++) {
    var theItemBO = theList.getBOAttr();
    var theItemTitle = get(get(opts,i), 'title');
    var theItemValue = get(get(opts,i), 'value');

    if(theItemValue === theItemBO.getValue()) {
      retVal = theItemTitle;
      break;
    }
  }
  return retVal;
}

Randomize list choices

Usage:

Copy the function in to the Application...onStart event.

In the event where you want to trigger the function place the code:

app.getSharedData().randomizeList(page.F_DropDown);
app.getSharedData().randomizeList(form.getPage("P_NewPage1").F_DropDown);

Function:

// Returns a random number between the numbers specified. */
app.getSharedData().randomFromInterval = function(from,to) {
  return Math.floor(Math.random()*(to-from+1)+from);
}

// Randomizes the selections of the specified list item.
app.getSharedData().randomizeList = function(theItem) {
  //store the current value
  var value = theItem.getBOAttr().getValue();

  //set up the arrays
  var arrOptions = theItem.getOptions();
  var tmpOptions = theItem.getOptions();
  var newOptions = new Array();

  //Randomize the list
  if(arrOptions !== null) {
    for ( var i = 0; i < arrOptions.length; i++ ) {
      //the random number should be between 0 and the length of the array
      var j = app.getSharedData().randomFromInterval(0,tmpOptions.length-1);
      var temp = get(tmpOptions, j);

      //assign the item to the new array
      newOptions.push(temp);

      //remove the item from the temp array
      tmpOptions.splice(j, 1);
    }

    //Set the options back
    theItem.setOptions(newOptions);
    //reset back to the current value
    theItem.getBOAttr().getValue(value);
  }
}

Remove blank options from list

Returns a new List that does not contain the blank value. Supports Select One, Select Many and Dropdown objects.

Usage:

Copy the code to the Settings...Events...onStart section of your application.

In the onShow event of the list object that you want to change call the function by doing something like:

//get the dropdown options  
var c_opts = form.getPage('P_NewPage').F_DropDown.getOptions();
var n_opts = app.getSharedData().removeBlankFromList(opts);
form.getPage('P_NewPage').F_DropDown.setOptions(n_opts);
This gets the existing list options, passes it to the function which returns a new list. That new list is then set into the list object.

Function:

// theList - the List item from which to remove blank options
app.getSharedData().removeBlankFromList = function(theList) {
  //check all options for a blank value and remove it...
  var newList = new Array();
  //loop through the current list
  for(var j=0; j<theList.length;j++) {
      var curItem = get(theList, j);
      if(curItem !== "") {
          newList.push(curItem);
      }
  }
  return newList;
}

Remove duplicates from list

This function will remove any duplicate options found the current set of options.

Usage:

Copy the code to the Settings...Events...onStart section of your application.

Then you need to decide where and when to call the function. In the case of a service call you would want to call it when the service completes. Which might look something like this:

var service = form.getServiceConfiguration("SC_ServiceThatReturnsList");
  service.connectEvent('onCallFinished', function(success)
   {
    if(success) {          
       var cList = form.getPage('P_thePage').F_DropDown2.getOptions();
       var nList = app.getSharedData().removeDuplicatesFromList(cList);
       form.getPage('P_thePage').F_DropDown2.setOptions(nList);
    }
});

I would place this listener into the onLoad event of the form. This technique works for dropdowns, check and radio lists. If you wanted to do this for a table you would have to change the process slightly.

Function:

app.getSharedData().removeDuplicatesFromList = function(theList) {
  var newList = new Array();

    //loop through the current list
  for(var j=0; j<theList.length;j++) {
    var curItem = get(theList, j);
    var itemFound = false;

    //loop through the newList checking to see if the item is there
    for(var k=0; k<newList.length;k++) {
      if(get(curItem, 'title') === get(get(newList, k), 'title')) {
        itemFound = true;
        break;
      }
    }
    if(!itemFound) {
      newList.push(curItem);
    }
  }
  return newList;
}

Remove item from list

Removes the blank option from the list even if it is not the first, will also remove any duplicate blank options.

Function:

//   theList - the List item from which to remove blank options
app.getSharedData().removeItemFromList = function(theList, theVal) {

  //check all options for a blank value and remove it...
  var newList = new Array();

 const validTypes = ["comboBox","radioGroup","checkGroup"];

  if(validTypes.indexOf(theList.getType()) !== -1) { 
    //loop through the current list
    var opts = theList.getOptions();
    for(var j=0; j<opts.length;j++) {
      var curItem = get(opts, j);
      if(get(curItem, 'title') !== theVal) {        
        newList.push(curItem);        
      }      
    }
  }
  theList.setOptions(newList);
}

Remove items from list

Removes the options from the list that are found in the omitNames data passed to it.

Usage:

const omit = ["red","green"];
app.getSharedData().removeItemsFromList(page.F_SelectMany1, omit);

Function:

// theList - the List item from which to remove blank options
// omitNames - string of items to omit from the list
app.getSharedData().removeItemsFromList = function(theList, omitNames) {

  //check all options for a blank value and remove it...
  var newList = new Array();
  const validTypes = ["comboBox","radioGroup","checkGroup"];

  if(validTypes.indexOf(theList.getType()) !== -1) { 
    //loop through the current list
    var opts = theList.getOptions();
    for(var j=0; j<opts.length;j++) {
      var curItem = get(opts, j);

      if(typeof omitNames !== "undefined" && omitNames.indexOf(get(curItem, 'title')) === -1) {
        newList.push(curItem);
      }         
    }
  }
  theList.setOptions(newList);
}

Table Item Functions

Calculate sum of column A where column B meets specified criteria

Returns the sum of columnA for all the rows where columnB matches the specified entry. For example: Sum all the 'currency fields' in the rows where dropdown = 'approved'.

Copy the code to the Settings...Events...onStart section of your application.

// table - pass the table object - BO.F_Table
// columnA - the string ID of the field to be part of the summation
// columnB - the string ID of the field used to determine if the row should be included
// entry - the string value to compare against columnB
app.getSharedData().sumColumnA_searchByColumnB = function(table, columnA, columnB, entry) {  
  var sum = 0;
  for(var i = 0; i < table.getLength(); i++) {
    var r = table.get(i);
    var fld = get(r,columnB);
    var val = fld.getValue();

    if(typeof val === "string") {
      if(val.indexOf(entry) !== -1) {
        sum += parseFloat(get(r,columnA).getValue());
      }
    } else {
      if(val.toString() === entry) {
        sum += parseFloat(get(r,columnA).getValue());
      }
    }
  }
  return sum;
}

Note: This function as written will not all using a date or timestamp field as the search modifier (ColumnB). If you want to enable it to support dates then it will need to be modified to include the instanceOf logic that may be found in the checkTableForEntry function.

This function assumes that you are trying to sum a set of rows of a table where a field in those rows meets a specified criteria. Copy the function into the Settings...Events section of your application.

To use this function you will need to place the following in the onShow event of the field where the sum is being shown, as well as the onAdd and onRemove events of the table:

BO.F_Currency4.setValue(app.getSharedData().sumColumnA_searchByColumnB(BO.F_Table, 'F_Currency', 'F_DropDown4', 'Approved'));

Check table for entry

Checks to see if the specified text exists anywhere (any cell, any row) within the table. If the string is found then the function returns true, otherwise false.

This is a really helpful function if you want to determine available features based on the existence of an item within a table. For example, you might want to make sure that a user can only enter text into the table once.

Copy the function code to the Settings...Events...onStart section of your application.

// table: This is the actual table object, not the string ID (e.g. BO.F_Table)
// entry: this is the value of what you want to search for, the content of the table column will be converted to a string
// usage: This is usually called within the onClick of a button that adds rows to a table.  Or you might want to search to see if a record exists in the table.

app.getSharedData().checkTableForEntry = function(table, entry) {
  var entryFound = false;

  for(var i = 0; i < table.getLength(); i++) {
      var row = table.get(i);
      var tmpentry = entry;

      //loop through all the columns of the source table
      var cols = row.getChildren();
      for(var j=0;j<cols.getLength();j++) {
          var fld = cols.get(j);
          var c = fld.getValue();

          if(typeof c === "string") {
            if(c.indexOf(tmpentry) !== -1) {
              return true;
            }
          } else if(fld.getType() === "date" || fld.getType() === "timeStamp") {
            var entryAsDt = new Date(entry);
            if(entryAsDt.getYear() === c.getYear() && entryAsDt.getMonth() === c.getMonth() && entryAsDt.getDay() === c.getDay()) {
              return true;
            }   
          } else {

            //Convert to string for comparison            
            c = c.toString();           

            if(c === entry) {
              return true;
            }
          }
        } //inner for
      } //outer for
  return entryFound;
}

This type of feature is typically called within the onClick event of a button that adds a row to the table.

var entryFound = false;
entryFound = app.getSharedData().checkTableForEntry(BO.F_Table, theSearchValue);

or

if(!app.getSharedData().checkTableForEntry(BO.F_Table, theSearchValue)) {
     //the string was NOT found in the table, so now do something!
}

Check table for entry by column

This function checks a specific column of a table for the specified string. If the string is found then the function returns true, otherwise false.

Copy the function code to the Settings...Events...onStart section of your application.

// table: This is the actual table object, not the string ID (e.g. BO.F_Table)
// column: This is the string of the columnID that you want to seach
// entry: this is the value of what you want to search for, the data type should match that of the column you are searching (i.e. string, int, date)
app.getSharedData().checkTableForEntryByColumn = function(table, column, entry) {
  var entryFound = false;

  for(var i = 0; i < table.getLength(); i++) {
    var row = table.get(i);
    var tmpfld = get(row,column);
    var tmpval = tmpfld.getValue();
    if(typeof tmpval === "string") {
      if(tmpval.indexOf(entry) !== -1) {
        entryFound = true;
        break;
      }
    } else if(tmpfld.getType() === "date" || tmpfld.getType() === "timeStamp") {
      var entryAsDt = new Date(entry);
      if(entryAsDt.getYear() === tmpval.getYear() && entryAsDt.getMonth() === tmpval.getMonth() && entryAsDt.getDay() === tmpval.getDay()) {
       entryFound = true;
       break;
      }   
    } else {
      tmpval = tmpval.toString();
      if(tmpval === entry) {
        entryFound = true;
        break;
      }
    }
  }
  return entryFound;
}

Convert table to javaScript object

This function will convert a FEB table into a javaScript object that you can then reference within your javaScript code.

app.getSharedData().convertTableToObj = function(table) {
  //setting the global table properties
  var tblObj = {rows: new Array(),id: table.getBOAttr().getId(),length: table.getBOAttr().getLength(),type:table.getBOAttr().getType(), active: table.getActive(), title: table.getTitle(), visible: table.getVisible(), hintText: table.getHintText(), hoverText: table.getHoverText(), cssClasses: table.getClasses()};

  //loop through all the table rows to create the rows object
  for(var i = 0; i < table.getBOAttr().getLength(); i++) {
    var rowObj = {};
    var row = table.getBOAttr().get(i); //get the first row
    var ol = row.getChildren(); //get the list object that contains the row fields
    for(var j=0;j<ol.getLength();j++) {
     var f = ol.get(j);
     var v = f.getValue();
     set(rowObj,f.getId(), v);
    }
    tblObj.rows.push(rowObj);
  }
  return tblObj;
}

Pass the UI object of the table to the function:

var obj = app.getSharedData().convertTableToObj(page.F_Table1);

The table object has the following structure:

{rows:[],
id:"F_Table1",
length:0,
type:"BusinessObjectList",
active:true,
title:"Table",
visible:true,
hintText:"",
cssClasses:[]}

rows is an array that will contain all the field ids and their stored values, for example:

"rows":[{"F_SingleLine2":"Chris","F_Paragraphtext1":"This is a multi-line text field",
"F_Date2":"2017-02-22T08:00:00.000Z","F_Number1":12,"F_DropDown1":"red","F_SelectMany1":"deer__#__bear"}]

You could then convert this object to JSON:

toJson(obj)

Copy table contents to another table

Copy all the rows from one table to another. This assumes that the tables have the same number of rows and it will copy the columns in order. There is no error checking so if you try to copy a datatype that does not match it may throw a run-time error.

// sourceTable - The table object you want to copy (e.g. BO.F_Table)
// targetTable - The table object you want to copy into (e.g. BO.F_Table1)
// clearTable - true/false - If true will empty the target table before copying.
app.getSharedData().copyRowsToAnotherTable = function(sourceTable, targetTable, clearTable) {
  if(clearTable) {
    targetTable.setValue(new Array());
  }

  for(var i = 0; i < sourceTable.getLength(); i++) {
    var row = sourceTable.get(i);
    var newRow = targetTable.createNew();

    //loop through all the columns of the source table
    var cols = row.getChildren();
    for(var j=0;j<cols.getLength();j++) {
      //get the row object of the target table
      var newCols = newRow.getChildren();

      //for each row of the source table we set the value into the same row of the target table
      newCols.get(j).setValue(cols.get(j).getValue());
    }
    targetTable.add(newRow);
  }
}

In the event where you want to trigger the function place code like:

app.getSharedData().copyRowsToAnotherTable(BO.F_Table, BO.F_Table0, true);

Count table rows that contain entry by column

Returns the number of rows that contain the specified entry.

Copy the code to the Settings...Events...onStart section of your application.

// table: This is the actual table object, not the string ID (e.g. BO.F_Table)
// column: This is the string of the columnID that you want to seach
// entry: this is the value of what you want to search for,
// the data type should match that of the column you are searching (i.e. string, int, date)
app.getSharedData().countTableRowsForEntry = function(table, column, entry) {
  var entryFoundCount = 0;
  for(var i = 0; i < table.getLength(); i++) {
    var r = table.get(i);
    var fld = get(r,column);
    var val = fld.getValue();

    if(typeof val === "string") {
      if(val.indexOf(entry) !== -1) {
        entryFoundCount++;
      }
    } else {
      if(val.toString() === entry) {
        entryFoundCount++;
      }
    }
  }
  return entryFoundCount;
}

Note: This function as written will not all using a date or timestamp field as the search modifier (ColumnB). If you want to enable it to support dates then it will need to be modified to include the instanceOf logic that may be found in the checkTableForEntry function.

To use this function you need to decide where and how you want to check the value that the user might be trying to add. When you call the function you would use the following statement:

app.getSharedData().countTableRowsForEntry(BO.F_theTableObj,'F_theColumnToSearch','theValueYouAreSearchingFor');

Duplicate selected row and append to same table

This function will duplicate the selected row of a table and then append it to the end of the table.

Copy the function to the Settings...Events...onStart or Form onLoad section of your application.

// theTable - The table object you want to copy (e.g. BO.F_Table)
// theRow - the row to duplicate
app.getSharedData().duplicateSelectedRow = function(theTable, theRow) {
  var newRow = theTable.createNew();

  //loop through all the columns of the source table
  var cols = theRow.getChildren();
  for(var j=0;j<cols.getLength();j++) {
    //get the row object of the target table
    var newCols = newRow.getChildren();

    //for each row of the source table we set the value into the same row of the target table
    newCols.get(j).setValue(cols.get(j).getValue());
  }
  theTable.add(newRow);
}

Call the function by placing the following code in the desired event (i.e. onClick of a button or the table iteself):

app.getSharedData().duplicateSelectedRow(BO.F_Table, page.F_Table.getSelection());

Get first table row that contains entry by column

Copy the code to the Settings...Events...onStart section of your application.

app.getSharedData().getTableRowThatContainsEntry= function(table, column, entry) {
  var tblRow = null;
  for(var i = 0; i < table.getLength(); i++) {
    if(get(table.get(i),column).getValue() === entry) {
      tblRow = table.get(i);
      break;
    }
  }

  return tblRow;
}

To use this function you need to decide where and how you want to check the value that the user might be trying to add. When you call the function you would use the following statement:

var row = app.getSharedData().getTableRowThatContainsEntry(BO.F_theTableObj, 'F_theColumnToSearch','theValueYouAreSearchingFor');
if(row !== null) {
 //do something with the row
}

Get index of a row in a table

This is a function that will allow you to determine the index of a specific row in a table. Now the intention of this function is to use it in conjunction with the table.getSelection() which returns the "selected" row, by passing that into this function you can get back the "index" of that row in the table.

// theTable: the BO object of the table
// theRow:   the BO object of the row to be moved
app.getSharedData().getIndexInTable = function(theTable, theRow) {   

   if(theTable !== null && theRow !== null) {

       for(var i=0; i<theTable.getLength(); i++) {
           var tr = theTable.get(i);
           if(tr === theRow) {
               return i;
           }
       }
    }
    return -1; //if we get here then the row was not found in the table
}

The use case is that a user wanted to be able to set some table fields to read-only if the selected row was the "last" row in the table. The row business object does not know anything about its position in its parent table, therefore we have to walk the table comparing the selected row against each one that we find. The code was placed in the onShow event of a field in the table:

var inx = app.getSharedData().getIndexInTable(app.getForm('F_Form1').getBO().F_Table1,app.getSharedData().selectedRow);

if(inx === app.getForm('F_Form1').getBO().F_Table1.getLength() - 1) {
  item.setActive(false);  //this is the current item
  //this is how you would reference another item in the same table      
  page.F_SingleLine2.setActive(false);
}

Calling the function from within a table (we have to begin the reference from the app level to get the right object):

var inx = app.getSharedData().getIndexInTable(app.getForm('F_Form1').getBO().F_Table1,app.getSharedData().selectedRow);

Calling the function from within a field on the form:

var inx = app.getSharedData().getIndexInTable(BO.F_Table1,page.F_Table1.selectedRow);

Move a table row down one position

Change the order of the table by moving a row down one position.

Function

// theTable: the BO object of the table
// theRow: the BO object of the row to be moved
app.getSharedData().moveSelectionDown = function(theTable, theRow) {
    var rowObjs = new Array();
    var rowMoved = false;
    //remove all rows from the table
    //store each row in a separate array
    while(theTable.getLength() > 0) {
        rowObjs.push(theTable.get(0));
        theTable.remove(theTable.get(0));
    }
    for(var i = 0; i < rowObjs.length; i++) {
        var firstRow = get(rowObjs, i);

        // check to make sure we are not the last row, if we are just put the row back in the table
        if(i < rowObjs.length - 1) {
          var secondRow = get(rowObjs, i+1);
          if(!rowMoved) {
            if(firstRow === theRow) {
                //add second row, then the current row
                theTable.add(secondRow);
                i++; //increment counter again because we just processed two rows in one loop
                rowMoved = true;
            }
          }

        }
        theTable.add(firstRow);
    }
}

Usage:

Place in a button's onClick event. Clearing the selection after moving a row is critical otherwise errors may occur.

app.getSharedData().moveSelectionDown(BO.F_InputParameters, page.F_InputParameters.getSelection())
page.F_InputParameters.setSelection(-1);

Move a table row up one position

Change the order of the table by moving a row up one position.

Function:

// theTable: the BO object of the table
// theRow: the BO object of the row to be moved
app.getSharedData().moveSelectionUp = function(theTable, theRow) {
    var rowObjs = new Array();
    var rowMoved = false;

    //remove all rows from the table
    //store each row in a separate array
    while(theTable.getLength() > 0) {
        rowObjs.push(theTable.get(0));
        theTable.remove(theTable.get(0));
    }
    for(var i = 0; i < rowObjs.length; i++) {
        var firstRow = get(rowObjs, i);
        var secondRow = get(rowObjs, i+1);

        if(!rowMoved) {
            if(secondRow === theRow) {
                //add second row first, then the current row
                theTable.add(secondRow);
                i++; //increment counter again because we just processed two rows in one loop
                rowMoved = true;
            }
        }
        theTable.add(firstRow);
    }
}

Usage:

Place in a button's onClick event. Clearing the selection after moving a row is critical otherwise errors may occur.

app.getSharedData().moveSelectionUp(BO.F_InputParameters, page.F_InputParameters.getSelection())
page.F_InputParameters.setSelection(-1);

This function is a quick way to render the content of a table, that comes back from a service call, in a pure text format. This is an iterative function that walks all the rows and columns of a table and converts its content to a string. This is just an example of the type of thing that could be accomplished. Ideally you would take the premise and expand it for your situation. You may want to add more detailed html or css to really fine-tune what the printed result looks like.

Function:

app.getSharedData().printTable = function(table) {
  var s = "<p>"; //can change your html properties here

  //loop through all the table rows
  for(var i = 0; i < table.getLength(); i++) {
    var row = table.get(i); //get the first row
    var ol = row.getChildren(); //get the list object that contains the row fields
    for(var j=0;j<ol.getLength();j++) {
     var f = ol.get(j);
     var v = f.getValue();
     s += "<b>" + f.getId() + ":</b> " + v + "<BR />"; //this controls the formatting of each column in the table row
    }
    s += "<BR /><BR />"; //this controls what happens when we move to the next row
  }
  s += "</p>"; //close the html elements that were started at the beginning
  return s;
}

Usage::

The most common use case would use this function to display the table content in an HTML Area so that you can apply your own custom HTML to provide the most detailed formatting. You could also place the content in a Text item but you would not have any control over the formatting (i.e. applying bold, italic, etc).

If you are using this as I did, where a service populates a table then you will want to do the processing after the service completes, something like:

var srv = form.getServiceConfiguration('SC_ServiceConfig0');
srv.connectEvent("onCallFinished", function(success)
{
  if(success) {
    form.getPage('P_NewPage').F_HTMLArea.setContent(app.getSharedData().printTable(BO.F_Table1));
  }
});

Miscellaneous Functions

Calculate duration

This function can be used to calculate the duration between two times. This function currently supports calculating the duration into hours and minutes. You could further enhance this function to support months and years, but the output will always be rendered as a string representing the number of hours.

Function:

// startTime - Can be date, time or dateTime
// endTime - Can be date, time or dateTime
// rndHours - Round the result to the highest integer (hour). Values are true or false.
app.getSharedData().calcDuration = function(startTime, endTime, rndHours) {  

  //convert to milliseconds
  var d1 = startTime.getTime();
  var d2 = endTime.getTime();  
  var diff = d2 - d1;
  var durHours = diff / 1000 / 60 / 60;
  if(rndHours) {
    Math.ceil(durHours);
  }
  return durHours;
}

Usage:

  1. Copy the code to the Settings...Events...onStart section of your application.

  2. To call the function, on the onItemChange of your date fields add:

var dur = app.getSharedData().calcDuration(BO.F_Time.getValue(),
BO.F_Time0.getValue(),true);
BO.F_SingleLine.setValue(dur);

This approach takes just the time values, but you could create a different function that took the actual field objects and then you could add validation and error messages if your business case required it.

For example, you could make sure the start time did not come after the end time. You could also force either time to be within a specific window (i.e. 8-5). By using the field objects as the parameters you could use the item.setValid() functions to set them invalid and show error text if the user entered an invalid value.

Calculate sum of fields

Returns the sum of all the fields specified

Function:

// fields - array of field objects - new Array(BO.F_Field1, BO.F_Field2, ...)
app.getSharedData().sumFields = function(theBO, fields) {  
  var sum = 0;
  for(var i = 0; i < fields.length; i++) {
    var f = get(fields, i);
    var itm = get(theBO, f);
    var v = itm.getValue();
    if(typeof itm !== "undefined" && v !== "" && !isNaN(+v)) {  //!isNaN(+v) is specifically to omit text fields when their value is not numeric
        sum += parseFloat(v);
    }
  }
  return sum;
}

Usage:

Copy the function to Settings...Events section of your application. Place this in the onShow event of the field where the sum is shown and then again in the onItemChange event of all the fields included in the summation:

BO.F_Sum.setValue(app.getSharedData().sumFields(BO, new Array("F_Field1", "F_Field2", "F_Field3", "F_Field4")));

This function assumes that the fields being provided are simple fields in your form, either String, Number or Currency.

Example that uses the code -> Sum Multiple Fields Using JavaScript

The example demonstrates how to use the recursive function to dynamically add the summation to the onItemChange event of all the fields defined in the array.

Check if Field takes an Input

Function:

app.getSharedData().isInputField = function(theItemType) {
  var r = false;
  const inputFieldTypes = ["text","textArea","date","checkGroup","checkBox","radioGroup","number","currency","comboBox","horizontalSlider","choiceSlider","time","webLink","name","surveyQuestion","emailAddress","password","timeStamp","richTextArea"];
  if(inputFieldTypes.indexOf(theItemType) !== -1) {
    r = true;
  }
  return r;
}

Usage:

This function is useful for quickly determining if an item takes user input. This function is commonly used in conjunction with the Recursive function (also on this page), where you may want to take some action for input items in your form in the onLoad event. For example, when the form loads connect an event listener so that some code will be executed when the event is triggered.

Copy the function in to the Application onStart event

Execute the function by calling it in the desired event:

  • if you place this in the mouseover event of an item it will show an alert indicating if the item takes an input or not:
alert(app.getSharedData().isInputField(item.getType()));
  • Perhaps you want to make a decision based on the result
    if(app.getSharedData().isInputField(item.getType())) {
      //if true
    } else {
      //if false
    }
    

Constrain field data length

This function will restrict the field length of a field. It will not allow anymore characters to be entered into the field once the limit is reached.

Function:

app.getSharedData().restrictFieldLength = function(theField, theLength) {
  var v = theField.getDisplayValue();
  if(v.length >= theLength) {
    theField.setDisplayValue(v.substr(0,theLength));
  }
}

Usage:

Place this function in the Settings > Events

Navigate to the field where you want to constrain the length. In the onItemLiveChange event place the code below.

app.getSharedData().restrictFieldLength(item, 3);

This will limit the length to (3). You can adjust this number according to your needs.

Invalidate field based on length

Function that can be used to check the content of a field and set it to invalid if it exceeds a specified length.

Function:

// theItem - the UI object of the item to validate
// theLength - the max length of the field, if exceeded the field will be set to invalid
// theMsg - The text to render if the length is exceeded.
app.getSharedData().fieldLengthValidation = function (theItem, theLength, theMsg) {
  var dval = theItem.getDisplayValue();
  if(dval.length > theLength) {
    theItem.getBOAttr().setValid(false, theMsg);
  } else {
    theItem.getBOAttr().setValid(true, '');
  }
}

Usage:

  • Place the function in the Settings...Events

  • Use the function in the onLiveItemChange event of the field you want to validate and change parameters as desired:

app.getSharedData().fieldLengthValidation(item, 3, 'Field will only accept 3 characters or less');

Multiply multiple field values

Function:

app.getSharedData().multiplyFields = function(fields) {   
  var result = 1;
  for(var i = 0; i < fields.length; i++) {
    var f = get(fields, i);
      if(f.getValue() !== "") {
        result *= parseFloat(f.getValue());
      }
     }
  return result;
}

Usage:

Copy the function to Settings...Events section of your application. Place this in the onShow event of the field where the sum is shown and then again in the onItemChange event of all the fields included in the summation:

BO.F_Result.setValue(app.getSharedData().multiplyFields(new Array(BO.F_Field1, BO.F_Field2, BO.F_Field3, BO.F_Field4)));

This function assumes that the fields being provided are simple fields in your form, either String, Number or Currency.

Pad field value

This function can be used to pad a string with a padding character up to a specified length.

Function:

// theVal - the string you want to pad
// padChar - the character you want to use to pad the string
// desiredLength - the total length the resulting string should be
app.getSharedData.padValue = function(theVal, padChar, desiredLength) {
  while(theVal.length < desiredLength) {
    theVal = padChar + theVal;              
  }
  return theVal;
}

Usage Notes:

Place this function in the onLoad event of the form. Then in the onItemChange event of the field you wish to pad place:

BOA.setValue(app.getSharedData.padValue(BOA.getValue(), "0", 4));

This will pad the value with zeros up to a total string length of 4.

Recursive function to loop over all items in a form

If you have a need to walk all the items in a form and then perform some operation on each one, you could use these functions.

Function:

app.getSharedData().processItem = function(item) {

  //do whatever you want to the item...
   alert(item.getId() + " : " + item.getValue());

  //set item to inactive
  if(app.getSharedData().isInputField(item.getType())) {
    item.setActive(false);
  }
 //if(item.getType() === "text") {
   //do your thing
 //}

}

/*
* Returns true if the current item has children, otherwise false.
*/
app.getSharedData().hasItems = function(containerID) {
  var list = containerID.getChildren();
  if(list.getLength() > 0) {
    return true;
  } else {
    return false;
  }   
}

/*
* Recursive function used for counting form items.
* containerID: UI item (i.e. page or item)
* processItem: the function that contains the work we want to perform on the item we have accessed
*/
app.getSharedData().getItem = function(containerID, processItem) {
  var itemList;
  var pageList;
  var pageCount = 1;
  debugger;

  //check to see if the container is a form as it requires different processing
  if(containerID.getType() === "form") {
    pageList = containerID.getPageIds(); //list of the page IDs - not the actual objects!!
    pageCount = pageList.length;
  } else {
    itemList = containerID.getChildren();    
  }

  //need a loop to account for different pages
  for(var p=0; p<pageCount;p++) {
    if(containerID.getType() === "form") {
      itemList = containerID.getPage(get(pageList, p)).getChildren(); //get the page object from the form
    }

    //loop all the items
    for(var i=0; i<itemList.getLength(); i++)
    {
      var theItem = itemList.get(i);
      if(app.getSharedData().hasItems(theItem)) {
        //if container go into it...
        app.getSharedData().getItem(theItem, processItem);   
      } else {
        //other wise do something with the item
        if(dojo.isFunction(processItem)) { //make sure that the parameter passed is a function
          processItem(theItem);        
        }
      }
    }
  }
}

Usage:

Place the functions in the Settings...Events...Custom Actions. Then you can call the main function app.getSharedData().getItem(page) and it will iterate through all the objects on that page, including all the items within a container (section, tabbed folder).

Some examples of this function are:

Walk all the items in a form:

app.getSharedData().getItem(form, app.getSharedData().processItem)

Walk all the items in a page:

app.getSharedData().getItem(form.getPage('P_Page1'), app.getSharedData().processItem) //if you place in the form onLoad event you have to qualify the page

Walk all the items in a section:

app.getSharedData().getItem(item, app.getSharedData().processItem) //if you place this in the onShow event of the section

Walk all the items in a tabbedFolder:

app.getSharedData().getItem(form.getPage('P_Page1').F_TabbedFolder, app.getSharedData().processItem) //if you place this in the onLoad event of the form

If you wanted to only perform an operation on a specific item type then you could further modify the function:

if(item.getType() === "text") {
  //do your thing
}

Validate field by pattern

This function will enforce a regex pattern on the specified field and will set the field to invalid if it does not match.

Note: The capability to use patterns and regular expressions for validating Single Line fields does not require javaScript as it can be set through the field's advanced property settings.

Function:

app.getSharedData().validateByPattern = function(theItem, pattern, errMsg) {
  var re = new RegExp(pattern);
  if(!re.test(theItem.getValue())) {
   theItem.setValid(false, errMsg);
  } else {
    theItem.setValid(true, "");
  }
}

Usage:

For example, you may want to validate a single line field as a US based phone number. In the onItemChange event of the field you would place:

app.getSharedData().validateByPattern(BOA, /^\(\d{3}\) \d{3}-\d{4}$/, "The number provided does not meet the required format");

Survey Item Functions

Calculate the average of survey item answers

When your survey answers have numeric saved values you might have a situation where you want to calculate the average of all the choices the user selected.

Included here is a javaScript function that can be used to accomplish that task.

Function:

//get the average from the survey item
app.getSharedData().getAverageFromSurvey = function (surveyItem) {
  var avg = 0;
  for(var i=0; i<surveyItem.getLength(); i++) {
    //get the value of the question
    var qval = surveyItem.get(i).getValue();
    if(qval !== "") {
      avg += parseInt(qval);
    }
  }
  return (avg / surveyItem.getLength());
}

Usage

The usage of the function is in the onItemChange of the survey item:

var surveyQList = page.F_Survey.getChildren();
BO.F_Number4.setValue(app.getSharedData().getAverageFromSurvey(surveyQList));

The F_Survey is the ID of the survey item whose average is being calculated.

The F_Number is the ID of the field where the average is being placed.

A few assumptions have been made for this function:

  1. The average is taken on all the questions in the survey item whether they are answered or not. You could modify the function if you wanted to only take the average of "answered" questions, however I assume that all the questions would be answered, which is why I didn't implement it.

  2. The saved value of the survey item choices must be numbers. The function doesn't check and may produce a strange result if you tried this with non-numeric saved values.

Rank survey choices

You could create a survey item that limits what the user can select. By using some custom JavaScript you can force a user to "rank" their selections. If a user tries to select the same rank for a second option then the previous option of the same rank is deselected.

image of a survey item that only allows one selection for each value

Below is the JavaScript that makes this work. It is written very generically so to apply to your form all you have to do is copy it into the onShow event of the Survey item where you want this "ranking" behavior enforced.

if(!app.getSharedData().rankSetup)
{
  /* This global flag insures that the function will only get called once */
  app.getSharedData().rankSetup = true;
  var children = field.getChildren();

  var createlisteningFunction = function(item){
    return (function(){
      var value = item.getValue();
      if(value.length === 0)
        return;

      /* clears the value of the other items if they share the same rank value */
      for(var i=0; i<children.getLength();i++)
      {
        if(children.get(i) === item)
          continue;
        if(children.get(i).getValue() === value)
          children.get(i).setValue("");
      }
    });
  };

  /*create and attach a listener function to each questions onChange event*/
  for(var i=0; i<children.getLength(); i++)
  {
    children.get(i).getBOAttr().connectEvent('onChange', createlisteningFunction(children.get(i)));
  }
}

Parent topic: Adding JavaScript