Files
catc/scripts/catc.js
2019-09-22 13:20:16 +02:00

478 lines
13 KiB
JavaScript

function CATC(asGlobals)
{
self = this;
this.consts = asGlobals.consts;
this.consts.hash_sep = '-';
this.consts.default_page = 'workshops';
this.consts.title = 'CATC';
this.consts.exitmodes = {same_page:'SP', new_page:'NP', closing:'LE'};
this.consts.root = location.protocol+'//'+location.host+location.pathname;
this.init = function()
{
//Variables & constants from php
self.vars('tmp', 'object');
self.vars('page', 'string');
self.vars('template', 'boolean');
self.updateVars(asGlobals.vars);
//page elem
self.elem = {};
self.elem.$Container = $('#container');
//on window resize
$(window).resize(self.onResize).resize();
//on Quit / F5
$(window).on('unload', function(){self.onQuitPage(self.consts.exitmodes.closing);});
//Hash management
self.resetTmpFunctions();
$(window)
.bind('hashchange', self.onHashChange)
.trigger('hashchange');
};
this.updateVars = function(asVars)
{
$.each(asVars, function(sKey, oValue){self.vars(sKey, oValue)});
};
/* Menu */
this.initMenu = function()
{
//Search - Input Autocomplete
self.refreshDefs();
self.elem.$Menu.find('#search')
.autocomplete({
autoFocus: true,
delay: 0,
minLength: 2,
classes:{'ui-autocomplete':'list-group'},
source: function(oRequest, fResponse) {
var sTerm = removeDiacritics(oRequest.term);
var rMatcher = new RegExp($.ui.autocomplete.escapeRegex(sTerm), 'i');
var asResults = [];
$.each(self.vars('lov-defs'), function(iDefId, oItem) {
var oTitleMatch = oItem.safe_title.match(rMatcher);
var oLabelMatch = oItem.safe_label.match(rMatcher);
var oMatch = {};
oItem.field = '';
if(oTitleMatch) {
oMatch = oTitleMatch;
oItem.field = 'title';
}
else if(oLabelMatch) {
oMatch = oLabelMatch;
oItem.field = 'label';
}
if(oItem.field != '') {
oItem.index = parseInt(oMatch.index, 10);
oItem.size = oMatch[0].length;
asResults.push(oItem);
}
oItem.id = iDefId;
});
self.elem.$Menu.find('#add_def')
.toggleClass('btn-outline-primary', (asResults.length > 0))
.toggleClass('btn-primary', (asResults.length == 0));
fResponse(asResults);
},
select: function(event, ui) {
return false; //Do not update input value
}
})
.data("ui-autocomplete")._renderItem = function(ul, item) {
var fGetHighLightedContent = function(sContent, iIndex, iSize){
return $('<span>')
.append(sContent.substr(0, iIndex))
.append($('<span>', {'class':'highlight text-secondary'}).text(sContent.substr(iIndex, iSize)))
.append(sContent.substr(iIndex + iSize));
};
var $Title = $('<div>', {'class':'title text-break text-capitalize font-weight-bold text-primary'});
var $Desc = $('<div>', {'class':'desc text-break'});
if(item.field=='title') {
$Title.append(fGetHighLightedContent(item.title, item.index, item.size));
$Desc.text(item.label);
}
else {
$Title.text(item.title);
$Desc.append(fGetHighLightedContent(item.label, item.index, item.size));
}
return $('<li>', {'class':'list-group-item shadow'})
.css('width', $('#search').outerWidth())
.data("item.autocomplete", item)
.append($('<div>', {'class':'row'})
.append($('<div>', {'class':'col'})
.append($Title)
.append($Desc)
)
.append($('<div>', {'class':'col-auto d-flex align-items-center'}).append($('<button>', {'class':'btn btn-outline-secondary'})
.data('id', item.id)
.attr('data-toggle', 'modal')
.attr('data-target', '#add-def')
.appendIcon('edit')
))
)
.appendTo(ul);
};
//Add/Edit Definition Interface
$('#add-def').on('show.bs.modal', function (event) {
var $Button = $(event.relatedTarget);
var iDefId = $Button.data('id');
var $Modal = $(this);
var asDef = iDefId?self.vars(['lov-defs', iDefId]):{'title':'', 'description':''};
$Modal.find('#add_def_title').text(iDefId?'Update a definition':'Add a new definition');
$Modal.find('#def_id').val(iDefId);
$Modal.find('#def_title').val(asDef.title);
$Modal.find('#def_description').val(asDef.description);
var asParams = Tools.serialize('def_form');
$Modal.find('#save').off('click').click(function(){
Tools.ajax(
'set_def',
function(asData){
self.onFeedback('success', 'Definition of "'+asData.def.title+'" '+(asData.new_def?'added':'updated'));
self.refreshDefs();
},
'def_form'
);
});
});
//Body schemas
self.vars('bodies', {
'Lymphatic System' : 'lymphatic_system',
'Muscular System' : 'muscular_system',
'Nervous System' : 'nervous_system',
'Skeletal System' : 'skeletal_system',
'12 Meridians' : '12_meridians'
});
$.each(self.vars('bodies'), function(sTitle, sFileName) {
var oMain = new Image();
oMain.src = 'images/'+sFileName+'.jpg';
var oThumb = new Image();
oThumb.src = 'images/'+sFileName+'_thumb.jpg';
self.vars(['bodies', sTitle], {'thumb':oThumb.src, 'main':oMain.src});
});
self.elem.$Menu.find('.body').popover({
container: '#menu',
html: true,
content: function() {
var $Cards = $('<div>', {'class':'card-deck', 'style':'width:40rem;'});
$.each(self.vars('bodies'), function(sTitle, asBody) {
var $Card = self.getTemplateItem('bodycard');
$Card.find('img').attr({'src':asBody.thumb, 'alt':sTitle});
$Card.find('.card-text').text(sTitle);
$Card
.data('title', sTitle)
.hover(function() {$(this).toggleClass('text-white bg-primary');})
.appendTo($Cards);
});
return $Cards;
},
boundary: 'viewport',
placement: 'bottom',
trigger: 'click'
});
$('#body-full').on('show.bs.modal', function(event) {
var sTitle = $(event.relatedTarget).data('title');
var asBody = self.vars(['bodies', sTitle]);
var $Modal = $(this);
$Modal.find('img').attr({'src':asBody.main, 'alt':sTitle});
$Modal.find('.body-title').text(sTitle);
});
//Home
self.elem.$Menu.find('.home').click(function(){self.setHash(self.consts.default_page);});
//log Off
$('#log-off').on('show.bs.modal', function(event) {
document.cookie = self.consts.cookie+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC";
location.href = self.consts.root;
});
self.elem.$Menu.slideDown('fast');
};
this.refreshDefs = function() {
Tools.ajax(
'get_defs',
(asData) => {
self.vars('lov-defs', asData);
$.each(self.vars('lov-defs'), function(iKey, asValues) {
self.vars(['lov-defs', iKey, 'safe_title'], removeDiacritics(asValues['title']));
self.vars(['lov-defs', iKey, 'label'], asValues['description']);
self.vars(['lov-defs', iKey, 'safe_label'], removeDiacritics(asValues['description']));
});
}
);
};
/* Events */
this.onResize = function()
{
self.vars('mobile', $('body').css('min-width')=='120px');
};
this.onHashChange = function()
{
var asHash = self.getHash();
self.switchPage(asHash);
};
this.resetTmpFunctions = function()
{
self.pageInit = function(asHash, bFirstPage){console.log('no init for the page: '+asHash.page)};
self.onSamePageMove = function(asHash){return false};
self.onQuitPage = function(sExitMode){return true};
self.onFeedback = function(sType, sMsg){Tools.feedback(sType, sMsg);};
};
this.feedback = function(sType, sMsg) {
self.onFeedback(sType, sMsg);
};
/* Hash Handling */
this.getHash = function()
{
var sHash = self.hash();
var asHash = sHash.split(self.consts.hash_sep);
var sPage = asHash.shift() || '';
return {hash:sHash, page:sPage, items:asHash};
};
this.setHash = function(sPage, asItems, bReboot)
{
bReboot = bReboot || false;
sPage = sPage || '';
asItems = asItems || [];
if(typeof asItems == 'string') asItems = [asItems];
if(sPage != '')
{
var sItems = (asItems.length > 0)?self.consts.hash_sep+asItems.join(self.consts.hash_sep):'';
self.hash(sPage+sItems, bReboot);
}
};
this.hash = function(hash, bReboot)
{
bReboot = bReboot || false;
if(!hash) return window.location.hash.slice(1);
else window.location.hash = '#'+hash;
if(bReboot) location.reload();
};
/* Page Switching */
this.loadHome = function(asData) {
self.vars('log_in', asData.log_in);
self.vars('id', asData.id);
$(window).off('keyup');
if(self.vars('log_in')) {
//Setup menu
self.initMenu();
//Swap to default page
self.setHash(self.consts.default_page);
}
};
this.setPageTitle = function(sTitle) {
$('#main_title').find('h1').text(sTitle);
};
this.getActionLink = function(sAction, oVars)
{
if(!oVars) oVars = {};
sVars = '';
for(i in oVars)
{
sVars += '&'+i+'='+oVars[i];
}
return self.consts.process_page+'?a='+sAction+sVars;
};
this.switchPage = function(asHash)
{
var sCurrPage = self.vars('page');
var sNextPage = asHash.page;
var bLoggedIn = self.vars('log_in');
var sDefaultPage = bLoggedIn?self.consts.default_page:'logon';
if( asHash.hash == '' ||
sNextPage == '' ||
!bLoggedIn && sNextPage != sDefaultPage ||
bLoggedIn && sNextPage == 'logon'
)
{
self.setHash(sDefaultPage); //force first page
}
else
{
var bSamePage = (sCurrPage == sNextPage);
if(self.onQuitPage(self.consts.exitmodes[bSamePage?'same_page':'new_page']) && !bSamePage || self.onSamePageMove(asHash))
{
//Delete tmp variables
self.vars('tmp', {});
//disable tmp functions
self.resetTmpFunctions();
//Officially a new page
var bFirstPage = (sCurrPage == '');
self.vars('page', sNextPage);
//Update Page Title
var sDetail = asHash.items[0] || '';
document.title = self.consts.title+' - '+sNextPage+' '+sDetail;
//Replacing DOM
var $Dom = $(self.consts.pages[sNextPage]);
if(bFirstPage)
{
self.elem.$Container.html($(self.consts.pages['template']));
self.elem.$Main = self.elem.$Container.find('#main');
self.elem.$Menu = self.elem.$Container.find('#menu');
if(self.vars.log_in) self.initMenu();
self.splash(self.elem.$Main, $Dom, asHash, bFirstPage); //first page
}
else
{
self.elem.$Main.stop().fadeTo('fast', 0, function(){self.splash(self.elem.$Main, $Dom, asHash, bFirstPage);}); //Switching page
}
}
}
};
this.splash = function($FadeInElem, $Dom, asHash, bFirstPage)
{
//Switch main content
$FadeInElem.empty();
$FadeInElem.html($Dom);
//Show main
$FadeInElem.fadeTo('fast', 1, function(){
self.pageInit(asHash, bFirstPage);
$('[data-toggle="tooltip"]').tooltip();
});
};
/* Variables Handling */
this.vars = function(sVarName, oValue)
{
var asTypes = {boolean:false, string:'', integer:0, array:[], object:{}};
var asVarName = (typeof sVarName == 'object')?sVarName:[sVarName];
var sFirstVarName = asVarName[0];
//Get, only name parameter
if(typeof oValue == 'undefined')
{
return getElem(self.vars, asVarName);
}
//Init, name & type / default value
else if(typeof oValue !== 'undefined' && typeof self.vars[sFirstVarName] === 'undefined')
{
self.vars[sFirstVarName] = (typeof asTypes[oValue] !== 'undefined')?asTypes[oValue]:oValue;
return self.vars[sFirstVarName];
}
//Set, name & value
else if(typeof oValue !== 'undefined' && typeof self.vars[sFirstVarName] !== 'undefined')
{
setElem(self.vars, asVarName, oValue);
return getElem(self.vars, asVarName);
}
return null;
};
this.tmp = function(sVarName, oValue)
{
var asVarName = (typeof sVarName == 'object')?sVarName:[sVarName];
asVarName.unshift('tmp');
return self.vars(asVarName, oValue);
};
this.getTemplateItem = function(sItemName) {
return $('.template-items').find('.'+sItemName).clone();
}
}
class Editor {
constructor(sEditorId, bReadOnly) {
this.keystrokes = 0;
this.readOnly = bReadOnly || false;
this.sCursorPos = '';
//DOM Elements
this.$Editor = $(sEditorId);
this.onKeyStroke = function() {};
this.oQuill = new Quill(sEditorId, {
theme: 'snow',
placeholder: 'Notes',
readOnly: bReadOnly/*,
modules: {
toolbar: [['bold', 'italic', 'underline'], [{ 'list': 'ordered'}, { 'list': 'bullet' }], ['clean']]
}*/
});
this._initEvents();
this._postInit();
}
_initEvents() {
//Key strokes
this.$Editor.keydown((e) => {
if($.inArray(e.which, [13, 37, 38, 39, 40]) == -1) this.onKeyStroke(e);
});
//On text modification
this.oQuill.on('text-change', (delta, oldDelta, sSource) => {this._incKeyStrokes();});
}
_postInit(iPage) {
if(!this.readOnly) this.oQuill.focus();
}
_incKeyStrokes() {
this.keystrokes += 1;
}
getContent() {
return this.oQuill.getContents().ops;
}
setContent(asOps) {
this.oQuill.setContents(asOps);
this._postInit();
}
isEmpty() {
const rEmpty = /^(<p>(<br>|<br\/>|<br\s\/>|\s+|)<\/p>|)$/gm;
return rEmpty.test(this.oQuill.getText().trim());
}
quill2Html(asDelta) {
var tempCont = document.createElement('div');
(new Quill(tempCont)).setContents(asDelta);
return tempCont.getElementsByClassName('ql-editor')[0].innerHTML;
}
}