gestioCOF/kfet/static/kfet/js/kfet.js
2018-01-10 19:09:41 +01:00

466 lines
12 KiB
JavaScript

/**
* @file Miscellaneous JS definitions for <tt>k-fet</tt> app.
* @copyright 2017 cof-geek
* @license MIT
*/
/**
* String method
* @memberof String
* @return {String} String formatted as trigramme
*/
String.prototype.formatTri = function() {
return this.toUpperCase().substr(0, 3);
}
/**
* String method
* @global
* @return {Boolean} true iff String follows trigramme pattern
*/
String.prototype.isValidTri = function() {
var pattern = /^[^a-z]{3}$/;
return pattern.test(this);
}
/**
* Checks if given argument is float ;
* if not, parses given argument to float value.
* @global
* @return {float}
*/
function floatCheck(v) {
if (typeof v === 'number')
return v;
return Number.parseFloat(v);
}
function intCheck(v) {
return Number.parseInt(v);
}
function floatCheck(v) {
if (typeof v === 'number')
return v;
return Number.parseFloat(v);
}
function booleanCheck(v) {
return v == true;
}
/**
* Short: Equivalent to python str format.
* Source: [MDN]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals}.
* @example
* var t1Closure = template`${0}${1}${0}!`;
* t1Closure('Y', 'A'); // "YAY!"
* @example
* var t2Closure = template`${0} ${'foo'}!`;
* t2Closure('Hello', {foo: 'World'}); // "Hello World!"
*/
function template(strings, ...keys) {
return (function(...values) {
var dict = values[values.length - 1] || {};
var result = [strings[0]];
keys.forEach(function(key, i) {
var value = Number.isInteger(key) ? values[key] : dict[key];
result.push(value, strings[i + 1]);
});
return result.join('');
});
}
/**
* Get and store K-Psul config from API.
* <br><br>
*
* Config should be accessed statically only.
*/
class Config {
/**
* Get or create config object.
* @private
* @return {object} object - config keys/values
*/
static _get_or_create_config() {
if (window.config === undefined)
window.config = {};
return window.config;
}
/**
* Get config from API.
* @param {jQueryAjaxComplete} [callback] - A function to be called when
* the request finishes.
*/
static reset(callback) {
$.getJSON(Urls['kfet.kpsul.get_settings']())
.done(function(data) {
for (var key in data) {
Config.set(key, data[key]);
}
})
.always(callback);
}
/**
* Get value for key in config.
* @param {string} key
*/
static get(key) {
return this._get_or_create_config()[key];
}
/**
* Set value for key in config.
* @param {string} key
* @param {*} value
*/
static set(key, value) {
// API currently returns string for Decimal type
if (['addcost_amount', 'subvention_cof'].indexOf(key) > -1)
value = floatCheck(value);
this._get_or_create_config()[key] = value;
}
}
/*
* CSRF Token
*/
var csrftoken = '';
if (typeof Cookies !== 'undefined')
csrftoken = Cookies.get('csrftoken');
// Add CSRF token in header of AJAX requests.
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
function add_csrf_form($form) {
$form.append(
$('<input>', {'name': 'csrfmiddlewaretoken', 'value': csrftoken})
);
}
/*
* Capslock management
*/
window.capslock = -1;
$(document).on('keypress', function(e) {
var s = String.fromCharCode(e.which);
if ((s.toUpperCase() === s && s.toLowerCase() !== s && !e.shiftKey)|| //caps on, shift off
(s.toUpperCase() !== s && s.toLowerCase() === s && e.shiftKey)) { //caps on, shift on
$('body').addClass('capslock_on')
} else if ((s.toLowerCase() === s && s.toUpperCase() !== s && !e.shiftKey)|| //caps off, shift off
(s.toLowerCase() !== s && s.toUpperCase() === s && e.shiftKey)) { //caps off, shift on
$('body').removeClass('capslock_on')
}
});
$(document).on('keydown', function(e) {
if (e.which == 20) {
$('body').toggleClass('capslock_on')
}
});
/*
* Generic Websocket class and k-psul ws instanciation
*/
class KfetWebsocket {
static get defaults() {
return {
relative_url: '',
default_msg: {},
handlers: [],
base_path: '/ws/k-fet/'
};
}
constructor(data) {
$.extend(this, this.constructor.defaults, data);
if (window.location.pathname.startsWith('/gestion/'))
this.base_path = '/gestion' + this.base_path;
}
get url() {
var protocol = window.location.protocol == 'https:' ? 'wss' : 'ws';
var host = window.location.host;
return protocol + "://" + host + this.base_path + this.relative_url;
}
add_handler(handler) {
if (!this.socket)
this.listen();
this.handlers.push(handler);
}
listen() {
var that = this;
this.socket = new ReconnectingWebSocket(this.url);
this.socket.onmessage = function(e) {
var data = $.extend({}, that.default_msg, JSON.parse(e.data));
for (let handler of that.handlers) {
handler(data);
}
}
}
}
var OperationWebSocket = new KfetWebsocket({
'relative_url': 'k-psul/',
'default_msg': {'opegroups':[],'opes':[],'checkouts':[],'articles':[]},
});
function dateUTCToParis(date) {
return moment.tz(date, 'UTC').tz('Europe/Paris');
}
function amountDisplay(amount, is_cof=false, tri='') {
if (tri == 'LIQ')
return (- amount).toFixed(2) +'€';
return amountToUKF(amount, is_cof);
}
function amountToUKF(amount, is_cof=false, account=false) {
var rounding = account ? Math.floor : Math.round ;
var coef_cof = is_cof ? 1 + Config.get('subvention_cof') / 100 : 1;
return rounding(amount * coef_cof * 10);
}
function isValidTrigramme(trigramme) {
var pattern = /^[^a-z]{3}$/;
return trigramme.match(pattern);
}
/**
* Dialogs with user via jconfirm
*/
class UserDialog {
static get defaults() {
return {'title': '', 'content': ''};
}
constructor(data) {
$.extend(this, this.constructor.defaults, data);
}
open(settings) {
// Arg management
var pre_content = settings.pre_content || '';
var post_content = settings.post_content || '';
var callback = settings.callback || $.noop;
var that = this;
$.confirm({
title: this.title,
content: pre_content + this.content + post_content,
backgroundDismiss: true,
animation:'top',
closeAnimation:'bottom',
keyboardEnabled: true,
confirm: function() {
var inputs = {};
this.$content.find('input').each(function () {
inputs[$(this).attr('name')] = $(this).val();
});
if (Object.keys(inputs).length > 1)
return callback(inputs);
else
return callback(inputs[Object.keys(inputs)[0]]);
},
onOpen: function() {
var that = this
this.$content.find('input').on('keydown', function(e) {
if (e.keyCode == 13) {
e.preventDefault();
that.$confirmButton.click();
}
});
},
onClose: function() { if (settings.next_focus) { this._lastFocused = settings.next_focus; } }
});
}
}
function getErrorsHtml(data) {
var content = '';
if (!data)
return "L'utilisateur n'est pas dans l'équipe";
if ('operation_group' in data['errors']) {
content += 'Général';
content += '<ul>';
if (data['errors']['operation_group'].indexOf('on_acc') != -1)
content += '<li>Pas de compte sélectionné</li>';
if (data['errors']['operation_group'].indexOf('checkout') != -1)
content += '<li>Pas de caisse sélectionnée</li>';
content += '</ul>';
}
if ('missing_perms' in data['errors']) {
content += 'Permissions manquantes';
content += '<ul>';
for (var i=0; i<data['errors']['missing_perms'].length; i++)
content += '<li>'+data['errors']['missing_perms'][i]+'</li>';
content += '</ul>';
}
if ('negative' in data['errors']) {
if (window.location.pathname.startsWith('/gestion/')) {
var url_base = '/gestion/k-fet/accounts/';
} else {
var url_base = '/k-fet/accounts/';
}
for (var i=0; i<data['errors']['negative'].length; i++) {
content += '<a class="btn btn-primary" href="'+url_base+data['errors']['negative'][i]+'/edit" target="_blank" style="width:100%">Autorisation de négatif requise pour '+data['errors']['negative'][i]+'</a>';
}
}
if ('addcost' in data['errors']) {
content += '<ul>';
if (data['errors']['addcost'].indexOf('__all__') != -1)
content += '<li>Compte invalide</li>';
if (data['errors']['addcost'].indexOf('amount') != -1)
content += '<li>Montant invalide</li>';
content += '</ul>';
}
if ('account' in data['errors']) {
content += 'Général';
content += '<ul>';
content += '<li>Opération invalide sur le compte '+data['errors']['account']+'</li>';
content += '</ul>';
}
return content;
}
function displayErrors(html) {
$.alert({
title: 'Erreurs',
content: html,
backgroundDismiss: true,
animation: 'top',
closeAnimation: 'bottom',
keyboardEnabled: true,
});
}
var authDialog = new UserDialog({
'title': 'Authentification requise',
'content': '<div class="capslock"><span class="glyphicon glyphicon-lock"></span><input type="password" name="password" autofocus><div>',
});
//Note/TODO: the returned ajax object can be improved by allowing chaining on errors 403/400
function api_with_auth(settings, password) {
if (window.api_lock == 1)
return false;
window.api_lock = 1;
var url = settings.url;
if (!url)
return false;
var data = settings.data || {} ;
var on_success = settings.on_success || $.noop ;
var on_400 = settings.on_400 || $.noop ;
return $.ajax({
dataType: "json",
url: url,
method: "POST",
data: data,
beforeSend: function ($xhr) {
$xhr.setRequestHeader("X-CSRFToken", csrftoken);
if (password)
$xhr.setRequestHeader("KFetPassword", password);
},
})
.done(function(data) {
on_success(data);
})
.fail(function($xhr) {
var response = $xhr.responseJSON;
switch ($xhr.status) {
case 403:
authDialog.open({
callback: function(password) {
api_with_auth(settings, password)
},
pre_content: getErrorsHtml(response),
next_focus: settings.next_focus,
});
break;
case 400:
on_400(response);
break;
}
})
.always(function() {
window.api_lock = 0;
});
}
String.prototype.pluralize = function(count, irreg_plural) {
if (Math.abs(count) >= 2)
return irreg_plural ? irreg_plural : this+'s' ;
return this ;
}
/**
* Setup jquery-confirm
*/
jconfirm.defaults = {
confirmButton: '<span class="glyphicon glyphicon-ok"></span>',
cancelButton: '<span class="glyphicon glyphicon-remove"></span>'
};
/**
* Create form node, given an url used as 'action', with csrftoken set.
*/
function create_form(url) {
let $form = $('<form>', {
'action': url,
'method': 'post',
});
add_csrf_form($form);
return $form;
}
/**
* Emit a POST request from <a> tag.
*
* Usage:
* <a href="#" data-url="{target url}" onclick="submit_url(this)">{…}</a>
*/
function submit_url(el) {
let url = $(el).data('url');
create_form(url).appendTo($('body')).submit();
}