Nepali Date Integration
Complete documentation for the two-way AD/BS synchronization engine in PHPRunner 11.2.
Before the JavaScript functions can work, Sajan Maharjan's library must be loaded. Loading it globally ensures the calendar works across your entire application without redundant code.
The cleanest way to apply this project-wide is by adding the HTML assets directly to PHPRunner's shared Header snippet.
- Open PHPRunner and go to the Visual Editor (Page Designer).
- Locate and click on the Header snippet (usually at the top of the page layout).
- Paste the following standard HTML into the editor:
<link href="https://cdn.jsdelivr.net/npm/@sajanm/nepali-date-picker@5.0.6/dist/nepali.datepicker.v5.0.6.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@sajanm/nepali-date-picker@5.0.6/dist/nepali.datepicker.v5.0.6.min.js"></script>
If you prefer keeping everything in the Events screen, navigate to Events > Global > Before display and add:
$pageObject->AddCSSFile("https://cdn.jsdelivr.net/npm/@sajanm/nepali-date-picker@5.0.6/dist/nepali.datepicker.v5.0.6.min.css");
$pageObject->AddJSFile("https://cdn.jsdelivr.net/npm/@sajanm/nepali-date-picker@5.0.6/dist/nepali.datepicker.v5.0.6.min.js");
Final Configuration Step
To make the utility available globally across your application, copy the following engine code and paste it into your custom_functions.js file (or your project's global JavaScript file).
/**
* Smart Nepali Date Picker Integration for PHPRunner
* Features:
* - Scoped securely via PHPRunner's pageid
* - Auto AD-to-BS and BS-to-AD synchronization
* - Safely handles PHPRunner's internal state via .setValue()
* * @param {number} pageid - The current PHPRunner page ID
* @param {string} bsFieldName - Name of the Bikram Sambat field
* @param {string} adFieldName - (Optional) Name of the AD field to sync with
* @param {Object} options - (Optional) Sajan's v5 configuration object
*/
function initNepaliDatePicker(pageid, bsFieldName, adFieldName, options) {
console.log("Nepali Date Picker: Initializing for page " + pageid);
// Wait for the library to exist
var checkExist = setInterval(function() {
var bsControl = Runner.getControl(pageid, bsFieldName);
if (!bsControl) return;
var bsInput = $("#value_" + bsFieldName + "_" + bsControl.id)[0];
if (!bsInput) return;
if (typeof bsInput.nepaliDatePicker === 'function' && typeof NepaliFunctions !== 'undefined') {
clearInterval(checkExist);
console.log("Nepali Date Picker: Loaded! Attaching to -> " + bsFieldName);
attachCalendar(bsControl, bsInput, adFieldName, options);
}
}, 100);
setTimeout(function() { clearInterval(checkExist); }, 3000);
// --- The Core Engine ---
function attachCalendar(bsControl, bsInput, adFieldName, options) {
var adControl = adFieldName ? Runner.getControl(pageid, adFieldName) : null;
var adInput = adControl ? $("#value_" + adFieldName + "_" + adControl.id)[0] : null;
// ANTI-INFINITE LOOP FLAG
var isSyncing = false;
function formatDate(dateObj) {
var m = dateObj.month < 10 ? "0" + dateObj.month : dateObj.month;
var d = dateObj.day < 10 ? "0" + dateObj.day : dateObj.day;
return dateObj.year + "-" + m + "-" + d;
}
// Centralized Synchronization Logic
function doSync(source, value) {
if (isSyncing) return;
if (!value) {
isSyncing = true;
if (source === "BS" && adControl) {
adControl.setValue("");
adInput.value = "";
}
if (source === "AD") {
bsControl.setValue("");
bsInput.value = "";
}
isSyncing = false;
return;
}
try {
if (source === "BS" && adControl) {
var cleanVal = value.replace(/\//g, "-").replace(/[^\d-]/g, "");
var parts = cleanVal.split("-");
if (parts.length !== 3) return;
var dateObj = { year: Number(parts[0]), month: Number(parts[1]), day: Number(parts[2]) };
var adObj = NepaliFunctions.BS2AD(dateObj);
var adString = formatDate(adObj);
isSyncing = true;
// 1. Update PHPRunner API & Visual DOM
adControl.setValue(adString);
adInput.value = adString;
// 2. Force Native Browser Events so PHPRunner's serializer catches the data
adInput.dispatchEvent(new Event('input', { bubbles: true }));
adInput.dispatchEvent(new Event('change', { bubbles: true }));
adInput.dispatchEvent(new Event('blur', { bubbles: true }));
isSyncing = false;
}
else if (source === "AD") {
var parsedDate = new Date(value);
if (isNaN(parsedDate.getTime())) return;
var adDateObj = {
year: parsedDate.getFullYear(),
month: parsedDate.getMonth() + 1,
day: parsedDate.getDate()
};
var bsObj = NepaliFunctions.AD2BS(adDateObj);
var bsString = formatDate(bsObj);
isSyncing = true;
// 1. Update PHPRunner API & Visual DOM
bsControl.setValue(bsString);
bsInput.value = bsString;
// 2. Force Native Browser Events
bsInput.dispatchEvent(new Event('input', { bubbles: true }));
bsInput.dispatchEvent(new Event('change', { bubbles: true }));
bsInput.dispatchEvent(new Event('blur', { bubbles: true }));
isSyncing = false;
}
} catch (error) {
console.error("Nepali Date Picker: Math Conversion Error ->", error);
isSyncing = false;
}
}
// Initialize Plugin
var config = Object.assign({
ndpYear: true,
ndpMonth: true,
ndpYearCount: 100,
ndpEnglishInput: true,
onChange: function() {
doSync("BS", bsInput.value);
}
}, options || {});
bsInput.nepaliDatePicker(config);
// --- Event Listeners ---
// Listen to Nepali Field
$(bsInput).on("blur", function() { doSync("BS", this.value); });
// Listen to English Field
if (adControl) {
// Catches typing
$(adInput).on("blur change input", function() {
doSync("AD", adControl.getValue() || this.value);
});
// Catches PHPRunner's built-in calendar popup selections
adControl.on("change", function() {
doSync("AD", adControl.getValue());
});
}
}
}
// ========================================================================
// GLOBAL STANDALONE DATE CONVERTERS
// ========================================================================
/**
* Converts an AD date string (e.g., "2023-08-29") to a BS date string ("2080-05-12")
* @param {string} adDateString - The English date string
* @returns {string} - The formatted Nepali date string, or empty string if invalid
*/
function ad2bs(adDateString) {
if (!adDateString || typeof NepaliFunctions === 'undefined') return "";
var parsedDate = new Date(adDateString);
if (isNaN(parsedDate.getTime())) return "";
var adDateObj = {
year: parsedDate.getFullYear(),
month: parsedDate.getMonth() + 1,
day: parsedDate.getDate()
};
try {
var bsObj = NepaliFunctions.AD2BS(adDateObj);
var m = bsObj.month < 10 ? "0" + bsObj.month : bsObj.month;
var d = bsObj.day < 10 ? "0" + bsObj.day : bsObj.day;
return bsObj.year + "-" + m + "-" + d;
} catch (e) {
console.error("ad2bs conversion error:", e);
return "";
}
}
/**
* Converts a BS date string (e.g., "2080-05-12") to an AD date string ("2023-08-29")
* @param {string} bsDateString - The Nepali date string
* @returns {string} - The formatted English date string, or empty string if invalid
*/
function bs2ad(bsDateString) {
if (!bsDateString || typeof NepaliFunctions === 'undefined') return "";
var cleanVal = bsDateString.replace(/\//g, "-").replace(/[^\d-]/g, "");
var parts = cleanVal.split("-");
if (parts.length !== 3) return "";
var bsDateObj = {
year: Number(parts[0]),
month: Number(parts[1]),
day: Number(parts[2])
};
try {
var adObj = NepaliFunctions.BS2AD(bsDateObj);
var m = adObj.month < 10 ? "0" + adObj.month : adObj.month;
var d = adObj.day < 10 ? "0" + adObj.day : adObj.day;
return adObj.year + "-" + m + "-" + d;
} catch (e) {
console.error("bs2ad conversion error:", e);
return "";
}
}
Use the initNepaliDatePicker function to attach the interactive calendar and bind the two fields together. This provides real-time, bidirectional conversion.
Navigate to the Javascript OnLoad event of your Add or Edit page and add:
// Basic Two-Way Sync
initNepaliDatePicker(pageid, "date_bs", "date_ad");
// Advanced Usage with Custom Plugin Options
initNepaliDatePicker(pageid, "date_bs", "date_ad", {
ndpYearCount: 200, // Show 200 years in dropdown
disableDaysBefore: 1, // Disable past dates
ndpEnglishInput: true // Allow typing in English digits
});
Databases should always store English (AD) dates for accurate sorting and filtering. To display these AD dates as Nepali (BS) dates on List, View, or Print pages, use CSS class targeting.
Step 1: Add the CSS Class
In the PHPRunner Page Designer, click on your date column. In the properties panel on the right, enter format-ad-to-bs into the Custom CSS Class box.
Step 2: Trigger the Converter
In the Javascript OnLoad event for the List or View page, call the auto-converter (if you implemented the list display function):
// Scans the page and converts all targeted CSS classes
autoConvertDisplayDates();
Available Classes:
format-ad-to-bs: Translates "2023-08-29" into "2080-05-12"format-bs-to-ad: Translates "2080-05-12" into "2023-08-29"
If you need to perform translations manually in your own custom JavaScript logic (e.g., inside custom buttons or validation routines), you can use the globally exposed helper functions.
// Convert English to Nepali
var myNepaliDate = ad2bs("2026-04-04");
console.log(myNepaliDate); // Outputs: "2082-12-21"
// Convert Nepali to English
var myEnglishDate = bs2ad("2080-05-12");
console.log(myEnglishDate); // Outputs: "2023-08-29"
Note: These functions return an empty string "" if an invalid date format is provided or if the core library has not finished loading.