/*
* Chat client. Uses Netsuite as backend DB to store messages. Defaults to 2 secs refresh interval and 60 seconds message cache.
* Created by Adolfo Garza (borncorp)
*/
var ENCODEMODULE, RUNTIMEMODULE, UIMODULE, URLMODULE, SEARCHMODULE, RECORDMODULE, DEPLOYMENT_URL; //I like loading my modules as globals so I can access them whenever I want
var CHATMESSAGE_RECORDTYPE = 'customrecord_bpc_chat_message';
var MESSAGE_TEXT_FIELD_ID = 'custrecord_bpc_chat_message_message_text';
var MESSAGE_USER_FIELD_ID = 'custrecord_bpc_chat_message_user';
var MESSAGE_TIMESTAMP_FIELD_ID = 'custrecord_bpc_chat_message_timestamp';
/**
*@NApiVersion 2.x
*@NScriptType Suitelet
*@NModuleScope Public
*/
define(["N/encode", "N/runtime", 'N/ui/serverWidget', 'N/url', 'N/search', 'N/record'], runSuitelet);
//********************** MAIN FUNCTION **********************
function runSuitelet(encode, runtime, ui, url, search, record){
ENCODEMODULE= encode;
RUNTIMEMODULE= runtime;
UIMODULE = ui;
URLMODULE = url;
SEARCHMODULE = search;
RECORDMODULE = record;
var returnObj = {};
returnObj.onRequest = execute;
return returnObj;
}
function execute(context){
DEPLOYMENT_URL = getDeploymentURL();
try {
if (context.request.method == 'GET') {
var form = getInjectableForm();
context.response.writePage(form);
return;
} else {
var params = context.request.parameters;
var body = context.request.body;
if(params.action == 'getUpdate'){
var results = getUpdate();
context.response.write("$ - CODE - $" + JSON.stringify(results) + "$ - CODE - $");
}
else if(params.action == 'saveMessage'){
var newMessageId = saveMessage(body);
context.response.write("$ - CODE - $" + newMessageId + "$ - CODE - $");
}
}
return;
} catch (e) {
log.error("ERROR", e.toString());
context.response.write(e.toString());
}
return;
}
function getUpdate(){
var mySearch = {};
var type = CHATMESSAGE_RECORDTYPE;
var columns = ['internalid', MESSAGE_TEXT_FIELD_ID, MESSAGE_USER_FIELD_ID];
var filters = [];
filters.push([MESSAGE_TIMESTAMP_FIELD_ID, 'after', ["secondsago30"]]);
mySearch.type = type;
mySearch.columns = columns;
mySearch.filters = filters;
var mySearchObj = SEARCHMODULE.create(mySearch);
var resultset = mySearchObj.run();
var results = resultset.getRange(0, 20); //Gets last 20 messages
var rows = [];
for (var i in results) {
var result = results[i];
var row = {};
row.id = result.getValue(result.columns[0]);
row.messageText = result.getValue(result.columns[1]);
row.user = result.getText(result.columns[2]);
rows.push(row);
}
return rows;
}
function saveMessage(message){
var newRecord = RECORDMODULE.create({
type: CHATMESSAGE_RECORDTYPE,
isDynamic: false,
});
newRecord.setValue(MESSAGE_TEXT_FIELD_ID, message);
var newMessageId = newRecord.save({
enableSourcing: true,
ignoreMandatoryFields: true
});
//log.debug('newMessageId' + newMessageId);
return newMessageId;
}
function getInjectableForm(){
//*********** Create Form ***********
var form = UIMODULE.createForm({
title: ' '
});
var bodyAreaField = form.addField({
id : 'custpage_bodyareafield',
type : UIMODULE.FieldType.INLINEHTML,
label : 'Body Area Field'
});
//*********** Prepare HTML and scripts to Inject ***********
var body = getBody();
clientCode = clientCode.replace('$PAGEBODY$', body).replace('$DEPLOYMENT_URL$', DEPLOYMENT_URL);
var base64ClientCode = toBase64(clientCode);
var scriptToInject = 'console.log(\'Added bottom script element\');';
scriptToInject += "eval( atob(\'" + base64ClientCode + "\') );";
//*********** Injecting HTML and scripts into an the Body Area Field ***********
bodyAreaField.defaultValue = '<script>var script = document.createElement(\'script\');script.setAttribute(\'type\', \'text/javascript\');script.appendChild(document.createTextNode(\"' + scriptToInject + '\" ));document.body.appendChild(script);</script>';
return form;
}
/**
* Gets HTML that will be injected into the Suitelet. Use an HTML minifier tool to achieve this one string output.
* @returns {string} HTML String
*/
function getBody(){
return "<div style='width:100%' class='container-fluid'> " +
//Replace with your HTML
"<div class='container'> <div class='row'> <div class='col-md-5'> <div class='panel panel-primary'> <div class='panel-heading'> <span class='glyphicon glyphicon-comment'></span> Chat </div> <div id = 'chat-area' class='panel-body'> </div> <div class='panel-footer'> <form id='chatform'> <div class='input-group'> <input id='btn-input' type='text' class='form-control input-sm' placeholder='Type your message here...' /> <span class='input-group-btn'> <button type='submit' class='btn btn-warning btn-sm' id='btn-chat'> Send</button> </span> </div> </form></div> </div> </div> </div> </div> "
+ "</div>";
}
var clientCode = 'run(); ' + function run() {
console.log('Running client code');
//*********** GLOBAL VARIABLES ***********
$ = jQuery;
DEPLOYMENT_URL = '$DEPLOYMENT_URL$';
USERNAME = nlapiGetContext().name;
REFRESH_INTERVAL = 2; //In Seconds
SECONDS_CACHE = 60; //In Seconds
//*********** After DOM loads run this: ***********
$(function() {
injectHeaderScripts(); //Loads Libraries that will be placed on header (Optional)
$(window).bind('load', injectHTML); //Replaces Suitelet's body with custom HTML once the window has fully loaded(Required)
waitForLibraries(['$.jStorage', '$'], runCustomFunctions); //Runs additional logic after required libraries have loaded (Optional)
});
return;
//*********** MAIN FUNCTIONS ***********
/**
* Loads Libraries that will be placed on header (Optional)
*/
function injectHeaderScripts(){
console.log('loadHeaderLibraries START');
//loadjscssfile( "https://code.jquery.com/jquery-1.11.3.min.js" );
loadjscssfile("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css");
loadjscssfile("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css");
loadjscssfile('https://cdn.rawgit.com/andris9/jStorage/master/jstorage.min.js');
//loadjscssfile( "https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.0/sweetalert.min.js");
//loadjscssfile("https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.0/sweetalert.min.css");
//loadjscssfile( "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" );
console.log('loadHeaderLibraries END');
}
function runCustomFunctions() {
console.log('Running custom functions');
loadListeners();
}
//*********** HELPER FUNCTIONS ***********
function loadjscssfile(filename) {
var filetype = filename.substr(filename.lastIndexOf('.') + 1).toLowerCase();
if (filetype == "js") { //if filename is a external JavaScript file
var fileref = document.createElement('script')
fileref.setAttribute("type", "text/javascript")
fileref.setAttribute("src", filename)
} else if (filetype == "css") { //if filename is an external CSS file
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
}
if (typeof fileref != "undefined"){
document.getElementsByTagName("head")[0].appendChild(fileref)
}
console.log(filename + ' plugin loaded');
}
function waitForLibraries(libraries, functionToRun){
var loadedLibraries = 0;
for (var i in libraries) {
var library = libraries[i];
if(eval('typeof ' + library)!='undefined'){
loadedLibraries++;
}
}
window.setTimeout(function(){
if(loadedLibraries != libraries.length){
waitForLibraries(libraries, functionToRun);
} else{
console.log(library + ' loaded');
functionToRun();
}
},500);
}
function injectHTML(){
var html = ' $PAGEBODY$ '; //This string will be replaced by the Suitelet
$("#main_form")[0].outerHTML = html;
}
//*********** CHAT FUNCTIONS ***********
function loadListeners(){
//*********** Listeners ***********/
doPoll();
listenForMessages();
$('#chatform').submit(execute);
//$('#btn-chat').submit(execute);
//*********** HELPER FUNCTIONS ***********/
function execute(e){
e.preventDefault();
var messageText = $('#btn-input').val();
$('#btn-input').val('');
sendMessage(messageText);
return false;
}
function doPoll() {
$.post(DEPLOYMENT_URL + '&action=getUpdate').done(function (data) {
/* Process Data */
var last20Messages = parseResponse(data);
processNewMessages(last20Messages);
}).always(function () {
setTimeout(doPoll, REFRESH_INTERVAL*1000);
});
}
//***** HELPER FUNCTIONS ****
function processNewMessages(last20Messages){
var cacheIndex = $.jStorage.index();
for(var i in last20Messages){
var id = last20Messages[i].id;
var messageText = last20Messages[i].messageText;
var user = last20Messages[i].user;
if (cacheIndex.indexOf(id)<0){
$.jStorage.publish("cache", last20Messages[i]);
}
}
}
function listenForMessages(){
$.jStorage.subscribe("cache", function(channel, payload){
var message = payload;
displayMessage(message);
});
}
function parseResponse(data){
var splittedResponse = data.split("$ - CODE - $");
splittedResponse.shift();
splittedResponse.pop();
var parsedResponse = JSON.parse(splittedResponse);
return parsedResponse;
}
function sendMessage(messageText){
$.ajax({
type: "POST",
url: DEPLOYMENT_URL + '&action=saveMessage',
contentType: "application/json; charset=utf-8",
processData: false,
async: true,
data: messageText,
}).done(function (data) {
/* Process Data */
var response = parseResponse(data);
var message = {};
message.id = response;
message.user = USERNAME;
message.messageText = messageText;
displayMessage(message);
//console.log(response);
});
}
function displayMessage(message){
var TEMPLATE_THEM = "<div class='right clearfix'><span class='chat-img pull-left'> </span> <div class='chat-body clearfix'> <div class='header'> <strong class='primary-font'>$SENDER$</strong> <small class='pull-right text-muted'> <span class='glyphicon glyphicon-time'></span>$TIMESTAMP$</small> </div> <p> $MESSAGE$ </p> </div> </div>";
var TEMPLATE_MINE = "<div class='right clearfix'><span class='chat-img pull-right'> </span> <div class='chat-body clearfix'> <div class='header'> <small class=' text-muted'><span class='glyphicon glyphicon-time'></span>$TIMESTAMP$</small> <strong class='pull-right primary-font'>$SENDER$</strong> </div> <p> $MESSAGE$ </p> </div> </div>";
var id = message.id;
var user = message.user;
var messageText = message.messageText;
var newDiv;
if(user == USERNAME){
user = 'Me';
newDiv = TEMPLATE_MINE.replace('$SENDER$', 'Me')
.replace('$TIMESTAMP$', timeStamp())
.replace('$MESSAGE$', messageText);
} else {
newDiv = TEMPLATE_THEM.replace('$SENDER$', user)
.replace('$TIMESTAMP$', timeStamp())
.replace('$MESSAGE$', messageText);
}
$.jStorage.set(id, message, {TTL: SECONDS_CACHE*1000});
$('#chat-area').append(newDiv);
}
function timeStamp() {
var now = new Date();
var time = [ now.getHours(), now.getMinutes()];
return time.join(":");
}
}
}
/**
* Gets deployment URL. Useful for sending POST requests to this same suitelet.
* @returns {string} Deployment URL
*/
function getDeploymentURL(){
return URLMODULE.resolveScript({
scriptId: RUNTIMEMODULE.getCurrentScript().id,
deploymentId: RUNTIMEMODULE.getCurrentScript().deploymentId,
returnExternalUrl: false,
});
}
function toBase64(stringInput){
return ENCODEMODULE.convert({
string: stringInput,
inputEncoding: ENCODEMODULE.Encoding.UTF_8,
outputEncoding: ENCODEMODULE.Encoding.BASE_64
});
}