This commit is contained in:
2018-06-28 23:37:38 +00:00
commit 4518298aaf
152 changed files with 24114 additions and 0 deletions

337
wwwroot/js/app.api.js Normal file
View File

@@ -0,0 +1,337 @@
/*
* app.api.js
* Ajax api helper module
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, io, app */
app.api = (function() {
"use strict";
var initModule,
getAuthHeaderObject,
RockFishVersion,
get,
remove,
create,
update,
uploadFile,
putAction,
createLicense,
getLicenseRequests,
generateFromRequest,
licenseEmailResponse;
RockFishVersion = "6.1";
//////////////////////////////////////////////////////////////////////////////////////
// NOT AUTHORIZED ERROR HANDLER
$(document).ajaxError(function(event, jqxhr, settings, thrownError) {
//unauthorized? Trigger logout which will trigger login after clearing creds
if (jqxhr.status == 401) {
window.location.replace("#!/logout");
}
});
//////////////////////////////////////////////////////////////////////////////////////
// UTILITY
///////////////////////////////////////////////////////////
// Return the auth token header
//
//
getAuthHeaderObject = function() {
return {
Authorization: "Bearer " + app.shell.stateMap.user.token
};
};
//////////////////////////////////////////////////////////////////////////////////////
// ROCKFISH CORE ROUTES
///////////////////////////////////////////////////////////
//Create
//Route app.post('/api/:obj_type/create', function (req, res) {
//
create = function(apiRoute, objData, callback) {
$.ajax({
method: "post",
dataType: "json",
url: app.shell.stateMap.apiUrl + apiRoute,
headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8",
data: JSON.stringify(objData),
success: function(data, textStatus) {
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
/////////////////
//Get - get anything, the caller provides the route, this should replace most legacy get
//
get = function(apiRoute, callback) {
$.ajax({
method: "GET",
dataType: "json",
url: app.shell.stateMap.apiUrl + apiRoute,
headers: getAuthHeaderObject(),
success: function(data, textStatus) {
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
////////////////////
///////////////////////////////////////////////////////////
//Update
//route: app.post('/api/:obj_type/update/:id', function (req, res) {
//
update = function(objType, objData, callback) {
var theId;
if (!objData.id) {
return callback({
error: 1,
msg: "app.api.js::update->Error: missing id field in update document",
error_detail: objData
});
}
theId = objData.id;
$.ajax({
method: "put",
dataType: "json",
url: app.shell.stateMap.apiUrl + objType + "/" + theId,
headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8",
data: JSON.stringify(objData),
success: function(data, textStatus) {
if (data == null) {
data = { ok: 1 };
}
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
///////////////////////////////////////////////////////////
//remove Item
remove = function(apiRoute, callback) {
$.ajax({
method: "DELETE",
dataType: "json",
url: app.shell.stateMap.apiUrl + apiRoute,
headers: getAuthHeaderObject(),
success: function(data, textStatus) {
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
///////////////////////////////////////////////////////////
// uploadFile
// (ajax route to upload a file)
//
uploadFile = function(apiRoute, objData, callback) {
$.ajax({
method: "post",
dataType: "json",
url: app.shell.stateMap.apiUrl + apiRoute,
headers: getAuthHeaderObject(),
contentType: false,
processData: false,
data: objData,
success: function(data, textStatus) {
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
//////////////////////////////////////////////////////////////
//putAction - ad-hoc put method used to trigger actions etc
//
putAction = function(apiRoute, callback) {
$.ajax({
method: "put",
dataType: "json",
url: app.shell.stateMap.apiUrl + apiRoute,
headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8",
//data: JSON.stringify(objData),
success: function(data, textStatus) {
if (data == null) {
data = { ok: 1 };
}
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
//////////////////////////////////////////////////////////////////////////////////////
// LICENSE KEY RELATED API METHODS
///////////////////////////////////////////////////////////
//CreateLicense
//Route app.post('/api/license/create', function (req, res) {
//
createLicense = function(objData, callback) {
$.ajax({
method: "post",
dataType: "text",
url: app.shell.stateMap.apiUrl + "license/generate",
headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8",
data: JSON.stringify(objData),
success: function(data, textStatus) {
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
///////////////////////////////////////////////////////////
//GetLicenseRequests
//Fetch license requests
//route: app.get('/api/license/requests', function (req, res) {
//
getLicenseRequests = function(callback) {
$.ajax({
method: "GET",
dataType: "json",
url: app.shell.stateMap.apiUrl + "license/requests",
headers: getAuthHeaderObject(),
success: function(data, textStatus) {
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
///////////////////////////////////////////////////////////
//GenerateFromRequest
//Fetch generated response to license request
//route: app.get('/api/license/generateFromRequest/:uid', function (req, res) {
//
generateFromRequest = function(uid, callback) {
$.ajax({
method: "GET",
dataType: "json",
url: app.shell.stateMap.apiUrl + "license/generateFromRequest/" + uid,
headers: getAuthHeaderObject(),
success: function(data, textStatus) {
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
///////////////////////////////////////////////////////////
//Email license request response
//app.post('/api/license/email_response', function (req, res) {
//
licenseEmailResponse = function(objData, callback) {
$.ajax({
method: "post",
dataType: "text",
url: app.shell.stateMap.apiUrl + "license/email_response",
headers: getAuthHeaderObject(),
contentType: "application/json; charset=utf-8",
data: JSON.stringify(objData),
success: function(data, textStatus) {
callback(data);
},
error: function(jqXHR, textStatus, errorThrown) {
callback({
error: 1,
msg: textStatus + "\n" + errorThrown,
error_detail: {}
});
}
});
};
initModule = function() {};
return {
initModule: initModule,
getAuthHeaderObject: getAuthHeaderObject,
RockFishVersion: RockFishVersion,
get: get,
remove: remove,
create: create,
update: update,
uploadFile: uploadFile,
putAction: putAction,
createLicense: createLicense,
getLicenseRequests: getLicenseRequests,
generateFromRequest: generateFromRequest,
licenseEmailResponse: licenseEmailResponse
};
})();

View File

@@ -0,0 +1,103 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.authenticate = (function() {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSubmit,
configModule,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
onSubmit = function(event) {
event.preventDefault();
//get creds
var login = $('#login').val();
var password = $('#password').val();
$.ajax({
method: "post",
dataType: "json",
url: app.shell.stateMap.apiUrl.replace('/api/', '/authenticate'),
data: {
login: login,
password: password
},
success: function(data, textStatus, jqXHR) {
if (data.ok == 1) {
app.shell.stateMap.user.authenticated = true;
app.shell.stateMap.user.token = data.token;
app.shell.stateMap.user.name = data.name;
app.shell.stateMap.user.id=data.id;
//token expiry date
app.shell.stateMap.user.expires = data.expires;
//tell the shell we've logged in successfully
$.gevent.publish('app-login', {
name: login
});
} else {
if (data.error) {
$.gevent.publish('app-show-error',data.error);
}
app.shell.stateMap.user.authenticated = false;
app.shell.stateMap.user.token = '';
app.shell.stateMap.user.name = 'please sign in';
$.gevent.publish('app-logout');
}
},
error: function(jqXHR, textStatus, errorThrown) {
app.shell.stateMap.user.authenticated = false;
app.shell.stateMap.user.token = '';
app.shell.stateMap.user.name = 'please sign in';
$.gevent.publish('app-logout');
$.gevent.publish('app-show-error',textStatus + " " + errorThrown);
}
});
return false; //prevent default?
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function(context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.authenticate']({}));
$('#btnSubmit').bind('click', onSubmit);
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,143 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.customerEdit = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSave, onDelete,
configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN EVENT HANDLERS -------------------
//ONSAVE
//
onSave = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
//is this a new record?
if (stateMap.id != 'new') {
//put id into the form data
submitData.id = stateMap.id;
app.api.update('customer', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
}
});
} else {
//it's a new record - create
app.api.create('customer', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
page('#!/customerEdit/' + res.id);
}
});
}
return false; //prevent default?
};
//ONDELETE
//
onDelete = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
var r = confirm("Are you sure you want to delete this record?");
if (r == true) {
//Delete customer and children
app.api.remove('customer/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//deleted, return to customers list
page('#!/customers');
return false;
}
});
} else {
return false;
}
return false; //prevent default?
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.customerEdit']({}));
////app.nav.setContextTitle("Customer");
//id should always have a value, either a record id or the keyword 'new' for making a new object
if (stateMap.id != 'new') {
//fetch existing record
app.api.get('customer/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//fill out form
app.utilB.formData(res);
}
});
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("customerSites/" + stateMap.id, "Sites", "city");//url title icon
} else {
$('#btn-delete').hide();
app.nav.contextClear();
}
// bind actions
$('#btn-save').bind('click', onSave);
$('#btn-delete').bind('click', onDelete);
};
// RETURN PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,168 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.customerSiteEdit = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSave, onDelete, configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
onSave = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
//is this a new record?
if (stateMap.id != 'new') {
//put id into the form data
submitData.id = stateMap.id;
app.api.update('site', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
}
});
} else {
//create new record
app.api.create('site', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
page('#!/customerSiteEdit/' + res.id + '/' + stateMap.context.params.cust_id);
return false;
}
});
}
return false; //prevent default
};
//ONDELETE
//
onDelete = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
var r = confirm("Are you sure you want to delete this record?");
if (r == true) {
//--------------------------------------------
//==== DELETE THE site and it's children ====
app.api.remove('site/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//deleted, return to customers list
page('#!/customerSites/' + stateMap.context.params.cust_id);
return false;
}
});
//--------------------
} else {
return false;
}
return false; //prevent default?
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.customerSiteEdit']({}));
var title = "Site";
if (stateMap.context.params.cust_id) {
//Append customer id as a hidden form field for referential integrity
$('<input />').attr('type', 'hidden')
.attr('name', "customerId")
.attr('value', stateMap.context.params.cust_id)
.appendTo('#frm');
//fetch existing record
app.api.get('customer/' + stateMap.context.params.cust_id + '/name', function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
title = 'Site - ' + res.name;
if (stateMap.id != 'new') {
//fetch existing record
app.api.get('site/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//fill out form
app.utilB.formData(res);
}
});
}
//set customer name
//app.nav.setContextTitle(title);
}
});
}
// bind actions
$('#btn-save').bind('click', onSave);
$('#btn-delete').bind('click', onDelete);
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("customerEdit/" + stateMap.context.params.cust_id, "Customer", "account");
app.nav.contextAddLink("customerSites/" + stateMap.context.params.cust_id, "Sites", "city");
if (stateMap.id != 'new') {
app.nav.contextAddLink("purchases/" + stateMap.id, "Purchases", "basket");
}
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,131 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.customerSites = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
configMap = {
//main_html: '',
settable_map: {}
},
stateMap = {
$append_target: null
},
onSubmit,
configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//--------------------- BEGIN DOM METHODS --------------------
// Begin private DOM methods
// End private DOM methods
//---------------------- END DOM METHODS ---------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
// Begin public method /initModule/
// Example : app.customer.initModule( $('#div_id') );
// Purpose : directs the module to being offering features
// Arguments : $container - container to use
// Action : Provides interface
// Returns : none
// Throws : none
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.customerSites']({}));
var title = "Sites";
if (stateMap.id) {
app.api.get('customer/' + stateMap.id + '/name', function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//set customer name
title = 'Sites - ' + res.name;
//app.nav.setContextTitle(title);
if (stateMap.id) {
//fetch sites list
//fetch existing record
app.api.get('customer/' + stateMap.id + '/sites', function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//get the list ul
var $appList = $('#rf-list');
$.each(res, function (i, obj) {
$appList.append("<li><a href=\"#!/customerSiteEdit/" + obj.id + "/" + stateMap.id + "\">" +
app.utilB.genListColumn(obj.name) +
"</a></li>")
});
}
});
}
}
});
}
//Context menu
app.nav.contextClear();
app.nav.contextAddLink('customerSiteEdit/new/' + stateMap.id, "New", "plus");
app.nav.contextAddLink("customerEdit/" + stateMap.id, "Customer", "account");
};
// End public method /initModule/
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

192
wwwroot/js/app.customers.js Normal file
View File

@@ -0,0 +1,192 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.customers = (function() {
"use strict";
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var stateMap = {},
configModule,
initModule,
generateCard,
onShowMore;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//////////////////
//Generate a card with collapsible middle section with more details
//
generateCard = function(obj) {
var editUrl = "#!/customerEdit/" + obj.id;
var cardClass = obj.active
? "border-primary text-primary"
: "border-secondary text-secondary";
var urlClass = obj.active ? "" : "text-secondary";
return (
'<div class="card ' +
cardClass +
' mb-3">' +
'<h4 class="card-header"><a class="' +
urlClass +
'" href=' +
editUrl +
">" +
obj.name +
"</a></h4>" +
'<div class="collapse" id="card-collapse' +
obj.id +
'">' +
'<div id="card-body' +
obj.id +
'" class="card-body"/>' +
"</div>" +
'<div class="card-footer">' +
'<button id="btnMore' +
obj.id +
'" class="btn btn-outline-dark mdi mdi-basket mdi-24px mr-3" type="button"/>' +
// '<a href="' + editUrl + '" class="btn btn-outline-success mdi mdi-account-edit mdi-24px "></a>' +
"</div>" +
"</div>"
);
};
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
////////////////////////////////////////////
//ONMORE
//
onShowMore = function(event) {
event.preventDefault();
var customerId = event.data;
var $cardbody = $("#card-body" + customerId);
var $collapseDiv = $("#card-collapse" + customerId);
var isOpen = $collapseDiv.hasClass("show");
//either way we don't want the old contents hanging around
$cardbody.empty();
//Reload the data?
if (!isOpen) {
//===================
//Get sites
app.api.get("customer/" + customerId + "/activesubforsites", function(
sites
) {
if (sites.error) {
$.gevent.publish("app-show-error", sites.msg);
} else {
var cardDisplay = '<ul class="list-unstyled">';
//Iterate the sites
for (var y = 0; y < sites.length; y++) {
//append the site name
cardDisplay +=
'<li class="font-weight-bold">' + sites[y].name + "</li>";
//append the active subs
//purchase link for future
//https://rockfish.ayanova.com/default.htm#!/purchaseEdit/<PURCHASEID>/<SITEID>
if (sites[y].children.length > 0) {
cardDisplay += '<ul class="mb-2">';
for (var x = 0; x < sites[y].children.length; x++) {
cardDisplay += "<li>" + sites[y].children[x].name + "</li>";
}
cardDisplay += "</ul>";
} else {
cardDisplay +=
'<ul class="text-danger"><li>NO ACTIVE SUBS</li></ul>';
}
}
cardDisplay += "</ul>";
$cardbody.append(cardDisplay);
//Toggle open after populating the card
$collapseDiv.collapse("toggle");
}
});
//=========/sites==============
} else {
//Toggle closed
$collapseDiv.collapse("toggle");
}
return false;
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function(context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function($container) {
if (typeof $container === "undefined") {
$container = $("#app-shell-main-content");
}
$container.html(Handlebars.templates["app.customers"]({}));
//===================
//Get customers
app.api.get("customer/list", function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
var $appList = $("#rf-list");
var activeCount = 0;
var inactiveCount = 0;
$.each(res, function(i, obj) {
if (obj.active) {
activeCount++;
} else {
inactiveCount++;
}
$appList.append(generateCard(obj));
$("#btnMore" + obj.id).bind("click", obj.id, onShowMore);
});
//Show the count of customers active and inactive
$("#rf-list-count")
.empty()
.append(
res.length +
" items (" +
activeCount +
" active, " +
inactiveCount +
" inactive)"
);
}
});
//=========/customers==============
app.nav.contextClear();
app.nav.contextAddLink("customerEdit/new", "New", "plus");
app.nav.contextAddLink("search", "Search", "magnify");
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
})();

View File

@@ -0,0 +1,56 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.fourohfour = (function() {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
configModule,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function(context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.fourohfour']({}));
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

167
wwwroot/js/app.inbox.js Normal file
View File

@@ -0,0 +1,167 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.inbox = (function() {
"use strict";
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var stateMap = {},
configModule,
initModule,
terminateModule,
getMessages,
timerVar=null;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
getMessages = function() {
stateMap.$appList.html("<h4>Checking...</h4>");
app.api.get("mail/salesandsupportsummaries", function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
stateMap.$appList.empty();
var newMessageCount = 0;
var lastAccount = "";
//The list
var displayedItems = 0;
var generatedHtml = '<ul class="list-group">';
//Iterate the results
for (var y = 0; y < res.length; y++) {
var obj = res[y];
if (!obj.flags.includes("deleted")) {
if (!obj.flags.includes("seen")) {
newMessageCount++;
}
var displayClass = obj.flags.includes("seen")
? "border-secondary text-secondary"
: "border-primary text-primary";
var answeredIconClass = obj.flags.includes("answered")
? " mdi mdi-reply"
: "";
//Make a group on change of account
if (lastAccount !== obj.account) {
lastAccount = obj.account;
//Insert as 'header' in list
generatedHtml +=
'<li class="list-group-item active">' + lastAccount + "</li>";
}
//LIST ITEM
generatedHtml +=
'<li class="list-group-item" ><a href="#!/mailEdit/' +
obj.account +
"/Inbox/" +
obj.id +
"\"><span class='" +
displayClass +
answeredIconClass +
"'>" +
obj.subject +
"&nbsp;-&nbsp;" +
obj.from +
"</span></a></li>";
displayedItems++;
} //if not deleted
} //loop
//Nothing to display?
if (!displayedItems) {
generatedHtml +=
'<li class="list-group-item">NO MESSAGES - ' +
moment().format("YYYY-MM-DD LT") +
"</li>";
}
//close list group
generatedHtml += "</ul>";
generatedHtml +=
'<div class="mt-5"><h6><small class="text-muted">Last check: ' +
moment().format("YYYY-MM-DD LT") +
"</small><h6></div>";
//SET IT
stateMap.$appList.append(generatedHtml);
//case 3516
if (newMessageCount > 0) {
document.title =
newMessageCount +
" NEW message" +
(newMessageCount == 1 ? "" : "s");
} else {
document.title = "No new messages";
}
}
//do it every 5 minutes
timerVar=setTimeout(getMessages,5*60*1000);
console.log("INBOX.GETMESSAGES - started timer " + timerVar);
});
};
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function(context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function($container) {
if (typeof $container === "undefined") {
$container = $("#app-shell-main-content");
}
$container.html(Handlebars.templates["app.inbox"]({}));
stateMap.$appList = $("#rf-list-div");
getMessages();
//auto refresh every 10 minutes
// intervalRef = setInterval(function() {
// getMessages();
// }, 10 * 60 * 1000);
app.nav.contextClear();
////app.nav.setContextTitle("inbox");
};
// TERMINATE MODULE
//
terminateModule = function() {
if(timerVar!=null){
clearTimeout(timerVar);
console.log("INBOX.TERMINATEMODULE - cleared timer" + timerVar);
}
//clear up event handler
// clearInterval(intervalRef);
// intervalRef=null;
//console.log("INBOX.TERMINATEMODULE");
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule,
terminateModule: terminateModule
};
//------------------- END PUBLIC METHODS ---------------------
})();

156
wwwroot/js/app.license.js Normal file
View File

@@ -0,0 +1,156 @@
/*
* app.license.js
* License key generator
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.license = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
configModule, initModule, onGenerate, onSelectAllAddOns;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
onGenerate = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
app.api.createLicense(submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
$('#key').val(res);
return false;
}
});
return false; //prevent default
};
onSelectAllAddOns = function (event) {
event.preventDefault();
$('#wbi').prop('checked', true);
$('#mbi').prop('checked', true);
$('#ri').prop('checked', true);
$('#qbi').prop('checked', true);
$('#qboi').prop('checked', true);
$('#pti').prop('checked', true);
$('#quickNotification').prop('checked', true);
$('#exportToXls').prop('checked', true);
$('#outlookSchedule').prop('checked', true);
$('#oli').prop('checked', true);
$('#importExportCSVDuplicate').prop('checked', true);
return false; //prevent default
};
// onTemplates = function(event) {
// event.preventDefault();
// alert("STUB: templates");
// return false; //prevent default
// };
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.license']({}));
//case 3233 customer list
//Fill customer list combo
var customerList = {};
//get customers
app.api.get('customer/list', function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
var html = '<option value="0">&lt;TRIAL&gt;</option>';
for (var i = 0, len = res.length; i < len; ++i) {
html += ('<option value="' + res[i]['id'] + '">' + res[i]['name'] + '</option>');
customerList[res[i]['id']] = res[i]['name'];
}
$('#customerId').append(html);
}
});
//Context menu
app.nav.contextClear();
////app.nav.setContextTitle("License");
//make context menu
//Context menu
app.nav.contextClear();
app.nav.contextAddButton('btn-generate', 'Make', 'key', onGenerate);
app.nav.contextAddButton('btn-select-all-addons', 'All', 'check-all', onSelectAllAddOns);
// app.nav.contextAddLink("licenseRequests/", "Requests", "voice");
app.nav.contextAddLink("licenseTemplates/", "", "layers");
//case 3233
app.nav.contextAddLink("licenses/", "List", "");
//set all date inputs to today plus one year
var oneYearFromNow = moment().add(1, 'years').toISOString().substring(0, 10);
var oneMonthFromNow = moment().add(1, 'months').toISOString().substring(0, 10);
$('input[type="date"]').val(oneYearFromNow);
$('#lockoutDate').val(oneMonthFromNow);
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,138 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.licenseRequestEdit = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSend, onCancel, onRegenerate, configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
////////////////////
//
onSend = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
app.api.licenseEmailResponse(submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//navigate back to licenseRequests
//alert("key has been sent!");
window.location.href = "#!/inbox/";
//$('#key').val(res);
return false;
}
});
return false; //prevent default
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.licenseRequestEdit']({}));
////app.nav.setContextTitle("Request");
//Append key hidden values for submit and processing by server
//This is set from the original click to open this form
$('<input />').attr('type', 'hidden')
.attr('name', "request_email_uid")
.attr('value', stateMap.id)
.appendTo('#frm');
//These are empty but will be filled in when the server responds with the "record"
//They are for re-submission back to the server to save a step of refetching the original info
//and recalculating stuff that was already done
$('<input />').attr('type', 'hidden')
.attr('name', "requestReplyToAddress")
.attr('value', '')
.appendTo('#frm');
$('<input />').attr('type', 'hidden')
.attr('name', "requestFromReplySubject")
.attr('value', '')
.appendTo('#frm');
//rfcore unused?
$('<input />').attr('type', 'hidden')
.attr('name', "greetingReplySubject")
.attr('value', '')
.appendTo('#frm');
$('<input />').attr('type', 'hidden')
.attr('name', "requestFromReplySubject")
.attr('value', '')
.appendTo('#frm');
//fetch existing record
app.api.generateFromRequest(stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//fill out form
app.utilB.formData(res);
}
});
//Context menu
app.nav.contextClear();
app.nav.contextAddButton('btn-generate', 'Send', 'send', onSend);
app.nav.contextAddLink("inbox/", "Inbox", "inbox");
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,102 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.licenseTemplates = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSave, configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//different than the other edit routes because it's global and there is only one
//so no ID
onSave = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
submitData["id"] = '1';
app.api.update('licenseTemplates', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
}
});
return false; //prevent default
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
// if (stateMap.context.params.id) {
// stateMap.id = stateMap.context.params.id;
// }
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.licenseTemplates']({}));
//fetch existing record
//Note license templates record id is always 1 as there is only ever one record in db
app.api.get('licenseTemplates/' + '1', function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//fill out form
app.utilB.formData(res);
}
});
//set title
// var title = "License message templates";
// //app.nav.setContextTitle(title);
// bind actions
$('#btn-save').bind('click', onSave);
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("license/", "License", "key");
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,116 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.licenseView = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSave, onDelete,
configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN EVENT HANDLERS -------------------
//ONSAVE
//
onSave = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
var isFetched=$('#fetched').prop('checked')
// var submitData = { isFetched: isFetched };
app.api.putAction('license/fetched/' + stateMap.id + "/" + isFetched, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
}
});
return false; //prevent default?
};
//ONDELETE
//
onDelete = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
var r = confirm("Are you sure you want to delete this record?");
if (r == true) {
app.api.remove('license/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
page('#!/licenses');
return false;
}
});
} else {
return false;
}
return false; //prevent default?
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.licenseView']({}));
document.title = 'License ';
//fetch existing record
app.api.get('license/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
//fill out form
app.utilB.formData(res);
}
});
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("licenses/", "List", "");
// bind actions
$('#btn-save').bind('click', onSave);
$('#btn-delete').bind('click', onDelete);
};
// RETURN PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

108
wwwroot/js/app.licenses.js Normal file
View File

@@ -0,0 +1,108 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.licenses = (function() {
"use strict";
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var stateMap = {},
configModule,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function(context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function($container) {
if (typeof $container === "undefined") {
$container = $("#app-shell-main-content");
}
$container.html(Handlebars.templates["app.licenses"]({}));
//case 3513
document.title = "Licenses";
//===================
//Get licenses
app.api.get("license/list", function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
var $appList = $("#rf-list");
$appList.append('<ul class="list-group">');
$.each(res, function(i, obj) {
if (obj.fetched) {
$appList.append(
"<li class='rfc-list-item-inactive list-group-item'><a href=\"#!/licenseView/" +
obj.id +
'">' +
"<span class='text-muted'>" +
(obj.trial
? "Trial "
: "") +
app.utilB.genListColumn(obj.regto) +
"&nbsp;&nbsp;" +
app.utilB.genListColumn(
app.utilB.epochToShortDate(obj.created)
) +
"</span>" +
"</a></li>"
);
} else {
$appList.append(
"<li class='list-group-item'><a href=\"#!/licenseView/" +
obj.id +
'">' +
(obj.trial
? "Trial "
: "") +
app.utilB.genListColumn(obj.regto) +
"&nbsp;&nbsp;" +
app.utilB.genListColumn(
app.utilB.epochToShortDate(obj.created)
) +
"</a></li>"
);
}
});
$appList.append("</ul>");
}
});
//=========/licenses==============
app.nav.contextClear();
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
})();

160
wwwroot/js/app.mailEdit.js Normal file
View File

@@ -0,0 +1,160 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.mailEdit = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSave, onDelete, onSend,
configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN EVENT HANDLERS -------------------
///////////////////////////////////
// SEND A REPLY OR NEW MESSAGE
//
onSend = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
//var submitData = {composition:$("#composition").val()};
var submitData = app.utilB.objectifyFormDataArray(formData);
//is this a new record?
if (stateMap.id != 'new') {
//put id into the form data
// submitData.id = stateMap.id;
app.api.create('mail/reply/' +
stateMap.context.params.mail_account +
'/' + stateMap.context.params.mail_id, submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
page('#!/inbox');
}
});
} else {
alert("STUB: New message composition not implemented yet");
// //it's a new record - create
// app.api.create('customer', submitData, function (res) {
// if (res.error) {
// $.gevent.publish('app-show-error',res.msg);
// } else {
// page('#!/inbox');
// }
// });
}
return false;
};
//ONDELETE
//
//removed
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.mailEdit']({}));
// stateMap.replyMode = false;
// if (stateMap.context.querystring.endsWith('reply')) {
// stateMap.replyMode = true;
// }
app.nav.contextClear();
app.nav.contextAddLink("inbox/", "Inbox", "inbox");
//id should always have a value, either a record id or the keyword 'new' for making a new object
if (stateMap.id != 'new') {
//fetch existing record
app.api.get(
'mail/preview/' +
stateMap.context.params.mail_account +
'/' + stateMap.context.params.mail_folder +
'/' + stateMap.context.params.mail_id, function (res) {
if (res.error) {
// app.nav.contextClear();
// app.nav.contextAddLink("inbox/", "Inbox", "inbox");
$.gevent.publish('app-show-error', res.msg);
} else {
//fill out form
// app.nav.contextClear();
//app.nav.contextAddLink("inbox/", "Inbox", "inbox");
$('#message').text(res.preview);
if(res.isKeyRequest){
app.nav.contextAddLink("licenseRequestEdit/" + res.id, "Make", "key");
}else{
$('#composition').text("\n\n- John\nwww.ayanova.com");
}
}
});
$('#btn-send').bind('click', onSend);
//Context menu
//app.nav.contextAddButton('btn-send', 'Send reply', 'send', onSend);
// app.nav.contextAddButton('btn-generate', 'Build', 'key', onReply);
// app.nav.contextAddLink("customerSites/" + stateMap.id, "Sites", "city");//url title icon
} else {
//NEW email options
var $group = $("#sendToGroup");
$group.removeClass("invisible");
// app.nav.contextClear();
// app.nav.contextAddLink("inbox/", "Inbox", "inbox");
// app.nav.contextAddButton('btn-send', 'Send', 'send', onSend);
}
};
// RETURN PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

102
wwwroot/js/app.nav.js Normal file
View File

@@ -0,0 +1,102 @@
/*
* app.nav.js
* Handle dynamic navigation related operations
* toobar, menu, fab etc
*
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.nav = (function () {
var contextClear, contextAddButton, contextAddLink, backUrl, backRemove, moreMenuInitialized,
setContextTitle, setSelectedMenuItem;
// ///////////////////////////////////////////
// //Set context menu title
// //
// var setContextTitle = function (title) {
// $("#rf-context-title").html(title + "...");
// }
////////////////////////////////////////////////
//Clear the contents of the context menu (reset it)
//
contextClear = function () {
$("#rf-context-group").empty();
$("#rf-context-group").addClass("invisible");
};
////////////////////////////////////////////////
//Add a non nav button item to the context menu
//
contextAddButton = function (id, title, icon, clickHandler) {
var $group=$("#rf-context-group");
$group.removeClass("invisible");
var iconClass = '';
if (icon) {
iconClass = "mdi mdi-" + icon;
}
$group.append(
'<button type="button" id="' + id + '" class="btn btn-outline-dark ' + iconClass + '" href="#">' + title + '</a>'
);
$('#' + id).bind('click', clickHandler);
};
// <button type="button" class="btn btn-outline-dark mdi mdi-key">Left</button>
////////////////////////////////////////////////
//Add an url item to the context menu handling
//phone vs larger sizes
//
contextAddLink = function (url, title, icon) {
var $group=$("#rf-context-group");
$group.removeClass("invisible");
var iconClass = '';
if (icon) {
iconClass = "mdi mdi-" + icon;
}
$group.append(
'<a class="btn btn-outline-dark ' + iconClass + '" href="#!/' + encodeURI(url) + '">' + title + '</a>'
);
};
////////////////////////////////////////////////
//Set the active class on the selected menu item
//
setSelectedMenuItem = function (selectedMenuItem) {
$(".nav-item").removeClass("active");
if (selectedMenuItem) {
$("#" + selectedMenuItem).addClass("active");
}
};
return {
contextClear: contextClear,
contextAddButton: contextAddButton,
contextAddLink: contextAddLink,
setContextTitle: setContextTitle,
setSelectedMenuItem: setSelectedMenuItem
};
}());

View File

@@ -0,0 +1,266 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.purchaseEdit = (function() {
"use strict";
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var stateMap = {},
onSave,
onDelete,
onRenew,
configModule,
initModule,
onPasteNotes;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
onSave = function(event) {
event.preventDefault();
$.gevent.publish("app-clear-error");
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
//is this a new record?
if (stateMap.id != "new") {
//put id into the form data
submitData.id = stateMap.id;
app.api.update("purchase", submitData, function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
}
});
} else {
//create new record
app.api.create("purchase", submitData, function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
page(
"#!/purchaseEdit/" + res.id + "/" + stateMap.context.params.site_id
);
return false;
}
});
}
return false; //prevent default
};
onRenew = function(event) {
event.preventDefault();
$.gevent.publish("app-clear-error");
if (stateMap.id == "new") {
$.gevent.publish(
"app-show-error",
"Save this record before attempting to renew it"
);
return false;
}
stateMap.id = "new";
//case 3396, no more renewal or dupe names
// var nm = $('#name').val();
// nm = "DUPE-" + nm;
// $('#name').val(nm);
//case 3396, set values accordingly
//Clear salesOrderNumber
$("#salesOrderNumber").val("");
//set purchaseDate to today
$("#purchaseDate").val(
moment()
.toISOString()
.substring(0, 10)
);
//set expireDate to plus one year from today
$("#expireDate").val(
moment()
.add(1, "years")
.toISOString()
.substring(0, 10)
);
//clear the couponCode
$("#couponCode").val("");
//clear the notes
$("#notes").val("");
$("#renewNoticeSent").prop("checked", false);
$("#cancelDate").val("");
return false; //prevent default
};
//ONDELETE
//
onDelete = function(event) {
event.preventDefault();
$.gevent.publish("app-clear-error");
var r = confirm("Are you sure you want to delete this record?");
if (r == true) {
//==== DELETE ====
app.api.remove("purchase/" + stateMap.id, function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
//deleted, return to master list
page("#!/purchases/" + stateMap.context.params.site_id);
return false;
}
});
} else {
return false;
}
return false; //prevent default?
};
onPasteNotes = function(event) {
var clipboardData, pastedData;
var e = event.originalEvent;
// // Stop data actually being pasted into div
// e.stopPropagation();
// e.preventDefault();
// Get pasted data via clipboard API
clipboardData = e.clipboardData || window.clipboardData;
pastedData = clipboardData.getData("Text");
//Iterate through the lines looking for the SHareIt name=value lines (they all contain equal signs)
var lines = pastedData.split("\n"); // lines is an array of strings
var purchaseData = {};
// Loop through all lines
for (var j = 0; j < lines.length; j++) {
var thisLine = lines[j];
if (thisLine.includes("=")) {
var thisElement = thisLine.split("=");
purchaseData[thisElement[0].trim()] = thisElement[1].trim();
}
}
//Now have an object with the value pairs in it
if (purchaseData["ShareIt Ref #"]) {
$("#salesOrderNumber").val(purchaseData["ShareIt Ref #"]);
}
// if (purchaseData["E-Mail"]) {
// $("#email").val(purchaseData["E-Mail"]);
// }
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function(context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function($container) {
if (typeof $container === "undefined") {
$container = $("#app-shell-main-content");
}
$container.html(Handlebars.templates["app.purchaseEdit"]({}));
var title = "Purchase";
if (!stateMap.context.params.site_id) {
throw "app.purchaseEdit.js::initModule - There is no stateMap.context.params.site_id!";
}
//Append master record id as a hidden form field for referential integrity
$("<input />")
.attr("type", "hidden")
.attr("name", "siteId")
.attr("value", stateMap.context.params.site_id)
.appendTo("#frm");
//fetch entire site record to get name *and* customer id which is required for redundancy
//RFC - get site name and customer name for form
app.api.get("site/" + stateMap.context.params.site_id, function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
//Also append customer ID redundantly
$("<input />")
.attr("type", "hidden")
.attr("name", "customerId")
.attr("value", res.customerId)
.appendTo("#frm");
title = "Purchase - " + res.name;
if (stateMap.id != "new") {
//fetch existing record
app.api.get("purchase/" + stateMap.id, function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
//fill out form
app.utilB.formData(res);
}
});
} else {
//it's a new record, set default
$("#purchaseDate").val(new Date().toISOString().substring(0, 10));
}
//set title
//app.nav.setContextTitle(title);
}
});
//Context menu
app.nav.contextClear();
app.nav.contextAddLink(
"purchases/" + stateMap.context.params.site_id,
"Purchases",
"basket"
);
// bind actions
$("#btn-save").bind("click", onSave);
$("#btn-delete").bind("click", onDelete);
$("#btn-renew").bind("click", onRenew);
$("#notes").bind("paste", onPasteNotes);
//Autocomplete
app.utilB.autoComplete("name", "purchase.name");
app.utilB.autoComplete("productCode", "purchase.productCode");
app.utilB.autoComplete("vendorName", "purchase.vendorName");
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
})();

145
wwwroot/js/app.purchases.js Normal file
View File

@@ -0,0 +1,145 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.purchases = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
configMap = {
//main_html: '',
settable_map: {}
},
stateMap = {
$append_target: null
},
onSubmit, loadList, configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
loadList = function () {
//----
//fetch
app.api.get('site/' + stateMap.id + '/purchases', function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//get the list ul
var $appList = $('#rf-list');
$appList.empty();
$.each(res, function (i, obj) {
if (obj.cancelDate) {
$appList.append("<li class='rfc-list-item-inactive'><a href=\"#!/purchaseEdit/" + obj.id + "/" + stateMap.id + "\">" +
// app.utilB.genListColumn(app.utilB.epochToShortDate(obj.purchaseDate)) +
// '&nbsp;' +
"<span class='text-muted'>"+
app.utilB.genListColumn(obj.name) +
"&nbsp;cancelled&nbsp;" +
app.utilB.genListColumn(app.utilB.epochToShortDate(obj.cancelDate)) +
"</span>"+
"</a></li>");
} else {
$appList.append("<li><a href=\"#!/purchaseEdit/" + obj.id + "/" + stateMap.id + "\">" +
// app.utilB.genListColumn(app.utilB.epochToShortDate(obj.purchaseDate)) +
// '&nbsp;' +
app.utilB.genListColumn(obj.name) +
"&nbsp;expires&nbsp;" +
app.utilB.genListColumn(app.utilB.epochToShortDate(obj.expireDate)) +
"</a></li>");
//
}
});
}
});
//------
}
//-------------------- END UTILITY METHODS -------------------
//--------------------- BEGIN DOM METHODS --------------------
// Begin private DOM methods
// End private DOM methods
//---------------------- END DOM METHODS ---------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
// Begin public method /initModule/
// Example : app.customer.initModule( $('#div_id') );
// Purpose : directs the module to being offering features
// Arguments : $container - container to use
// Action : Provides interface
// Returns : none
// Throws : none
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
stateMap.sortOrder = 'd';
$container.html(Handlebars.templates['app.purchases']({}));
if (!stateMap.id) {
throw ('app.purchases.js::initModule - There is no stateMap.id!');
}
app.api.get('site/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//Context menu
app.nav.contextClear();
app.nav.contextAddLink('purchaseEdit/new/' + stateMap.id, "New", "plus");
app.nav.contextAddLink("customerEdit/" + res.customerId, "Customer", "account");
app.nav.contextAddLink("customerSiteEdit/" + stateMap.id + '/' + res.customerId, "Site", "city");
if (stateMap.id) {
loadList();
}
}
});
};
// End public method /initModule/
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,67 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.reportData = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
configModule,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.reportData']({}));
//Context menu
app.nav.contextClear();
////app.nav.setContextTitle("Reports");
app.nav.contextAddLink("templates", "Templates", "widgets");//url title icon
var $appList = $('#rf-list');
$appList.empty();
$appList.append("<li><a href=\"#!/reportDataProdEmails/\">" + app.utilB.genListColumn("Get emails CSV by product code") + "</a></li>");
$appList.append("<li><a href=\"#!/reportDataExpires/\">" + app.utilB.genListColumn("Expiring subscriptions") + "</a></li>");
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,79 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.reportDataExpires = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
configModule,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.reportDataExpires']({}));
//fetch data
app.api.get('report/expires', function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//get the list ul
var $appList = $('#rf-list');
$appList.empty();
$.each(res, function (i, obj) {
$appList.append("<li><a href=\"#!/purchaseEdit/" + obj.id + "/" + obj.site_id + "\">" +
app.utilB.genListColumn(app.utilB.epochToShortDate(obj.expireDate)) +
app.utilB.genListColumn(obj.customer) +
app.utilB.genListColumn(obj.name) +
"</a></li>")
});
}
});
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("reportData/", "Reports", "book-open-variant");//url title icon
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,139 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.reportDataProdEmail = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onGenerate, configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
onGenerate = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
//var submitData = app.utilB.objectifyFormDataArray(formData);
//Now convert this specially for this method into a clean object to send with only what is required
var submitData = {};
submitData.products = [];
var l = formData.length;
for (var i = 0; i < l; i++) {
var fname = formData[i].name;
var fvalue = formData[i].value;
if (fname == 'ckIncidental') {
submitData.ckIncidental = (fvalue == 'true');
} else if (fname == 'ckNoContact') {
submitData.ckNoContact = (fvalue == 'true');
} else if (fname == 'csvdata') {
;//do nothing
} else {
//it's a product key
if (fvalue == 'true') {
submitData.products.push(fname);
}
}
}
app.api.create('report/emailsforproductcodes',submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
$('#csvdata').val(res);
return false;
}
});
return false; //prevent default
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
// if (stateMap.context.params.id) {
// stateMap.id = stateMap.context.params.id;
// }
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.reportDataProdEmail']({}));
//fetch data
app.api.get('purchase/productcodes',function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
$.each(res, function (i, obj) {
var pcode = obj.productCode;
var badCode = (!pcode);
//-----------
$('<input />', {
type: 'checkbox',
id: 'id' + i,
name: obj.productCode,
disabled: badCode
}).appendTo("#cbdiv");
$('#cbdiv').append('<label for="' + 'id' + i + '">&nbsp;' + (badCode ? '[NO PRODUCT CODE ENTERED]' : obj.productCode) + '&nbsp;' + obj.name + '</label>');
$('#cbdiv').append('<br/>');
//--------------
});
}
});
//set title
var title="Email addresses by product code";
//app.nav.setContextTitle(title);
//Context menu
app.nav.contextClear();
app.nav.contextAddButton('btn-generate', 'Build list', 'build', onGenerate);
app.nav.contextAddLink("reportData/", "Reports", "book-open-variant");
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,283 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.rfcaseEdit = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSave, onDelete,
configModule, initModule, onAttachment, onUpload, onAppend;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN EVENT HANDLERS -------------------
//ONSAVE
//
onSave = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
//is this a new record?
if (stateMap.id != 'new') {
//put id into the form data
submitData.id = stateMap.id;
app.api.update('rfcase', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
}
});
} else {
//it's a new record - create
//set dtCreated as it's not a form field
submitData.dtCreated = app.utilB.getCurrentDateTimeAsEpoch();
app.api.create('rfcase', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
page('#!/rfcaseEdit/' + res.id);
}
});
}
return false; //prevent default?
};
//ONDELETE
//
onDelete = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
var r = confirm("Are you sure you want to delete this record?");
if (r == true) {
//Delete
app.api.remove('rfCase/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
//deleted, return to list
page('#!/rfcases');
return false;
}
});
} else {
return false;
}
return false; //prevent default?
};
////////////////////////////////////////////
//Handle click on attachment
//
onAttachment = function (event) {
event.preventDefault();
var attachmentId = event.data;
alert("STUB: Attachment click id = " + attachmentId);
app.api.get('rfcase/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
;
}
});
return false;
};
////////////////////////////////////////////
//Handle upload click
//
onUpload = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
var fileUpload = $("#files").get(0);
var files = fileUpload.files;
var fileData = new FormData();
for (var i = 0; i < files.length; i++) {
fileData.append(files[i].name, files[i]);
}
app.api.uploadFile('rfcaseblob/upload?rfcaseid=' + stateMap.id, fileData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
page('#!/rfcaseEdit/' + stateMap.id);
}
});
return false;
};
////////////////////////////////////
//ONAPPEND
// Append date and time and a horizontal line to make a new entry
//
onAppend = function (event) {
event.preventDefault();
var $notes = $('#notes');
var txt = $notes.val();
txt += "\r\n====================\r\n";
txt += moment().format('MMM Do YYYY H:mm A');//"Aug 28th 2017 11:01 AM"
txt += ' Edited by ' + app.shell.stateMap.user.name;
txt += '\r\n\r\n';
$notes.focus()
.val("")
.val(txt);
return false; //prevent default?
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.rfcaseEdit']({}));
////app.nav.setContextTitle("Case");
var $cbProjects = $('#rfCaseProjectId');
var projectList = {};
//get projects
app.api.get('rfcaseproject', function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
var html = '';
for (var i = 0, len = res.length; i < len; ++i) {
html += ('<option value="' + res[i]['id'] + '">' + res[i]['name'] + '</option>');
projectList[res[i]['id']] = res[i]['name'];
}
$cbProjects.append(html);
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("rfcases/", "Cases", "bug");
app.nav.contextAddLink("rfcaseEdit/new", "New", "plus");
app.nav.contextAddButton('btn-save-top', 'Save', '', onSave);
//#####
//Now load the case itself
//------------
//id should always have a value, either a record id or the keyword 'new' for making a new object
if (stateMap.id != 'new') {
//case 3513 (ironically I can't see it yet)
document.title = 'Case ' + stateMap.id;
//fetch existing record
app.api.get('rfcase/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
//set the caseid header //<span class="badge badge-secondary">New</span>
$('#caseid').html(stateMap.id);
$('#dtcreated').html('created ' + app.utilB.epochToLocalShortDateTime(res.dtCreated));
//fill out form
app.utilB.formData(res);
//Get attachments separately
//===============
app.api.get('rfcase/' + stateMap.id + '/attachments', function (attachments) {
if (attachments.error) {
$.gevent.publish('app-show-error', attachments.msg);
} else {
//Create a list item for each attachment
var alist = '';
for (var x = 0; x < attachments.attach.length; x++) {
var attachment = attachments.attach[x];
alist += '<a target="_blank" href="/api/rfcaseblob/download/' + attachment.id + '?dlkey=' + attachments.dlkey + '" class="btn btn-default list-group-item list-group-item-action">' + attachment.name + '</a>'
}
$('#attachments').html(alist);
}
});
//================
}
});
$('#btn-upload').bind('click', onUpload);
$('#frmUpload').removeClass('invisible');
} else {
$('#btn-delete').hide();
//select rockfish as the default project
$cbProjects.val(44);
//3 is the default new item priority
$('#priority').val(3);
//case 3513
document.title = 'NEW CASE';
}
//-------------
//#####
}
});
// bind actions
$('#btn-save').bind('click', onSave);
$('#btn-delete').bind('click', onDelete);
$('#btn-append').bind('click', onAppend);
};
// RETURN PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

196
wwwroot/js/app.rfcases.js Normal file
View File

@@ -0,0 +1,196 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.rfcases = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
configModule, initModule,
$cbProjects, $appList, $open, $priority, $search,
projectList,
onFilterChange, loadCases, getPriorityColorClass,
restoreSelections;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
getPriorityColorClass = function (priority) {
switch (priority) {
case 1:
return 'success';
break;
case 2:
return 'warning';
break;
case 3:
return 'danger';
break;
default:
return 'secondary';
break;
}
}
//case 3363
restoreSelections = function () {
//if there are selections then restore them
if (stateMap.savedSelections) {
$cbProjects.val(stateMap.savedSelections.project);
$open.prop('checked', stateMap.savedSelections.open);
$priority.val(stateMap.savedSelections.priority);
$search.val(stateMap.savedSelections.search);
} else {
//Defaults
//select Rockfish as the default project
$cbProjects.val(44);
}
}
loadCases = function (projects) {
$appList.empty();
//get the filters
// public JsonResult GetList(long? Project, bool? Open, int? Priority, string Search)
var selectedProject = $cbProjects.val();
var selectedOpen = $open.prop('checked')
var selectedPriority = $priority.val();
var selectedSearch = $search.val();
stateMap.savedSelections = {
project: selectedProject,
open: selectedOpen,
priority: selectedPriority,
search: selectedSearch
}
if (selectedSearch) {
selectedSearch = encodeURI(selectedSearch);
}
//case 3363 save settings here
var filterUrl = '?project=' + selectedProject + '&open=' + selectedOpen + '&priority=' + selectedPriority + '&search=' + selectedSearch;
var that = this;
//get the cases
app.api.get('rfcase/list' + filterUrl, function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
//case 3450 count
$('#rf-list-count').empty().append(res.length+" items");
$.each(res, function (i, obj) {
var badgeClass = getPriorityColorClass(obj.priority);
var idColumn = '<span class="rf-larger"><strong>' + obj.id + '</strong></span><span class="rf-smaller ml-2 badge badge-' + badgeClass + '">' + obj.priority + '</span>';
$appList.append("<li class='mdc-list-item'><a href=\"#!/rfcaseEdit/" + obj.id + "\">" +
app.utilB.genListColumn(idColumn) +
'&nbsp;' +
(selectedProject == 0 ? app.utilB.genListColumn(projects[obj.rfCaseProject_Id]) : '') +
'&nbsp;' +
app.utilB.genListColumn(obj.title) +
'&nbsp;' +
app.utilB.genListColumn(app.utilB.epochToLocalShortDateTime(obj.dtCreated)) +
"</a></li>");
});
}
});
}
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
onFilterChange = function (event) {
event.preventDefault();
loadCases(projectList);
return false; //prevent default
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.rfcases']({}));
//cache dom items
$appList = $('#rf-list');
$cbProjects = $('#projects');
$open = $('#open');
$priority = $("#priority");
$search = $("#csearch");
projectList = {};
//get projects
app.api.get('rfcaseproject', function (res) {
if (res.error) {
$.gevent.publish('app-show-error', res.msg);
} else {
var html = '<option value="0">All</option>';
for (var i = 0, len = res.length; i < len; ++i) {
html += ('<option value="' + res[i]['id'] + '">' + res[i]['name'] + '</option>');
projectList[res[i]['id']] = res[i]['name'];
}
$cbProjects.append(html);
//case 3363 re-hydrate settings here
restoreSelections();
//subscribe to change event
$cbProjects.change(onFilterChange);
$open.change(onFilterChange);
$priority.change(onFilterChange);
$search.change(onFilterChange);
loadCases(projectList);
}
});
app.nav.contextClear();
app.nav.contextAddLink("rfcaseEdit/new", "New", "plus");
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,101 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.rfsettings = (function() {
"use strict";
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var stateMap = {},
configModule,
onChangePassword,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
///////////////////////////////
//ONUPDATE
//
onChangePassword = function(event) {
event.preventDefault();
$.gevent.publish("app-clear-error");
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
app.api.create(
"user/" + app.shell.stateMap.user.id + "/changepassword",
submitData,
function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
page("#!/logout");
}
}
);
return false; //prevent default?
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function(context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function($container) {
if (typeof $container === "undefined") {
$container = $("#app-shell-main-content");
}
$container.html(Handlebars.templates["app.rfsettings"]({}));
// bind actions
$("#btn-change-password").bind("click", onChangePassword);
//Context menu
app.nav.contextClear();
app.api.get("meta/server_version/", function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
} else {
$("#about").append(
"<p>Rockfish client version: " +
app.api.RockFishVersion +
"</p><p>Rockfish server version: " +
res.server_version +
"</p>"
);
}
});
////app.nav.setContextTitle("Search");
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
})();

119
wwwroot/js/app.search.js Normal file
View File

@@ -0,0 +1,119 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.search = (function() {
"use strict";
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var stateMap = {},
configModule,
onSearch,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//ONSEARCH
//
onSearch = function(event) {
event.preventDefault();
$.gevent.publish("app-clear-error");
//get form data
var query = $("#searchquery").val();
//app.api.get('customer/' + stateMap.id, function (res) {
//app.get('/api/search?query=querytext'
app.api.get("search?query=" + query, function(res) {
if (res.error) {
$.gevent.publish("app-show-error", res.msg);
return false;
}
var $appList = $("#rf-list");
$appList.empty();
$.each(res, function(i, obj) {
var editUrl = "";
switch (obj.obj) {
case "customer":
editUrl = "customerEdit/" + obj.id;
break;
case "site":
editUrl = "customerSiteEdit/" + obj.id + "/" + obj.customerId;
break;
case "purchase":
editUrl = "purchaseEdit/" + obj.id + "/" + obj.site_id;
break;
case "license":
editUrl = "licenseView/" + obj.id;
break;
default:
alert("UNKNOWN SEARCH TYPE OBJECT RESULT: " + obj.obj + " WHUPS!");
}
$appList.append(
'<li><a href="#!/' +
editUrl +
'">' +
app.utilB.genListColumn(
obj.name + " @ " + obj.obj + "." + obj.fld
) +
"</a></li>"
);
//cache it
app.shell.stateMap.search_cache.has_cache = true;
app.shell.stateMap.search_cache.html = $("#rf-list").html();
app.shell.stateMap.search_cache.query = query;
});
});
return false; //prevent default?
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function(context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function($container) {
if (typeof $container === "undefined") {
$container = $("#app-shell-main-content");
}
$container.html(Handlebars.templates["app.search"]({}));
// bind actions
$("#searchbutton").bind("click", onSearch);
//reconstitute from last search cache
if (app.shell.stateMap.search_cache.has_cache == true) {
$("#rf-list").html(app.shell.stateMap.search_cache.html);
$("#searchquery").val(app.shell.stateMap.search_cache.query);
}
//Context menu
app.nav.contextClear();
////app.nav.setContextTitle("Search");
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
})();

561
wwwroot/js/app.shell.js Normal file
View File

@@ -0,0 +1,561 @@
/*
* app.shell.js
* Shell module for SPA
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.shell = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {
$container: undefined,
anchor_map: {},
user: {
authenticated: false,
token: '',
name: '',
id: 0
},
apiUrl: '',
search_cache: {
has_cache: false
}
},
tokenExpired,
//onUPdate,
onLogin, onLogout, onShowError, onClearError,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
tokenExpired = function () {
//fetch stored creds if available
var creds = store.get('rockfish.usercreds');
if (creds) {
//check if token has expired
//is the date greater than the expires date
var rightNowUtc = new Date();
var expDate = new Date(creds.expires * 1000);
if (rightNowUtc > expDate) {
return true;
} else {
return false;
}
} else {
return true;
}
}
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
onLogin = function (event, login_user) {
//store creds
store.set('rockfish.usercreds', stateMap.user);
//New token needs to reset lastNav
stateMap.lastNav = new Date();
//Go to the home page
page('#!/customers/');
return false;
};
onLogout = function (event) {
store.clear();
stateMap.user.authenticated = false;
stateMap.user.name = '';
stateMap.user.token = '';
stateMap.user.id = 0;
page('#!/authenticate/');
return false;
};
// onUpdate = function (event) {
// window.location.reload(true);
// return false;
// };
onShowError = function (event, msg) {
$("#app-error-div").removeClass("d-none");
$('#app-error-message').text(msg);
return false;
};
onClearError = function (event) {
$("#app-error-div").addClass("d-none");
return false;
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
initModule = function ($container) {
document.title = 'Rockfish ' + app.api.RockFishVersion;
//PRE FLIGHT CHECK
//================
//local storage required
if (!store.enabled) {
alert('Local storage is not supported by your browser. Please disable "Private Mode", or upgrade to a modern browser.');
return;
}
//wait indicator for ajax functions
$(document).ajaxStart(function () {
//disable all buttons
$('.app-frm-buttons button').prop('disabled', true).addClass('disabled');
//$('.app-frm-buttons button').attr('disabled', 'disabled');
$("body").css("cursor", "progress");
//add app-ajax-busy style to buttons div where user clicked
$('.app-frm-buttons').addClass('app-ajax-busy');
}).ajaxStop(function () {
//with delay in case it's too fast to see
setTimeout(function () {
$('.app-frm-buttons button').prop('disabled', false).removeClass('disabled');
//$('.app-frm-buttons button').removeAttr('disabled');
$("body").css("cursor", "default");
$('.app-frm-buttons').removeClass('app-ajax-busy');
}, 250);
});
//save on app so can call from anywhere
// app.mdInit = mdInit;
// load HTML and map jQuery collections
stateMap.$container = $container;
$container.html(Handlebars.templates['app.shell']({}));
//auto hide navbar on click
//rfac is a class only on the items that should trigger collapse
$('.rfac').on('click', function () {
$('.navbar-collapse').collapse('hide');
});
//SEARCH
// $('#searchbutton').bind('click', onSearch);
//determine API url and save it
stateMap.apiUrl = app.utilB.getApiUrl();
//fetch stored creds if available
var creds = store.get('rockfish.usercreds');
// if (creds) {
//check if token has expired
if (tokenExpired()) {
stateMap.user.authenticated = false;
stateMap.user.name = '';
stateMap.user.token = '';
stateMap.user.id = 0;
} else {
//Show the logout item in the menu
$(".app-mnu-logout").removeClass("app-hidden");
stateMap.user.authenticated = true;
stateMap.user.name = creds.name;
stateMap.user.token = creds.token;
stateMap.user.id = creds.id;
}
//}
//EVENT SUBSCRIPTIONS
$.gevent.subscribe($container, 'app-login', onLogin);
$.gevent.subscribe($container, 'app-logout', onLogout);
//$.gevent.subscribe($container, 'rf-update', onUpdate);
$.gevent.subscribe($container, 'app-show-error', onShowError);
$.gevent.subscribe($container, 'app-clear-error', onClearError);
//ROUTES
//
page.base('/default.htm');
page('*', beforeUrlChange);
page('/authenticate', authenticate);
page('/', inbox);
page('/reportData', reportData);
page('/reportDataProdEmails', reportDataProdEmail);
page('/reportDataExpires', reportDataExpires);
page('/search', search);
page('/logout', function () {
$.gevent.publish('app-logout');
});
page('/customers', customers);
page('/customerEdit/:id', customerEdit);
page('/customerSites/:id', customerSites);
page('/customerSiteEdit/:id/:cust_id', customerSiteEdit);
page('/purchases/:id', purchases);
page('/purchaseEdit/:id/:site_id', purchaseEdit);
page('/license', license);
page('/licenseTemplates', licenseTemplates);
page('/licenseRequests', licenseRequests);
page('/licenseRequestEdit/:id', licenseRequestEdit);
//case 3233
page('/licenses', licenses);
page('/licenseView/:id', licenseView);
page('/subscription', subscription);
page('/subnotify', subnotify);
page('/templates', templates);
page('/templateEdit/:id', templateEdit);
page('/inbox', inbox);
page.exit('/inbox', function(ctx,next) {
app.inbox.terminateModule();
next();
});
page('/mailEdit/:mail_account/:mail_folder/:mail_id', mailEdit);
page('/rfcases', rfcases);
page('/rfcaseEdit/:id', rfcaseEdit);
page('/rfsettings', rfsettings);
page('*', notFound);
page({
hashbang: true
});
// /ROUTES
};
// End PUBLIC method /initModule/
var beforeUrlChange = function (ctx, next) {
$.gevent.publish('app-clear-error');
app.shell.stateMap.mediaSize = app.utilB.getMediaSize();
//case 3513
document.title = 'Rockfish ' + app.api.RockFishVersion;
//bypass stuff below if about to logout
if (ctx.path == '/logout') {
return next();
}
//================================================================
//Check authentication token to see if expired, but only if it's been a few minutes since last navigation
if (stateMap.user.authenticated) {
//This first bit sets the last nav in cases where it's never been set before
//default to one hour ago in case it hasn't ever been set yet or isn't in statemap
if (!stateMap.lastNav) {
//Isn't this sketchy? Is this a date or a moment being set here
stateMap.lastNav = moment().subtract(3600, 's').toDate();//60 minutes ago
}
var mNow = moment(new Date()); //todays date
var mLastNav = moment(stateMap.lastNav); // another date
var duration = moment.duration(mNow.diff(mLastNav));
var secondsSinceLastNav = duration.asSeconds();
if (secondsSinceLastNav > 300)//have we checked in the last 5 minutes?
{
if (tokenExpired()) {
stateMap.user.authenticated = false;
}
}
stateMap.lastNav = new Date();
}
//===============================================================
//Not logged in and trying to go somewhere but authenticate?
if (!stateMap.user.authenticated) {
//hide nav
$("#rf-nav").hide({
duration: 200
});
//page nav to authenticate
if (ctx.path != '/authenticate/')
return page('#!/authenticate/');
} else {
//logged in so make sure to show toolbar here
$("#rf-nav").show({
duration: 200
});
}
next();
}
//TODO: Clean up this coral reef steaming mess
//replace with a function that generates these functions since they are all (nearly) identical
var authenticate = function (ctx) {
app.authenticate.configModule({
context: ctx
});
app.authenticate.initModule();
}
var reportData = function (ctx) {
app.nav.setSelectedMenuItem('reportData');
app.reportData.configModule({
context: ctx
});
app.reportData.initModule();
}
var reportDataProdEmail = function (ctx) {
app.nav.setSelectedMenuItem('reportData');
app.reportDataProdEmail.configModule({
context: ctx
});
app.reportDataProdEmail.initModule();
}
var reportDataExpires = function (ctx) {
app.nav.setSelectedMenuItem('reportData');
app.reportDataExpires.configModule({
context: ctx
});
app.reportDataExpires.initModule();
}
var search = function (ctx) {
app.nav.setSelectedMenuItem('search');
app.search.configModule({
context: ctx
});
app.search.initModule();
}
var customers = function (ctx) {
app.nav.setSelectedMenuItem('customers');
app.customers.configModule({
context: ctx
});
app.customers.initModule();
}
var customerEdit = function (ctx) {
app.nav.setSelectedMenuItem('customers');
app.customerEdit.configModule({
context: ctx
});
app.customerEdit.initModule();
}
var customerSites = function (ctx) {
app.nav.setSelectedMenuItem('customers');
app.customerSites.configModule({
context: ctx
});
app.customerSites.initModule();
}
var customerSiteEdit = function (ctx) {
app.nav.setSelectedMenuItem('customers');
app.customerSiteEdit.configModule({
context: ctx
});
app.customerSiteEdit.initModule();
}
var purchases = function (ctx) {
app.nav.setSelectedMenuItem('customers');
app.purchases.configModule({
context: ctx
});
app.purchases.initModule();
}
var purchaseEdit = function (ctx) {
app.nav.setSelectedMenuItem('customers');
app.purchaseEdit.configModule({
context: ctx
});
app.purchaseEdit.initModule();
}
var license = function (ctx) {
app.nav.setSelectedMenuItem('license');
app.license.configModule({
context: ctx
});
app.license.initModule();
}
var licenseTemplates = function (ctx) {
app.nav.setSelectedMenuItem('license');
app.licenseTemplates.configModule({
context: ctx
});
app.licenseTemplates.initModule();
}
var licenseRequests = function (ctx) {
app.nav.setSelectedMenuItem('license');
app.licenseRequests.configModule({
context: ctx
});
app.licenseRequests.initModule();
}
var licenseRequestEdit = function (ctx) {
app.nav.setSelectedMenuItem('license');
app.licenseRequestEdit.configModule({
context: ctx
});
app.licenseRequestEdit.initModule();
}
//case 3233
var licenses = function (ctx) {
app.nav.setSelectedMenuItem('license');
app.licenses.configModule({
context: ctx
});
app.licenses.initModule();
}
//case 3233
var licenseView = function (ctx) {
app.nav.setSelectedMenuItem('license');
app.licenseView.configModule({
context: ctx
});
app.licenseView.initModule();
}
var subscription = function (ctx) {
app.nav.setSelectedMenuItem('subscription');
app.subscription.configModule({
context: ctx
});
app.subscription.initModule();
}
var subnotify = function (ctx) {
app.nav.setSelectedMenuItem('subscription');
app.subnotify.configModule({
context: ctx
});
app.subnotify.initModule();
}
var templates = function (ctx) {
app.nav.setSelectedMenuItem('templates');
app.templates.configModule({
context: ctx
});
app.templates.initModule();
}
var templateEdit = function (ctx) {
app.nav.setSelectedMenuItem('templates');
app.templateEdit.configModule({
context: ctx
});
app.templateEdit.initModule();
}
var inbox = function (ctx) {
app.nav.setSelectedMenuItem('inbox');
app.inbox.configModule({
context: ctx
});
app.inbox.initModule();
}
var mailEdit = function (ctx) {
app.nav.setSelectedMenuItem('inbox');
app.mailEdit.configModule({
context: ctx
});
app.mailEdit.initModule();
}
var rfcases = function (ctx) {
app.nav.setSelectedMenuItem('rfcases');
app.rfcases.configModule({
context: ctx
});
app.rfcases.initModule();
}
var rfcaseEdit = function (ctx) {
app.nav.setSelectedMenuItem('rfcases');
app.rfcaseEdit.configModule({
context: ctx
});
app.rfcaseEdit.initModule();
}
var rfsettings = function (ctx) {
app.nav.setSelectedMenuItem('rfsettings');
app.rfsettings.configModule({
context: ctx
});
app.rfsettings.initModule();
}
var notFound = function (ctx) {
app.fourohfour.configModule({
context: ctx
});
app.fourohfour.initModule();
}
return {
initModule: initModule,
stateMap: stateMap
};
//------------------- END PUBLIC METHODS ---------------------
}());

132
wwwroot/js/app.subnotify.js Normal file
View File

@@ -0,0 +1,132 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.subnotify = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
configModule,
initModule,
onSend;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
/////////////////////////
//SEND (create draft) of renewal warning notification email
//
onSend = function (event) {
event.preventDefault();
//Gather purchase id's
var s = $(this).data("purchaseidlist");
if(s==undefined || s==null){
return false;
}
//Convert string of comma separate numeric values to numeric array
var ids = new Array();
if (typeof s == 'string') {
ids = s.split(",");
var arrayLength = ids.length;
for (var i = 0; i < arrayLength; i++) {
ids[i] = parseInt(ids[i], 10);
}
} else {
//it's a number
ids[0] = s;
}
//post to a route that will make the draft emails then tag the purchase as notified
app.api.create('subscription/sendnotify', ids, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
page('#!/subnotify');
}
});
return false; //prevent default
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.subnotify']({}));
//fetch data
app.api.get('subscription/notifylist', function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//get the list ul
var $appList = $('#rf-list');
$appList.empty();
var displayedItems = 0;
$.each(res, function (i, obj) {
$appList.append("<li>" +
app.utilB.genListColumnNoLink(obj.customer + ' -> ') +
'&nbsp;' +
app.utilB.genListColumnNoLink(obj.purchasenames) +
'&nbsp;' +
app.utilB.genListColumnNoLink("<span class='RFSEND mdi mdi-cube-send btn btn-sm btn-outline-primary' data-purchaseidlist='" + obj.purchaseidlist.toString() + "'>Send</span>") +
"</li>");
displayedItems++;
});
if(displayedItems==0){
$appList.append('<li>NO RENEWALS IMMINENT - ' + moment().format('YYYY-MM-DD LT') + '</li>');
}
//event handler for buttons added dynamically
$('.RFSEND').bind('click', onSend);
}
});
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("subscription/", "Subscriptions", "basket");
};
// return public methods
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,69 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.subscription = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
configModule,
initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.subscription']({}));
//Context menu
app.nav.contextClear();
////app.nav.setContextTitle("Subscriptions");
var $appList = $('#rf-list');
$appList.empty();
$appList.append('<ul class="list-group">');
$appList.append("<li><a class=\"list-group-item\" href=\"#!/subnotify/\">" + app.utilB.genListColumn("Notify users with imminent expiring subscriptions") + "</a></li>");
$appList.append("<li><a class=\"list-group-item\" href=\"#!/reportDataExpires/\">" + app.utilB.genListColumn("Show subscriptions by expiry") + "</a></li>");
$appList.append("<li><a class=\"list-group-item\" href=\"#!/reportDataProdEmails/\">" + app.utilB.genListColumn("Build email address CSV by product code") + "</a></li>");
$appList.append('</ul>');
//not implemented yet
// $appList.append("<li><a href=\"#!/subrenew/\">" + app.utilB.genListColumn("Renew") + "</a></li>");
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,141 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.templateEdit = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
onSave, onDelete,
configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN EVENT HANDLERS -------------------
//ONSAVE
//
onSave = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
//get form data
var formData = $("form").serializeArray({
checkboxesAsBools: true
});
var submitData = app.utilB.objectifyFormDataArray(formData);
//is this a new record?
if (stateMap.id != 'new') {
//put id into the form data
submitData.id = stateMap.id;
app.api.update('textTemplate', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
}
});
} else {
//it's a new record - create
app.api.create('textTemplate', submitData, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
page('#!/templateEdit/' + res.id);
}
});
}
return false; //prevent default?
};
//ONDELETE
//
onDelete = function (event) {
event.preventDefault();
$.gevent.publish('app-clear-error');
var r = confirm("Are you sure you want to delete this record?");
if (r == true) {
app.api.remove('textTemplate/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//deleted, return to list
page('#!/templates');
return false;
}
});
} else {
return false;
}
return false; //prevent default?
};
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.templateEdit']({}));
//id should always have a value, either a record id or the keyword 'new' for making a new object
if (stateMap.id != 'new') {
//fetch existing record
app.api.get('textTemplate/' + stateMap.id, function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
//fill out form
app.utilB.formData(res);
}
});
} else {
$('#btn-delete').hide();
}
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("templates/", "Templates", "layers");
// bind actions
$('#btn-save').bind('click', onSave);
$('#btn-delete').bind('click', onDelete);
};
// RETURN PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

View File

@@ -0,0 +1,68 @@
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.templates = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
stateMap = {},
configModule, initModule;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
//-------------------- END UTILITY METHODS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
//-------------------- END EVENT HANDLERS --------------------
//------------------- BEGIN PUBLIC METHODS -------------------
//CONFIGMODULE
//
configModule = function (context) {
stateMap.context = context.context;
if (stateMap.context.params.id) {
stateMap.id = stateMap.context.params.id;
}
};
//INITMODULE
//
initModule = function ($container) {
if (typeof $container === 'undefined') {
$container = $('#app-shell-main-content');
}
$container.html(Handlebars.templates['app.templates']({}));
app.api.get('texttemplate/list', function (res) {
if (res.error) {
$.gevent.publish('app-show-error',res.msg);
} else {
var $appList = $('#rf-list');
$.each(res, function (i, obj) {
$appList.append("<li><a href=\"#!/templateEdit/" + obj.id + "\">" +
app.utilB.genListColumn(obj.name) +
"</a></li>")
});
}
});
//Context menu
app.nav.contextClear();
app.nav.contextAddLink("templateEdit/new", "New", "plus");
};
//PUBLIC METHODS
//
return {
configModule: configModule,
initModule: initModule
};
//------------------- END PUBLIC METHODS ---------------------
}());

80
wwwroot/js/app.util.js Normal file
View File

@@ -0,0 +1,80 @@
/*
* app.util.js
* General JavaScript utilities
*
* Michael S. Mikowski - mmikowski at gmail dot com
* These are routines I have created, compiled, and updated
* since 1998, with inspiration from around the web.
*
* MIT License
*
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
app.util = (function () {
var makeError, setConfigMap;
// Begin Public constructor /makeError/
// Purpose: a convenience wrapper to create an error object
// Arguments:
// * name_text - the error name
// * msg_text - long error message
// * data - optional data attached to error object
// Returns : newly constructed error object
// Throws : none
//
makeError = function ( name_text, msg_text, data ) {
var error = new Error();
error.name = name_text;
error.message = msg_text;
if ( data ){ error.data = data; }
return error;
};
// End Public constructor /makeError/
// Begin Public method /setConfigMap/
// Purpose: Common code to set configs in feature modules
// Arguments:
// * input_map - map of key-values to set in config
// * settable_map - map of allowable keys to set
// * config_map - map to apply settings to
// Returns: true
// Throws : Exception if input key not allowed
//
setConfigMap = function ( arg_map ){
var
input_map = arg_map.input_map,
settable_map = arg_map.settable_map,
config_map = arg_map.config_map,
key_name, error;
for ( key_name in input_map ){
if ( input_map.hasOwnProperty( key_name ) ){
if ( settable_map.hasOwnProperty( key_name ) ){
config_map[key_name] = input_map[key_name];
}
else {
error = makeError( 'Bad Input',
'Setting config key |' + key_name + '| is not supported'
);
throw error;
}
}
}
};
// End Public method /setConfigMap/
return {
makeError : makeError,
setConfigMap : setConfigMap
};
}());

334
wwwroot/js/app.utilB.js Normal file
View File

@@ -0,0 +1,334 @@
/**
* app.utilB.js
* JavaScript browser utilities
*
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app, getComputedStyle */
app.utilB = (function () {
'use strict';
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
configMap = {
regex_encode_html: /[&"'><]/g,
regex_encode_noamp: /["'><]/g,
html_encode_map: {
'&': '&#38;',
'"': '&#34;',
"'": '&#39;',
'>': '&#62;',
'<': '&#60;'
}
},
decodeHtml, encodeHtml, getEmSize, getApiUrl, getUrlParams, formData, objectifyFormDataArray,
getMediaSize, prepareObjectForClient, fixDatesToStrings,
epochToShortDate, epochToLocalShortDate, epochToLocalShortDateTime, getCurrentDateTimeAsEpoch,
genListColumn,
genListColumnNoLink, autoComplete, prepareObjectDatesForServer,
fixStringToServerDate;
configMap.encode_noamp_map = $.extend({}, configMap.html_encode_map);
delete configMap.encode_noamp_map['&'];
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
// Begin decodeHtml
// Decodes HTML entities in a browser-friendly way
// See http://stackoverflow.com/questions/1912501/\
// unescape-html-entities-in-javascript
//
decodeHtml = function (str) {
return $('<div/>').html(str || '').text();
};
// End decodeHtml
// Begin encodeHtml
// This is single pass encoder for html entities and handles
// an arbitrary number of characters
//
encodeHtml = function (input_arg_str, exclude_amp) {
var
input_str = String(input_arg_str),
regex, lookup_map;
if (exclude_amp) {
lookup_map = configMap.encode_noamp_map;
regex = configMap.regex_encode_noamp;
} else {
lookup_map = configMap.html_encode_map;
regex = configMap.regex_encode_html;
}
return input_str.replace(regex,
function (match, name) {
return lookup_map[match] || '';
}
);
};
// End encodeHtml
// Begin getEmSize
// returns size of ems in pixels
//
getEmSize = function (elem) {
return Number(
getComputedStyle(elem, '').fontSize.match(/\d*\.?\d*/)[0]
);
};
// End getEmSize
//Begin getApiUrl
//returns url for api methods by parsing current window location url
//
getApiUrl = function () {
var u = window.location.href.replace(window.location.hash, "").replace('default.htm', '') + "api/";
//is it a dev local url?
if (u.indexOf("localhost:8080") != -1) {
u = u.replace("8080", "8081");
}
// End getApiUrl
//fix for random recurrence of extraneous ? on iPhone when using api
//(Cannot post to http://gl-gztw.rhcloud.com/?api/list (404))
u = u.replace("?", "");
return u;
}
// Begin formData
// Get or set all form fields
//
formData = function (data) {
//fix dates into strings
prepareObjectForClient(data);
var inps = $(":input").get();
if (typeof data != "object") {
// return all data
data = {};
$.each(inps, function () {
if (this.name && (this.checked || /select|textarea/i.test(this.nodeName) || /text|hidden|password/i.test(this.type))) {
data[this.name] = $(this).val();
}
});
return data;
} else {
$.each(inps, function () {
if (this.name && data[this.name]) {
if (this.type == "checkbox" || this.type == "radio") {
$(this).prop("checked", (data[this.name]));
} else {
$(this).val(data[this.name]);
}
} else if (this.type == "checkbox") {
$(this).prop("checked", false);
}
});
return $(this);
}
};
// End formdata
// Begin getMediaSize
// Retrieves results of css media query in hidden pseudo element
// :after body element
//objectifyFormDataArray
getMediaSize = function () {
return window.getComputedStyle(document.querySelector('body'), ':after').getPropertyValue('content').replace(/\"/g, '');
};
// End getMediaSize
//Begin objectifyFormDataArray
//takes name value form input pairs in array and turns into a keyed object
//suitable for sending as json object
//
objectifyFormDataArray = function (arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
if (arr[i] !== undefined) {
rv[arr[i].name] = arr[i].value.trim();//case 3205 added trim
}
return prepareObjectDatesForServer(rv);
}
//Prepare an object for server needed format before submission
//called by objectifyFormDataArray
prepareObjectDatesForServer = function (obj) {
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
fixStringToServerDate(obj[i]);
}
} else {
fixStringToServerDate(obj);
}
return obj;
}
//
//Turn string date fields from client into server compatible format (Unix epoch like this: 1498262400 1499904000)
fixStringToServerDate = function (obj) {
var keys = Object.keys(obj);
keys.forEach(function (key) {
if (key.endsWith('Date') || key.startsWith('dt')) {
var value = obj[key];
if (value == null) {
obj[key] = '';
} else {
//this is the sample format we will see: 2017-07-13
//TODO: is this assuming UTC?
obj[key] = moment.utc(value, "YYYY-MM-DD").unix();
}
}
});
}
//This function exists to change the properties of the passed in object
//to values compatible with jquery form filling functions (mostly dates to strings for now)
//
prepareObjectForClient = function (obj) {
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
fixDatesToStrings(obj[i]);
}
} else {
fixDatesToStrings(obj);
}
return obj;
}
//
//Turn date fields of object coming from db into stringified values for consumption by client
//turn null dates into empty strings and iso date values into strings in iso format
fixDatesToStrings = function (obj) {
var keys = Object.keys(obj);
keys.forEach(function (key) {
if (key.endsWith('Date') || key.startsWith('dt')) {
var value = obj[key];
if (value == null) {
obj[key] = '';
} else {
//Now with sqlite they come and go as unix epoch seconds
//needs to be yyyy-MM-dd
obj[key] = moment.utc(new Date(value * 1000)).format("YYYY-MM-DD");
}
}
});
}
//
//Turn date values coming in from server into displayable short date format
//used to display dates in various lists (where the source epoch is already localized)
epochToShortDate = function (epoch) {
if (epoch == null || epoch == 0) return '';
return moment.utc(new Date(epoch * 1000)).format("YYYY-MM-DD");
}
//
//LOCAL VERSION: Turn date values coming in from server into displayable short date format
//used to display dates in various lists where the source epoch is in UTC
epochToLocalShortDate = function (epoch) {
if (epoch == null || epoch == 0) return '';
var utdate = moment.utc(new Date(epoch * 1000));
var localdate = moment(utdate).local();
return localdate.format("YYYY-MM-DD");
}
//LOCAL VERSION: Turn date values coming in from server into displayable short date AND TIME format
//used to display dates in various lists where the source epoch is in UTC
epochToLocalShortDateTime = function (epoch) {
if (epoch == null || epoch == 0) return '';
var utdate = moment.utc(new Date(epoch * 1000));
var localdate = moment(utdate).local();
return localdate.format("YYYY-MM-DD LT");
}
//////////////////////////
//Get current date and time as a utc unix epoch
getCurrentDateTimeAsEpoch = function () {
return moment().utc().unix();
}
// Begin genListColumn
// This function is used to demarcate 'columns' of fields in basic list forms by wrapping each column field in html
//
genListColumn = function (val) {
return '<span class="rf-list-column">' + val + '</span>'
};
// End genListColumn
// Begin genListColumnNoLink
// This function is used to demarcate and style 'columns' of fields in basic list forms by wrapping each column field in html
// that are not link columns
genListColumnNoLink = function (val) {
return '<span class="rf-list-column-nolink">' + val + '</span>'
};
// End genListColumn
// Begin autoComplete
// This function is used to attach an autocomplete method to an input
//
autoComplete = function (controlId, acGetToken) {
$('#' + controlId).autocomplete({
serviceUrl: app.shell.stateMap.apiUrl + 'autocomplete',
params: {
acget: acGetToken
},
ajaxSettings: {
headers: app.api.getAuthHeaderObject()
}
});
};
// End autoComplete
// export methods
return {
decodeHtml: decodeHtml,
encodeHtml: encodeHtml,
getEmSize: getEmSize,
getApiUrl: getApiUrl,
getUrlParams: getUrlParams,
formData: formData,
getMediaSize: getMediaSize,
objectifyFormDataArray: objectifyFormDataArray,
epochToShortDate: epochToShortDate,
epochToLocalShortDate: epochToLocalShortDate,
epochToLocalShortDateTime: epochToLocalShortDateTime,
getCurrentDateTimeAsEpoch: getCurrentDateTimeAsEpoch,
genListColumn: genListColumn,
genListColumnNoLink: genListColumnNoLink,
autoComplete: autoComplete
};
//------------------- END PUBLIC METHODS ---------------------
}());

23
wwwroot/js/index.js Normal file
View File

@@ -0,0 +1,23 @@
/*
* app.js
* Root namespace module
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global $, app */
var app = (function () {
'use strict';
var initModule = function ( $container ) {
app.api.initModule();
// app.model.initModule();
app.shell.initModule( $container );
};
return { initModule: initModule };
}());

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,154 @@
/*
* jQuery global custom event plugin (gevent)
*
* Copyright (c) 2013 Michael S. Mikowski
* (mike[dot]mikowski[at]gmail[dotcom])
*
* Dual licensed under the MIT or GPL Version 2
* http://jquery.org/license
*
* Versions
* 0.1.5 - initial release
* 0.1.6 - enhanced publishEvent (publish) method pass
* a non-array variable as the second argument
* to a subscribed function (the first argument
* is always the event object).
* 0.1.7-10, 0.2.0
* - documentation changes
* 1.0.2 - cleaned-up logic, bumped version
* 1.1.2 - added keywords
*
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, nomen : true, plusplus : true,
regexp : true, sloppy : true, vars : false,
white : true
*/
/*global jQuery*/
(function ( $ ) {
'use strict';
$.gevent = (function () {
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
subscribeEvent, publishEvent, unsubscribeEvent,
$customSubMap = {}
;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN PUBLIC METHODS -------------------
// BEGIN public method /publishEvent/
// Example :
// $.gevent.publish(
// 'spa-model-msg-receive',
// [ { user : 'fred', msg : 'Hi gang' } ]
// );
// Purpose :
// Publish an event with an optional list of arguments
// which a subscribed handler will receive after the event object.
// Arguments (positional)
// * 0 ( event_name ) - The global event name
// * 1 ( data ) - Optional data to be passed as argument(s)
// to subscribed functions after the event
// object. Provide an array for multiple
// arguments.
// Throws : none
// Returns : none
//
publishEvent = function () {
var arg_list = [],
arg_count, event_name,
event_obj, data, data_list;
arg_list = arg_list.slice.call( arguments, 0 );
arg_count = arg_list.length;
if ( arg_count === 0 ) { return false; }
event_name = arg_list.shift();
event_obj = $customSubMap[ event_name ];
if ( ! event_obj ) { return false; }
if ( arg_count > 1 ) {
data = arg_list.shift();
data_list = $.isArray( data ) ? data : [ data ];
}
else {
data_list = [];
}
event_obj.trigger( event_name, data_list );
return true;
};
// END public method /publishEvent/
// BEGIN public method /subscribeEvent/
// Example :
// $.gevent.subscribe(
// $( '#msg' ),
// 'spa-msg-receive',
// onModelMsgReceive
// );
// Purpose :
// Subscribe a function to a published event on a jQuery collection
// Arguments (positional)
// * 0 ( $collection ) - The jQuery collection on which to bind event
// * 1 ( event_name ) - The global event name
// * 2 ( fn ) - The function to bound to the event on the collection
// Throws : none
// Returns : none
//
subscribeEvent = function ( $collection, event_name, fn ) {
$collection.on( event_name, fn );
if ( $customSubMap[ event_name ] ) {
$customSubMap[ event_name ]
= $customSubMap[ event_name ].add( $collection );
}
else {
$customSubMap[ event_name ] = $collection;
}
};
// END public method /subscribeEvent/
// BEGIN public method /unsubscribeEvent/
// Example :
// $.gevent.unsubscribe(
// $( '#msg' ),
// 'spa-model-msg-receive'
// );
// Purpose :
// Remove a binding for the named event on a provided collection
// Arguments (positional)
// * 0 ( $collection ) - The jQuery collection on which to bind event
// * 1 ( event_name ) - The global event name
// Throws : none
// Returns : none
//
unsubscribeEvent = function ( $collection, event_name ) {
if ( ! $customSubMap[ event_name ] ){ return false; }
$customSubMap[ event_name ]
= $customSubMap[ event_name ].not( $collection );
if ( $customSubMap[ event_name ].length === 0 ){
delete $customSubMap[ event_name ];
}
return true;
};
// END public method /unsubscribeEvent/
//------------------- END PUBLIC METHODS ---------------------
// return public methods
return {
publish : publishEvent,
subscribe : subscribeEvent,
unsubscribe : unsubscribeEvent
};
}());
}( jQuery ));

View File

@@ -0,0 +1,783 @@
/*
* jQuery plugin for unified mouse and touch events
*
* Copyright (c) 2013 Michael S. Mikowski
* (mike[dot]mikowski[at]gmail[dotcom])
*
* Dual licensed under the MIT or GPL Version 2
* http://jquery.org/license
*
* Versions
* 1.2.0 - ignore_class => ignore_select, now defaults to ''
* 1.1.9 - Fixed ue-test.html demo to scale properly
* 1.1.8 - Removed prevent default from non-ue events
* 1.1.7 - Corrected desktop zoom motion description
* 1.1.0-5 - No code changes. Updated npm keywords. Fixed typos.
* Bumped version to represent maturity and stability.
* 0.6.1 - Change px_radius from 5 to 10 pixels
* 0.6.0 - Added px_tdelta_x and px_tdelta_y for deltas from start
* - Fixed onheld and drag conflicts
* 0.5.0 - Updated docs, removed cruft, updated for jslint,
* updated test page (zoom)
* 0.4.3 - Removed fatal execption possibility if originalEvent
* is not defined on event object
* 0.4.2 - Updated documentation
* 0.3.2 - Updated to jQuery 1.9.1.
* Confirmed 1.7.0-1.9.1 compatibility.
* 0.3.1 - Change for jQuery plugins site
* 0.3.0 - Initial jQuery plugin site release
* - Replaced scrollwheel zoom with drag motion.
* This resolved a conflict with scrollable areas.
*
*/
/*jslint browser : true, continue : true,
devel : true, indent : 2, maxerr : 50,
newcap : true, plusplus : true, regexp : true,
sloppy : true, vars : false, white : true
*/
/*global jQuery */
(function ( $ ) {
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
var
$Special = $.event.special, // Shortcut for special event
motionMapMap = {}, // Map of pointer motions by cursor
isMoveBound = false, // Flag if move handlers bound
pxPinchZoom = -1, // Distance between pinch-zoom points
optionKey = 'ue_bound', // Data key for storing options
doDisableMouse = false, // Flag to discard mouse input
defaultOptMap = { // Default option map
bound_ns_map : {}, // Map of bound namespaces e.g.
// bound_ns_map.utap.fred
px_radius : 10, // Tolerated distance before dragstart
ignore_select : '', // Selector of elements to ignore (e.g. :input)
max_tap_ms : 200, // Maximum time allowed for tap
min_held_ms : 300 // Minimum time require for long-press
},
callbackList = [], // global callback stack
zoomMouseNum = 1, // multiplier for mouse zoom
zoomTouchNum = 4, // multiplier for touch zoom
boundList, Ue,
motionDragId, motionHeldId, motionDzoomId,
motion1ZoomId, motion2ZoomId,
checkMatchVal, removeListVal, pushUniqVal, makeListPlus,
fnHeld, fnMotionStart, fnMotionMove,
fnMotionEnd, onMouse, onTouch
;
//----------------- END MODULE SCOPE VARIABLES ---------------
//------------------- BEGIN UTILITY METHODS ------------------
// Begin utiltity /makeListPlus/
// Returns an array with much desired methods:
// * remove_val(value) : remove element that matches
// the provided value. Returns number of elements
// removed.
// * match_val(value) : shows if a value exists
// * push_uniq(value) : pushes a value onto the stack
// iff it does not already exist there
// Note: the reason I need this is to compare objects to
// objects (perhaps jQuery has something similar?)
checkMatchVal = function ( data ) {
var match_count = 0, idx;
for ( idx = this.length; idx; 0 ) {
if ( this[--idx] === data ) { match_count++; }
}
return match_count;
};
removeListVal = function ( data ) {
var removed_count = 0, idx;
for ( idx = this.length; idx; 0 ) {
if ( this[--idx] === data ) {
this.splice(idx, 1);
removed_count++;
idx++;
}
}
return removed_count;
};
pushUniqVal = function ( data ) {
if ( checkMatchVal.call(this, data ) ) { return false; }
this.push( data );
return true;
};
// primary utility
makeListPlus = function ( input_list ) {
if ( input_list && $.isArray(input_list) ) {
if ( input_list.remove_val ) {
console.warn( 'The array appears to already have listPlus capabilities' );
return input_list;
}
}
else {
input_list = [];
}
input_list.remove_val = removeListVal;
input_list.match_val = checkMatchVal;
input_list.push_uniq = pushUniqVal;
return input_list;
};
// End utility /makeListPlus/
//-------------------- END UTILITY METHODS -------------------
//--------------- BEGIN JQUERY SPECIAL EVENTS ----------------
// Unique array for bound objects
boundList = makeListPlus();
// Begin define special event handlers
Ue = {
setup : function( data, name_list, bind_fn ) {
var
this_el = this,
$to_bind = $(this_el),
seen_map = {},
option_map, idx, namespace_key, ue_namespace_code, namespace_list
;
// jslint hack to allow unused arguments
if ( data && bind_fn ) { console.log( 'unused arguments' ); }
// if previous related event bound do not rebind, but do add to
// type of event bound to this element, if not already noted
if ( $.data( this, optionKey ) ) { return; }
option_map = {};
$.extend( true, option_map, defaultOptMap );
$.data( this_el, optionKey, option_map );
namespace_list = makeListPlus(name_list.slice(0));
if ( ! namespace_list.length
|| namespace_list[0] === ""
) { namespace_list = ["000"]; }
NSPACE_00:
for ( idx = 0; idx < namespace_list.length; idx++ ) {
namespace_key = namespace_list[idx];
if ( ! namespace_key ) { continue NSPACE_00; }
if ( seen_map.hasOwnProperty(namespace_key) ) { continue NSPACE_00; }
seen_map[namespace_key] = true;
ue_namespace_code = '.__ue' + namespace_key;
$to_bind.bind( 'mousedown' + ue_namespace_code, onMouse );
$to_bind.bind( 'touchstart' + ue_namespace_code, onTouch );
}
boundList.push_uniq( this_el ); // record as bound element
if ( ! isMoveBound ) {
// console.log('first element bound - adding global binds');
$(document).bind( 'mousemove.__ue', onMouse );
$(document).bind( 'touchmove.__ue', onTouch );
$(document).bind( 'mouseup.__ue' , onMouse );
$(document).bind( 'touchend.__ue' , onTouch );
$(document).bind( 'touchcancel.__ue', onTouch );
isMoveBound = true;
}
},
// arg_map.type = string - name of event to bind
// arg_map.data = poly - whatever (optional) data was passed when binding
// arg_map.namespace = string - A sorted, dot-delimited list of namespaces
// specified when binding the event
// arg_map.handler = fn - the event handler the developer wishes to be bound
// to the event. This function should be called whenever the event
// is triggered
// arg_map.guid = number - unique ID for event handler, provided by jQuery
// arg_map.selector = string - selector used by 'delegate' or 'live' jQuery
// methods. Only available when these methods are used.
//
// this - the element to which the event handler is being bound
// this always executes immediate after setup (if first binding)
add : function ( arg_map ) {
var
this_el = this,
option_map = $.data( this_el, optionKey ),
namespace_str = arg_map.namespace,
event_type = arg_map.type,
bound_ns_map, namespace_list, idx, namespace_key
;
if ( ! option_map ) { return; }
bound_ns_map = option_map.bound_ns_map;
if ( ! bound_ns_map[event_type] ) {
// this indicates a non-namespaced entry
bound_ns_map[event_type] = {};
}
if ( ! namespace_str ) { return; }
namespace_list = namespace_str.split('.');
for ( idx = 0; idx < namespace_list.length; idx++ ) {
namespace_key = namespace_list[idx];
bound_ns_map[event_type][namespace_key] = true;
}
},
remove : function ( arg_map ) {
var
elem_bound = this,
option_map = $.data( elem_bound, optionKey ),
bound_ns_map = option_map.bound_ns_map,
event_type = arg_map.type,
namespace_str = arg_map.namespace,
namespace_list, idx, namespace_key
;
if ( ! bound_ns_map[event_type] ) { return; }
// No namespace(s) provided:
// Remove complete record for custom event type (e.g. utap)
if ( ! namespace_str ) {
delete bound_ns_map[event_type];
return;
}
// Namespace(s) provided:
// Remove namespace flags from each custom event typei (e.g. utap)
// record. If all claimed namespaces are removed, remove
// complete record.
namespace_list = namespace_str.split('.');
for ( idx = 0; idx < namespace_list.length; idx++ ) {
namespace_key = namespace_list[idx];
if (bound_ns_map[event_type][namespace_key]) {
delete bound_ns_map[event_type][namespace_key];
}
}
if ( $.isEmptyObject( bound_ns_map[event_type] ) ) {
delete bound_ns_map[event_type];
}
},
teardown : function( name_list ) {
var
elem_bound = this,
$bound = $(elem_bound),
option_map = $.data( elem_bound, optionKey ),
bound_ns_map = option_map.bound_ns_map,
idx, namespace_key, ue_namespace_code, namespace_list
;
// do not tear down if related handlers are still bound
if ( ! $.isEmptyObject( bound_ns_map ) ) { return; }
namespace_list = makeListPlus(name_list);
namespace_list.push_uniq('000');
NSPACE_01:
for ( idx = 0; idx < namespace_list.length; idx++ ) {
namespace_key = namespace_list[idx];
if ( ! namespace_key ) { continue NSPACE_01; }
ue_namespace_code = '.__ue' + namespace_key;
$bound.unbind( 'mousedown' + ue_namespace_code );
$bound.unbind( 'touchstart' + ue_namespace_code );
$bound.unbind( 'mousewheel' + ue_namespace_code );
}
$.removeData( elem_bound, optionKey );
// Unbind document events only after last element element is removed
boundList.remove_val(this);
if ( boundList.length === 0 ) {
// console.log('last bound element removed - removing global binds');
$(document).unbind( 'mousemove.__ue');
$(document).unbind( 'touchmove.__ue');
$(document).unbind( 'mouseup.__ue');
$(document).unbind( 'touchend.__ue');
$(document).unbind( 'touchcancel.__ue');
isMoveBound = false;
}
}
};
// End define special event handlers
//--------------- BEGIN JQUERY SPECIAL EVENTS ----------------
//------------------ BEGIN MOTION CONTROLS -------------------
// Begin motion control /fnHeld/
fnHeld = function ( arg_map ) {
var
timestamp = +new Date(),
motion_id = arg_map.motion_id,
motion_map = arg_map.motion_map,
bound_ns_map = arg_map.bound_ns_map,
event_ue
;
delete motion_map.tapheld_toid;
if ( ! motion_map.do_allow_held ) { return; }
motion_map.px_end_x = motion_map.px_start_x;
motion_map.px_end_y = motion_map.px_start_y;
motion_map.ms_timestop = timestamp;
motion_map.ms_elapsed = timestamp - motion_map.ms_timestart;
if ( bound_ns_map.uheld ) {
event_ue = $.Event('uheld');
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
}
// remove tracking, as we want no futher action on this motion
if ( bound_ns_map.uheldstart ) {
event_ue = $.Event('uheldstart');
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
motionHeldId = motion_id;
}
else {
delete motionMapMap[motion_id];
}
};
// End motion control /fnHeld/
// Begin motion control /fnMotionStart/
fnMotionStart = function ( arg_map ) {
var
motion_id = arg_map.motion_id,
event_src = arg_map.event_src,
request_dzoom = arg_map.request_dzoom,
option_map = $.data( arg_map.elem, optionKey ),
bound_ns_map = option_map.bound_ns_map,
$target = $(event_src.target ),
do_zoomstart = false,
motion_map, cb_map, event_ue
;
// this should never happen, but it does
if ( motionMapMap[ motion_id ] ) { return; }
// ignore on zoom
if ( request_dzoom && ! bound_ns_map.uzoomstart ) { return; }
// :input selector includes text areas
if ( $target.is( option_map.ignore_select ) ) { return; }
// Prevent default only after confirming handling this event
event_src.preventDefault();
cb_map = callbackList.pop();
while ( cb_map ) {
if ( $target.is( cb_map.selector_str )
|| $( arg_map.elem ).is( cb_map.selector_str )
) {
if ( cb_map.callback_match ) {
cb_map.callback_match( arg_map );
}
}
else {
if ( cb_map.callback_nomatch ) {
cb_map.callback_nomatch( arg_map );
}
}
cb_map = callbackList.pop();
}
motion_map = {
do_allow_tap : bound_ns_map.utap ? true : false,
do_allow_held : ( bound_ns_map.uheld || bound_ns_map.uheldstart )
? true : false,
elem_bound : arg_map.elem,
elem_target : event_src.target,
ms_elapsed : 0,
ms_timestart : event_src.timeStamp,
ms_timestop : undefined,
option_map : option_map,
orig_target : event_src.target,
px_current_x : event_src.clientX,
px_current_y : event_src.clientY,
px_end_x : undefined,
px_end_y : undefined,
px_start_x : event_src.clientX,
px_start_y : event_src.clientY,
timeStamp : event_src.timeStamp
};
motionMapMap[ motion_id ] = motion_map;
if ( bound_ns_map.uzoomstart ) {
if ( request_dzoom ) {
motionDzoomId = motion_id;
}
else if ( ! motion1ZoomId ) {
motion1ZoomId = motion_id;
}
else if ( ! motion2ZoomId ) {
motion2ZoomId = motion_id;
event_ue = $.Event('uzoomstart');
do_zoomstart = true;
}
if ( do_zoomstart ) {
event_ue = $.Event( 'uzoomstart' );
motion_map.px_delta_zoom = 0;
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
return;
}
}
if ( bound_ns_map.uheld || bound_ns_map.uheldstart ) {
motion_map.tapheld_toid = setTimeout(
function() {
fnHeld({
motion_id : motion_id,
motion_map : motion_map,
bound_ns_map : bound_ns_map
});
},
option_map.min_held_ms
);
}
};
// End motion control /fnMotionStart/
// Begin motion control /fnMotionMove/
fnMotionMove = function ( arg_map ) {
var
motion_id = arg_map.motion_id,
event_src = arg_map.event_src,
do_zoommove = false,
motion_map, option_map, bound_ns_map,
is_over_rad, event_ue, px_pinch_zoom,
px_delta_zoom, mzoom1_map, mzoom2_map
;
if ( ! motionMapMap[ motion_id ] ) { return; }
// Prevent default only after confirming handling this event
event_src.preventDefault();
motion_map = motionMapMap[motion_id];
option_map = motion_map.option_map;
bound_ns_map = option_map.bound_ns_map;
motion_map.timeStamp = event_src.timeStamp;
motion_map.elem_target = event_src.target;
motion_map.ms_elapsed = event_src.timeStamp - motion_map.ms_timestart;
motion_map.px_delta_x = event_src.clientX - motion_map.px_current_x;
motion_map.px_delta_y = event_src.clientY - motion_map.px_current_y;
motion_map.px_current_x = event_src.clientX;
motion_map.px_current_y = event_src.clientY;
motion_map.px_tdelta_x = motion_map.px_start_x - event_src.clientX;
motion_map.px_tdelta_y = motion_map.px_start_y - event_src.clientY;
is_over_rad = (
Math.abs( motion_map.px_tdelta_x ) > option_map.px_radius
|| Math.abs( motion_map.px_tdelta_y ) > option_map.px_radius
);
// native event object override
motion_map.timeStamp = event_src.timeStamp;
// disallow held or tap if outside of zone
if ( is_over_rad ) {
motion_map.do_allow_tap = false;
motion_map.do_allow_held = false;
}
// disallow tap if time has elapsed
if ( motion_map.ms_elapsed > option_map.max_tap_ms ) {
motion_map.do_allow_tap = false;
}
if ( motion1ZoomId && motion2ZoomId
&& ( motion_id === motion1ZoomId
|| motion_id === motion2ZoomId
)) {
motionMapMap[motion_id] = motion_map;
mzoom1_map = motionMapMap[motion1ZoomId];
mzoom2_map = motionMapMap[motion2ZoomId];
px_pinch_zoom = Math.floor(
Math.sqrt(
Math.pow((mzoom1_map.px_current_x - mzoom2_map.px_current_x),2)
+ Math.pow((mzoom1_map.px_current_y - mzoom2_map.px_current_y),2)
) +0.5
);
if ( pxPinchZoom === -1 ) { px_delta_zoom = 0; }
else { px_delta_zoom = ( px_pinch_zoom - pxPinchZoom ) * zoomTouchNum;}
// save value for next iteration delta comparison
pxPinchZoom = px_pinch_zoom;
do_zoommove = true;
}
else if ( motionDzoomId === motion_id ) {
if ( bound_ns_map.uzoommove ) {
px_delta_zoom = motion_map.px_delta_y * zoomMouseNum;
do_zoommove = true;
}
}
if ( do_zoommove ){
event_ue = $.Event('uzoommove');
motion_map.px_delta_zoom = px_delta_zoom;
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
return;
}
if ( motionHeldId === motion_id ) {
if ( bound_ns_map.uheldmove ) {
event_ue = $.Event('uheldmove');
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
}
return;
}
if ( motionDragId === motion_id ) {
if ( bound_ns_map.udragmove ) {
event_ue = $.Event('udragmove');
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
}
return;
}
if ( bound_ns_map.udragstart
&& motion_map.do_allow_tap === false
&& motion_map.do_allow_held === false
&& !( motionDragId && motionHeldId )
) {
motionDragId = motion_id;
event_ue = $.Event('udragstart');
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
if ( motion_map.tapheld_toid ) {
clearTimeout(motion_map.tapheld_toid);
delete motion_map.tapheld_toid;
}
}
};
// End motion control /fnMotionMove/
// Begin motion control /fnMotionEnd/
fnMotionEnd = function ( arg_map ) {
var
motion_id = arg_map.motion_id,
event_src = arg_map.event_src,
do_zoomend = false,
motion_map, option_map, bound_ns_map, event_ue
;
doDisableMouse = false;
if ( ! motionMapMap[motion_id] ) { return; }
motion_map = motionMapMap[motion_id];
option_map = motion_map.option_map;
bound_ns_map = option_map.bound_ns_map;
motion_map.elem_target = event_src.target;
motion_map.ms_elapsed = event_src.timeStamp - motion_map.ms_timestart;
motion_map.ms_timestop = event_src.timeStamp;
if ( motion_map.px_current_x ) {
motion_map.px_delta_x = event_src.clientX - motion_map.px_current_x;
motion_map.px_delta_y = event_src.clientY - motion_map.px_current_y;
}
motion_map.px_current_x = event_src.clientX;
motion_map.px_current_y = event_src.clientY;
motion_map.px_end_x = event_src.clientX;
motion_map.px_end_y = event_src.clientY;
motion_map.px_tdelta_x = motion_map.px_start_x - motion_map.px_end_x;
motion_map.px_tdelta_y = motion_map.px_start_y - motion_map.px_end_y;
// native event object override
motion_map.timeStamp = event_src.timeStamp
;
// clear-out any long-hold tap timer
if ( motion_map.tapheld_toid ) {
clearTimeout(motion_map.tapheld_toid);
delete motion_map.tapheld_toid;
}
// trigger utap
if ( bound_ns_map.utap
&& motion_map.ms_elapsed <= option_map.max_tap_ms
&& motion_map.do_allow_tap
) {
event_ue = $.Event('utap');
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
}
// trigger udragend
if ( motion_id === motionDragId ) {
if ( bound_ns_map.udragend ) {
event_ue = $.Event('udragend');
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
}
motionDragId = undefined;
}
// trigger heldend
if ( motion_id === motionHeldId ) {
if ( bound_ns_map.uheldend ) {
event_ue = $.Event('uheldend');
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
}
motionHeldId = undefined;
}
// trigger uzoomend
if ( motion_id === motionDzoomId ) {
do_zoomend = true;
motionDzoomId = undefined;
}
// cleanup zoom info
else if ( motion_id === motion1ZoomId ) {
if ( motion2ZoomId ) {
motion1ZoomId = motion2ZoomId;
motion2ZoomId = undefined;
do_zoomend = true;
}
else { motion1ZoomId = undefined; }
pxPinchZoom = -1;
}
if ( motion_id === motion2ZoomId ) {
motion2ZoomId = undefined;
pxPinchZoom = -1;
do_zoomend = true;
}
if ( do_zoomend && bound_ns_map.uzoomend ) {
event_ue = $.Event('uzoomend');
motion_map.px_delta_zoom = 0;
$.extend( event_ue, motion_map );
$(motion_map.elem_bound).trigger(event_ue);
}
// remove pointer from consideration
delete motionMapMap[motion_id];
};
// End motion control /fnMotionEnd/
//------------------ END MOTION CONTROLS -------------------
//------------------- BEGIN EVENT HANDLERS -------------------
// Begin event handler /onTouch/ for all touch events.
// We use the 'type' attribute to dispatch to motion control
onTouch = function ( event ) {
var
this_el = this,
timestamp = +new Date(),
o_event = event.originalEvent,
touch_list = o_event ? o_event.changedTouches || [] : [],
touch_count = touch_list.length,
idx, touch_event, motion_id, handler_fn
;
doDisableMouse = true;
event.timeStamp = timestamp;
switch ( event.type ) {
case 'touchstart' : handler_fn = fnMotionStart; break;
case 'touchmove' : handler_fn = fnMotionMove; break;
case 'touchend' :
case 'touchcancel' : handler_fn = fnMotionEnd; break;
default : handler_fn = null;
}
if ( ! handler_fn ) { return; }
for ( idx = 0; idx < touch_count; idx++ ) {
touch_event = touch_list[idx];
motion_id = 'touch' + String(touch_event.identifier);
event.clientX = touch_event.clientX;
event.clientY = touch_event.clientY;
handler_fn({
elem : this_el,
motion_id : motion_id,
event_src : event
});
}
};
// End event handler /onTouch/
// Begin event handler /onMouse/ for all mouse events
// We use the 'type' attribute to dispatch to motion control
onMouse = function ( event ) {
var
this_el = this,
motion_id = 'mouse' + String(event.button),
request_dzoom = false,
handler_fn
;
if ( doDisableMouse ) {
event.stopImmediatePropagation();
return;
}
if ( event.shiftKey ) { request_dzoom = true; }
// skip left or middle clicks
if ( event.type !== 'mousemove' ) {
if ( event.button !== 0 ) { return true; }
}
switch ( event.type ) {
case 'mousedown' : handler_fn = fnMotionStart; break;
case 'mouseup' : handler_fn = fnMotionEnd; break;
case 'mousemove' : handler_fn = fnMotionMove; break;
default : handler_fn = null;
}
if ( ! handler_fn ) { return; }
handler_fn({
elem : this_el,
event_src : event,
request_dzoom : request_dzoom,
motion_id : motion_id
});
};
// End event handler /onMouse/
//-------------------- END EVENT HANDLERS --------------------
// Export special events through jQuery API
$Special.ue
= $Special.utap = $Special.uheld
= $Special.uzoomstart = $Special.uzoommove = $Special.uzoomend
= $Special.udragstart = $Special.udragmove = $Special.udragend
= $Special.uheldstart = $Special.uheldmove = $Special.uheldend
= Ue
;
$.ueSetGlobalCb = function ( selector_str, callback_match, callback_nomatch ) {
callbackList.push( {
selector_str : selector_str || '',
callback_match : callback_match || null,
callback_nomatch : callback_nomatch || null
});
};
}(jQuery));

View File

@@ -0,0 +1,87 @@
(function($) {
$.fn.serialize = function(options) {
return $.param(this.serializeArray(options));
};
$.fn.serializeArray = function(options) {
var o = $.extend({
checkboxesAsBools: false
}, options || {});
var rselectTextarea = /select|textarea/i;
var rinput = /text|hidden|password|date|search/i;
return this.map(function() {
return this.elements ? $.makeArray(this.elements) : this;
})
.filter(function() {
return this.name && !this.disabled &&
(this.checked || (o.checkboxesAsBools && this.type === 'checkbox') || rselectTextarea.test(this.nodeName) || rinput.test(this.type));
})
.map(function(i, elem) {
var val = $(this).val();
//this block changed here by me to break out the overly tight but obscure code
if (val == null) return null;
if ($.isArray(val)) {
//Array return
return $.map(val, function(val, i) {
return {
name: elem.name,
value: val
};
})
} else {
if (o.checkboxesAsBools && this.type === 'checkbox') {
return {
name: elem.name,
value: (this.checked ? 'true' : 'false')
}
}
//for now dates are handled at the backend
//by convention of field name ending in "Date" (sentDate, readDate etc)
// if (this.type === 'date') {
// return {
// name: elem.name,
// value: (val === '' ? '' : val) //empty dates sb null for mongo db backend
// }
// }
//default all other types
return {
name: elem.name,
value: val
}
}
// /changed by me
//ORIGINAL BLOCK:
// return val == null ?
// null :
// $.isArray(val) ?
// $.map(val, function (val, i) {
// return { name: elem.name, value: val };
// }) :
// {
// name: elem.name,
// value: (o.checkboxesAsBools && this.type === 'checkbox') ? //moar ternaries!
// (this.checked ? 'true' : 'false') :
// val
// };
}).get();
};
})(jQuery);

7
wwwroot/js/lib/moment.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1113
wwwroot/js/lib/page.js Normal file

File diff suppressed because it is too large Load Diff

7
wwwroot/js/lib/store.min.js vendored Normal file
View File

@@ -0,0 +1,7 @@
/* Copyright (c) 2010-2016 Marcus Westin */
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.store = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (global){
"use strict";module.exports=function(){function e(){try{return o in n&&n[o]}catch(e){return!1}}var t,r={},n="undefined"!=typeof window?window:global,i=n.document,o="localStorage",a="script";if(r.disabled=!1,r.version="1.3.20",r.set=function(e,t){},r.get=function(e,t){},r.has=function(e){return void 0!==r.get(e)},r.remove=function(e){},r.clear=function(){},r.transact=function(e,t,n){null==n&&(n=t,t=null),null==t&&(t={});var i=r.get(e,t);n(i),r.set(e,i)},r.getAll=function(){},r.forEach=function(){},r.serialize=function(e){return JSON.stringify(e)},r.deserialize=function(e){if("string"==typeof e)try{return JSON.parse(e)}catch(t){return e||void 0}},e())t=n[o],r.set=function(e,n){return void 0===n?r.remove(e):(t.setItem(e,r.serialize(n)),n)},r.get=function(e,n){var i=r.deserialize(t.getItem(e));return void 0===i?n:i},r.remove=function(e){t.removeItem(e)},r.clear=function(){t.clear()},r.getAll=function(){var e={};return r.forEach(function(t,r){e[t]=r}),e},r.forEach=function(e){for(var n=0;n<t.length;n++){var i=t.key(n);e(i,r.get(i))}};else if(i&&i.documentElement.addBehavior){var c,u;try{u=new ActiveXObject("htmlfile"),u.open(),u.write("<"+a+">document.w=window</"+a+'><iframe src="/favicon.ico"></iframe>'),u.close(),c=u.w.frames[0].document,t=c.createElement("div")}catch(l){t=i.createElement("div"),c=i.body}var f=function(e){return function(){var n=Array.prototype.slice.call(arguments,0);n.unshift(t),c.appendChild(t),t.addBehavior("#default#userData"),t.load(o);var i=e.apply(r,n);return c.removeChild(t),i}},d=new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]","g"),s=function(e){return e.replace(/^d/,"___$&").replace(d,"___")};r.set=f(function(e,t,n){return t=s(t),void 0===n?r.remove(t):(e.setAttribute(t,r.serialize(n)),e.save(o),n)}),r.get=f(function(e,t,n){t=s(t);var i=r.deserialize(e.getAttribute(t));return void 0===i?n:i}),r.remove=f(function(e,t){t=s(t),e.removeAttribute(t),e.save(o)}),r.clear=f(function(e){var t=e.XMLDocument.documentElement.attributes;e.load(o);for(var r=t.length-1;r>=0;r--)e.removeAttribute(t[r].name);e.save(o)}),r.getAll=function(e){var t={};return r.forEach(function(e,r){t[e]=r}),t},r.forEach=f(function(e,t){for(var n,i=e.XMLDocument.documentElement.attributes,o=0;n=i[o];++o)t(n.name,r.deserialize(e.getAttribute(n.name)))})}try{var v="__storejs__";r.set(v,v),r.get(v)!=v&&(r.disabled=!0),r.remove(v)}catch(l){r.disabled=!0}return r.enabled=!r.disabled,r}();
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}]},{},[1])(1)
});

View File

@@ -0,0 +1,9 @@
<div>
<img src="android-chrome-192x192.png" alt="Rockfish logo" >
<h2>Login</h2>
<form method="post" action="index.html">
<p><input type="text" id="login" value="" placeholder="Username" autocapitalize="none"></p>
<p><input type="password" id="password" value="" placeholder="Password"></p>
<p class="submit"><input type="submit" id="btnSubmit" value="Login"></p>
</form>
</div>

View File

@@ -0,0 +1,54 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" type="text" id="name" name="name" value="">
</div>
<div class="form-group">
<label for="adminEmail">Admin / license / support emails</label>
<input class="form-control" type="text" id="adminEmail" name="adminEmail" placeholder="License related, comma separated" value="">
</div>
<div class="form-group">
<label for="supportEmail">Support only emails</label>
<input class="form-control" type="text" id="supportEmail" name="supportEmail" placeholder="License related, comma separated" value="">
</div>
<div class="form-group">
<label for="affiliateNumber">Affiliate number</label>
<input class="form-control" type="text" id="affiliateNumber" name="affiliateNumber" value="">
</div>
<div class="form-check">
<label class="form-check-label" for="doNotContact">
<input class="form-check-input" type="checkbox" name="doNotContact" id="doNotContact">
Do not contact
</label>
</div>
<div class="form-check">
<label class="form-check-label" for="active">
<input class="form-check-input" type="checkbox" name="active" id="active">
Active</label>
</div>
<div class="form-group">
<label for="notes">Notes</label>
<textarea class="form-control form-control-lg" id="notes" name="notes" rows="10"/>
</div>
<div class="app-frm-buttons mt-5">
<button id="btn-save" class="btn btn-success">Save</button>
<button id="btn-delete" class="btn btn-outline-dark">Delete</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,88 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" type="text" id="name" name="name" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="country">Country</label>
<input class="form-control" type="text" id="country" name="country" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="stateProvince">State / Province</label>
<input class="form-control" type="text" id="stateProvince" name="stateProvince" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="networked">
<input class="form-check-input" type="checkbox" name="networked" id="networked">
Networked</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="dbType">Database type</label>
<input class="form-control" type="text" id="dbType" name="dbType" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="serverOS">Server OS</label>
<input class="form-control" type="text" id="serverOS" name="serverOS" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="serverBits">Server bits</label>
<input class="form-control" type="text" id="serverBits" name="serverBits" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="hosted">
<input class="form-check-input" type="checkbox" name="hosted" id="hosted">
Hosted</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="hostName">Host name</label>
<input class="form-control" type="text" id="hostName" name="hostName" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="hostingStartDate">Hosting start</label>
<input class="form-control" type="date" id="hostingStartDate" name="hostingStartDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="hostingEndDate">Hosting end</label>
<input class="form-control" type="date" id="hostingEndDate" name="hostingEndDate" value="">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="notes">Notes</label>
<textarea class="form-control" id="notes" name="notes" rows="10"/>
</div>
</div>
</div>
<div class="app-frm-buttons mt-5">
<button id="btn-save" class="btn btn-success">Save</button>
<button id="btn-delete" class="btn btn-outline-dark">Delete</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<ul id="rf-list" class="rf-list" />
</div>

View File

@@ -0,0 +1,4 @@
<div>
<div id="rf-list-count"/>
<div id="rf-list" class="rf-list"/>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<h2>404 NOT FOUND 404</h2>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<ul id="rf-list-div" />
</div>

View File

@@ -0,0 +1,183 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="row">
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="keyWillLockout">
<input class="form-check-input" type="checkbox" name="keyWillLockout" id="keyWillLockout"> Expiring test key</label>
</div>
<input class="form-control" type="date" id="lockoutDate" name="lockoutDate" value="">
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="isLite">
<input class="form-check-input" type="checkbox" name="isLite" id="isLite"> AyaNova LITE</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="licenseType">License type</label>
<select class="form-control" name="licenseType">
<option value="new">New</option>
<option value="addon">Renewal / Add-on</option>
<option value="licensedTrial">Licensed trial</option>
<option value="webRequestedTrial">Web requested trial</option>
</select>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="registeredTo">Registered to</label>
<input class="form-control" type="text" id="registeredTo" name="registeredTo" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="customerId">Customer</label>
<select class="form-control" id="customerId" name="customerId" />
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="emailAddress">Email address</label>
<input class="form-control" type="text" id="emailAddress" name="emailAddress" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="users">Users</label>
<select class="form-control" name="users">
<option value="1">1</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="15">15</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="999">999</option>
</select>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="supportExpiresDate">Support expires</label>
<input class="form-control" type="date" id="supportExpiresDate" name="supportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="wbi">
<input class="form-check-input" type="checkbox" name="wbi" id="wbi"> WBI
</label>
<input class="form-control" type="date" id="wbiSupportExpiresDate" name="wbiSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="mbi">
<input class="form-check-input" type="checkbox" name="mbi" id="mbi"> MBI
</label>
<input class="form-control" type="date" id="mbiSupportExpiresDate" name="mbiSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="ri">
<input class="form-check-input" type="checkbox" name="ri" id="ri"> RI
</label>
<input class="form-control" type="date" id="riSupportExpiresDate" name="riSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="qbi">
<input class="form-check-input" type="checkbox" name="qbi" id="qbi"> QBI
</label>
<input class="form-control" type="date" id="qbiSupportExpiresDate" name="qbiSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="qboi">
<input class="form-check-input" type="checkbox" name="qboi" id="qboi"> QBOI
</label>
<input class="form-control" type="date" id="qboiSupportExpiresDate" name="qboiSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="pti">
<input class="form-check-input" type="checkbox" name="pti" id="pti"> PTI
</label>
<input class="form-control" type="date" id="ptiSupportExpiresDate" name="ptiSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="quickNotification">
<input class="form-check-input" type="checkbox" name="quickNotification" id="quickNotification"> Quick notification</label>
<input class="form-control" type="date" id="quickNotificationSupportExpiresDate" name="quickNotificationSupportExpiresDate"
value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="exportToXls">
<input class="form-check-input" type="checkbox" name="exportToXls" id="exportToXls"> Export to XLS</label>
<input class="form-control" type="date" id="exportToXlsSupportExpiresDate" name="exportToXlsSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="outlookSchedule">
<input class="form-check-input" type="checkbox" name="outlookSchedule" id="outlookSchedule"> Outlook Schedule</label>
<input class="form-control" type="date" id="outlookScheduleSupportExpiresDate" name="outlookScheduleSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="oli">
<input class="form-check-input" type="checkbox" name="oli" id="oli"> OLI
</label>
<input class="form-control" type="date" id="oliSupportExpiresDate" name="oliSupportExpiresDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="importExportCSVDuplicate">
<input class="form-check-input" type="checkbox" name="importExportCSVDuplicate" id="importExportCSVDuplicate"> Import / export CSV duplicate</label>
<input class="form-control" type="date" id="importExportCSVDuplicateSupportExpiresDate" name="importExportCSVDuplicateSupportExpiresDate"
value="">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="key">Key</label>
<textarea class="form-control" id="key" name="key" rows="10" />
</div>
</div>
</div>
<div class="app-frm-buttons">
</div>
</form>
</div>

View File

@@ -0,0 +1,26 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="request">Request</label>
<textarea class="form-control" readonly id="request" name="request" rows="10"/>
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="greeting">Greeting message</label>
<textarea class="form-control" id="greeting" name="greeting" rows="10"/>
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="keycode">Keycode message</label>
<textarea class="form-control" readonly id="keycode" name="keycode" rows="10"/>
</div>
</div>
</div>
</form>
</div>

View File

@@ -0,0 +1,75 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="row">
<div class="col-sm-12">
<h2>FULL KEY</h2>
<div class="form-group">
<label for="fullNew">New</label>
<textarea class="form-control" id="fullNew" name="fullNew" rows="10" />
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="fullAddOn">Add-On</label>
<textarea class="form-control" id="fullAddOn" name="fullAddOn" rows="10" />
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="fullTrial">Licensed Trial</label>
<textarea class="form-control" id="fullTrial" name="fullTrial" rows="10" />
</div>
</div>
<div class="col-sm-12">
<h2>Full trial greeting</h2>
<div class="form-group">
<label for="fullTrialGreeting">Greeting</label>
<textarea class="form-control" id="fullTrialGreeting" name="fullTrialGreeting" rows="10" />
</div>
</div>
<div class="col-sm-12">
<h2>LITE KEY</h2>
<div class="form-group">
<label for="liteNew">Lite New</label>
<textarea class="form-control" id="liteNew" name="liteNew" rows="10" />
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="liteAddOn">Lite Add-On</label>
<textarea class="form-control" id="liteAddOn" name="liteAddOn" rows="10" />
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="liteTrial">Lite Licensed Trial</label>
<textarea class="form-control" id="liteTrial" name="liteTrial" rows="10">
</div>
</div>
<div class="col-sm-12">
<h2>Lite trial greeting</h2>
<div class="form-group">
<label for="liteTrialGreeting">Lite Greeting</label>
<textarea class="form-control" id="liteTrialGreeting" name="liteTrialGreeting"/>
</div>
</div>
</div>
<div class="app-frm-buttons mt-5">
<button id="btn-save" class="btn btn-success">Save</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,56 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="form-group">
<label for="regTo">Registered to</label>
<input class="form-control" type="text" id="regTo" name="regTo" value="" readonly>
</div>
<div class="form-group">
<label for="customerName">Rockfish customer name</label>
<input class="form-control" type="text" id="customerName" name="customerName" value="" readonly>
</div>
<div class="form-group">
<label for="dtcreated">Created</label>
<input class="form-control" type="date" id="dtcreated" name="dtcreated" value="" readonly>
</div>
<div class="form-group">
<label for="email">Email</label>
<input class="form-control" type="text" id="email" name="email" value="" readonly>
</div>
<div class="form-group">
<label for="code">Fetch code</label>
<input class="form-control" type="text" id="code" name="code" value="" readonly>
</div>
<div class="form-check">
<label class="form-check-label text-success font-weight-bold" for="fetched">
<input class="form-check-input" type="checkbox" name="fetched" id="fetched"> License fetched</label>
</div>
<div class="form-group">
<label for="dtFetched">Fetched on</label>
<input class="form-control" type="date" id="dtfetched" name="dtfetched" value="" readonly>
</div>
{{!-- <div class="form-group">
<label for="fetchFrom">Fetched from</label>
<input class="form-control" type="text" id="fetchFrom" name="fetchFrom" value="" readonly>
</div> --}}
<div class="form-group">
<label for="key">Key</label>
<textarea class="form-control form-control-lg" id="key" name="key" rows="10" readonly/>
</div>
<div class="app-frm-buttons mt-5">
<button id="btn-save" class="btn btn-success">Save</button>
<button id="btn-delete" class="btn btn-outline-dark">Delete</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<div id="rf-list" class="rf-list"/>
</div>

View File

@@ -0,0 +1,30 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="form-group">
<label for="message">Message</label>
<textarea class="form-control" id="message" rows="20" readonly />
</div>
<div id="sendToGroup" class="form-group invisible">
<label for="sendTo">Send to</label>
<input class="form-control" type="text" id="sendTo" name="sendTo" value="">
</div>
<div class="form-group">
<label for="composition">Reply</label>
<div class="btn-group float-right ml-5" role="group">
<button id="btn-send" type="button" class="btn btn-sm btn-outline-primary mdi mdi-send"></button>
</div>
<div class="form-check form-check-inline float-right">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="trackDelivery" name="trackDelivery" >Receipt
</label>
</div>
<textarea class="form-control" id="composition" name="composition" rows="10" />
</div>
</form>
</div>

View File

@@ -0,0 +1,82 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="name">Product name</label>
<input class="form-control" type="text" id="name" name="name" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="productCode">Product code</label>
<input class="form-control" type="text" id="productCode" name="productCode" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="salesOrderNumber">Sales order number</label>
<input class="form-control" type="text" id="salesOrderNumber" name="salesOrderNumber" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="vendorName">Vendor</label>
<input class="form-control" type="text" id="vendorName" name="vendorName" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="purchaseDate">Purchased</label>
<input class="form-control" type="date" id="purchaseDate" name="purchaseDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-check">
<label class="form-check-label" for="renewNoticeSent">
<input class="form-check-input" type="checkbox" name="renewNoticeSent" id="renewNoticeSent">
Renew notice sent</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="expireDate">Subscription expire</label>
<input class="form-control" type="date" id="expireDate" name="expireDate" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="cancelDate">Cancel date</label>
<input class="form-control" type="date" id="cancelDate" name="cancelDate" value="">
</div>
</div>
{{!-- <div class="col-sm-6">
<div class="form-group">
<label for="email">Email</label>
<input class="form-control" type="text" id="email" name="email" value="">
</div>
</div> --}}
<div class="col-sm-6">
<div class="form-group">
<label for="couponCode">Coupon code</label>
<input class="form-control" type="text" id="couponCode" name="couponCode" value="">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="notes">ShareIt Order</label>
<textarea class="form-control" id="notes" name="notes" rows="10"/>
</div>
</div>
</div>
<div class="app-frm-buttons mt-5">
<button id="btn-save" class="btn btn-success">Save</button>
<button id="btn-delete" class="btn btn-outline-dark">Delete</button>
<button id="btn-renew" class="btn btn-outline-primary">Renew</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<ul id="rf-list" class="rf-list" />
</div>

View File

@@ -0,0 +1,3 @@
<div>
<ul id="rf-list" class="rf-list" />
</div>

View File

@@ -0,0 +1,4 @@
<div>
<ul id="rf-list" class="rf-list" />
</div>

View File

@@ -0,0 +1,32 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="row">
<div class="half column">
<h6>Note: this will fetch all support and admin email addresses</h6>
<div class="form-group">
<label for="nocontact">Include 'Do not contact' emails</label>
<input type="checkbox" name="ckNoContact" id="ckNoContact">
</div>
<fieldset>
<legend>Products:</legend>
<div id="cbdiv"></div>
</fieldset>
</div>
<div class="half column">
<div class="form-group">
<label for="csvdata">Emails</label>
<textarea id="csvdata" name="csvdata" />
</div>
</div>
</div>
</form>
</div>

View File

@@ -0,0 +1,109 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="mb-4">
<span class="display-4" id="caseid"></span>
<span class="text-muted align-top" id="dtcreated"></span>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="title">Title</label>
<input class="form-control" type="text" id="title" name="title" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="project">Project</label>
<select class="form-control" name="rfCaseProjectId" id="rfCaseProjectId" />
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="priority">Priority</label>
<select class="form-control" name="priority" id="priority">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
</div>
{{!--
<div class="col-sm-12">
<div class="btn-group" role="group">
<button id="btn-append" type="button" class="btn btn-outline-primary">Append</button>
</div>
</div> --}}
<div class="col-sm-12">
<div class="form-group">
<label for="notes">Notes</label>
<div class="btn-group float-right" role="group">
<button id="btn-append" type="button" class="btn btn-sm btn-outline-primary">Append</button>
</div>
<textarea class="form-control" id="notes" name="notes" rows="15" />
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="dtClosed"> Closed</label>
<input class="form-control" type="date" id="dtClosed" name="dtClosed" value="">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="title">Released in version</label>
<input class="form-control" type="text" id="releaseVersion" name="releaseVersion" value="">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="title">Release notes</label>
<input class="form-control" type="text" id="releaseNotes" name="releaseNotes" value="">
</div>
</div>
<div class="col-sm-12">
<label for="attachments">Attachments</label>
<ul id="attachments" class="list-group">
</ul>
</div>
</div>
<div class="app-frm-buttons mt-5">
<button id="btn-save" class="btn btn-success">Save</button>
<button id="btn-delete" class="btn btn-outline-dark">Delete</button>
</div>
</form>
<form id="frmUpload" class="invisible my-5" method="post" enctype="multipart/form-data" action="/api/rfcaseblob/upload?rfcaseid=1">
<hr/>
<div>
<p>Upload attachments:</p>
<input type="file" name="files" id="files" multiple/>
</div>
<input type="button" id="btn-upload" value="Attach selected files" />
</form>
</div>

View File

@@ -0,0 +1,50 @@
<div>
<div class="row">
<div class="col-sm-3">
<div class="form-group">
<label for="project">Projects</label>
<select class="form-control" id="projects" />
</div>
</div>
<div class="col-sm-3">
<div class="form-check">
<label class="form-check-label" for="open">
<input class="form-check-input" type="checkbox" id="open" checked/>
Open</label>
</div>
</div>
<div class="col-sm-3">
<div class="form-group">
<label for="priority">Priority</label>
<select class="form-control" id="priority">
<option value="0" selected >All</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
</div>
<div class="col-sm-3">
<div class="form-group">
<label for="csearch">Search</label>
<input class="form-control" type="text" id="csearch" value="">
</div>
</div>
</div>
</div>
<hr/>
<div>
<div id="rf-list-count"/>
<ul id="rf-list" class="rf-list" />
</div>
</div>

View File

@@ -0,0 +1,15 @@
<div>
<div class="alert alert-success mb-5" id="about" />
<form id="frm" method="post" action="index.html">
<div class="form-group">
<label for="oldpassword">Change password</label>
<input class="form-control" type="text" id="oldpassword" name="oldpassword" placeholder="current password" value="">
<input class="form-control" type="text" id="newpassword" name="newpassword" placeholder="new password" value="">
<div class="app-frm-buttons mt-5">
<button id="btn-change-password">Update</button>
</div>
</div>
</form>
</div>

View File

@@ -0,0 +1,18 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="form-group">
<label for="query">Search</label>
<input class="form-control" type="text" id="searchquery" value="">
<div class="app-frm-buttons">
<button id="searchbutton">Search</button>
</div>
</div>
<div>
<ul id="rf-list" class="rf-list" />
</div>
</form>
</div>

View File

@@ -0,0 +1,57 @@
<nav id="rf-nav" class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li id="inbox" class="nav-item">
<a class="rfac nav-link mdi mdi-inbox" href="#!/inbox">Inbox </a>
</li>
<li id="customers" class="nav-item">
<a class="rfac nav-link mdi mdi-contacts" href="#!/customers">Customers </a>
</li>
<li id="subscriptions" class="nav-item">
<a class="rfac nav-link mdi mdi-basket" href="#!/subscription">Subscriptions </a>
</li>
<li id="license" class="nav-item">
<a class="rfac nav-link mdi mdi-key" href="#!/license">License </a>
</li>
<li id="rfcases" class="nav-item">
<a class="rfac nav-link mdi mdi-bug" href="#!/rfcases">Cases </a>
</li>
<li id="rfsettings" class="nav-item">
<a class="rfac nav-link mdi mdi-settings" href="#!/rfsettings">Settings </a>
</li>
<li class="nav-item">
<a class="rfac nav-link mdi mdi-logout" href="#!/logout">Log off </a>
</li>
</ul>
</div>
</nav>
<div class="rf-content mx-2 ">
<div id="app-error-div" class="alert alert-danger d-none" role="alert">
<p id="app-error-message"></p>
</div>
<div>
<div id="rf-context-group" class="btn-group my-3" role="group">
</div>
</div>
<main id="app-shell-main-content">
</main>
</div>

View File

@@ -0,0 +1,4 @@
<div>
<ul id="rf-list" class="rf-list" />
</div>

View File

@@ -0,0 +1,3 @@
<div>
<ul id="rf-list" class="rf-list" />
</div>

View File

@@ -0,0 +1,28 @@
<div>
<form id="frm" method="post" action="index.html">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" type="text" id="name" name="name" value="">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label for="template">template</label>
<textarea class="form-control" id="template" name="template" rows="15" />
</div>
</div>
</div>
<div class="app-frm-buttons mt-5">
<button id="btn-save" class="btn btn-success">Save</button>
<button id="btn-delete" class="btn btn-outline-dark">Delete</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<ul id="rf-list" class="rf-list" />
</div>

File diff suppressed because one or more lines are too long