"use strict";
moment().localeData("fr");
moment().utcOffset(0);
// console.log("moment().utcOffset()",moment().utcOffset());
Array.prototype.m_remove = function(val) {
var pos = this.indexOf(val);
if (~pos) this.splice(pos, 1);
};
/**
* M_ is a javascript application framework to build single page web applications, rather graphic than theory.
* @namespace M_
* @version 1.0
* @author David Miglior <david@miglior.fr>
* @todo Renomer les balises HTML utilisés en "data-" (par convention)
* @todo Si le dernier argument de M_.App.open est false alors ne pas appeler d'action (permet de faire un bouton cancel)
* @todo Donner un nom : Bootstrap commander
*
*/
M_ = {
_registeredModules: {}
};
M_.registerModule = (name, module) => {
// console.log("name,module",name,module);
M_._registeredModules[name] = module;
};
/**
* The Morphine application, instancied
* @class
* @property {boolean} inDevelopment - display more errors or helps
* @property {boolean} useWebsocket - use websocket for M_.Utils.[get,post,put,delete]Json functions
* @property {string} name - application name
* @property {type} routes - Todo
* @property {string} defaultController - the first controller to call
* @property {array} tabController -
* @property {type} tabOutlets
* @property {type} container
* @property {type} currentController
* @property {type} namespace
* @property {type} templates
* @property {type} models
* @property {type} scripts
* @property {type} app
* @property {type} beforeAllAjax
* @property {type} afterAllAjax
* @property {type} lastModule
* @property {type} lastAction
* @property {type} executeNextAction
* @property {type} controllersDir
*/
M_.App = new class {
/**
* Already instancied
* @return {type} [description]
*/
constructor() {}
/**
* To document
* @param {type} opts
*/
create(opts) {
this.defaults = {
inDevelopment: true,
useWebsocket: false,
name: "",
routes: {},
defaultController: "Home",
tabController: [],
tabOutlets: {},
container: null,
currentController: null,
namespace: "",
templates: null,
models: {},
scripts: null,
app: null,
_tabScriptsLoaded: [],
_tabModelsLoaded: [],
beforeAllAjax: null,
afterAllAjax: null,
_cmptAjax: 0,
_lastHashBis: "-------",
_urlArgs: [],
lastModule: "-------",
lastAction: "-------",
// _hashIsLoading: false,
executeNextAction: true,
_ready: null,
_beforeReady: null,
controllersDir: "app/controllers/",
moduleChange: null,
beforeModuleChange: null
};
$.extend(this, this.defaults, opts);
$(window).resize(() => {
if (this.M_tsResize) window.clearTimeout(this.M_tsResize);
this.M_tsResize = window.setTimeout(() => {
this.resize();
}, 300);
});
if ("onhashchange" in window) {
$(window).on("hashchange", () => {
this._hashChanged();
});
// } else {
// this.lastHash = window.location.hash;
// window.setInterval(()=> {
// if (window.location.hash != this.lastHash) {
// this.lastHash = window.location.hash;
// this._hashChanged();
// }
// }, 100);
}
$.ajaxPrefilter((options, originalOptions, jqXHR) => {
//log("c un ajax 1 ",arguments)
this.onBeforeAllAjax(options, originalOptions, jqXHR);
jqXHR.always(data => {
//log("c un ajax 2 ",arguments)
this.onAfterAllAjax(data, options, originalOptions, jqXHR);
});
});
return this;
}
/**
* @memberOf! M_.App
* @instance
* @param {Object} options
* @param {Object} originalOptions
* @param {jqXHR} jqXHR
*/
onBeforeAllAjax(options, originalOptions, jqXHR) {}
/**
* To document
* @memberOf! M_.App
* @param {Object} data
* @param {Object} options
* @param {Object} originalOptions
* @param {jqXHR} jqXHR
*/
onAfterAllAjax(data, options, originalOptions, jqXHR) {}
/**
* To document
* @return {M_.App}
*/
resize() {
var tabController = this.tabController;
for (var i = 0; i < tabController.length; i++) {
tabController[i].onResize();
}
return this;
}
/**
* @param {Function}
* @return {M_.App}
*/
beforeReady(fn) {
this._beforeReady = fn;
return this;
}
/**
* @param {Function}
* @return {M_.App}
*/
ready(fn) {
// log("ready")
this._ready = fn;
M_.Help.createMHelp();
if (this._beforeReady) {
this._beforeReady(() => {
this._reallyReady();
});
} else this._reallyReady();
return this;
}
_reallyReady() {
if (document.readyState == "complete") {
this._ready();
this._hashChanged();
} else {
$(document).ready(() => {
this._ready();
this._hashChanged();
});
}
}
/**
* Open a page
* @param {String} module
* @param {String} action
* @return {M_.App}
*/
open(module, action) {
// log("open", this.currentController)
this._lastHashBis = window.location.hash;
var url = "";
var args = [];
this.executeNextAction = true;
if (module.substring(0, 1) == "/") {
url = "#" + module;
} else {
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] !== false) args.push(arguments[i]);
else this.executeNextAction = false;
}
url = "#/" + args.join("/");
}
//log("module,url,args",module, ";", url,";",args)
if (url == window.location.hash) {
this._hashChanged();
} else {
window.open(url, "_self");
}
return this;
}
_getHashArgs() {
var hash = window.location.hash.substring(2, window.location.hash.length);
var t = hash.split("/");
return t;
}
_hashChanged() {
var args = this._getHashArgs();
var module = this.defaultController;
if (args[0] && args[0] !== "") module = args[0];
var justCreated = false;
if (this[module] === undefined) {
// var Mod = System.get('js6/controllers/'+module+".js")[module] ;
// this[module] = new Mod({controllerName:module, tpl:JST[module]}) ;
// console.log("JST",JST,M_._registeredModules);
// import {mymod} from './'+module+'.js' ;
this[module] = new M_._registeredModules[module]({
controllerName: module,
tpl: JST[module]
});
this.tabController.push(this[module]);
justCreated = true;
}
this[module].resolve(ok => {
if (ok) {
if (this.beforeModuleChange) {
let ok2 = this.beforeModuleChange(module, this.lastModule);
if (ok2 !== false) this.__hashChanged(justCreated);
} else this.__hashChanged(justCreated);
}
});
}
__hashChanged(justCreated) {
var tabController = this.tabController;
for (var i = 0; i < tabController.length; i++) {
tabController[i].onExit();
}
// if (this._hashIsLoading) return ;
// log("_hashChanged9")
var args = this._getHashArgs();
var module = this.defaultController;
if (args[0] && args[0] !== "") module = args[0];
var action = "";
if (args[1] && args[1] !== "") action = args[1];
// log("module2",module)
// log("currentController", this.currentController)
if (this.currentController && this.lastModule !== module) {
var currentControllerNameSaved = this.currentController.controllerName;
$("#part_" + this.currentController.controllerName)
.css("left", 0)
.css("z-index", 1)
.css("transform-origin", "5% 50%")
.transition(
{
// x: $(window).width(),
perspective: "1000px",
rotateY: "50deg",
scale: 0.7,
opacity: 0.3
},
500,
el => {
$("#part_" + currentControllerNameSaved).hide();
// this._hashChangedMore() ;
}
);
} else {
// this._hashChangedMore() ;
}
if (!justCreated) {
if (this.lastModule == module) {
this[module]._onShow(false);
} else {
// var tabController = this.tabController ;
for (i = 0; i < tabController.length; i++) {
if (this.currentController.controllerName == tabController[i].controllerName) continue;
// tabController[i].hide() ;
$("#part_" + tabController[i].controllerName).css("z-index", 0);
}
this[module]._onShow(true);
}
} else {
// import {mod} from 'controllers/'+module ;
// log("module",module, System.get('js6/controllers/'+module+".js"))
this[module].init.call(this[module]);
this[module].renderTemplate.call(this[module]);
this[module].create.call(this[module]);
this[module]._onShow.call(this[module], true);
}
if (action === "") action = this[module].defaultAction;
if (!this[module][action + "Action"]) {
log("You must define function '" + action + "Action" + "()' in " + this[module].controllerName + " controller");
this.open(module, "index");
} else {
args.splice(0, 2);
this[module][action + "Action"].apply(this[module], args);
}
if (this.moduleChange) this.moduleChange(module, this.lastModule);
this.lastModule = module;
this.lastAction = action;
}
/**
* @param {jQuery} container
* @param {JSTemplate} tpl
* @param {Object} tplData
* @return {jQuery}
*/
renderMustacheTo(container, tpl, tplData = {}) {
//, tplPartials={}
var jEl = $(this.renderMustache(tpl, tplData)); //, tplPartials
jEl.appendTo(container);
return jEl;
}
/**
* @param {JSTemplate} tpl
* @param {Object} tplData
* @return {String}
*/
renderMustache(tpl, tplData = {}) {
//, tplPartials={}
// console.log("tplPartials",tplPartials)
// console.log('_.templateSettings.imports',_.templateSettings.imports)
// console.log("tpl",tpl);
var template = tpl(tplData);
return template;
}
}();
/**
A generic controller to extends
* @class
* @example
export class Calculator extends M_.Controller {
init() {
}
create() {
}
}
*/
M_.Controller = class {
/**
* Generally, you don't need to extend this class. Extend create method.
* @param {object} opts Configuration object
*/
constructor(opts) {
var defaults = {
controller: "",
controllerName: "",
controllerSrc: null,
defaultAction: "index",
jEl: null,
tpl: null,
tplData: {},
tplPartials: {},
models: [],
templates: [],
scripts: []
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
}
/**
* To document
*/
init() {}
/**
* @param {Function}
*/
resolve(next) {
next(true);
}
/**
* To document
*/
renderTemplate() {
if (this.tpl) {
this.jEl = M_.App.renderMustacheTo(M_.App.container, this.tpl, this.tplData, this.tplPartials);
this.jEl.attr("id", "part_" + this.controllerName);
this.jEl.addClass("M_part");
}
}
/**
* To document
*/
render() {}
/**
* To document
*/
indexAction() {}
/**
* To document
*/
onResize() {}
/**
* To document
*/
onExit() {}
/**
* To document
*/
onControllerChange() {}
/**
* Extend this method to initialize your UI
*/
doLayout() {}
/**
* This method is called each time the controller is shown.
* @param {boolean} firstTime True when this function is called for the first time.
*/
show(firstTime) {}
_onShow(anim) {
// console.log("_onShow");
// var args = this._urlArgs.slice(0) ;
// args.unshift(firstTime) ;
// this.show.apply(this, args) ;
M_.App.currentController = this;
this.onControllerChange();
this.show.apply(this);
// console.log("_onShow");
this.jEl
// .css('scale', 1)
// .css('rotateX',0)
// .css('perspective','0px')
// .css('rotateY','0deg')
.css("transform", "rotateY(0deg)")
.css("transform", "perspective(0)")
.css("opacity", 1)
.css("z-index", 2)
.show();
if (anim) {
// console.log("anim");
this.jEl.css("left", $(window).width()).transition({
left: 0,
delay: 100
}, 500, () => {
// console.log("finit");
});
} else {
this.jEl.css("left", 0);
}
}
/**
* onShow description
* @param {Boolean} firstTime [description]
*/
onShow(firstTime) {}
/**
* hide description
*/
hide() {
if (this.jEl) this.jEl.hide();
}
};
/**
* Usefull methods for manipulate date, string, paths, etc...
*/
M_.Utils = class {
static preloadImages(preload, cb) {
// var preload = ["a.gif", "b.gif", "c.gif"];
var promises = [];
for (var i = 0; i < preload.length; i++) {
(function(url, promise) {
var img = new Image();
img.onload = function() {
promise.resolve();
};
img.src = url;
})(preload[i], (promises[i] = $.Deferred()));
}
$.when.apply($, promises).done(function() {
// alert("All images ready sir!");
cb();
});
}
static isEventSupported(eventName) {
var TAGNAMES = {
select: "input",
change: "input",
search: "input",
submit: "form",
reset: "form",
error: "img",
load: "img",
abort: "img"
};
var el = document.createElement(TAGNAMES[eventName] || "div");
eventName = "on" + eventName;
var isSupported = eventName in el;
if (!isSupported) {
el.setAttribute(eventName, "return;");
isSupported = typeof el[eventName] == "function";
}
el = null;
return isSupported;
}
static humanFileSizeString(fileSizeInBytes) {
var i = -1;
var byteUnits = [" kB", " MB", " GB", " TB", "PB", "EB", "ZB", "YB"];
do {
fileSizeInBytes = fileSizeInBytes / 1024;
i++;
} while (fileSizeInBytes > 1024);
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
}
/**
* Idem as jQuery $.isArray(obj)
* @param {object} obj The object to test
* @return {boolean} True if obj is an array
*/
static isArray(obj) {
return $.isArray(obj);
}
static get_html_translation_table(table, quote_style) {
// discuss at: http://phpjs.org/functions/get_html_translation_table/
// original by: Philip Peterson
// revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// bugfixed by: noname
// bugfixed by: Alex
// bugfixed by: Marco
// bugfixed by: madipta
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: T.Wild
// improved by: KELAN
// improved by: Brett Zamir (http://brett-zamir.me)
// input by: Frank Forte
// input by: Ratheous
// note: It has been decided that we're not going to add global
// note: dependencies to php.js, meaning the constants are not
// note: real constants, but strings instead. Integers are also supported if someone
// note: chooses to create the constants themselves.
// example 1: get_html_translation_table('HTML_SPECIALCHARS');
// returns 1: {'"': '"', '&': '&', '<': '<', '>': '>'}
var entities = {},
hash_map = {},
decimal;
var constMappingTable = {},
constMappingQuoteStyle = {};
var useTable = {},
useQuoteStyle = {};
// Translate arguments
constMappingTable[0] = "HTML_SPECIALCHARS";
constMappingTable[1] = "HTML_ENTITIES";
constMappingQuoteStyle[0] = "ENT_NOQUOTES";
constMappingQuoteStyle[2] = "ENT_COMPAT";
constMappingQuoteStyle[3] = "ENT_QUOTES";
useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : "HTML_SPECIALCHARS";
useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : "ENT_COMPAT";
if (useTable !== "HTML_SPECIALCHARS" && useTable !== "HTML_ENTITIES") {
throw new Error("Table: " + useTable + " not supported");
// return false;
}
entities["38"] = "&";
if (useTable === "HTML_ENTITIES") {
entities["160"] = " ";
entities["161"] = "¡";
entities["162"] = "¢";
entities["163"] = "£";
entities["164"] = "¤";
entities["165"] = "¥";
entities["166"] = "¦";
entities["167"] = "§";
entities["168"] = "¨";
entities["169"] = "©";
entities["170"] = "ª";
entities["171"] = "«";
entities["172"] = "¬";
entities["173"] = "­";
entities["174"] = "®";
entities["175"] = "¯";
entities["176"] = "°";
entities["177"] = "±";
entities["178"] = "²";
entities["179"] = "³";
entities["180"] = "´";
entities["181"] = "µ";
entities["182"] = "¶";
entities["183"] = "·";
entities["184"] = "¸";
entities["185"] = "¹";
entities["186"] = "º";
entities["187"] = "»";
entities["188"] = "¼";
entities["189"] = "½";
entities["190"] = "¾";
entities["191"] = "¿";
entities["192"] = "À";
entities["193"] = "Á";
entities["194"] = "Â";
entities["195"] = "Ã";
entities["196"] = "Ä";
entities["197"] = "Å";
entities["198"] = "Æ";
entities["199"] = "Ç";
entities["200"] = "È";
entities["201"] = "É";
entities["202"] = "Ê";
entities["203"] = "Ë";
entities["204"] = "Ì";
entities["205"] = "Í";
entities["206"] = "Î";
entities["207"] = "Ï";
entities["208"] = "Ð";
entities["209"] = "Ñ";
entities["210"] = "Ò";
entities["211"] = "Ó";
entities["212"] = "Ô";
entities["213"] = "Õ";
entities["214"] = "Ö";
entities["215"] = "×";
entities["216"] = "Ø";
entities["217"] = "Ù";
entities["218"] = "Ú";
entities["219"] = "Û";
entities["220"] = "Ü";
entities["221"] = "Ý";
entities["222"] = "Þ";
entities["223"] = "ß";
entities["224"] = "à";
entities["225"] = "á";
entities["226"] = "â";
entities["227"] = "ã";
entities["228"] = "ä";
entities["229"] = "å";
entities["230"] = "æ";
entities["231"] = "ç";
entities["232"] = "è";
entities["233"] = "é";
entities["234"] = "ê";
entities["235"] = "ë";
entities["236"] = "ì";
entities["237"] = "í";
entities["238"] = "î";
entities["239"] = "ï";
entities["240"] = "ð";
entities["241"] = "ñ";
entities["242"] = "ò";
entities["243"] = "ó";
entities["244"] = "ô";
entities["245"] = "õ";
entities["246"] = "ö";
entities["247"] = "÷";
entities["248"] = "ø";
entities["249"] = "ù";
entities["250"] = "ú";
entities["251"] = "û";
entities["252"] = "ü";
entities["253"] = "ý";
entities["254"] = "þ";
entities["255"] = "ÿ";
}
if (useQuoteStyle !== "ENT_NOQUOTES") {
entities["34"] = """;
}
if (useQuoteStyle === "ENT_QUOTES") {
entities["39"] = "'";
}
entities["60"] = "<";
entities["62"] = ">";
// ascii decimals to real symbols
for (decimal in entities) {
if (entities.hasOwnProperty(decimal)) {
hash_map[String.fromCharCode(decimal)] = entities[decimal];
}
}
return hash_map;
}
static html_entity_decode(string, quote_style) {
// discuss at: http://phpjs.org/functions/html_entity_decode/
// original by: john (http://www.jd-tech.net)
// input by: ger
// input by: Ratheous
// input by: Nick Kolosov (http://sammy.ru)
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: marc andreu
// revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// bugfixed by: Onno Marsman
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: Fox
// depends on: get_html_translation_table
// example 1: html_entity_decode('Kevin & van Zonneveld');
// returns 1: 'Kevin & van Zonneveld'
// example 2: html_entity_decode('&lt;');
// returns 2: '<'
var hash_map = {},
symbol = "",
tmp_str = "",
entity = "";
tmp_str = string.toString();
if (false === (hash_map = this.get_html_translation_table("HTML_ENTITIES", quote_style))) {
return false;
}
// fix & problem
// http://phpjs.org/functions/get_html_translation_table:416#comment_97660
delete hash_map["&"];
hash_map["&"] = "&";
for (symbol in hash_map) {
entity = hash_map[symbol];
tmp_str = tmp_str.split(entity).join(symbol);
}
tmp_str = tmp_str.split("'").join("'");
return tmp_str;
}
static getIframeDocument(jEl) {
var iframeDoc = jEl.get(0).contentWindow || jEl.get(0).contentDocument;
if (iframeDoc.document) {
iframeDoc = iframeDoc.document;
}
return iframeDoc;
}
// static generateAndDownloadFile(urlGenerate, urlDownload, args={}) {
// var defaults = {
// methodGenerate: 'POST',
// methodDownload: 'POST',
// argsGenerate: {},
// argsDownload: {},
// };
// var myargs = $.extend({}, defaults, args) ;
// this.ajaxJson(urlGenerate, myargs.argsGenerate, myargs.methodDownload, (data)=> {
//
// }
// this.downloadFile(urlDownload, myargs.argsDownload, myargs.methodDownload) ;
// }
static downloadFile(url, args = {}, method = "POST") {
$("#M_FileDownload").remove();
$(document.body).append("<iframe id='M_FileDownload' src='about:blank' style='display:none;'></iframe>");
var html = "";
_.each(args, (val, key) => {
html += `<input type="hidden" name="${key}" value="${val}">`;
});
var doc = this.getIframeDocument($("#M_FileDownload"));
// console.log("html", html);
doc.write("<html><head></head><body><form method='" + method + "' action='" + url + "'>" + html + "</form></body></html>");
var started = false;
$(doc)
.find("form")
.submit();
}
static downloadFile2(url, args = {}, method = "POST") {
$("#M_FileDownload").remove();
$(document.body).append("<iframe id='M_FileDownload' src='" + url + "' style='display:block; width:100%; height:200px;'></iframe>");
window.setTimeout(() => {
var doc = this.getIframeDocument($("#M_FileDownload"));
var toto = doc.toString();
// console.log("doc.window", doc);
// var txt = $(doc).html() ;
// console.log("txt", txt, JSON.parse(txt));
}, 2000);
}
static urlIze(str) {
str = M_.Utils.trim(str);
if (str.substring(0, 7) == "http://") return str;
return "http://" + str;
}
static limitText(str, n, useWordBoundary) {
var toLong = str.length > n,
s_ = toLong ? str.substr(0, n - 1) : this;
s_ = useWordBoundary && toLong ? s_.substr(0, s_.lastIndexOf(" ")) : s_;
return toLong ? s_ + "…" : s_;
}
/**
* toggleAppFullScreen description
* @return {Boolean} true if actually passed in full screen (after function executed)
*/
static toggleAppFullScreen() {
var element = document.body;
$(document.body).css("width", "100%");
if (!M_.Utils._appIsFullScreen) {
var requestMethod =
element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullscreen;
if (requestMethod) {
requestMethod.call(element);
} else if (typeof window.ActiveXObject !== "undefined") {
// Older IE.
var wscript = new window.ActiveXObject("WScript.Shell");
if (wscript !== null) {
wscript.SendKeys("{F11}");
}
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
}
M_.Utils._appIsFullScreen = !M_.Utils._appIsFullScreen;
}
/**
* Return moment object
* @return {boolean} True if obj is an array
*/
static moment() {
return moment;
}
/**
* Idem as jQuery $.isFunction()
* @param {object} obj The object to test
* @return {boolean} True if obj is an function
*/
static isFunction(obj) {
return $.isFunction(obj);
}
/**
* Idem as jQuery $.isEmptyObject()
* @param {object} obj The object to test
* @return {boolean} True if obj is {}
*/
static isEmptyObject(obj) {
return $.isEmptyObject(obj);
}
/**
* Check if d is a moment date
* @param {moment} obj The object to test
* @return {boolean} True if obj is {}
*/
static isDate(d) {
if (moment(d).isValid()) return true;
return false;
}
/**
* Return the extension of a path.
* @param {String} path The path
* @return {String} The extension or ''
* @example
* var ext = M_.Utils.getExtensionFromPath("foo/bar/my_file.html") ;
* // ext = "html"
*/
static getExtensionFromPath(path) {
return path.split(".").pop();
}
/**
* Return the base name of a path.
* @param {String} path The path
* @return {String} The name of the file
* @example
* var filename = getBaseNameFromPath("foo/bar/myfile.html") ;
* // filename = "myfile.html"
*/
static getBaseNameFromPath(path) {
return path.split("/").pop();
}
/**
* Return the directory of a path.
* @param {String} path The path
* @return {String} The name of the file
* @example
* var filename = getDirFromPath("foo/bar/myfile.html") ;
* // filename = "foo/bar"
*/
static getDirFromPath(path) {
var tabPaths = path.split("/");
tabPaths.pop();
return tabPaths.join("/");
}
/**
* Convert hexadecimal to RGB
* @param {number} hex Number to convert
* @return {object} Object contain keys : r, g, b ; {r:x, g:y, b:z}
*/
static hexToRgb(hex) {
//Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
}
: null;
}
/**
* Convert RGB to Hexa
* @param {number} r Red
* @param {integer} g Green
* @param {integer} b Blue
* @return {number} Number of hexadecimal
*/
static rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
/**
* Convert RGB from css (like #FFFFFF) to Hexa
* @param {number} rgb
* @return {number} Object contain keys : r, g, b ; {r:x, g:y, b:z}
*/
static rgbFromCssToHex(rgb) {
if (rgb.indexOf("#") === 0) return rgb;
if (rgb.indexOf("rgb") === 0) {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/i);
return (
"#" +
("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) +
("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) +
("0" + parseInt(rgb[3], 10).toString(16)).slice(-2)
);
}
}
/**
* Return a uniq ID
* @return {String} A incremental number start from 1
*/
static id() {
if (!this.cmpUniqId) this.cmpUniqId = 0;
this.cmpUniqId++;
return "_M_el_" + this.cmpUniqId;
}
/**
* Don't use that...
*/
static removeEventsNotAttachedToDocument() {
window.setTimeout(() => {
// log("go")
var tab = $._data(document, "events").click;
// log("tab",tab)
if (!tab) return;
var nb = 0;
var tabToRemove = [];
for (var i = 0; i < tab.length; i++) {
if (tab[i].selector && $(tab[i].selector).length === 0 && tab[i].selector.substring(0, 7) == "#_M_el_") {
nb++;
tabToRemove.push(tab[i].selector);
}
}
// log("tabToRemove",tabToRemove)
for (i = 0; i < tabToRemove.length; i++) {
$(document).off("click", tabToRemove[i]);
}
// log("nb", nb) ;
}, 500);
}
/**
* Set the z-index upper
* @param {jQuery} jEl jQuery object
*/
static setZIndexForModal(jEl) {
this._cmptZIndexModal += 10;
jEl.css("z-index", this._cmptZIndexModal);
$(".modal-backdrop")
.last()
.css("z-index", this._cmptZIndexModal - 1);
}
// static getMaxZIndex() {
// var heighest = -999 ;
// $("*").each(function() {
// var current = parseInt($(this).css("z-index"), 10);
// heighest = Math.max(current, heighest) ;
// });
// if (heighest==-999) heighest = 0 ;
// return heighest ;
// }
/**
* Clone an object... simple level !!!!
* @param {object} obj
* @return {object} the new object
*/
static clone(obj) {
var target = {};
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
target[i] = obj[i];
}
}
return target;
}
/**
* Create a UUID with a random algorithme
* @return {String} The number is like 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
*/
static createUUID() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
/**
* Clone an object... simple level !!!!
* @param {object} nb
* @param {object} singular
* @param {object} plural
* @return {object} singular
* @example
var plural1 = M_.Utils.plural(3, "guitare")
// plural1 = guitares
var plural2 = M_.Utils.plural(3, "guitare", "guitarez")
// plural2 = guitarez
var plural3 = M_.Utils.plural(3, "une guitare", "une des {nb} guitares")
// plural3 = une des 3 guitares
*/
static plural(nb, singular, plural = null, withoutnumber = false) {
if (nb <= 1 && nb >= -1) {
if (singular.indexOf("{{nb}}") >= 0) {
return singular.replace(/\{\{nb\}\}/, nb);
}
if (withoutnumber) return singular;
return nb + " " + singular;
}
if (!plural) plural = singular + "s";
if (plural.indexOf("{{nb}}") >= 0) {
return plural.replace(/\{\{nb\}\}/, nb);
}
if (withoutnumber) return plural;
return nb + " " + plural;
}
/**
* Create a delay
* @param {function} callback - une fonction à appeler
* @param {number} time - un temp en millisecondes
* @param {String} [arbitralName="arbitralName"] - donner de préférence un nom unique
*/
static delay(callback, time, arbitralName = "arbitralName") {
if (!this.tabDelay) this.tabDelay = {};
if (arbitralName === "") arbitralName = "arbitralName";
if (this.tabDelay[arbitralName]) window.clearTimeout(this.tabDelay[arbitralName]);
this.tabDelay[arbitralName] = window.setTimeout(() => {
//log("callback")
callback();
}, time);
}
/**
* Check if variable is empty : [], undef, null, false, 0, "", "0", "undefined", undefined
* @param {object} obj
* @return {boolean} true if empty
*/
static isEmpty(obj) {
var undef, key, i, len;
var emptyValues = [undef, null, false, 0, "", "0", "undefined", undefined];
if ($.type(obj) === "date") return false;
for (i = 0, len = emptyValues.length; i < len; i++) {
if (obj === emptyValues[i] || obj == emptyValues[i]) {
return true;
}
}
if (typeof obj === "object") {
for (key in obj) {
//TODO: should we check for own properties only?
//if (obj.hasOwnProperty(key)) {
return false;
//}
}
return true;
}
return false;
}
/**
* Format a phone number... for french format !!! to update...
* @param {String} num
* @return {String} the formated number
*/
static formatPhone(num) {
var ret = "";
var toAdd = "";
//$phone_number = preg_replace("/[^\+0-9]/", "", $phone_number);
num = num.replace(/[^\+0-9]/g, "");
if (num.substring(0, 1) == "+") {
toAdd = num.substr(0, 3) + " ";
num = num.substr(3);
}
for (let kk = 0; kk < num.length; kk++) {
if (kk % 2 === 0) ret = " " + ret;
ret = num.substr(num.length - kk - 1, 1) + ret;
}
return toAdd + ret.trim();
}
static alphaNum(str) {
if (!str) return "";
var reg1 = /[^\+0-9]/g;
str = str.replace(reg1, "");
return str;
}
/**
* Strip html tags
* @param {String} num
* @param {String} allowed
* @return {String} the new string
*/
static strip_tags(input, allowed) {
//From: http://phpjs.org/functions
// console.log("input",input);
input += "";
if (!input) return "";
allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join("");
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
return input.replace(commentsAndPhpTags, "").replace(tags, function($0, $1) {
return allowed.indexOf("<" + $1.toLowerCase() + ">") > -1 ? $0 : "";
});
}
/**
* First char to upper case
* @param {String} str
* @return {String} the new string
*/
static ucfirst(str) {
// discuss at: http://phpjs.org/functions/ucfirst/
// original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// bugfixed by: Onno Marsman
// improved by: Brett Zamir (http://brett-zamir.me)
// example 1: ucfirst('kevin van zonneveld');
// returns 1: 'Kevin van zonneveld'
str += "";
str = str.toLowerCase();
var f = str.charAt(0).toUpperCase();
return f + str.substr(1);
}
/**
* First char to upper case
* @param {String} str - the base string
* @param {number} len - add 'padStr' X times
* @param {String} [padStr='0'] - the string to add
* @param {number} [direction=-1] - -1=left, 1=right
* @return {String} the new string
*/
static str_pad(str, len, padStr = "0", direction = -1) {
str += "";
var res = "";
//warning if len<str.length
if (direction < 0)
str = str
.split("")
.reverse()
.join("");
for (var i = 0; i < len; i++) {
var c = str.charAt(i);
if (c === "") res += padStr;
res += str.charAt(i);
}
if (direction < 0)
res = res
.split("")
.reverse()
.join("");
return res;
}
/**
* Remove break line, space and tabulation
* @param {String} str - the base string
* @return {String} the new string
*/
static trim(str) {
//return str.replace(/^\s+|\s+$/g, '');
str = $.trim(str);
str = str.replace(/^(\n\r)+|(\n\r)+$/g, "");
return str;
}
/**
* Set cookie
* @param {String} cname - the name of the cookie
* @param {String} cvalue - the value of the cookie
* @param {String} [expireDays=7] - expiration in days
*/
static setCookie(cname, cvalue, expireDays = 7) {
var d = new Date();
d.setTime(d.getTime() + expireDays * 24 * 60 * 60 * 1000);
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
/**
* Get cookie
* @param {String} cname - the name of the cookie
* @return {String} the value of the cookie
*/
static getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(";");
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return null;
}
/**
* Check if browser is on macos
* @return {boolean} true if ok
*/
static isMacos() {
var isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
return isMac;
}
static valInArray(tab, key, keyName = "key", valName = "val") {
var f = this.findInArray(tab, key, keyName);
if (f) return _.result(f, valName);
else return "";
}
static findInArray(tab, key, keyName = "key") {
var obj = {};
obj[keyName] = key;
var f = _.find(tab, obj);
// console.log("f", f);
return f;
}
/**
* Get cookie
* @param {String} cname - the name of the cookie
* @return {String} the value of the cookie
*/
static getFromSimpleArray(tab, what) {
for (var i2 = 0; i2 < tab.length; i2++) {
if (tab[i2][0] == what) return tab[i2][1];
}
return "";
}
/**
* Get cookie
* @param {String} cname - the name of the cookie
* @return {String} the value of the cookie
*/
static getSimpleArray(tab, index, what) {
var pos = this.searchSimpleArray(tab, index, what);
if (pos >= 0) return tab[pos];
return null;
}
/**
* Get cookie
* @param {String} cname - the name of the cookie
* @return {String} the value of the cookie
*/
static searchSimpleArray(tab, index, what) {
for (var i2 = 0; i2 < tab.length; i2++) {
if (tab[i2][index] == what) return i2;
}
return -1;
}
/**
* Get cookie
* @param {String} cname - the name of the cookie
* @return {String} the value of the cookie
*/
static searchArrayObjects(tab, what, val) {
for (var i2 = 0; i2 < tab.length; i2++) {
if (tab[i2][what] == val) return i2;
}
return -1;
}
/**
* Get cookie
* @param {String} cname - the name of the cookie
* @return {String} the value of the cookie
*/
static getFromArrayObjects(tab, what, val) {
for (var i2 = 0; i2 < tab.length; i2++) {
if (tab[i2][what] == val) return tab[i2];
}
return null;
}
/**
* To document
*/
static inArray(needle, haystack) {
var length = haystack.length;
for (var i = 0; i < length; i++) {
if (haystack[i] == needle) return true;
}
return false;
}
/**
* To document
* @param {String} str the string to convert
* @param {Boolean} is_xhtml [description]
* @return {String} modified string
*/
static nl2br(str, is_xhtml) {
var breakTag = is_xhtml || typeof is_xhtml === "undefined" ? "<br " + "/>" : "<br>"; //Adjust comment to avoid issue on phpjs.org display
return (str + "").replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, "$1" + breakTag + "$2");
}
/**
* replaceCommaWithPoint description
* @param {String} txt
* @return {String}
*/
static replaceCommaWithPoint(txt) {
txt += "";
/*if (typeof(txt)=="string" ) {
if (txt.indexOf(",")>=0) {
var txt1 = txt.substring(0,txt.indexOf(",")) ;
var txt2 = txt.substring(txt.indexOf(",")+1,txt.length) ;
return txt1+"."+txt2 ;
}
}
*/
var exp = new RegExp(",", "g");
return txt.replace(exp, ".");
}
/**
* Like PHP function
* @param {Array} tab
* @param {String} field
* @return {Boolean} true if is set
*/
static isset(tab, field) {
if (typeof tab[field] != "undefined") return true;
return false;
}
/**
* Like PHP function
* @param {String} path
* @param {String} options
* @return {String}
*/
static pathinfo(path, options) {
// discuss at: http://phpjs.org/functions/pathinfo/
// original by: Nate
// revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: Brett Zamir (http://brett-zamir.me)
// input by: Timo
// note: Inspired by actual PHP source: php5-5.2.6/ext/standard/string.c line #1559
// note: The way the bitwise arguments are handled allows for greater flexibility
// note: & compatability. We might even standardize this code and use a similar approach for
// note: other bitwise PHP functions
// note: php.js tries very hard to stay away from a core.js file with global dependencies, because we like
// note: that you can just take a couple of functions and be on your way.
// note: But by way we implemented this function, if you want you can still declare the PATHINFO_*
// note: yourself, and then you can use: pathinfo('/www/index.html', PATHINFO_BASENAME | PATHINFO_EXTENSION);
// note: which makes it fully compliant with PHP syntax.
// depends on: basename
// example 1: pathinfo('/www/htdocs/index.html', 1);
// returns 1: '/www/htdocs'
// example 2: pathinfo('/www/htdocs/index.html', 'PATHINFO_BASENAME');
// returns 2: 'index.html'
// example 3: pathinfo('/www/htdocs/index.html', 'PATHINFO_EXTENSION');
// returns 3: 'html'
// example 4: pathinfo('/www/htdocs/index.html', 'PATHINFO_FILENAME');
// returns 4: 'index'
// example 5: pathinfo('/www/htdocs/index.html', 2 | 4);
// returns 5: {basename: 'index.html', extension: 'html'}
// example 6: pathinfo('/www/htdocs/index.html', 'PATHINFO_ALL');
// returns 6: {dirname: '/www/htdocs', basename: 'index.html', extension: 'html', filename: 'index'}
// example 7: pathinfo('/www/htdocs/index.html');
// returns 7: {dirname: '/www/htdocs', basename: 'index.html', extension: 'html', filename: 'index'}
var opt = "",
optName = "",
optTemp = 0,
tmp_arr = {},
cnt = 0,
i = 0;
var have_basename = false,
have_extension = false,
have_filename = false;
// Input defaulting & sanitation
if (!path) {
return false;
}
if (!options) {
options = "PATHINFO_ALL";
}
// Initialize binary arguments. Both the string & integer (constant) input is
// allowed
var OPTS = {
PATHINFO_DIRNAME: 1,
PATHINFO_BASENAME: 2,
PATHINFO_EXTENSION: 4,
PATHINFO_FILENAME: 8,
PATHINFO_ALL: 0
};
// PATHINFO_ALL sums up all previously defined PATHINFOs (could just pre-calculate)
for (optName in OPTS) {
OPTS.PATHINFO_ALL = OPTS.PATHINFO_ALL | OPTS[optName];
}
if (typeof options !== "number") {
// Allow for a single string or an array of string flags
options = [].concat(options);
for (i = 0; i < options.length; i++) {
// Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4
if (OPTS[options[i]]) {
optTemp = optTemp | OPTS[options[i]];
}
}
options = optTemp;
}
// Internal Functions
var __getExt = function(path) {
var str = path + "";
var dotP = str.lastIndexOf(".") + 1;
return !dotP ? false : dotP !== str.length ? str.substr(dotP) : "";
};
// Gather path infos
if (options & OPTS.PATHINFO_DIRNAME) {
var dirName = path.replace(/\\/g, "/").replace(/\/[^\/]*\/?$/, ""); // dirname
tmp_arr.dirname = dirName === path ? "." : dirName;
}
if (options & OPTS.PATHINFO_BASENAME) {
if (false === have_basename) {
have_basename = this.basename(path);
}
tmp_arr.basename = have_basename;
}
if (options & OPTS.PATHINFO_EXTENSION) {
if (false === have_basename) {
have_basename = this.basename(path);
}
if (false === have_extension) {
have_extension = __getExt(have_basename);
}
if (false !== have_extension) {
tmp_arr.extension = have_extension;
}
}
if (options & OPTS.PATHINFO_FILENAME) {
if (false === have_basename) {
have_basename = this.basename(path);
}
if (false === have_extension) {
have_extension = __getExt(have_basename);
}
if (false === have_filename) {
have_filename = have_basename.slice(
0,
have_basename.length - (have_extension ? have_extension.length + 1 : have_extension === false ? 0 : 1)
);
}
tmp_arr.filename = have_filename;
}
// If array contains only 1 element: return string
cnt = 0;
for (opt in tmp_arr) {
cnt++;
}
if (cnt == 1) {
return tmp_arr[opt];
}
// Return full-blown array
return tmp_arr;
}
/**
* Format a number with good decimal and add devise
* @param {Number} nb
* @param {Number} decimal
* @param {String} devise
* @return {String}
*/
static formatPrice(nb, decimal = 2, devise = M_.i18n.devise) {
return this.price(nb, decimal, devise);
}
/**
* Alias of formatPrice
* @param {Number} nb
* @param {Number} decimal
* @param {String} devise
* @return {String}
*/
static price(nb, decimal = 2, devise = M_.i18n.devise, sep = " ") {
return this.number_format(nb, decimal, ",", ".") + sep + devise;
}
static shortPrice(nb, devise = M_.i18n.devise, sep = " ") {
var res = "";
if (nb < 1000) res += Math.round(nb) + "";
else if (nb < 1000000) res += Math.round(nb / 1000) + "K";
else res += Math.round(nb / 1000000) + "M";
return res + sep + devise;
}
/**
* Format number in %
* @param {Number} nb
* @param {Number} decimal
* @return {String}
*/
static purcent(nb, decimal = 2, sep = " ") {
return this.number_format(nb, decimal, ",", ".") + sep + "%";
}
/**
* Like PHP function
* @param {Number} a
* @param {Number} b Number of decimal
* @param {String} c decimal separator
* @param {String} d thousen separator
* @return {String} this formated number
*/
static number_format(a, b, c, d) {
var i, e, f, g, h;
a = Number(a);
a = Math.round(a * Math.pow(10, b)) / Math.pow(10, b);
e = a + "";
f = e.split(".");
if (!f[0]) f[0] = "0";
if (!f[1]) f[1] = "";
if (f[1].length < b) {
g = f[1];
for (i = f[1].length + 1; i <= b; i++) {
g += "0";
}
f[1] = g;
}
if (d !== "" && f[0].length > 3) {
h = f[0];
f[0] = "";
for (var j = 3; j < h.length; j += 3) {
i = h.slice(h.length - j, h.length - j + 3);
f[0] = d + i + f[0] + "";
}
j = h.substr(0, h.length % 3 === 0 ? 3 : h.length % 3);
f[0] = j + f[0];
}
c = b <= 0 ? "" : c;
return f[0] + c + f[1];
}
/**
* Text selection in HTML input field
* @param {jQuery} jEl
* @param {Number} start from which char
* @param {Number} stop to which char
*/
static setSelectionRange(jEl, start = 0, stop = 0) {
if (jEl instanceof $) jEl = jEl[0];
if (jEl.childNodes.length) {
// log("jEl.childNodes[0]",jEl.childNodes[0])
var range = document.createRange();
var sel = window.getSelection();
range.setStart(jEl.childNodes[0], start);
range.collapse(true);
if (stop > 0) range.setEnd(jEl.childNodes[0], stop);
sel.removeAllRanges();
sel.addRange(range);
}
// log("range",range)
}
/**
* Return the upper z-index
* @param {jQuery} [selectorLimit]
* @return {number}
*/
static getMaxZIndex(selectorLimit = null) {
var max = 0;
$(selectorLimit || "*").each((index, el) => {
var jEl = $(el);
if (el.id == "M_Help") return true;
var zindex = parseInt(jEl.css("z-index"), 10) || 0;
max = Math.max(max, zindex);
});
// log("max",max)
return max;
}
/**
* Get max z-index + 10
* @param {Number} [increment] number to increment
* @return {number} next z-index
*/
static getNextZIndex(increment = 10) {
return this.getMaxZIndex() + increment;
}
/**
* Get default speed limit
* @return {Number} number of milliseconds
*/
static getSpeedAnim() {
return 500;
}
/**
* Display smoothly an element
* @param {jQuery} jEl the element
* @param {Function} cb callback function after finished animation
*/
static showSmoothly(jEl, cb) {
if (jEl.is(":hidden"))
jEl
.css("opacity", 0)
.show()
.transition({ opacity: 1 }, this.getSpeedAnim(), () => {
if (cb) cb();
});
else if (cb) cb();
}
/**
* Hide smoothly an element
* @param {jQuery} jEl the element
* @param {Function} cb callback function after finished animation
*/
static hideSmoothly(jEl, cb) {
if (jEl.is(":visible"))
jEl.css("opacity", 1).transition({ opacity: 0 }, this.getSpeedAnim(), () => {
jEl.hide();
if (cb) cb();
});
else if (cb) cb();
}
/**
* Do a POST of json data
* @param {String} url the url to call
* @param {object} args arguments to send
* @param {Function} cb callback function with "data" argument
* @return {jqXHR}
*/
static postJson(url, args, cb) {
return this.ajaxJson(url, args, "POST", cb);
}
/**
* Do a PUT of json data
* @param {String} url the url to call
* @param {object} args arguments to send
* @param {Function} cb callback function with "data" argument
* @return {jqXHR}
*/
static putJson(url, args, cb) {
return this.ajaxJson(url, args, "PUT", cb);
}
/**
* Do a GET of json data
* @param {String} url the url to call
* @param {object} args arguments to send
* @param {Function} cb callback function with "data" argument
* @return {jqXHR}
*/
static getJson(url, args, cb) {
return this.ajaxJson(url, args, "GET", cb);
}
/**
* Do a DELETE of json data
* @param {String} url the url to call
* @param {object} args arguments to send
* @param {Function} cb callback function with "data" argument
* @return {jqXHR}
*/
static deleteJson(url, args, cb) {
return this.ajaxJson(url, args, "DELETE", cb);
}
/**
* Do a Ajax call of json data
* @param {String} url the url to call
* @param {object} args arguments to send
* @param {String} POST|PUT|GET|DELETE
* @param {Function} cb callback function with "data" argument
* @return {jqXHR}
*/
static ajaxJson(url, args, method, cb) {
var opts = {
url: url,
type: method,
dataType: "json",
success: function(data) {
cb(data);
},
error: function(jqXHR, textStatus, errorThrown) {
cb(jqXHR.responseJSON);
}
};
if (method == "GET" || method == "DELETE") {
if (args) opts.data = args;
} else {
opts.data = JSON.stringify(args);
opts.contentType = "application/json";
opts.processData = false;
opts.cache = false;
}
// log("opts",opts)
if (M_.App.useWebsocket) {
if (method == "POST")
return io.socket.post(url, opts, (data, jwres) => {
cb(data);
});
if (method == "PUT")
return io.socket.put(url, opts, (data, jwres) => {
cb(data);
});
if (method == "GET")
return io.socket.get(url, opts, (data, jwres) => {
cb(data);
});
if (method == "DELETE")
return io.socket.delete(url, opts, (data, jwres) => {
cb(data);
});
}
return $.ajax(opts);
}
// exemple :
// M_.Utils.saveFiles([form.find('co_avatar_send').jEl.get(0)], '/avatarupdate', {}, function() {
// }) ;
/**
* Save a file with Ajax technic
* @param {Array} files array of input html files
* @param {String} url url to call
* @param {Object} moreArgs send more arguments
* @param {Function} cb callback after finished
* @return {jqXHR}
* @example
M_.Utils.saveFiles([form.find('co_avatar_send').jEl.get(0)], '/avatarupdate', {}, function() {}) ;
*/
static saveFiles(files, url, moreArgs = {}, cb = null) {
if (files.length === 0) return cb();
var formData = new FormData();
for (var key in moreArgs) {
formData.append(key, moreArgs[key]);
}
// console.log("files", files);
for (var i = 0; i < files.length; i++) {
// console.log("files[i].files", files[i].files);
for (var j = 0; j < files[i].files.length; j++) {
var name = files[i].name;
// if (files[i].files.length>0) name += '['+j+']' ;
// console.log("name", name);
formData.append(name, files[i].files[j]);
}
}
return $.ajax({
url: url,
type: "POST",
//headers: {Connection: close},
data: formData,
contentType: false,
processData: false,
dataType: "json",
cache: false,
success: data => {
cb(data);
}
});
}
/**
* Check if a script is loaded (warning : check exactly the file name)
* @param {String} url url to call
* @return {Boolean} true if already loaded
*/
static isLoadedScript(url) {
var ok = false;
$("script").each(function() {
if (this.src === url) ok = true;
});
return ok;
}
/**
* Load a javascript file dynamically ; warning, no callback !!!
* @param {String} url url to call
*/
static loadScript(url) {
var script = document.createElement("script");
script.type = "text/javascript";
// script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp' +'&signed_in=true&callback=initialize';
script.src = url;
document.body.appendChild(script);
}
/**
* To document
*/
static saveSelection() {
if (window.getSelection) {
var sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
this._selection = sel.getRangeAt(0);
}
} else if (document.selection && document.selection.createRange) {
this._selection = document.selection.createRange();
} else this._selection = null;
}
/**
* To document
*/
static restoreSelection() {
if (this._selection) {
if (window.getSelection) {
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(this._selection);
} else if (document.selection && this._selection.select) {
this._selection.select();
}
}
}
};
M_.Utils._appIsFullScreen = false;
/**
* Observable interface
* @interface M_.Observable
*/
M_.Observable = {
/**
* Call this method in init() method of your class where you implement this mixin
*/
initObservable: function() {
this._listeners = [];
if (this.listeners) {
for (var i = 0; i < this.listeners.length; i++) {
this.addListener(this.listeners[i][0], this.listeners[i][1]);
}
}
//log("this._listeners", this._listeners.length)
},
/**
* Add a listner to this object
* @param {String} evtName The event name
* @param {Function} fct A callback function to execute when event is trigged
*/
addListener: function(evtName, fct) {
this._listeners.push({ evtName: evtName, fct: fct });
},
/**
* Remove a listner to this object
* @param {String} evtName The event name
* @param {Function} fct A callback function previously saved whith addListener()
*/
removeListener: function(evtName, fct) {
var listenersOk = [];
for (var i = 0; i < this._listeners.length; i++) {
if (this._listeners[i].evtName == evtName && this._listeners[i].fct == fct) {
listenersOk.push(this._listeners[i]);
}
}
this._listeners = listenersOk;
},
/**
* Trigger the event
* @param {String} evtName The event name previously registered with addListener()
* @return {boolean} Return the result of callback function
*/
trigger: function(evtName) {
var res = true;
for (var i = 0; i < this._listeners.length; i++) {
if (this._listeners[i].evtName == evtName) {
var args = Array.prototype.slice.call(arguments);
res = this._listeners[i].fct.apply(this, args.slice(1));
}
}
return res;
}
};
/**
* Redefine this constants in your language
*/
M_.i18n = {
// days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
// months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
// daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
// monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
devise: "€",
translate: function() {}
};
/**
* Create a model class. Generally you extends this class like exemple below
* @class
* @implements M_.Observable
* @property {array} fields - the fields definition
* @property {object} row - the values
* @property {String} primaryKey - the primary key name
* @example
M_.ModelKeyVal = class extends M_.Model {
getDefinition() {
return {
primaryKey: "key",
fields: [{ name: 'key' }, { name: 'val' }]
};
}
};
*/
M_.Model = class {
constructor(opts) {
var defaults = {
fields: null,
row: {},
store: null,
primaryKey: null
};
$.extend(this, defaults, opts, this.getDefinition());
_.mixin(this, M_.Observable);
this.initObservable();
if (!this.row) log("Vous devez définir opts.row");
// if (!this.store) log("Vous devez définir opts.store") ;
var fields = this.fields;
for (var j = 0; j < fields.length; j++) {
if (!fields[j].type) fields[j].type = "string";
}
this.updateRow();
}
/**
* You must extends this class to return a definition
* @function M_.Model#getDefinition
*/
getDefinition() {
return {};
}
/**
* @return {type}
*/
updateRow() {
var fields = this.fields;
// log("fields",fields)
for (var j = 0; j < fields.length; j++) {
var val = "";
if (fields[j].type == "date") {
if (this.row[fields[j].name] === undefined) continue;
if ($.type(this.row[fields[j].name]) == "date") {
val = this.row[fields[j].name];
} else {
// var fd = 'Y-m-d' ;
if (this.row[fields[j].name]) {
// if (this.row[fields[j].name].length>10) fd = 'Y-m-d H:i:s' ;
// log("this.row[fields[j].name",this.row[fields[j].name])
// val = M_.Utils.parseDate(this.row[fields[j].name], fd) ;
val = moment(this.row[fields[j].name]);
// log("val",val)
} else val = "";
}
this.row[fields[j].name] = val;
}
if (fields[j].type == "number") {
val = this.row[fields[j].name];
//log("number",fields[j].name,val)
this.row[fields[j].name] = val * 1;
}
}
}
/**
* Get an object that represent the data model
* @returns {object} un nombre
*/
getData() {
return this.getArray();
}
getRowData() {
return this.getArray();
}
getArray() {
var ret = {};
var fields = this.fields;
for (var j = 0; j < fields.length; j++) {
var val = this.get(fields[j].name);
if (val !== null) {
ret[fields[j].name] = val;
}
}
for (var key in this.row) {
if (!M_.Utils.isset(ret, key)) ret[key] = this.row[key];
}
return ret;
}
/**
* Set a object that represent the data model
* @param {object} row - The row
* @param {object} silently - Event "update" is not called
*/
setRow(row, silently = false) {
this.row = row;
if (silently !== true) {
this.trigger("update", this);
if (this.store) this.store.trigger("update", this.store);
}
}
/**
* Set only one field of model
* @param {String} field - The field name
* @param {something} val - The value
* @param {object} silently - Event "update" is not called
*/
set(field, val, silently) {
//log("val",field,val)
this.row[field] = val;
if (silently !== true) {
this.trigger("update", this);
if (this.store) this.store.trigger("update", this.store);
}
}
/**
* Create an empty model
*/
createEmptyRow() {
var fields = this.fields;
for (var j = 0; j < fields.length; j++) {
this.row[fields[j].name] = "";
}
}
//warning : stocker le résultat ???
/**
* Get a field
* @param {String} field - The field name
*/
get(field) {
var val,
fields = this.fields;
for (var j = 0; j < fields.length; j++) {
// log("fields[j].name",fields[j].name)
if (fields[j].name == field) {
if (fields[j].fn) {
//if (this.row[field]) val = this.row[field] ;
//else val = eval(fields[j].fn) ;
} else if (this.row[field] !== undefined) {
//fields[j].type &&
if (fields[j].type == "string" || fields[j].type == "text") {
val = this.row[field];
} else if (fields[j].type == "number") {
val = this.row[field] * 1;
} else if (fields[j].type == "array") {
val = this.row[field];
} else if (fields[j].type == "boolean") {
if (this.row[field] == "true" || this.row[field] === true || this.row[field] === "1" || this.row[field] === 1) val = true;
else val = false;
} else if (fields[j].type == "date") {
if ($.type(this.row[field]) == "date") val = this.row[field];
else {
// var fd = 'Y-m-d' ;
if (this.row[fields[j]]) {
// if (this.row[fields[j]].length>10) fd = 'Y-m-d H:i:s' ;
// val = M_.Utils.parseDate(this.row[field]) ;
val = moment(this.row[field]);
}
}
}
}
break;
}
}
if (val === undefined && M_.Utils.isset(this.row, field)) val = this.row[field];
return val;
}
/**
* Get the id of row (defined in primaryKey)
*/
getId() {
return this.get(this.primaryKey);
}
};
/**
* Use a standard model key / value. No params.
* @class M_.ModelKeyVal
* @example
// Very useful with combobox
var combo = new M_.Form.Combobox({
name: 'of_typecook',
label: "Type de cuisine",
container: $("#here"),
store: new M_.Store({
controller: this,
model: M_.ModelKeyVal,
currentSort: ["key", 1],
rows: [{key:1, val:"Cuisine américaine"}, {key:2, val:"Cuisine niçoise"}]
})
}) ;
*/
M_.ModelKeyVal = class extends M_.Model {
getDefinition() {
return {
primaryKey: "key",
fields: [{ name: "key" }, { name: "val" }]
};
}
};
/**
* A store is a client cache of rows. It can do ajax requests in json
* @class
* @implements M_.Observable
* @property {array} [rows=[]] - the data rows
* @property {boolean} [useWebsocket=M_.App.useWebsocket] - true if use websocket and not ajax
* @property {array} [rowsModel=[]] - the model rows
* @property {String} [url=""] - the model rows
* @property {object} [args={}] - arguments object to pass to each ajax request
* @property {String} [rootData="data"] - property where read data in json response
* @property {array} [currentSort=null] - sort the store : ['name', 'asc'] ; the second is 'asc' or 'desc'
* @property {M_.Model} [model=null] - the model to use
* @property {String} [primaryKey=""] - the primary key
* @property {number} [limit=null] - send 'limit' param to ajax request
* @property {boolean} [sortOnRemote=false] - send 'sort' param to ajax request
* @property {number} [skip=0] - send 'skip' param to ajax request
* @property {array} [unshiftRows=[]] - add this rows in first position, just after load
* @property {array} [pushRows=[]] - add this rows in last positions, just after load
* @example
// json store
var store = new M_.Store({
controller: this,
model: MT_Contacts,
url: "/contacts",
limit: 200,
listeners: [
['load',(store, data)=> {
// data are loaded
}]
]
}) ;
// ajax response would have the fields define in your model
{
data: [
{id:1, name:"David Miglior"},
{id:2, name:"Michael Miglior"}
]
}
*/
M_.Store = class {
/**
* Initialize the store. If you extend this method, you should call this._super()
*
* @param {object} opts The configuration object
*/
constructor(opts) {
var defaults = {
rows: [],
useWebsocket: M_.App.useWebsocket,
rowsModel: [],
_tabFieldsForSort: [],
url: "",
args: {},
rootData: "data",
currentSort: null,
model: null,
lastLoadArgs: {},
primaryKey: "",
limit: null,
sortOnRemote: false,
skip: 0,
lastTotal: 0,
unshiftRows: [],
pushRows: [],
_loaded: false,
_loading: false
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
_.mixin(this, M_.Observable);
this.initObservable();
if (this.model) {
var model = new this.model({ row: {}, store: {} });
this.primaryKey = model.primaryKey;
}
//must be last init, because update event
if (this.rows && this.rows.length > 0) {
//M_.Utils.delay("", $.proxy(function() {
this.setRows(this.rows);
//}, this), 10) ;
// log("this.unshiftRows",this.unshiftRows)
if (this.unshiftRows && this.unshiftRows.length > 0) {
for (let i = 0; i < this.unshiftRows.length; i++) this.rows.unshift(this.unshiftRows[i]);
}
if (this.pushRows && this.pushRows.length > 0) {
for (let i = 0; i < this.pushRows.length; i++) this.rows.push(this.pushRows[i]);
}
this.useModel();
}
}
/**
* Return the first model instance where the value of field equal val. The comparaison is do with ==, so "2"==2
*
* @param {String} field The searched field name
* @param {string|number} val The value researched.
* @return {model} Return the model instance founded or null
*/
//val can be a RegExp
getRowBy(field, val) {
var rows = this.rowsModel;
for (var i = 0; i < rows.length; i++) {
var v;
if (this.model) v = rows[i].get(field);
else v = rows[i][field];
if (v == val) {
return rows[i];
}
}
return null;
}
/**
* @param {type}
* @param {type}
* @return {type}
*/
getIndexBy(field, val) {
var rows = this.rowsModel;
for (var i = 0; i < rows.length; i++) {
var v = rows[i].get(field);
if (v == val) {
return i;
}
}
return -1;
}
/**
* @param {type}
* @return {type}
*/
getRowByIndex(index) {
var rows = this.rowsModel;
if (rows[index]) return rows[index];
return null;
}
/**
* @param {type}
* @return {type}
*/
getRow(val) {
return this.getRowBy(this.primaryKey, val);
}
/**
* @param {type}
* @return {type}
*/
getIndexById(id) {
var rows = this.rowsModel;
for (var i = 0; i < rows.length; i++) {
var v = rows[i].getId();
if (v == id) {
return i;
}
}
return -1;
}
//val can be a RegExp
/**
* @param {type}
* @param {type}
* @return {type}
*/
getRowsBy(field, val) {
var res = [];
var rows = this.rowsModel;
for (var i = 0; i < rows.length; i++) {
var v = rows[i].get(field);
if (v == val) {
res.push(rows[i]);
}
}
return res;
}
/**
* @param {type}
* @return {type}
*/
getRowAt(index) {
return this.rowsModel[index];
}
/**
* @param {type}
*/
setRows(rows) {
this.rows = rows;
this.useModel();
//log("this.rows1",this.rows)
//log("setRows",this.rowsModel)
this.trigger("update", this, this.rowsModel);
}
/**
* @param {type}
*/
setModels(rows) {}
/**
* @param {type}
*/
addModel(row) {}
/**
* @param {type}
*/
addRow(row) {
this.addRows([row]);
}
/**
* @param {type}
*/
addModels(rows) {}
/**
* @param {type}
*/
addRows(rows) {
//this.rows = this.rows.concat(rows) ;
//this.useModel() ;
for (var i = 0; i < rows.length; i++) {
var m = null;
var row = rows[i];
if (row instanceof M_.Model) {
m = row;
row = row.getArray();
} else {
m = new this.model({ row: row, store: this });
}
this.rows.push(row);
this.rowsModel.push(m);
// this.addRow(rows[i]) ;
this.trigger("addedRow", this, m);
}
this.trigger("update", this, this.rowsModel);
}
/**
* @return {type}
*/
getRows() {
return this.rowsModel;
}
/**
* @return {type}
*/
count() {
return this.rowsModel.length;
}
/**
* @return {type}
*/
countTotal() {
//log("countTotal",this.rowsModel,this.rows)
if (this.lastTotal === 0) return this.count();
return this.lastTotal;
}
/**
* @return {type}
*/
deleteAll() {
this.rows = [];
this.rowsModel = [];
this.trigger("removedAll", this);
this.trigger("update", this, this.rowsModel);
}
/**
* To document
*/
setKeysOnRows() {
for (var i = 0; i < this.rows.length; i++) {
this.rows[i]._mid = i;
}
}
_sortRemote(fields, direction) {
//log("sortRemote")
if (!this.lastLoadArgs) this.lastLoadArgs = {};
// this.lastLoadArgs.sort = this.currentSort[0] ;
// this.lastLoadArgs.direction = this.currentSort[1] ;
// this.lastLoadArgs.sort = fields ;
// if (direction>0) this.lastLoadArgs.sort += " ASC" ;
// else this.lastLoadArgs.sort += " ASC" ;
this.reload();
}
/**
* To document
* @param {Array} fields
* @param {type} direction - direction=1 ASC, direction=-1 DESC, direction=0 toggle
* @param {type} silently
* @return {type}
* @example
* store.sort('co_name') ;
* store.sort('co_name', 1) ;
* store.sort([['co_name', 1], ['co_date',-1]]) ;
*/
sort(fields, direction, silently) {
// console.log("fields", fields);
if (!direction) direction = 0;
if (_.isFunction(fields)) {
} else {
if (direction === 0) {
if (!this._tabFieldsForSort[fields]) {
this._tabFieldsForSort[fields] = 1;
} else {
this._tabFieldsForSort[fields] = this._tabFieldsForSort[fields] * -1;
}
direction = this._tabFieldsForSort[fields];
} else {
this._tabFieldsForSort[fields] = direction;
}
this.currentSort = [fields, direction];
}
if (this.trigger("beforeSort", this, fields, direction) === false) return false;
//log("this.currentSort",this.currentSort)
if (this.sortOnRemote) {
this._sortRemote(fields, direction);
return;
}
// console.log("ok");
this.rowsModel.sort((a, b) => {
// log("a,b",a,b)
var a0;
var b0;
if (_.isFunction(fields)) {
a0 = fields(a);
b0 = fields(b);
// console.log("a0, b0", a0, b0);
} else {
a0 = a.get(fields);
b0 = b.get(fields);
}
if (a0 === null || b0 === null) return 0;
if (typeof a0 == "number") {
if (direction == -1) return b0 - a0;
return a0 - b0;
} else if (a0 instanceof Date) {
if (direction == -1) return b0 - a0;
return a0 - b0;
} else if (a0 && b0 && a0.getDate && a0.getDate() instanceof Date) {
if (direction == -1) return b0.getDate() - a0.getDate();
return a0.getDate() - b0.getDate();
} else if (a0 && b0) {
if (direction == -1) return b0.localeCompare(a0);
return a0.localeCompare(b0);
} else return 0;
});
this.trigger("sort", this);
if (silently !== true) {
this.trigger("update", this, this.rowsModel);
}
}
// sortByFunction(fun) {
// this.rowsModel.sort((a, b)=> {
// var vala = fun(a) ;
// var valb = fun(b) ;
// }) ;
// this.trigger("sort", this);
// }
/**
* To document
*/
useModel() {
if (this.model) {
this.rowsModel = [];
// var fields = this.model.fields ;
// log("this.rows",this.rows)
for (var i = 0; i < this.rows.length; i++) {
this.rowsModel[i] = new this.model({ row: this.rows[i], store: this });
//this.rowsModel[i].updateRow() ;
}
} else {
this.rowsModel = this.rows;
}
}
/**
* @param {Function} fn
*/
each(fn) {
//log("length",this.rowsModel)
for (var i = 0; i < this.rowsModel.length; i++) {
var ret = fn.call(this, this.rowsModel[i], i);
if (ret === false) break;
}
}
/**
* @return {type}
*/
createEmptyRow() {
var mod = new this.model({ row: {}, store: this });
mod.createEmptyRow();
return mod;
}
/**
* @param {type} skip
*/
setSkip(skip) {
this.skip = skip;
if (!this.lastLoadArgs) this.lastLoadArgs = {};
this.lastLoadArgs.skip = skip;
this.reload();
}
/**
* @param {type} query
*/
setQuery(query) {
this.reload();
}
/**
* @return {Boolean}
*/
isLoading() {
return this._loading;
}
/**
* @return {Boolean}
*/
isLoaded() {
return this._loaded;
}
/**
* @param {type} args
* @param {Function} callback
* @return {type}
*/
load(args, callback) {
// log("load")
var okArgs = {};
if (this.currentSort) {
if (_.isArray(this.currentSort)) okArgs.sort = this.currentSort[0] + " " + this.currentSort[1];
else okArgs.sort = this.currentSort;
}
if (this.limit && this.limit > 0) okArgs.limit = this.limit;
if (this.skip) okArgs.skip = this.skip;
if (args && (args.skip || args.skip === 0)) this.skip = args.skip;
// if (args && args.sort && _.isArray(args.sort)) this.currentSort[0] = args.sort ;
// if (args && args.sort && _.isString(args.sort)) this.currentSort = args.sort ;
// if (args && args.direction) this.currentSort[1] = args.direction ;
if (args && args.limit) this.limit = args.limit;
var thisArgs = this.args;
if (_.isFunction(thisArgs)) thisArgs = this.args();
$.extend(okArgs, thisArgs, args);
var obj = { url: this.url, args: okArgs };
if (this.trigger("beforeLoad", this, obj) === false) return false;
if (obj.args.onBeforeLoad) obj.args.onBeforeLoad(this, obj);
this.lastLoadArgs = obj.args;
this._loaded = false;
this._loading = true;
if (this.useWebsocket) {
io.socket.get(obj.url, okArgs, (data, jwres) => {
this._treatDataStore(data, callback);
});
} else {
// if (this.loadAjaxXHR) this.loadAjaxXHR.abort() ;
// log("okArgs",okArgs)
this.loadAjaxXHR = M_.Utils.getJson(obj.url, okArgs, data => {
this._treatDataStore(data, callback);
});
// this.loadAjaxXHR = $.ajax({
// url: obj.url,
// type: 'GET',
// contentType: 'application/json',
// data: okArgs,
// dataType: 'json',
// success: (data) => {
// // log("data",data)
// this._treatDataStore(data, callback) ;
// }
// });
}
}
_treatDataStore(data, callback) {
// log("_treatDataStore",data)
this._loaded = true;
this._loading = false;
this.lastdata = data;
this.trigger("loadraw", this, data);
var i;
//if (this.lastLoadArgs.addRows) this.rows = this.rows.concat(data.data) ;
//else
if (data.total || data.total === 0) this.lastTotal = data.total * 1;
// log("this.unshiftRows",this.unshiftRows)
if (this.unshiftRows.length > 0) {
for (i = 0; i < this.unshiftRows.length; i++) data[this.rootData].unshift(this.unshiftRows[i]);
}
if (this.pushRows.length > 0) {
for (i = 0; i < this.pushRows.length; i++) data[this.rootData].push(this.pushRows[i]);
}
//if (this.lastLoadArgs.add) ;
//else
this.rows = data[this.rootData];
// this.lastLoadArgs.add = false ;
this.useModel();
// if (this.currentSort && !this.sortOnRemote && _.isArray(this.currentSort))
// this.sort(this.currentSort[0], this.currentSort[1], true) ;
//this.setKeysOnRows() ;
this.trigger("load", this, this.rowsModel);
this.trigger("update", this, this.rowsModel);
if (this.lastLoadArgs.onUpdate) this.lastLoadArgs.onUpdate(this.rowsModel);
if (this.lastLoadArgs.onBeforeLoad) this.lastLoadArgs.onBeforeLoad = null;
if (this.lastLoadArgs.onUpdate) this.lastLoadArgs.onUpdate = null;
if (callback) callback(this);
}
/**
* @param {Boolean} [reset=false]
* @param {type} [moreArgs=null]
* @return {type}
*/
reload(reset = false, moreArgs = null, cb = null) {
if (reset) {
this.skip = 0;
this.lastLoadArgs = {};
}
if (moreArgs) $.extend(this.lastLoadArgs, moreArgs);
this.load(this.lastLoadArgs, cb);
}
};
/**
* A base graphical object
* @class
* @implements M_.Observable
*/
M_.Outlet = class {
constructor(opts) {
var defaults = {
controller: null,
container: null,
id: "",
view: null,
jEl: null,
help: null,
rendered: false,
top: 0,
left: 0
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
_.mixin(this, M_.Observable);
this.initObservable();
if (M_.Utils.isEmpty(this.id)) this.id = M_.Utils.id();
if (!M_.Utils.isEmpty(this.jEl) && $.type(this.jEl) == "string") this.jEl = $("#" + this.jEl);
// if (!M_.Utils.isEmpty(this.container) && $.type(this.container)=="string") this.container = $("#"+this.container) ;
if (M_.Utils.isEmpty(this.jEl)) this.create();
if (this.jEl && M_.Utils.isEmpty(this.jEl.attr("id"))) this.jEl.attr("id", this.id);
this._attachHelp();
// if (M_.Utils.isEmpty(this.id)) {
// if (this.jEl && !M_.Utils.isEmpty(this.jEl.attr('id'))) {
// this.id = this.jEl.attr('id') ;
// } else {
// this.id = M_.Utils.id() ;
// }
// }
// if (this.jEl && M_.Utils.isEmpty(this.jEl.attr('id'))) this.jEl.attr('id', this.id) ;
// M_.App.registerOutlet(this) ;
this.trigger("created", this);
}
_attachHelp() {
if (this.help && this.jEl) {
new M_.Help({
controller: this.controller,
text: this.help,
attachedObj: this.container
});
}
}
/**
* Extend this class to create your UI.
*/
create() {
this.jEl = $("<div></div>");
this.container.append(this.jEl);
}
/**
* Destroy the outlet
*/
destroy() {
if (!this.jEl) return;
this.jEl.remove();
this.container.empty();
}
/**
* Return jEl element. Generally the main object.
* @returns {jquery} the main jQuery element
*/
getEl() {
return this.jEl;
}
/**
* Search a class or selector in the container, like $.find()
* @param {string} selector
* @return {jQuery}
*/
findEl(selector) {
return this.container.find(selector);
}
};
/**
* Implement this interface to get a store
* @interface
*/
M_.Stored = {
//autoFocus: null,
/**
* Call this method in your init() function
*/
initStored: function() {
if (!this.store) log("store is mandatory in M_.Stored");
else
//if (this.store) this.setStore(this.store) ;
this.store.addListener("update", $.proxy(this.updateStore, this));
},
/**
* Configure a new store
* @param {M_.Store} store The new store used
*/
setStore: function(store) {
this.store = store;
this.store.addListener("update", $.proxy(this.updateStore, this));
},
/**
* Return the store object
* @return {M_.Store} The store object or null
*/
getStore: function() {
return this.store;
}
/**
* Redefine this method to update your UI
* @param {M_.Store} store The store object
*/
// updateStore: function(store) {
// }
};
/**
* Display an help on jquery element
* @class
* @property {type} controller
* @property {type} text
* @property {type} attachedObj
* @property {type} shiftX
* @property {type} shiftY
* @property {type} maxWidth
*/
M_.Help = class {
constructor(opts) {
var defaults = {
controller: null,
text: "",
attachedObj: null,
shiftX: 15,
shiftY: -15,
maxWidth: 300,
showNow: false
};
opts = opts ? opts : {};
opts = $.extend(this, defaults, opts);
this.create();
if (this.showNow) {
this.show();
$(document).on("mousemove", $.proxy(this._mousemove, this));
}
}
_mousemove(evt) {
var top = evt.pageY * 1 + this.shiftY;
var left = evt.pageX * 1 + this.shiftX;
if (left + 300 + 100 > $(window).width()) {
left = evt.pageX * 1 - this.shiftX - $("#M_Help").outerWidth();
if ($("#M_Help").hasClass("left"))
$("#M_Help")
.removeClass("left")
.addClass("right");
if (!$("#M_Help").hasClass("right")) $("#M_Help").addClass("right");
} else {
if ($("#M_Help").hasClass("right"))
$("#M_Help")
.removeClass("right")
.addClass("left");
if (!$("#M_Help").hasClass("left")) $("#M_Help").addClass("left");
}
if (top + $("#M_Help").outerHeight() - 10 > $(window).height()) {
top = $(window).height() - $("#M_Help").outerHeight() - 10;
// $('#M_Help.left:after').css('top',top) ;
}
$("#M_Help").css({
top: top,
left: left
});
}
/**
* To document
*/
create() {
// console.log("this.attachedObj", this.attachedObj);
this.attachedObj.mouseenter(evt => {
this.show(evt);
$(document).on("mousemove", $.proxy(this._mousemove, this));
});
this.attachedObj.mouseleave(evt => {
this.hide(evt);
$(document).off("mousemove", $.proxy(this._mousemove, this));
});
this.attachedObj.click(evt => {
this.hide(evt);
});
if (this.attachedObj.prop("tagName").toLowerCase() == "input") {
this.attachedObj.focus(evt => {
this.hide();
});
this.attachedObj.focus(evt => {
this.hide();
});
}
}
/**
* @param {event} evt
*/
show(evt) {
$("#M_Help")
.html(this.text)
.css("max-width", this.maxWidth)
.show();
}
/**
* @param {event} evt
*/
hide(evt) {
$("#M_Help").hide();
}
/**
*
*/
static createMHelp() {
$("body").append("<div id='M_Help'></div>");
}
/**
*
*/
static hideMHelp() {
$("#M_Help").hide();
}
};
/**
* Like a card layout. Use HTML to define them.
* @class
* @property {String} firstTab
*/
M_.Tabs = class extends M_.Outlet {
constructor(opts) {
var defaults = {
firstTab: null,
buttons: null,
onChange: null
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
if (this.buttons) {
this.buttons.find(".M_TabButton").each((ind, el) => {
$(el).click(evt => {
var jEl = $(evt.target);
if (jEl.hasClass("disabled")) return;
// console.log("jEl.attr('for')", jEl.attr('for'));
this.show(jEl.attr("for"), next => {
if (this.onChange) {
this.onChange(jEl.attr("for"), next);
} else {
next();
}
});
});
});
}
// this.container.children('.M_Tab').each(function() {
// this.tabTabs.push($(this)) ;
// }) ;
}
// addTab(jEl) {
// this.tabTabs.push(tab) ;
// }
/**
* @param {type}
* @param {type}
* @return {type}
*/
show(jElId, cbBeforeShow, anim = true) {
var speed = M_.Utils.getSpeedAnim();
// log("show",jElId)
var current = null;
this.container.children(".M_Tab").each((ind, el) => {
// log("$(this).attr('id')",$(this).attr('id'))
if ($(el).hasClass("active")) {
current = $(el);
return false;
}
});
// log("current",current)
if (current && current.attr("id") == jElId) {
cbBeforeShow(cbAfterShow => {
if (cbAfterShow === false) return;
if (cbAfterShow) cbAfterShow();
});
return;
} else if (current) {
if (anim) {
current.transition({ opacity: 0 }, speed, () => {
current.removeClass("active");
$("#" + jElId)
.css("opacity", 0.01)
.addClass("active");
cbBeforeShow(cbAfterShow => {
if (cbAfterShow === false) return;
$("#" + jElId).transition({ opacity: 1 }, speed, () => {
if (cbAfterShow) cbAfterShow();
});
});
});
} else {
current.removeClass("active");
cbBeforeShow(cbAfterShow => {
if (cbAfterShow === false) return;
$("#" + jElId)
.css("opacity", 1)
.addClass("active");
if (cbAfterShow) cbAfterShow();
});
}
} else {
if (anim) {
$("#" + jElId)
.css("opacity", 0.01)
.addClass("active");
cbBeforeShow(cbAfterShow => {
if (cbAfterShow === false) return;
$("#" + jElId).transition({ opacity: 1 }, speed, () => {
if (cbAfterShow) cbAfterShow();
});
});
} else {
cbBeforeShow(cbAfterShow => {
if (cbAfterShow === false) return;
$("#" + jElId)
.css("opacity", 1)
.addClass("active");
if (cbAfterShow) cbAfterShow();
});
}
}
if (this.buttons) {
this.buttons.find(".M_TabButton").each((ind, el) => {
$(el).removeClass("active");
if ($(el).attr("for") == jElId) $(el).addClass("active");
});
}
}
};
/**
* A graphical tree
* @class
* @extends M_.Outlet
* @property {String} autoLoad
* @property {String} tpl
*/
M_.Tree = class extends M_.Outlet {
constructor(opts) {
var defaults = {
autoLoad: false,
displayRootNode: true,
url: "",
cls: "M_Tree",
retractable: true,
movable: true,
rootNode: {
expended: true,
hidden: false,
label: "RootNode",
id: "rootnode",
draggable: false,
droppable: true,
nodes: []
}
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
}
/**
* @return {type}
*/
create() {
this.jEl = $("<div class='" + this.cls + "'></div>");
this.container.append(this.jEl);
this._drawNodes();
}
/**
* Draw the tree
*/
draw() {
this._drawNodes();
}
_drawNodes() {
var html = "";
html += "<ul>";
html += this._createChildNodes(this.rootNode);
html += "</ul>";
this.jEl.html(html);
this.jEl
.find("li:has(ul)")
.addClass("M_TreeParentLi")
.find(" > span"); //.attr('title', 'Collapse this branch')
if (this.retractable) {
this.jEl.find("li.M_TreeParentLi > span > i").on("click", evt => {
var target = $(evt.target);
var children = target.closest("li.M_TreeParentLi").find(" > ul > li");
if (children.is(":visible")) {
children.slideUp();
target
.find(" > i")
.addClass("fa-plus-square")
.removeClass("fa-minus-square"); //.attr('title', 'Expand this branch')
} else {
children.slideDown();
target
.find(" > i")
.addClass("fa-minus-square")
.removeClass("fa-plus-square"); //.attr('title', 'Collapse this branch')
}
evt.stopPropagation();
});
}
this.jEl.find("li > span").on("click", evt => {
var nodeId = $(evt.target)
.closest("li")
.attr("m_id");
this.setSelected(nodeId);
var node = this.getNode(nodeId);
if (node.clickable !== false) this.trigger("nodeclick", this, nodeId, node);
});
this._dragMID = null;
if (this.movable) {
this.jEl
.find("li.draggable > span")
.attr("draggable", true)
.on("drag", (evt, evt2) => {
// console.log("drag",evt);
this._dragMID = $(evt.target)
.closest("li")
.attr("m_id");
});
this.jEl
.find("li.droppable > span")
.on("dragover", evt => {
// console.log("dragover",evt);
var nodeDrop = $(evt.target)
.closest("li")
.attr("m_id");
if (nodeDrop != this._dragMID) evt.preventDefault();
})
.on("drop", evt => {
// console.log("drop",evt);
evt.preventDefault();
var mid = this._dragMID;
var nodeDrop = $(evt.target)
.closest("li")
.attr("m_id");
console.log("this.isNodeParentOf(mid, nodeDrop)", this.isNodeParentOf(mid, nodeDrop));
if (mid != nodeDrop && !this.isNodeParentOf(mid, nodeDrop)) {
let ok = this.trigger("beforenodemove", this, mid, nodeDrop);
if (ok === false) return;
this.moveNode(mid, nodeDrop);
}
});
}
}
_createChildNodes(parent) {
var html = "";
if (!parent.id) parent.id = M_.Utils.id();
let myclass = "";
if (parent.draggable) myclass += " draggable";
if (parent.droppable) myclass += " droppable";
html += "<li m_id='" + parent.id + "' class='" + myclass + "'>";
let fa = "";
if (parent.nodes && parent.nodes.length) fa = "fa fa-minus-square";
html += "<span><i class='" + fa + "'></i><div class='txt'>" + parent.label + "</div></span>";
if (parent.nodes && parent.nodes.length) {
html += "<ul>";
_.each(parent.nodes, (node, index) => {
html += this._createChildNodes(node);
});
html += "</ul>";
}
html += "</li>";
return html;
}
_iter(parent, nodeId, action) {
// if (action=='path') this._nodepath.push(parent.id) ;
if (nodeId == "rootnode") {
if (action == "get") {
this._nodeparent = null;
this._resNodeId = parent;
}
if (action == "add") {
if (!parent.nodes) parent.nodes = [];
parent.nodes.push(this._nodeToAdd);
}
return;
}
if (parent.nodes && parent.nodes.length) {
_.each(parent.nodes, (node, index) => {
if (!node) return;
if (node.id == nodeId) {
if (action == "remove") parent.nodes.splice(index, 1);
if (action == "get") {
this._nodeparent = parent;
this._resNodeId = node;
}
if (action == "add") {
if (!node.nodes) node.nodes = [];
node.nodes.push(this._nodeToAdd);
}
}
this._iter(node, nodeId, action);
});
}
// return '' ;
}
/**
* @param {NodeObject} node
* @param {String} parentId
*/
addNode(node, parentId, silently) {
this._nodeToAdd = node;
this._iter(this.rootNode, parentId, "add");
this._drawNodes();
if (!silently) this.trigger("nodeadd", this, node, parentId);
}
/**
* @param {String} nodeId
* @param {String} parentId
*/
moveNode(nodeId, parentId, silently) {
// log("nodeId, parentId",nodeId, parentId)
var node = this.getNode(nodeId);
this.removeNode(nodeId, true);
this.addNode(node, parentId, true);
this._drawNodes();
if (!silently) this.trigger("nodemove", this, nodeId, parentId);
}
/**
* @param {type}
* @return {Array}
*/
getNodePath(nodeId, names) {
// console.log("getNodePath",nodeId);
var nodepath = [];
let node = this.getNode(nodeId);
if (!node) return nodepath;
if (names) nodepath.push(node.label);
else nodepath.push(node.id);
// this._iter(this.rootNode, nodeId, 'path') ;
var nodeIdStart = nodeId;
if (nodeId == "rootnode") return nodepath;
for (var i = 0; i < 100; i++) {
var parentNode = this.getNodeParent(nodeIdStart);
if (names) {
nodepath.unshift(parentNode.label);
} else {
nodepath.unshift(parentNode.id);
}
nodeIdStart = parentNode.id;
if (parentNode.id == "rootnode") break;
}
return nodepath;
}
getNodeParent(nodeId) {
this._nodeparent = null;
this._iter(this.rootNode, nodeId, "get");
// return M_.Utils.clone(this._nodeparent) ;
return this._nodeparent;
}
isNodeChildOf(nodeId, childOfNodeId) {
let path1 = this.getNodePath(nodeId);
let path2 = this.getNodePath(childOfNodeId);
}
isNodeParentOf(nodeId, childOfNodeId) {
let path1 = this.getNodePath(nodeId);
let path2 = this.getNodePath(childOfNodeId);
// console.log("path1,path2",path1,path2);
let dif = [];
let ok = false;
_.each(path2, (p2, i2) => {
if (!path1[i2]) {
ok = true;
return false;
}
if (p2 == path1[i2]) return;
if (p2 != path1[i2]) return false;
});
return ok;
}
/**
* @param {String} nodeId
*/
removeNode(nodeId, silently) {
// this.jEl.find("li[m_id='"++"']").remove() ;
this._iter(this.rootNode, nodeId, "remove");
this._drawNodes();
if (!silently) this.trigger("noderemove", this, nodeId);
}
/**
* @param {String} nodeId
* @return {NodeObject}
*/
getNode(nodeId) {
this._resNodeId = null;
this._iter(this.rootNode, nodeId, "get");
// return M_.Utils.clone(this._resNodeId) ;
return this._resNodeId;
}
/**
* @param {NodeObject} rootNode
*/
setRootNode(rootNode) {
this.rootNode = rootNode;
this._drawNodes();
}
getSelected() {
let sel = "rootnode";
if (this.jEl.find("span.selected").closest("li").length)
sel = this.jEl
.find("span.selected")
.closest("li")
.attr("m_id");
return sel;
}
setSelected(nodeId) {
// console.log("nodeId",nodeId);
this.jEl.find("span.selected").removeClass("selected");
this.jEl.find('li[m_id="' + nodeId + '"] > span').addClass("selected");
}
_listenDocClick(evt) {
// console.log("_listenDocClick", this._currentRenameId, $(evt.target).closest('li'));
if (
$(evt.target)
.closest("li")
.attr("m_id") == this._currentRenameId
)
return;
this.jEl.find('li[m_id="' + this._currentRenameId + '"] > span > div').attr("contenteditable", false);
$(document).off("click", $.proxy(this._listenDocClick, this));
let label = this.jEl.find('li[m_id="' + this._currentRenameId + '"] > span > div').text();
this.trigger("noderename", this, this._currentRenameId, label);
}
renameNode(nodeId) {
this._currentRenameId = nodeId;
let el = this.jEl.find('li[m_id="' + nodeId + '"] > span > div');
el.attr("contenteditable", true);
var range = document.createRange();
range.selectNodeContents(el.get(0));
var txtsel = window.getSelection();
txtsel.removeAllRanges();
txtsel.addRange(range);
window.setTimeout(() => {
$(document).on("click", $.proxy(this._listenDocClick, this));
}, 200);
}
};
/**
* A graphical list to implement
* @class
* @extends M_.Outlet
* @implements M_.Stored
* @property {String} autoLoad
* @property {String} tpl
*/
M_.List = class extends M_.Outlet {
constructor(opts) {
var defaults = {
autoLoad: false,
tpl: ""
};
// if (!opts.jEl && !opts.colsDef) log("jEl is mandatory in M_.List") ;
// if (!opts.tpl && !opts.colsDef) log("tpl is mandatory in M_.List") ;
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
// $.extend(true, this.prototype, M_.Stored) ;
_.mixin(this, M_.Stored);
// log("store", Object.mixin)
this.initStored();
//this.render();
}
/**
* @return {type}
*/
updateStore() {
this.render();
}
/**
* Redefine this method to render your UI
*/
render() {}
};
// M_.Draggable = class {
// constructor(el, jElHandle=null, jElHelper=null) {
// //tabDraggable.push({el:el, jElHandle:jElHandle}) ;
// jElHandle.mousedown({el:el}, $.proxy(function(evt) {
// this.dragEl = evt.data.el ;
// this.dragStart = true ;
// this.dragDiff = {top: evt.pageY-this.dragEl.offset().top, left: evt.pageX-this.dragEl.offset().left}
// }, this)) ;
// $(document).mouseup({el:el}, $.proxy(function() {
// this.dragStart = false ;
// }, this)) ;
// $(document).mousemove({el:el}, $.proxy(function(evt) {
// if (this.dragStart && this.dragEl == evt.data.el) {
// var top = evt.pageY ;
// var left = evt.pageX ;
// this.dragEl.offset({top: top-this.dragDiff.top, left: left-this.dragDiff.left}) ;
// }
// }, this)) ;
// }
/**
* A simple list "à la Apple"
* @class
* @extends M_.List
* @property {type} classItems
* @property {type} itemValue
* @property {type} itemKey
* @property {type} selectionVisible
* @property {type} multipleSelection
* @property {type} currentSelection
* @property {type} dynamic
* @property {type} lineHeight
* @property {type} loadLimit
* @property {type} itemsDraggableTo
*/
M_.SimpleList = class extends M_.List {
constructor(opts) {
var defaults = {
classItems: "M_SimpleListItem",
itemValue: "",
itemKey: "",
selectionVisible: false,
multipleSelection: false,
currentSelection: [],
dynamic: false,
lineHeight: 34,
loadLimit: 400,
oddEven: true,
// _startPosition: 0,
itemsDraggableTo: false,
_dragStarted: false,
_lastSelection: null
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
// this.setSelection(this.currentSelection) ;
// this.create() ;
}
/**
* @return {type}
*/
updateStore() {
super.updateStore();
this.setSelection(this.currentSelection);
}
/**
* @return {type}
*/
create() {
var html = "";
html += `<div class='M_SimpleList'>
<div class='M_SimpleListContent'>
<div class='M_SimpleListContentFake1'></div>
<div class='M_SimpleListContentReal'></div>
<div class='M_SimpleListContentFake2'></div>
</div>
</div>`;
this.jEl = $(html);
this.container.append(this.jEl);
if (!this.dynamic) this.jEl.find(".M_SimpleListContent").css("background-image", "none");
if (this.dynamic) {
this.jEl.scroll(evt => {
M_.Utils.delay(
() => {
var diff = this.jEl.offset().top - this.jEl.find(".M_SimpleListContent").offset().top;
var _skipPosition = Math.ceil(diff / this.lineHeight) - Math.ceil(this.loadLimit / 4);
if (_skipPosition < 0) _skipPosition = 0;
this.store.reload(false, { skip: _skipPosition, limit: this.loadLimit });
},
100,
"m_simplelist_scroll"
);
});
}
}
/**
* @return {type}
*/
render() {
// log("render")
var html = "",
mid = this.store.primaryKey;
this.store.each((model, indexTemp) => {
// log("cg_created",model.get('cg_name'))
var val = "";
if ($.isFunction(this.itemValue)) val = this.itemValue(model);
else val = model.get(this.itemValue);
var clsTr = this.classItems;
if (this.oddEven) {
if (indexTemp % 2 === 0) clsTr += " M_TableListOdd";
else clsTr += " M_TableListEven";
}
html += "<div class='" + clsTr + " M_Noselectable' data-m_id='" + model.get(mid) + "'>" + val + "</div>";
});
this.jEl.find(".M_SimpleListContentReal").html(html);
// this.jEl.find('.'+this.classItems).m_draggable() ;
// $("."+this.classItems).draggable({
// cursor: "move",
// cursorAt: { top: -12, left: -20 },
// helper: function( event ) {
// return $( "<div class='ui-widget-header' style='z-index:100000;'>I'm a custom helper</div>" );
// }
// });
if (this.dynamic) {
var totalHeight = this.lineHeight * this.store.countTotal();
this.jEl.find(".M_SimpleListContent").height(totalHeight);
this.jEl.find(".M_SimpleListContentFake1").height(this.store.skip * this.lineHeight);
}
// this.jEl.find('.M_SimpleListContentFake2').height(totalHeight - ((this._skipPosition+100)*this.lineHeight)) ;
this.jEl.find("." + this.classItems).on("mousedown", evt => {
// log("this.currentSelection",this.currentSelection)
var item = $(evt.target).closest("." + this.classItems);
var m_id = item.attr("data-m_id");
var model = this.store.getRow(m_id);
if (this.trigger("beforeClickItem", this, m_id, model, evt) === false) return;
var ok = true,
dontremoveselection = false;
if (this.multipleSelection) {
if ((M_.Utils.isMacos && evt.metaKey) || evt.ctrlKey) {
// evt.preventDefault() ;
// evt.stopPropagation() ;
if (item.hasClass("selected")) ok = false;
} else if (evt.shiftKey) {
dontremoveselection = true;
var lastIndex = this.store.getIndexById(this._lastSelection);
var currentIndex = this.store.getIndexById(m_id);
var min = Math.min(lastIndex, currentIndex);
var max = Math.max(lastIndex, currentIndex);
// var r =
this.store.each((row, i) => {
if (i >= min && i <= max) {
var m_idTemp = row.getId();
this.jEl.find("." + this.classItems + '[data-m_id="' + m_idTemp + '"]').addClass("selected");
if (this.currentSelection.indexOf(m_idTemp) == -1) this.currentSelection.push(m_idTemp);
}
});
} else {
if (!item.hasClass("selected")) {
this.jEl.find("." + this.classItems).removeClass("selected");
this.currentSelection = [];
} else {
dontremoveselection = true;
ok = false;
}
}
} else {
this.jEl.find("." + this.classItems).removeClass("selected");
this.currentSelection = [];
}
if (ok) {
item.addClass("selected");
this.trigger("clickItem", this, m_id, model, evt);
this._lastSelection = m_id;
if (this.currentSelection.indexOf(m_id) == -1) this.currentSelection.push(m_id);
} else {
if (!dontremoveselection) {
item.removeClass("selected");
this.currentSelection.m_remove(m_id);
}
}
// log("this.currentSelection",this.currentSelection)
if (this.itemsDraggableTo) {
// this.jEl.on('mousedown', (evt)=> {
// // evt.preventDefault() ;
// // evt.stopPropagation() ;
$(document).on("mousemove", $.proxy(this.moveViewable, this));
// }) ;
$(document).on("mouseup", evt => {
// log("this.viewable",this.viewable)
if (this.viewable) this.viewable.remove();
$(document).off("mousemove", $.proxy(this.moveViewable, this));
$(document).off("mouseenter", this.itemsDraggableTo, $.proxy(this.enterViewable, this));
$(document).off("mouseleave", this.itemsDraggableTo, $.proxy(this.leaveViewable, this));
var elTemp = $(this.itemsDraggableTo + ".over");
if (elTemp.length) {
this.trigger("droped", elTemp, elTemp.attr("data-m_id"), this.getSelection());
$(this.itemsDraggableTo).removeClass("over");
}
this._dragStarted = false;
});
$(document).on("mouseenter", this.itemsDraggableTo, $.proxy(this.enterViewable, this));
$(document).on("mouseleave", this.itemsDraggableTo, $.proxy(this.leaveViewable, this));
// (evt)=> {
// log("evt.target", $(evt.target))
// }) ;
}
});
// $( "#contacts_list_contacts .M_SimpleListContentReal" ).selectable();
// this.jEl.find('.M_SimpleListContentReal').selectable();
// this.jEl.find('.M_SimpleListContentReal').draggable();
this.setSelection(this.currentSelection);
this.trigger("render", this);
}
/**
* @param {type}
* @return {type}
*/
leaveViewable(evt) {
$(evt.target).removeClass("over");
}
/**
* @param {type}
* @return {type}
*/
enterViewable(evt) {
// log("evt.target", $(evt.target))
// $(this.itemsDraggableTo).removeClass('over') ;
$(evt.target).addClass("over");
}
/**
* @param {type}
* @return {type}
*/
moveViewable(evt) {
evt.preventDefault();
// log("this._dragStarted",this._dragStarted)
if (!this._dragStarted) {
var sel = this.getSelection();
this.viewable = $(
"<div style='width:20px; height:20px; position:relative; z-index:100000;'><div class='M_Badge' style='right:-30px;'>" +
sel.length +
"</div><span class='fa fa-group' style='font-size:30px;'></span></div>"
);
$("body").append(this.viewable);
}
this.viewable.offset({ top: evt.pageY, left: evt.pageX + 30 });
this._dragStarted = true;
}
/**
* @param {type}
*/
setSelection(m_id) {
// log("setSelection",m_id)
// if (!m_id) return ;
if (!$.isArray(m_id)) m_id = [m_id];
if (!this.multipleSelection) this.jEl.find("." + this.classItems).removeClass("selected");
var goodItem = [];
for (var i = 0; i < m_id.length; i++) {
// log("m_id[i]",m_id[i])
goodItem = this.jEl.find("." + this.classItems + '[data-m_id="' + m_id[i] + '"]');
goodItem.addClass("selected");
}
// this.currentSelection = m_id ;
if (this.selectionVisible && goodItem.length) {
var posYRow = goodItem.position().top;
var posYParent = goodItem.parent().position().top;
// log(posYRow, posYParent)
var goodContainer = goodItem.closest(".M_SimpleList");
// log('goodContainer.scrollTop()',goodContainer.scrollTop())
if (
1 * (posYRow - posYParent) < goodContainer.scrollTop() ||
1 * (posYRow - posYParent) > goodContainer.scrollTop() + goodContainer.height() - goodItem.height() - 30
) {
goodItem.closest(".M_SimpleList").animate(
{
scrollTop: 1 * (posYRow - posYParent) - goodContainer.height() / 3
},
"slow"
);
}
}
}
/**
* @return {type}
*/
getSelection() {
var tabSelection = [];
// log("this.currentSelection",this.currentSelection)
for (var i = 0; i < this.currentSelection.length; i++) {
var model = this.store.getRow(this.currentSelection[i]);
tabSelection.push(model);
}
return tabSelection;
}
};
/**
* A multi columns list, a table
* @class
* @extends M_.SimpleList
* @property {type} colsDef
* @property {type} classHeader
* @property {type} classItems
* @property {type} styleTable
* @property {type} fullHeight
* @property {type} oddEven
* @property {type} limitRows
* @property {type} moreText
* @property {type} lessText
* @property {type} withMouseOverRaw
*/
M_.TableList = class extends M_.SimpleList {
constructor(opts) {
var defaults = {
colsDef: null,
classHeader: "M_TableListHeader",
classItems: "M_TableListItem",
styleTable: "",
fullHeight: true,
limitRows: 0,
moreText: "%n lignes de plus",
lessText: "Masquer les lignes supplémentaires",
withMouseOverRaw: true,
headerHeight: 30,
_colsToHide: [],
draggableRows: false
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
}
/**
* @return {type}
*/
create() {
var html = "",
cls = "";
if (this.fullHeight) cls += "M_FullHeight";
if (this.withMouseOverRaw) cls += " M_TableListOverRaw";
this._idMore = M_.Utils.id();
html += `<div class='M_TableList ${cls}' style='${this.styleTable}'>
<div>
<table cellpadding="0" cellspacing="0">
<thead></thead>
<tbody></tbody>
</table>
</div>
<div class="M_AlignRight"><a id="${this._idMore}" href='javascript:void(0);'>${this.getMoreText()}</a></div>
</div>`;
this.jEl = $(html);
this.container.append(this.jEl);
this.jEl.css("padding-top", this.headerHeight);
this._limitRows = true;
if (this.limitRows) {
$("#" + this._idMore)
.parent()
.show();
this._limitRows = true;
} else {
$("#" + this._idMore)
.parent()
.hide();
this._limitRows = false;
}
$("#" + this._idMore).click(() => {
// console.log("click");
this._limitRows = !this._limitRows;
this.render();
});
}
_setMoreLessText() {
if (this.limitRows) {
if (this._limitRows) {
$("#" + this._idMore).html(this.getMoreText());
} else {
$("#" + this._idMore).html(this.getLessText());
}
if (this._getNbRowsLimited() > 0) $("#" + this._idMore).show();
else $("#" + this._idMore).hide();
}
}
_getNbRowsLimited() {
return this.store.count() - this.limitRows;
}
/**
* @return {type}
*/
getMoreText() {
var val = this.moreText;
val = val.replace(/%n/, this._getNbRowsLimited());
return val;
}
/**
* @return {type}
*/
getLessText() {
var val = this.lessText;
val = val.replace(/%n/, this._getNbRowsLimited());
return val;
}
/**
* @return {type}
*/
render() {
var html = "",
mid = this.store.primaryKey,
colsDef = this.colsDef;
html += "<tr>";
for (var i = 0; i < colsDef.length; i++) {
let cls = this.classHeader + " M_Noselectable";
let style = "";
if (colsDef[i].align && colsDef[i].align == "right") cls += " M_AlignRight";
if (colsDef[i].align && colsDef[i].align == "center") cls += " M_AlignCenter";
if (colsDef[i].align && colsDef[i].align == "left") cls += " M_AlignLeft";
if (!colsDef[i].align) cls += " M_AlignLeft";
if (colsDef[i].width) {
var w = "";
if ($.type(colsDef[i].width) === "string") w = colsDef[i].width;
else w = colsDef[i].width + "px";
style += "width:" + w + "; ";
}
html += "<th data-m-col='" + i + "' class='" + cls + "' style='" + style + "'>" + colsDef[i].label;
if (this.fullHeight) html += "<div style='" + style + "'>" + colsDef[i].label + "</div>";
html += "</th>";
}
html += "</tr>";
this.jEl
.find("thead")
.empty()
.html(html);
html = "";
this.store.each((model, indexTemp) => {
// log("model",model)
if (this.limitRows && indexTemp >= this.limitRows && this._limitRows) return true;
var clsTr = "";
if (this.oddEven) {
if (indexTemp % 2 === 0) clsTr += " M_TableListOdd";
else clsTr += " M_TableListEven";
}
var draggable = "";
if (this.draggableRows) draggable = "draggable='true'";
html += "<tr class='" + clsTr + "' " + draggable + ">";
for (var i = 0; i < colsDef.length; i++) {
let val = "",
cls = this.classItems + " M_Noselectable",
mystyle = { cls: "", style: "" };
if ($.isFunction(colsDef[i].val)) {
// console.log("model", model);
val = colsDef[i].val(model, mystyle);
} else {
if (model instanceof M_.Model) val = model.get(colsDef[i].val);
else val = model[colsDef[i].val];
}
if (colsDef[i].align && colsDef[i].align == "right") cls += " M_AlignRight";
if (colsDef[i].align && colsDef[i].align == "center") cls += " M_AlignCenter";
if (colsDef[i].align && colsDef[i].align == "left") cls += " M_AlignLeft";
cls += " " + mystyle.cls;
let datamid = "";
if (model instanceof M_.Model) datamid = model.get(mid);
else datamid = model[mid];
html += "<td class='" + cls + "' style='" + mystyle.style + "' data-m_id='" + datamid + "'>" + val + "</td>";
}
html += "</tr>";
});
this.jEl
.find("tbody")
.empty()
.html(html);
this.jEl.find("." + this.classHeader).on("click", evt => {
// console.log("evt", evt);
// this.store.sort()
var item = $(evt.target).closest("." + this.classHeader);
var numcol = item.attr("data-m-col");
var colDef = this.colsDef[numcol];
if (colDef) {
let direction = -1;
if (colDef._sortDirection) direction = colDef._sortDirection;
direction = direction * -1;
colDef._sortDirection = direction;
// console.log("colDef._sortDirection", colDef._sortDirection);
if (colDef.sort) this.store.sort(colDef.sort, direction);
else this.store.sort(colDef.val, direction);
// this.render() ;
}
});
this.jEl.find("." + this.classItems).on("click", evt => {
// log("dddddddddddddd")
var item = $(evt.target).closest("." + this.classItems);
var m_id = item.attr("data-m_id");
var model = this.store.getRow(m_id);
// get num col
var col = item.index();
var row = item.parent().index();
if (this.trigger("beforeClickItem", this, m_id, model, evt, col, row) === false) return;
// item.addClass('selected') ;
this.trigger("clickItem", this, m_id, model, evt, col, row);
});
// this.jEl.find("tbody tr").on('mouseover', (evt)=> {
// }) ;
// this.jEl.find("tbody tr").on('mouseout', (evt)=> {
// }) ;
this._setMoreLessText();
this.hideColumns(this._colsToHide);
this.trigger("render", this, this.jEl);
}
showAllColumns() {
this._colsToHide = [];
$("#" + this.id)
.find("tr td")
.show();
}
showColumns(cols) {
if (!$.isArray(cols)) cols = [cols];
this._colsToHide = _.difference(this._colsToHide, cols);
$("#" + this.id)
.find("tr td")
.show();
$("#" + this.id)
.find("tr th")
.show();
this.hideColumns(this._colsToHide);
}
hideColumns(cols) {
if (!$.isArray(cols)) cols = [cols];
this._colsToHide = this._colsToHide.concat(cols);
this._colsToHide = _.uniq(this._colsToHide);
// console.log("this._colsToHide", this._colsToHide);
_.each(this._colsToHide, col => {
$("#" + this.id)
.find("tr td:nth-child(" + col + ")")
.hide();
$("#" + this.id)
.find("tr th:nth-child(" + col + ")")
.hide();
});
}
};
/**
* Implements this to get CRUD (get/post/put/delete) ajax requests
* @interface
*/
M_.CRUD = {
/**
* @return {type}
*/
initCRUD() {
// var defaults = {
// url: "",
// args: {},
// model: null,
// useWebsocket: M_.App.useWebsocket
// };
// $.extend(this, defaults) ;
},
// onLoad: function(model) {},
// onSave: function(data) {},
// onDelete: function(data) {},
/**
* @param {type}
* @param {Object}
* @param {Function}
* @return {type}
*/
load(id, args = {}, callback = null) {
this._callbackFormLoaded = callback;
//this.callbackLoadRow = callback ;
var okArgs = {};
//var okArgs = {action: 'get'} ;
//okArgs[this.primaryKey] = id ;
$.extend(okArgs, this.args, args);
if (this.onBeforeLoad) this.onBeforeLoad();
var obj = { url: this.url, args: okArgs };
if (this.trigger("beforeLoad", this, obj) === false) return false;
//this.lastLoadArgs = okArgs ;
var url = obj.url;
var method = "GET";
if (this.urls) {
let aaurl = this._analyseUrl(this.urls.findone);
method = aaurl.method;
url = aaurl.url;
}
// console.log("method, url",method, url);
if (id) url += "/" + id;
// alert("this.useWebsocket",this.useWebsocket)
if (this.useWebsocket) {
io.socket.get(url, okArgs, (data, jwres) => {
this._treatDataCrud(data, callback);
});
} else {
if (this.ajaxLoad) this.ajaxLoad.abort();
this.ajaxLoad = M_.Utils.ajaxJson(url, okArgs, method, data => {
this._treatDataCrud(data, callback);
});
}
},
_treatDataCrud(data, callback) {
// log("_treatDataCrud",data)
var row = null;
if (data && data[this.rootData]) row = data[this.rootData];
else row = data;
this.trigger("loadraw", this, row);
if (this.processData) this.processData(row);
var m;
if (this.model) {
m = new this.model({ row: row });
} else {
m = row;
}
this.dataLoaded = m;
if (this.onLoad) this.onLoad(m);
this.trigger("load", this, m);
if (callback) callback(m);
},
_analyseUrl(theurl) {
if (theurl.indexOf(" ") >= 0) {
var [method, url] = theurl.split(" ");
return { method: method, url: url };
} else return { method: "POST", url: theurl };
},
/**
* @param {type}
* @param {Object}
* @param {Function}
* @return {type}
*/
save(modelOrData = null, args = {}, callback = null) {
this._callbackFormSaved = callback;
// if (this.validAndAlert()) {
// log("modelOrData",modelOrData)
this._isForm = false;
if (!modelOrData) {
modelOrData = this.getValues(true);
this._isForm = true;
}
// log("modelOrData", modelOrData)
var data = modelOrData;
var key;
if (data instanceof M_.Model) {
data = data.getArray();
}
// log("data",data)
// log("data", data)
for (key in data) {
// if (data[key] && data[key].toDate && data[key].toDate() instanceof Date) {
if (moment.isMoment(data[key])) {
data[key] = data[key].toISOString();
}
}
// log("data", data)
var okArgs = { action: "save" };
$.extend(okArgs, this.args, args, data);
// log("okArgs",okArgs);
/*var fields=this.fields ;
for(var j=0 ; j<fields.length ; j++) {
if (fields[j].type=='date') {
this.row[fields[j].name] = M_.Utils.formatDate(this.row[fields[j].name], 'Y-m-d H:i:s') ;
}
}
$.extend(okArgs, this.row) ;
$.extend(okArgs, this.args, args) ;
if (this.trigger("beforeLoad", this, {url: this.url, args:okArgs})===false) return false ;
this.lastLoadArgs = okArgs ;
*/
var url = this.url;
var method = "POST";
if (this.urls) {
let aaurl = this._analyseUrl(this.urls.create);
method = aaurl.method;
url = aaurl.url;
}
var moreUrl = "";
if (this.model) {
var modelTemp = new this.model({ row: {} });
if (!_.isEmpty(okArgs[modelTemp.primaryKey])) {
method = "PUT";
moreUrl = "/" + okArgs[modelTemp.primaryKey];
if (this.urls) {
let aaurl = this._analyseUrl(this.urls.update);
method = aaurl.method;
url = aaurl.url;
}
}
// log("this.model.primaryKey",modelTemp.primaryKey,okArgs)
}
// console.log("url,method",url,method,this.model,okArgs);
var obj = { url: url + moreUrl, method: method, args: okArgs };
if (this.trigger("beforeSave", this, obj) === false) return false;
// return ;
// var formData = new FormData() ;
// for(key in okArgs) {
// formData.append(key, okArgs[key]) ;
// }
M_.Utils.ajaxJson(obj.url, obj.args, obj.method, data => {
// log("dataaaaa",data)
if (data.error) {
let errTxt = "";
if (data.error == "E_VALIDATION") {
_.each(data.invalidAttributes, (attr, key) => {
errTxt += key + " : ";
_.each(attr, msg => {
errTxt += msg.message;
});
errTxt += "\n";
});
}
if (this.informValidReturnSails) this.informValidReturnSails(data);
this.trigger("error", this, data, errTxt);
} else {
this.trigger("save", this, data);
if (this._callbackFormSaved) this._callbackFormSaved(data);
}
});
// $.ajax({
// url: this.url+moreUrl,
// type: method,
// //headers: {Connection: close},
// data: JSON.stringify(okArgs),
// contentType: 'application/json',
// processData: false,
// dataType: 'json',
// cache: false,
// success: (data)=> {
// //&this.trigger("saved", data);
// this.trigger("save", this, data);
// if (this._callbackFormSaved) this._callbackFormSaved(data) ;
// }
// });
// }
},
/**
* @param {type}
* @param {Object}
* @param {Function}
* @return {type}
*/
delete(id, args = {}, callback = null) {
this._callbackFormDelete = callback;
var okArgs = {};
$.extend(okArgs, this.args, args);
var url = this.url;
var method = "DELETE";
if (this.urls) {
let aaurl = this._analyseUrl(this.urls.destroy);
method = aaurl.method;
url = aaurl.url;
}
if (this.trigger("beforeDelete", this, { url: url, args: okArgs }) === false) return false;
M_.Utils.ajaxJson(url + "/" + id, okArgs, method, data => {
this.trigger("delete", this, data);
if (this._callbackFormDelete) this._callbackFormDelete(data);
});
// $.ajax({
// url: this.url+"/"+id,
// type: 'DELETE',
// //contentType: 'application/json',
// data: okArgs,
// dataType: 'json',
// success: (data)=> {
// this.trigger("delete", this, data);
// if (this._callbackFormDelete) this._callbackFormDelete(data) ;
// }
// });
}
};
/**
* To do
* @class
* @extends M_.SimpleList
* @implements M_.Observable
* @property {type} jEl
* @property {type} alignTo
* @property {type} position
* @property {type} height
* @property {type} width
* @property {type} speed
* @property {type} visible
* @property {type} floating
*/
M_.Drawer = class {
constructor(opts) {
var defaults = {
jEl: null,
alignTo: null,
position: "bottom",
height: 45,
width: 45,
speed: 300,
visible: false,
floating: true
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
_.mixin(this, M_.Observable);
this.initObservable();
$(window).resize(() => {
if (this.tsResize) window.clearTimeout(this.tsResize);
this.tsResize = window.setTimeout(() => {
if (this.floating) this.showOrHide(this.visible, false);
}, 100);
});
}
/**
* @return {Boolean}
*/
isVisible() {
return this.visible;
}
/**
* @return {type}
*/
show() {
if (this.visible) return;
if (this.trigger("beforeShow", this) === false) return;
this.showOrHide(true);
this.trigger("show", this);
}
/**
* @return {type}
*/
hide() {
if (!this.visible) return;
if (this.trigger("beforeHide", this) === false) return;
this.showOrHide(false);
this.trigger("hide", this);
}
/**
* @param {type}
* @param {Boolean}
* @return {type}
*/
showOrHide(ok, anim = true) {
var top, left, topStart, leftStart;
this.jEl.outerWidth(this.width);
this.jEl.outerHeight(this.height);
if (!this.floating && !ok) {
this.alignTo.height("100%");
}
this.jEl.css("position", "absolute");
if (this.position == "bottom") {
this.jEl.outerWidth(this.alignTo.outerWidth());
top = this.alignTo.offset().top + this.alignTo.outerHeight() - this.jEl.outerHeight();
left = this.alignTo.offset().left;
topStart = top + this.jEl.outerHeight();
leftStart = left;
}
if (this.position == "top") {
this.jEl.outerWidth(this.alignTo.outerWidth());
top = this.alignTo.offset().top;
left = this.alignTo.offset().left;
topStart = top - this.jEl.outerHeight();
leftStart = left;
}
if (this.position == "left") {
this.jEl.outerHeight(this.alignTo.outerHeight());
top = this.alignTo.offset().top;
left = this.alignTo.offset().left;
topStart = top;
leftStart = left - this.jEl.outerWidth();
}
if (this.position == "right") {
this.jEl.outerHeight(this.alignTo.outerHeight());
top = this.alignTo.offset().top;
left = this.alignTo.offset().left + this.alignTo.outerWidth() - this.jEl.outerWidth();
topStart = top;
leftStart = left - this.jEl.outerWidth();
}
if (anim) {
if (ok) {
this.jEl
.show()
.offset({ top: topStart, left: leftStart })
.transition({ top, left }, M_.Utils.getSpeedAnim(), () => {
// log("finis")
});
} else {
this.jEl.offset({ top, left }).transition({ top: topStart, left: leftStart }, M_.Utils.getSpeedAnim());
}
// if (ok) {
// this.jEl.addClass('M_TransitionNone')
// .show()
// .offset({top:topStart, left:leftStart}) ;
// this.jEl[0].offsetHeight;
// this.jEl.removeClass('M_TransitionNone')
// .offset({top, left}) ;
// this.jEl.on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd',()=> {
// this.jEl.off('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd') ;
// }) ;
// } else {
// this.jEl.addClass('M_TransitionNone')
// .offset({top, left}) ;
// this.jEl[0].offsetHeight;
// this.jEl.removeClass('M_TransitionNone')
// .offset({top:topStart, left:leftStart}) ;
// this.jEl.on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd',()=> {
// this.jEl.off('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd') ;
// this.jEl.hide() ;
// }) ;
// }
// if (ok) {
// this.jEl.offset({top:topStart, left:leftStart}).animate({
// top, left
// }, this.speed, ()=> {
// if (!this.floating) this.jEl.css('position', 'static') ;
// }) ;
// } else {
// this.jEl.offset({top, left}).animate({
// top:topStart, left:leftStart
// }, this.speed, ()=> {
// }) ;
// }
} else {
if (ok) this.jEl.offset({ top, left });
else this.jEl.offset({ top: topStart, left: leftStart });
}
if (!this.floating && ok) {
this.alignTo.height("calc(100% - " + this.height + "px)");
}
this.visible = ok;
}
};
/**
* Display a background on html document
* @class
* @implements M_.Observable
* @property {type} visible
* @property {type} focusOn
* @property {type} clickHide
* @property {type} alpha
* @property {type} zindex
*/
M_.Modal = class {
constructor(opts) {
var defaults = {
visible: false,
focusOn: false,
clickHide: false,
alpha: 0.5,
zindex: 500,
isLoading: null,
text: ""
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
_.mixin(this, M_.Observable);
this.initObservable();
$(window).resize(() => {
if (this.tsResize) window.clearTimeout(this.tsResize);
this.tsResize = window.setTimeout(() => {
this.showOrHide(this.visible, false);
}, 100);
});
if (this.focusOn) {
this.jEl1 = $("<div class='M_Modal'></div>");
$("body").append(this.jEl1);
this.jEl2 = $("<div class='M_Modal'></div>");
$("body").append(this.jEl2);
this.jEl3 = $("<div class='M_Modal'></div>");
$("body").append(this.jEl3);
this.jEl4 = $("<div class='M_Modal'></div>");
$("body").append(this.jEl4);
if (this.clickHide) {
this.jEl1.click(() => {
this.hide();
});
this.jEl2.click(() => {
this.hide();
});
this.jEl3.click(() => {
this.hide();
});
this.jEl4.click(() => {
this.hide();
});
}
} else {
this.jEl = $("<div class='M_Modal'><p>" + this.text + "</p></div>");
$("body").append(this.jEl);
if (this.clickHide) {
this.jEl.click(() => {
this.hide();
});
}
if (this.isLoading) {
this.jEl.html(
"<div style='width:300px;margin:200px auto 0 auto; text-align:center;'><i style='font-size:100px;' class='fa fa-clock-o faa-spin animated'></i><br><h1 style='color:white;'>" +
this.isLoading +
"</h1></div>"
);
}
}
}
/**
* @return {Boolean}
*/
isVisible() {
return this.visible;
}
/**
* @return {type}
*/
show() {
if (this.trigger("beforeShow", this) === false) return false;
this.showOrHide(true);
this.trigger("show", this);
}
/**
* @return {type}
*/
hide() {
if (this.trigger("beforeHide", this) === false) return false;
this.showOrHide(false);
this.trigger("hide", this);
}
/**
* @param {type}
* @param {Boolean}
* @return {type}
*/
showOrHide(ok, anim = false) {
var speed = M_.Utils.getSpeedAnim();
if (this.focusOn) {
if (ok) {
let posTop1 = this.focusOn.offset().top;
let posTop2 = this.focusOn.offset().top + this.focusOn.outerHeight();
let posTop3 = $(window).height();
let posLeft1 = this.focusOn.offset().left;
let posLeft2 = this.focusOn.offset().left + this.focusOn.outerWidth();
let posLeft3 = $(window).width();
this.jEl1
.css({
"z-index": this.zindex,
top: 0,
left: 0,
width: "100%",
height: posTop1
})
.transition({ opacity: this.alpha }, speed);
this.jEl2
.css({
"z-index": this.zindex,
top: posTop1,
left: 0,
width: posLeft1,
height: posTop2 - posTop1
})
.transition({ opacity: this.alpha }, speed);
this.jEl3
.css({
"z-index": this.zindex,
top: posTop1,
left: posLeft2,
width: posLeft3 - posLeft2,
height: posTop2 - posTop1
})
.transition({ opacity: this.alpha }, speed);
this.jEl4
.css({
"z-index": this.zindex,
top: posTop2,
left: 0,
width: "100%",
height: posTop3 - posTop2
})
.transition({ opacity: this.alpha }, speed);
} else {
this.jEl1.css({ "z-index": this.zindex }).transition({ opacity: 0 }, speed, () => {
this.jEl1.remove();
this.jEl2.remove();
this.jEl3.remove();
this.jEl4.remove();
});
this.jEl2.css({ "z-index": this.zindex }).transition({ opacity: 0 }, speed);
this.jEl3.css({ "z-index": this.zindex }).transition({ opacity: 0 }, speed);
this.jEl4.css({ "z-index": this.zindex }).transition({ opacity: 0 }, speed);
}
} else {
if (ok)
this.jEl
.css({ "z-index": this.zindex })
.show()
.transition({ opacity: this.alpha }, speed);
else
this.jEl.transition({ opacity: 0 }, speed, () => {
this.jEl.remove();
});
}
this.visible = ok;
}
};
/**
* Display a window from a template
* @class
* @implements M_.Observable
* @property {type} visible
* @property {type} clickHide
* @property {type} alpha
* @property {type} zindex
* @property {type} width
* @property {type} maxWidth
* @property {type} height
* @property {type} html
* @property {type} tpl
* @property {type} tplData
* @property {type} tplPartials
* @property {type} cls
* @property {type} modal
* @property {type} animFrom
* @property {type} top
* @property {type} left
* @property {type} position
*/
M_.Window = class {
constructor(opts) {
var defaults = {
visible: false,
clickHide: false,
alpha: 0.5,
zindex: M_.Utils.getNextZIndex(),
width: 400,
maxWidth: 0,
height: "auto",
minHeight: false,
html: null,
tpl: null,
tplData: {},
tplPartials: {},
cls: "",
modal: true,
animFrom: null,
top: 100,
left: 0,
position: "center",
offsetPositionRight: 20,
offsetPositionLeft: 20,
offsetPositionTop: 20,
offsetPositionBottom: 20
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
_.mixin(this, M_.Observable);
this.initObservable();
$(window).resize(() => {
if (this.tsResize) window.clearTimeout(this.tsResize);
this.tsResize = window.setTimeout(() => {
// this.showOrHide(this.visible, false) ;
}, 100);
});
this.create();
}
/**
* @return {type}
*/
create() {
this.jEl = $("<div class='M_Window'></div>").hide();
$("body").append(this.jEl);
// if (this.width=='auto') this.width = '100%' ;
if (this.minHeight)
this.jEl.css({
"min-height": this.minHeight
});
this.jEl.css({
"z-index": this.zindex,
width: this.width,
height: this.height
});
if (this.tpl) {
this.jEl.html(M_.App.renderMustache(this.tpl, this.tplData, this.tplPartials));
} else if (this.html) {
this.jEl.html(this.html);
}
}
/**
* Search a class or selector in the container, like $.find()
* @param {string} selector
* @return {jQuery}
*/
findEl(selector) {
return this.jEl.find(selector);
}
/**
* @return {Boolean}
*/
isVisible() {
return this.visible;
}
/**
* @param {Boolean}
* @return {type}
*/
show(anim = true) {
if (this.trigger("beforeShow", this) === false) return false;
this.showOrHide(true, anim);
this.trigger("show", this);
M_.Window._lastWindow = this;
}
/**
* @param {Boolean}
* @return {type}
*/
hide(anim = true) {
if (this.trigger("beforeHide", this) === false) return false;
this.showOrHide(false, anim);
this.trigger("hide", this);
M_.Window._lastWindow = null;
}
maximize() {
this.jEl.height($(window).height() - 150);
var l = ($(window).width() - this.jEl.outerWidth()) / 2;
var t = 50;
this.jEl.css({
left: l,
top: t
});
}
/**
* @return {type}
*/
center() {
this.jEl.height("auto");
var l = ($(window).width() - this.jEl.outerWidth()) / 2;
var t = ($(window).height() - this.jEl.outerHeight()) / 2;
// console.log("$(window).height()",$(window).height()-200,this.jEl.outerHeight());
if (this.jEl.outerHeight() > $(window).height() - 150) {
this.jEl.height($(window).height() - 150);
// console.log("setheigt",$(window).height()-200);
// t = ($(window).height()-this.jEl.height()-200)/2 ;
t = 50;
}
this.jEl.css({
left: l,
top: t
});
return this;
}
/**
* @param {type}
* @param {Boolean}
* @return {type}
*/
showOrHide(ok, anim = true) {
// log("showOrHide")
var speed = M_.Utils.getSpeedAnim();
if (ok) {
this.zindex = M_.Utils.getNextZIndex();
if (this.modal && !this.modalObj) {
this.modalObj = new M_.Modal({
clickHide: this.clickHide,
alpha: this.alpha,
zindex: this.zindex - 1,
listeners: [
[
"beforeHide",
() => {
if (this.trigger("beforeHide", this) === false) return false;
}
]
]
});
this.modalObj.show();
}
// this.jEl.height(this.height) ;
var l1 = this.left,
l2 = this.left,
t1 = this.top,
t2 = this.top,
w1 = this.width,
w2 = this.width,
h1 = this.height,
h2 = this.height,
s1 = 0.5,
s2 = 1,
o1 = 0,
o2 = 1;
// if (animFrom) {
// t1 = animFrom.offset().top - $(window).scrollTop() ;
// l1 = animFrom.offset().left ;
// // w1 = animFrom.width() ;
// // h1 = animFrom.height() ;
// log("t,l",t1,l1, animFrom)
// }
this.jEl
.addClass(this.cls)
.css({
scale: 1,
opacity: 0,
"z-index": this.zindex,
left: 0,
top: 0,
width: w1,
height: h1
})
.show();
// log("this.jEl.height()",this.jEl.height(), $(window).height())
if (this.maxWidth !== 0) {
// w1 = w2 = this.maxWidth ;
this.jEl.css({
"max-width": this.maxWidth
});
}
if (this.jEl.outerHeight() + 100 > $(window).height()) {
h1 = h2 = $(window).height() - 100;
}
if (this.position == "center" || this.position == "top") {
l1 = l2 = ($(window).width() - this.jEl.outerWidth()) / 2;
if (h2 == "auto") t1 = t2 = ($(window).height() - this.jEl.height()) / 2;
else t1 = t2 = ($(window).height() - h2) / 2;
}
if (this.position == "top") {
s1 = 1;
o1 = 1;
if (h2 == "auto") {
t1 = -1 * this.jEl.height() - this.offsetPositionTop;
} else {
t1 = h2 - 30;
}
t2 = this.offsetPositionTop;
}
this.jEl
.addClass(this.cls)
.css({ scale: s1, opacity: o1, left: l1, top: t1, width: w1, height: h1 })
.show()
.transition({ scale: s2, opacity: o2, left: l2, top: t2, width: w2 + 1, height: h2 }, speed, () => {
// this.jEl.outerWidth(this.width+1);
});
} else {
this.jEl.transition({ scale: 0.5, opacity: 0 }, speed, () => {
this.jEl.hide();
});
if (this.modalObj) {
this.modalObj.hide();
this.modalObj = null;
}
}
this.visible = ok;
}
static _escapeButtonListener() {
M_.Window._lastWindow = null;
$(document).keydown(evt => {
if (evt.keyCode == 27) {
if (M_.Window._lastWindow) {
M_.Window._lastWindow.hide();
}
}
});
}
};
M_.Window._escapeButtonListener();
/**
* Display a button
* @class
* @extends M_.Outlet
* @property {type} text
* @property {type} cls
* @property {type} handler
*/
M_.Button = class extends M_.Outlet {
constructor(opts) {
var defaults = {
text: "Save",
cls: "primary",
handler: null
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
}
/**
* @return {type}
*/
create() {
var html = `<button class="M_Button ${this.cls}" id="${this.id}">${this.text}</button>`;
this.jEl = $(html);
this.container.append(this.jEl);
if (this.handler) this.jEl.click(this.handler);
}
};
/**
* Display a alert dialog window or a confirm dialog window
* @property {type} textButtonOk
* @property {type} textButtonCancel
*/
M_.Dialog = new class {
constructor(opts) {
this.defaults = {
textButtonOk: "OK",
textButtonCancel: "Annuler"
};
opts = opts ? opts : {};
$.extend(this, this.defaults, opts);
}
_createTemplate(title, text, icon = "fa-warning") {
var html = `<div style="overflow:auto; height:90%;">
<h1 style="line-height:22px;">${title}</h1>
<div>
<div class='M_FloatLeft' style='width:25%'><span class='fa ${icon} M_IconBig'></span></div>
<div class='M_FloatLeft' style='width:75%'>${text}</div>
<div class='M_Clear'></div>
</div>
</div>`;
return html;
}
/**
* @param {type}
* @param {type}
* @param {type}
* @return {type}
*/
alert(title, text, callbackOk) {
// this.callbackOk = callbackOk ;
var html = this._createTemplate(title, text);
html += `<div class="M_margintop">
<div class='M_FloatRight'>
<button type="button" class='M_DialogOK M_Button primary'>${this.textButtonOk}</button>
</div>
<div class='M_Clear'></div>
</div>`;
var win = new M_.Window({
html: html
});
win.show();
win.jEl.find(".M_DialogOK").click(() => {
if (callbackOk) callbackOk();
win.hide();
});
}
/**
* @param {type}
* @param {type}
* @param {type}
* @param {type}
* @return {type}
*/
confirm(title, text, callbackOk, callbackCancel) {
// this.callbackOk = callbackOk ;
// this.callbackCancel = callbackCancel ;
var html = this._createTemplate(title, text);
html += `<div class="M_margintop">
<div class='M_FloatRight'>
<button type="button" class='M_DialogOK M_Button primary'>${this.textButtonOk}</button>
</div>
<div class='M_FloatRight'>
<button type="button" class='M_DialogCancel M_ModalCancel M_Button'>${this.textButtonCancel}</button>
</div>
<div class='M_Clear'></div>
</div>`;
var winConfirm = new M_.Window({
html: html
});
winConfirm.show();
winConfirm.jEl.find(".M_DialogOK").click(() => {
winConfirm.hide();
if (callbackOk) callbackOk.apply(this);
});
winConfirm.jEl.find(".M_DialogCancel").click(() => {
winConfirm.hide();
if (callbackCancel) callbackCancel.apply(this);
});
}
hide() {
this.win.hide();
}
/**
* alert user
* @param {String} text
* @param {String} position
* @param {Number} time number of milliseconds
* @param {Function} callbackClose
*/
notify(text, time = 2000, position = "top", callbackClose = null) {
var html = "";
html += `<div class="">
${text}
</div>`;
var win = new M_.Window({
html: html,
modal: false,
position: "top"
});
win.jEl.click(() => {
win.hide();
});
window.setTimeout(() => {
if (callbackClose) callbackClose();
win.hide();
}, time);
win.show();
}
}();
/**
* Create a dropdown
* @class
* @extends M_.Outlet
* @property {type} destroyOnHide
* @property {type} alignTo
* @property {type} offsetTop
* @property {type} offsetLeft
* @property {type} container
* @property {type} constraints
* @property {type} autoSize
* @property {type} autoShow
* @property {type} items
* @property {type} html
* @property {type} alwaysDropdownBelow
*/
M_.Dropdown = class extends M_.Outlet {
constructor(opts) {
var defaults = {
destroyOnHide: true,
_visible: false,
alignTo: null,
offsetTop: 0,
offsetLeft: 0,
container: "body",
alwaysDropdownBelow: false,
constraints: $(window),
autoSize: true,
autoShow: false,
items: [],
html: null,
itemsClass: "",
drawEachItem: null,
// id: null,
_items: [],
width: 0
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
// this.create() ;
}
drawItem(item, i, ul, items) {
if (this.drawEachItem) return this.drawEachItem.call(this, item, i, ul, items);
if (!item.text || item.text == "_m_separation") {
ul.append($(`<li class="M_DropdownSeparation"></li>`));
return;
}
var more = "";
if (item.disabled) more += " disabled";
if (this.itemsClass) more += " " + this.itemsClass;
if (item.cls) more += " " + item.cls;
var htmlItem = `<li class="M_DropdownMenu ${more}">${item.text}</li>`;
var jElItem = $(htmlItem);
// jElItem.data('indexitem', i) ;
ul.append(jElItem);
if (item.click && !item.disabled) {
jElItem.click({ fn: item }, evt => {
// var el = $(evt.target).closest('.M_DropdownMenu') ;
evt.data.fn.click(evt, evt.data.fn);
this.hide();
});
}
}
/**
* @function setItems
* @memberOf! M_.Dropdown
* @instance
* @param {type}
*/
setItems(items) {
this.jEl.empty();
this.jEl.height("auto");
// log("setItems",this.jEl)
if (items.length) {
var ul = $("<ul>");
this.jEl.append(ul);
for (var i = 0; i < items.length; i++) {
this.drawItem(items[i], i, ul, items);
}
this._setPosition();
}
}
/**
* @function create
* @memberOf! M_.Dropdown
* @instance
* @return {type}
*/
create() {
// console.log("create");
var html = `<div class="M_Relative"><div id="${this.id}" class="M_Dropdown"></div></div>`;
// log("this.container",this.container)
$(this.container).append(html);
this.jEl = $(this.container).find(".M_Dropdown");
this.jEl.css("z-index", M_.Utils.getNextZIndex());
if (this.width > 0) this.jEl.width(this.width);
if (this.items.length) this.setItems(this.items);
else if (this.html) this.jEl.html(this.html);
// if (this.autoShow) this.show() ;
this.jEl.on("M_DropdownHide", evt => {
// log("M_DropdownHide")
// evt.stopPropagation() ;
this.hide();
});
// this.jEl.on('M_DropdownShow', (evt)=> {
// // evt.stopPropagation() ;
// log("show!!!")
// this.show() ;
// }) ;
// log("ddd",this.jEl)
}
/**
* @function show
* @memberOf! M_.Dropdown
* @instance
* @return {type}
*/
show() {
// log("show",this._visible)
// var evt = $.Event('click') ;
// evt.stopPropagation() ;
// log("show", this._visible, this.jEl)
// window.event.stopPropagation();
// M_.Dropdown.closeAllDropdown() ;
if (this._visible) return;
if (this.trigger("beforeShow", this) === false) return false;
this.showOrHide(true);
this.trigger("show", this);
}
/**
* @function hide
* @memberOf! M_.Dropdown
* @instance
* @return {type}
*/
hide() {
// log("hide", this.destroyOnHide, this.jEl.get(0).id)
// log("hide ",this._visible, this.jEl)
if (!this._visible) return;
if (this.trigger("beforeHide", this) === false) return false;
this.showOrHide(false);
this.trigger("hide", this);
}
/**
* @function isVisible
* @memberOf! M_.Dropdown
* @instance
* @return {Boolean}
*/
isVisible() {
return this._visible;
}
_waitEndClose() {
window.setTimeout(() => {
this._waitEndClose();
}, 10);
}
/**
* @function realign
* @memberOf! M_.Dropdown
* @instance
* @return {type}
*/
realign() {
this._setPosition();
}
_setPosition() {
// search parent overflow with auto or hidden
var rettemp = this.jEl
.parents()
.filter(function() {
if ($(this).css("overflow") === "auto" || $(this).css("overflow") === "hidden") return true;
return false;
})
.first();
// var compareTo = {top: $(window).scrollTop(), height: $(window).height()} ;
// if (rettemp.length) compareTo = {top: }
var compareTo = $("body"); // work because body height == window height !!!!
if (rettemp.length) compareTo = rettemp;
this.jEl.height("auto");
var t = this.alignTo.offset().top + this.alignTo.outerHeight() + this.offsetTop;
var l = this.alignTo.offset().left + this.offsetLeft;
this.jEl.show().offset({ top: t, left: l });
// log("this.offsetLeft",this.offsetLeft)
var top = this.jEl.offset().top;
var h = this.jEl.outerHeight();
var topCompareTo = compareTo.offset().top;
var hCompareTo = compareTo.outerHeight();
// console.log("hCompareTo", hCompareTo);
// console.log("top,h,topCompareTo,hCompareTo", top,h,topCompareTo,hCompareTo,this.jEl);
if (top + h > topCompareTo + hCompareTo && this.alwaysDropdownBelow !== true) {
// check if space above is bigger than below
if (top - topCompareTo > topCompareTo + hCompareTo - top) {
//more big above
// max height
let maxh = top - topCompareTo - 35;
let moreH = 0;
if (h > maxh && this.autoSize) {
h = maxh;
this.jEl.height(h - 50);
} else moreH = 30;
t = top - h - moreH;
this.jEl.offset({ top: t, left: l });
} else {
// more big bottom
let maxh = topCompareTo + hCompareTo - top - 25;
if (h > maxh && this.autoSize) {
h = maxh;
this.jEl.height(h);
}
}
}
// var hWin = $(window).height() ;
// var top = this.jEl.offset().top ;
// var h = this.jEl.height() ;
// if (top+h-$(window).scrollTop() > hWin) {
// // try to set dropdown on top of alignTo
// if (this.alignTo.offset().top>(hWin/2) && h<(hWin/2)) {
// t = this.alignTo.offset().top - h - 30 ;
// this.jEl.offset({top:t, left:l}) ;
// // else set dropdown height
// } else {
// var h = hWin-(top-$(window).scrollTop())-30 ;
// this.jEl.height(h) ;
// }
// }
}
/**
* @function showOrHide
* @memberOf! M_.Dropdown
* @instance
* @param {type}
* @return {type}
*/
showOrHide(ok) {
// log("showOrHide",ok,this.jEl)
var speed = M_.Utils.getSpeedAnim();
if (ok) {
// this.jEl.show().css({opacity:0}).offset({top, left}).transition({opacity:1}) ;
M_.Dropdown.closeAllDropdown();
// log("this.jEl",this.jEl)
this.jEl
.show()
.css({ opacity: 0.5 })
.transition({ opacity: 1 }, speed);
this._setPosition();
this._visible = true;
} else {
// this.jEl.transition({opacity:0}, speed, ()=> {
this.jEl.hide();
this._visible = false;
if (this.destroyOnHide) this.destroy();
// }) ;
}
// log("_visible",this._visible)
}
/**
* @function destroy
* @memberOf! M_.Dropdown
* @instance
* @return {type}
*/
destroy() {
// log("destroy");
// $(document).off('click', $.proxy(this.onDocumentClick, this)) ;
this.jEl.parent().remove();
this.trigger("destroy", this);
}
/**
* @return {type}
*/
static closeAllDropdown() {
// log("closeAllDropdown")
$(".M_Dropdown").trigger("M_DropdownHide");
// if (this.tabDropdowns) {
// for(var i=0 ; i<this.tabDropdowns.length ; i++) {
// if (this.tabDropdowns[i]) this.tabDropdowns[i].hide() ;
// }
// }
}
static _goGlobalListeners() {
$(document)
.on("click", ".M_Dropdown", evt => {
M_.Help.hideMHelp();
evt.stopPropagation();
})
.on("click", evt => {
M_.Help.hideMHelp();
// log("clickDocument")
// if ($(evt.target).closest('.M_Dropdown').length===0) {
this.closeAllDropdown();
// $('.M_Dropdown').trigger('M_DropdownHide') ;
// }
});
}
};
M_.Dropdown._goGlobalListeners();
/**
* Create month calendar to display directly on page or in a M_.Form.Date
* @class
* @implements M_.Observable
* @property {type} container
* @property {type} dateViewed
* @property {type} dateSelected
* @property {type} renderCellDay
* @property {type} dateFormat
* @property {type} dateFormatFrom
* @property {type} firstDay
* @property {type} noDays
* @property {type} noMonths
* @property {type} allways6lines
* @property {type} cls
* @property {type} displayHeader
* @property {type} showDateSelected
* @property {type} showDateViewed
*/
M_.CalendarMonth = class {
constructor(opts) {
var defaults = {
container: null,
dateViewed: moment(),
dateSelected: moment(),
renderCellDay: null,
// disabledDatesTo: null,
// disabledDatesFrom: null,
disabledDates: null,
dateFormat: "DD/MM/YYYY",
dateFormatFrom: "YYYY-MM-DD",
firstDay: 1, //0=sunday, 1=monday
noDays: false,
noMonths: false,
_thefirstDay: null,
_thelastDay: null,
allways6lines: false,
cls: "",
_cls: "",
displayHeader: true,
selectable: true,
showDateSelected: true,
showDateViewed: true,
showWeekNumber: false,
selectWeek: false
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
_.mixin(this, M_.Observable);
this.initObservable();
this.create();
}
// tpl: 'tpl_M_CalendarMonth',
/**
* @return {type}
*/
create() {
var html = `<div class="M_CalendarMonth ${this._cls} ${this.cls}">
<div class="M_CalendarMonth_days">
<div class="M_CalendarMonth_previous"><span class="fa fa-arrow-circle-left"></span></div>
<div class="M_CalendarMonth_date">Date</div>
<div class="M_CalendarMonth_next"><span class="fa fa-arrow-circle-right"></span></div>
<div class="M_CalendarMonth_content"></div>
</div>
<div class="M_CalendarMonth_months">
<div class="M_CalendarMonth_previous"><span class="fa fa-arrow-circle-left"></span></div>
<div class="M_CalendarMonth_date">Date</div>
<div class="M_CalendarMonth_next"><span class="fa fa-arrow-circle-right"></span></div>
<div class="M_CalendarMonth_content"></div>
</div>
<div class="M_CalendarMonth_years">
<div class="M_CalendarMonth_previous"><span class="fa fa-arrow-circle-left"></span></div>
<div class="M_CalendarMonth_date">Date</div>
<div class="M_CalendarMonth_next"><span class="fa fa-arrow-circle-right"></span></div>
<div class="M_CalendarMonth_content"></div>
</div>
</div>`;
this.jEl = $(html);
this.container.append(this.jEl);
this.redraw();
if (!this.displayHeader) this.container.find(".M_CalendarMonth_date, .M_CalendarMonth_previous, .M_CalendarMonth_next").hide();
else this.container.find(".M_CalendarMonth_date, .M_CalendarMonth_previous, .M_CalendarMonth_next").show();
}
/**
* @param {type}
* @param {type}
*/
setDateViewed(dateViewed, dateSelected) {
this.dateViewed = dateViewed;
// log("this.dateViewed",this.dateViewed)
if (dateSelected) this.dateSelected = dateSelected;
this.redraw();
}
/**
* @param {type}
* @param {type}
*/
setDateSelected(dateSelected, dateViewed) {
this.dateSelected = dateSelected;
if (dateViewed) this.dateViewed = dateViewed;
this.redraw();
}
/**
* @return {type}
*/
getDateSelected() {
return moment(this.dateSelected);
}
/**
* @return {type}
*/
redraw() {
this.showDays();
}
/**
* @return {type}
*/
showYears() {
this.jEl.find(".M_CalendarMonth_days").hide();
this.jEl.find(".M_CalendarMonth_months").hide();
this.jEl.find(".M_CalendarMonth_years").show();
var yearStart = Math.floor(this.dateViewed.year() / 10) * 10 - 1;
var html = "";
html += "<table>";
for (var i = 0; i < 12; i++) {
var dActu = moment({ year: yearStart + i, month: this.dateViewed.month(), day: 1 });
if (i % 4 === 0) html += "<tr>";
var active = "";
if (dActu.year() == this.dateSelected.year()) active = "class='active'";
html += "<td " + active + " data-m-date='" + dActu.format("YYYY-MM-DD") + "'>" + dActu.year() + "</td>";
if (i % 4 === 3) html += "</tr>";
}
html += "</table>";
this.jEl.find(".M_CalendarMonth_years .M_CalendarMonth_content").html(html);
this.jEl.find(".M_CalendarMonth_years .M_CalendarMonth_date").html(yearStart + 1 + " - " + (yearStart + 11));
this.jEl.find(".M_CalendarMonth_years .M_CalendarMonth_content td").off("click");
this.jEl.find(".M_CalendarMonth_years .M_CalendarMonth_content td").on("click", evt => {
var d = $(evt.target).attr("data-m-date");
this.selectYear(d);
});
this.jEl.find(".M_CalendarMonth_years .M_CalendarMonth_next").off("click");
this.jEl.find(".M_CalendarMonth_years .M_CalendarMonth_next").on("click", evt => {
this.nextYears();
});
this.jEl.find(".M_CalendarMonth_years .M_CalendarMonth_previous").off("click");
this.jEl.find(".M_CalendarMonth_years .M_CalendarMonth_previous").on("click", evt => {
this.previousYears();
});
this.trigger("viewedChanged", this, moment(this.dateViewed));
}
/**
* @return {type}
*/
previousYears() {
this.dateViewed.add(-10, "years"); // = new Date(this.dateViewed.getFullYear()*1-10, this.dateViewed.getMonth(), 1) ;
this.showYears();
this.trigger("yearViewedChanged", this, moment(this.dateViewed));
}
/**
* @return {type}
*/
nextYears() {
this.dateViewed.add(10, "years"); //this.dateViewed = new Date(this.dateViewed.getFullYear()*1+10, this.dateViewed.getMonth(), 1) ;
this.showYears();
this.trigger("yearViewedChanged", this, moment(this.dateViewed));
}
/**
* @param {type}
* @return {type}
*/
selectYear(date) {
// if ($.type(date)=="string") date = moment(date) ; //date = M_.Utils.parseDate(date, 'Y-m-d') ;
this.dateViewed = moment(date);
if (this.noMonths) {
date.month(0).date(1);
//this.selectDate(date) ;
this.trigger("selected", this, moment(date));
} else {
this.showMonths();
this.trigger("yearViewedChanged", this, moment(this.dateViewed));
}
}
/**
* @return {type}
*/
showMonths() {
if (this.noMonths) {
this.showYears();
return;
}
this.jEl.find(".M_CalendarMonth_days").hide();
this.jEl.find(".M_CalendarMonth_months").show();
this.jEl.find(".M_CalendarMonth_years").hide();
var html = "";
html += "<table>";
var tabMonths = moment.months();
for (var i = 0; i < 12; i++) {
var dActu = moment({ year: this.dateViewed.year(), month: i, day: 1 }); //new Date(this.dateViewed.year(), i, 1) ;
if (i % 3 === 0) html += "<tr>";
var active = "";
if (dActu.year() == this.dateSelected.year() && dActu.month() == this.dateSelected.month()) active = "class='active'";
var moreD = "";
if (this.showWeekNumber) {
var dTemp3 = moment(dActu).endOf("month");
moreD += "<br>(S" + dActu.week() + " - S" + dTemp3.week() + ")";
}
html += "<td " + active + " data-m-date='" + dActu.format("YYYY-MM-DD") + "'>" + tabMonths[i] + moreD + "</td>";
if (i % 3 == 2) html += "</tr>";
}
html += "</table>";
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_content").html(html);
this.jEl
.find(".M_CalendarMonth_months .M_CalendarMonth_date")
.html(this.dateViewed.year() + " <span class='fa fa-arrow-circle-down'></span>");
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_content td").off("click");
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_content td").on("click", evt => {
var d = $(evt.target).attr("data-m-date");
this.selectMonth(d);
});
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_next").off("click");
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_next").on("click", evt => {
this.nextYear();
});
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_date").off("click");
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_date").on("click", evt => {
this.showYears();
});
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_previous").off("click");
this.jEl.find(".M_CalendarMonth_months .M_CalendarMonth_previous").on("click", evt => {
this.previousYear();
});
this.trigger("viewedChanged", this, moment(this.dateViewed));
}
/**
* @param {type}
* @return {type}
*/
selectMonth(date) {
// if ($.type(date)=="string") date = moment(date) ;
this.dateViewed = moment(date);
if (this.noDays) {
this.dateViewed.date(1);
//this.selectDate(date) ;
this.trigger("selected", this, moment(this.dateViewed));
} else {
this.showDays();
this.trigger("dateViewedChanged", this, moment(this.dateViewed));
}
}
/**
* @return {type}
*/
previousYear() {
var d = this.dateViewed.date();
if (this.dateViewed.date() * 1 > 28) d = 28;
this.dateViewed.subtract(1, "years");
this.showMonths();
this.trigger("yearViewedChanged", this, moment(this.dateViewed));
}
/**
* @return {type}
*/
nextYear() {
var d = this.dateViewed.date();
if (this.dateViewed.date() * 1 > 28) d = 28;
this.dateViewed.add(1, "years");
this.showMonths();
this.trigger("yearViewedChanged", this, moment(this.dateViewed));
}
/**
* @return {type}
*/
showDays() {
var i;
if (this.noDays) {
this.showMonths();
return;
}
this.jEl.find(".M_CalendarMonth_days").show();
this.jEl.find(".M_CalendarMonth_months").hide();
this.jEl.find(".M_CalendarMonth_years").hide();
var today = moment();
var year = this.dateViewed.year();
var month = this.dateViewed.month();
// var date = this.dateViewed.date() ;
var nbDaysInMonth = this.dateViewed.daysInMonth();
var firstDate = moment({ year: year, month: month, day: 1 }).startOf("day");
var decalage = 0;
// log("firstDate",firstDate,decalage)
for (i = 1; i <= 7; i++) {
if (firstDate.day() == 1) break;
decalage--;
firstDate.add(-1, "days");
}
// log("firstDate",firstDate,decalage)
var html = "";
html += "<table>";
html += "<thead><tr>";
if (this.showWeekNumber) html += "<th>S</th>";
var tabDaysShort = moment.weekdaysShort();
for (i = 0; i < 7; i++) {
var j = (i + 1) % 7;
html += "<th>" + tabDaysShort[j] + "</th>";
}
html += "</tr></thead>";
html += "<tbody>";
var tabCells = [];
var nbMax = Math.ceil(nbDaysInMonth + 15);
var nbLines = 0;
// log("firstDate",nbMax,firstDate, this.dateViewed)
for (i = 0; i < nbMax; i++) {
var dActu = moment()
.year(year)
.month(month)
.date(1 + decalage + i)
.startOf("day"); //{year:year, month:month, day:1+decalage+i}
if (i === 0) this._thefirstDay = moment(dActu);
if (dActu.day() == 1) {
html += "<tr>";
if (this.showWeekNumber) {
let tdClsWeek = "";
if (this.disabledDates && this.disabledDates(this, dActu)) tdClsWeek = "notselectable";
html +=
"<td class='" + tdClsWeek + "' data-m-date='" + dActu.format("YYYY-MM-DD") + "'><b>" + dActu.week() + "</b> </td>";
}
nbLines++;
if (this.allways6lines && nbLines == 7) {
this._thelastDay = moment(dActu);
break;
}
}
var tdCls = "";
if (
this.showDateViewed &&
dActu.year() == this.dateSelected.year() &&
dActu.month() == this.dateSelected.month() &&
dActu.date() == this.dateSelected.date()
)
tdCls += "active ";
if (dActu.month() != this.dateViewed.month()) tdCls += "notthegoodmonth ";
var isDisabled = false;
if (this.disabledDates) isDisabled = this.disabledDates(this, dActu);
if (isDisabled) tdCls += "notselectable ";
if (this.showDateSelected && dActu.year() == today.year() && dActu.month() == today.month() && dActu.date() == today.date())
tdCls += "current ";
var contentTemp = dActu.date();
var idCell = M_.Utils.id();
//if (this.renderCellDay) contentTemp = this.renderCellDay(this, dActu, idCell) ;
tabCells.push([dActu, idCell]);
html +=
"<td class='" +
tdCls +
"' id='" +
idCell +
"' data-m-date='" +
dActu.format("YYYY-MM-DD") +
"'><div class='M_CalendarMonth_txt'>" +
contentTemp +
"</div></td>";
if (dActu.day() === 0) html += "</tr>";
if (dActu.month() != month && dActu.day() === 0 && !this.allways6lines && nbLines >= 2) {
this._thelastDay = moment(dActu);
break;
}
}
html += "</tbody>";
html += "</table>";
var tabMonths = moment.months();
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_content").html(html);
this.jEl
.find(".M_CalendarMonth_days .M_CalendarMonth_date")
.html(tabMonths[this.dateViewed.month()] + " " + this.dateViewed.year() + " <span class='fa fa-arrow-circle-down'></span>");
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_content td").off("click");
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_content td").on("click", evt => {
var target = $(evt.target).closest("td");
if (target.hasClass("notselectable")) return;
var d = target.attr("data-m-date");
var date = moment(d);
if (this.selectWeek) date.startOf("week");
//this.selectDate(d) ;
if (this.selectable) this.setDateSelected(date);
this.trigger("selected", this, date);
});
if (this.selectWeek) {
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_content tr").mouseenter(evt => {
$(evt.target)
.closest("tr")
.addClass("alllineover");
});
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_content tr").mouseleave(evt => {
$(evt.target)
.closest("tr")
.removeClass("alllineover");
});
}
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_next").off("click");
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_next").on("click", evt => {
this.nextMonth();
});
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_date").off("click");
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_date").on("click", evt => {
this.showMonths();
});
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_previous").off("click");
this.jEl.find(".M_CalendarMonth_days .M_CalendarMonth_previous").on("click", evt => {
this.previousMonth();
});
if (this.renderCellDay) {
for (i = 0; i < tabCells.length; i++) {
var el = $("#" + tabCells[i][1]);
var contentTemp2 = this.renderCellDay(this, tabCells[i][0], el);
el.html(contentTemp2);
}
}
this.trigger("viewedChanged", this, moment(this.dateViewed));
}
/**
* @return {type}
*/
getFirstDay() {
return this._thefirstDay;
}
/**
* @return {type}
*/
getLastDay() {
return this._thelastDay;
}
/**
* @return {type}
*/
previousMonth() {
// console.log("this.dateViewed", this.dateViewed);
this.dateViewed.subtract(1, "month").startOf("months");
this.showDays();
}
/**
* @return {type}
*/
nextMonth() {
// this.dateViewed = moment({year:this.dateViewed.year(), month:this.dateViewed.month()*1+1, day:1}) ;
this.dateViewed.add(1, "month").startOf("months");
this.showDays();
}
/**
* @return {type}
*/
destroy() {
this.jEl.remove();
this.trigger("destroy", this);
}
};
M_.Calendar = {};
M_.Calendar.MonthView = class extends M_.CalendarMonth {
constructor(opts) {
var defaults = {
displayHeader: false,
_cls: "M_CalendarMonthView",
cellMinHeight: 50,
cellHeadMinHeight: 20,
showDateViewed: false,
showDateSelected: true,
provideEvents: null,
selectable: false,
modelDef: {
key: "key",
start: "start",
end: "end",
text: "text",
cls: "cls"
},
events: [
// {
// id: '1',
// start: moment(),
// end: moment().add(8, 'days'),
// text: "Céline",
// bgcolor: 'red'
// }, {
// id: '2',
// start: moment(),
// end: moment().add(2, 'days'),
// text: "Bernard",
// bgcolor: 'green'
// }, {
// id: '3',
// start: moment().subtract(2, 'days'),
// end: moment().add(8, 'days'),
// text: "Bernard2",
// bgcolor: 'green'
// }, {
// id: '4',
// start: moment().subtract(60, 'days'),
// end: moment().subtract(15, 'days'),
// text: "Henri",
// bgcolor: 'purple'
// }
]
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
_.mixin(this, M_.Stored);
// log("store", Object.mixin)
this.initStored();
}
loadEvents() {
if (this.store)
this.store.load({
skip: 0,
// where: "{'va_start':{'<': '"+moment(this.dateViewed).startOf('month').add(1, 'month').format('YYYY-MM-DD')+"'}, 'va_end':{'>':'"+moment(this.dateViewed).startOf('month').format('YYYY-MM-DD')+"'}}"
start: moment(this.dateViewed)
.startOf("month")
.format("YYYY-MM-DD"),
end: moment(this.dateViewed)
.startOf("month")
.add(1, "month")
.format("YYYY-MM-DD")
});
}
selectMonth(date) {
super.selectMonth(date);
this.loadEvents();
}
updateStore() {
// this.showDays() ;
this.drawEvents();
// log("this.store",this.store) ;
}
showDays() {
super.showDays();
// this._loadEvents() ;
// if (this.store) this.store.reload() ;
}
_loadEvents() {
// if (this.provideEvents) {
// this.provideEvents((events)=> {
// this.events = events ;
// this.drawEvents() ;
// }) ;
// } else {
// this.drawEvents() ;
// }
}
drawEvents() {
this.jEl.find(".M_CalendarMonthView_bar").remove();
if (this.cellMinHeight > 0) {
this.jEl.find(".M_CalendarMonth_days tbody tr").css("min-height", this.cellMinHeight);
this.jEl.find(".M_CalendarMonth_days tbody tr").css("height", this.cellMinHeight);
}
if (this.cellHeadMinHeight > 0) {
this.jEl.find(".M_CalendarMonth_days thead tr").css("min-height", this.cellHeadMinHeight);
this.jEl.find(".M_CalendarMonth_days thead tr").css("height", this.cellHeadMinHeight);
}
var widthCell = this.jEl.find("td").outerWidth();
// var tabNbEventsByDate = {} ; // stock le nb d'événements pour une journée
var tabNbEventsByWeek = {}; // stock le nb d'événements dans la semaine
var tabBars = [];
// _.each(this.events, (evt)=> {
this.store.each(model => {
var start = moment(model.get(this.modelDef.start)).startOf("day");
var end = moment(model.get(this.modelDef.end)).startOf("day");
var start2 = model.get(this.modelDef.start2) || false;
var end2 = model.get(this.modelDef.end2) || false;
var nbDays = end.diff(start, "days") + 1;
var dActu = moment(start);
for (var nj = 0; nj < nbDays; nj++) {
var isFirst = false;
var isLast = false;
var startOfWeek = moment(dActu)
.startOf("week")
.format("YYYY-MM-DD");
if (nj === 0 || dActu.day() === 1) {
if (!tabNbEventsByWeek[startOfWeek]) tabNbEventsByWeek[startOfWeek] = 0;
if (nj === 0) isFirst = true;
var objTemp = {
id: model.get(this.modelDef.key),
start: moment(dActu),
start2: start2,
end2: end2,
isFirst: isFirst,
isLast: isLast,
nbDays: 0,
startOfWeek: startOfWeek,
position: tabNbEventsByWeek[startOfWeek]
// text: model.get(this.modelDef.text),
// color: model.get(this.modelDef.color),
// bgcolor: model.get(this.modelDef.bgcolor)
};
if (_.isFunction(this.modelDef.text)) objTemp.text = this.modelDef.text(model);
else objTemp.text = model.get(this.modelDef.text);
if (_.isFunction(this.modelDef.cls)) objTemp.cls = this.modelDef.cls(model);
else objTemp.cls = model.get(this.modelDef.cls);
tabBars.push(objTemp);
tabNbEventsByWeek[startOfWeek]++;
}
tabBars[tabBars.length - 1].nbDays++;
if (nj === nbDays - 1) tabBars[tabBars.length - 1].isLast = true;
// if (!tabNbEventsByDate[dActu.format('YYYY-MM-DD')]) tabNbEventsByDate[dActu.format('YYYY-MM-DD')] = 0 ;
// tabNbEventsByDate[dActu.format('YYYY-MM-DD')]++ ;
dActu.add(1, "days");
}
});
// _.each(tabBars, (barEvt)=> {
for (let barEvt of tabBars) {
var bar = $("<div class='M_CalendarMonthView_bar " + barEvt.cls + "' data-m-evt='" + barEvt.id + "'>" + barEvt.text + "</div>");
var css = {
top: barEvt.position * 15 + 25
};
if (barEvt.bgcolor) css["background-color"] = barEvt.bgcolor;
if (barEvt.color) css.color = barEvt.color;
var moreLeft = 0;
var moreWidth = 0;
if (barEvt.start2 && barEvt.isFirst) moreLeft = 50; //widthCell/2
if (barEvt.end2 && barEvt.isLast) moreWidth = 50; //widthCell/2
css.left = moreLeft + "%";
bar
.width(101 * barEvt.nbDays - moreLeft - moreWidth + "%") //widthCell*barEvt.nbDays
.css(css)
.html(barEvt.text);
var el2 = this.jEl.find("td[data-m-date='" + barEvt.start.format("YYYY-MM-DD") + "']");
if (el2) el2.append(bar);
var tr = this.jEl.find("td[data-m-date='" + barEvt.startOfWeek + "']").parent();
var h = 25 + 15 * tabNbEventsByWeek[barEvt.startOfWeek] + 5;
tr.css("height", Math.max(h, this.cellMinHeight));
}
this.jEl
.find(".M_CalendarMonthView_bar")
.click(evt => {
evt.stopPropagation();
var bar = $(evt.target);
var id = bar.closest(".M_CalendarMonthView_bar").attr("data-m-evt");
var found = this.store.getRow(id);
if (found) this.trigger("clickitem", this, found, bar);
})
.mouseenter(evt => {
var bar = $(evt.target);
var id = bar.closest(".M_CalendarMonthView_bar").attr("data-m-evt");
var found = this.store.getRow(id);
this.trigger("enteritem", this, found, bar);
});
}
setEvents(events) {
this.events = events;
this.redraw();
}
};
/**
* All form elements
* @namespace
*/
M_.Form = {};
/**
* Manage a form
* @class
* @implements M_.Observable
* @implements M_.CRUD
* @property {type} useWebsocket
* @property {type} items
* @property {type} itemsDefaults
* @property {type} model
* @property {type} url
*/
M_.Form.Form = class {
constructor(opts) {
var defaults = {
useWebsocket: M_.App.useWebsocket,
items: [],
_items: [],
itemsDefaults: {},
_currentModel: null,
model: null,
url: "",
urls: null,
rootData: "data",
validBeforeSave: true
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
_.mixin(this, M_.Observable);
this.initObservable();
_.mixin(this, M_.CRUD);
this.initCRUD();
var items = this.items;
for (var i = 0; i < items.length; i++) {
this.addItem(items[i]);
}
if (this.autoUnedit) {
// $(document).click((evt)=> {
// if ($(evt.target).closest('.edit').length===0) {
// this.edit(false) ;
// }
// }) ;
}
}
/**
* @return {type}
*/
reset() {
var _items = this._items;
for (var i = 0; i < _items.length; i++) {
_items[i].reset();
}
this.trigger("reset", this);
}
disable() {
var _items = this._items;
for (var i = 0; i < _items.length; i++) {
_items[i].disable();
}
this.trigger("disable", this);
}
enable() {
var _items = this._items;
for (var i = 0; i < _items.length; i++) {
_items[i].enable();
}
this.trigger("disable", this);
}
/**
* @return {type}
*/
valid(err = []) {
var ok = true;
var _items = this._items;
for (var i = 0; i < _items.length; i++) {
if (!_items[i].valid()) {
ok = false;
var label = _items[i].name;
if (_items[i].label !== "") label = _items[i].label;
err.push({ key: _items[i].name, label: label });
}
// console.log("_items[i].valid(),_items[i].name", _items[i].valid(),_items[i].name);
}
var ok2 = this.trigger("valid", this, ok, err);
// console.log("ok2", ok2, ok);
return ok && ok2;
}
/**
* @param {type}
* @return {type}
*/
informValidReturnSails(data) {
// log("informValidReturnSails",data)
var errTxt = "";
_.each(data.invalidAttributes, (attr, key) => {
// log("attr, key",attr, key)
if (this.find(key)) {
this.find(key).informValid(false);
let key2 = this.find(key).label;
if (key2 === "") key2 = key;
key = key2;
}
errTxt += "<b>" + key + "</b> : ";
_.each(attr, msg => {
errTxt += msg.message;
});
errTxt += "<br/>";
});
M_.Dialog.alert("Erreur", "Merci de corriger les champs en rouge :<br/>" + errTxt);
}
/**
* @param {type}
* @return {type}
*/
onLoad(model) {
if (this.model)
this.setValues(model); //.getData()
else this.setValues(model);
}
/**
* @return {type}
*/
onBeforeLoad() {
this.resetValid();
}
/**
* @param {type}
*/
setValues(dataOrModel) {
// console.log("this._items", this._items);
this.reset();
if (!(dataOrModel instanceof M_.Model) && this.model) dataOrModel = new this.model({ row: dataOrModel });
this._currentModel = dataOrModel;
var _items = this._items;
// log("_items",_items)
for (var i = 0; i < _items.length; i++) {
let v;
if (this.model) v = this._currentModel.get(_items[i].name);
else v = this._currentModel[_items[i].name];
// log("_items[i].name",i, _items[i].name, v)
if (v !== undefined) {
if (_items[i] instanceof M_.Form.Combobox) {
let v2;
if (this.model) v2 = this._currentModel.get(_items[i].name + "_val");
else v2 = this._currentModel[_items[i].name + "_val"];
// log("v2",v2,name+'_val')
if (v2) _items[i].setValueAndRawValue(v, v2);
else {
_items[i].setValue(v);
}
} else {
// console.log("v", v, _items[i].inputType);
_items[i].setValue(v);
}
} else {
_items[i].reset();
}
}
// this._hideIfEmpty() ;
this.trigger("update", this);
}
// _hideIfEmpty() {
// var _items = this._items ;
// for(var i=0 ; i<_items.length ; i++) {
// // _items[i]._hideIfEmpty() ;
// // log("hideIfEmpty",_items[i].hideIfEmpty2)
// if (_items[i].hideContainerIfEmpty && M_.Utils.isEmpty(_items[i].getValue())) {
// _items[i].container.hide() ;
// } else {
// _items[i].container.show() ;
// }
// // if (this._inEdit || _items[i].value!=='') _items[i].container.closest(_items[i].hideIfEmpty).show() ;
// // else if (_items[i].value==='') _items[i].container.closest(_items[i].hideIfEmpty).hide() ;
// // }
// }
// }
/**
* @param {Boolean}
* @param {Boolean}
* @return {type}
*/
getValues(serialized = false, returnModel = false) {
// var model = new this.model() ;
// log("model",model)
var _items = this._items,
data = {};
for (var i = 0; i < _items.length; i++) {
if (_items[i] instanceof M_.Form.File) continue;
// // log("AAAAAAAAAAA")
// var resTemp = _items[i].getValue(serialized) ;
// if (resTemp==="") continue ;
// else data[_items[i].name] = resTemp ;
// } else
var val = _items[i].getValue(serialized);
if (_items[i].name.slice(-2) === "[]") {
var nameTemp = _items[i].name.slice(0, -2);
if (!data[nameTemp]) data[nameTemp] = [];
data[nameTemp].push(val);
} else {
data[_items[i].name] = val;
}
// if (data[_items[i].name]==undefined) {
// data[_items[i].name] = val ;
// } else {
// if (Array.isArray(data[_items[i].name])) {
// data[_items[i].name].push(val) ;
// } else {
// data[_items[i].name] = [data[_items[i].name], val] ;
// }
// }
// if (moment.isMoment(data[_items[i].name])) data[_items[i].name] = data[_items[i].name].format("YYYY-MM-DD HH:mm:ss") ;
// if (_items[i].)
}
// log("getValues",data);
if (returnModel) return new this.model({ row: data });
return data;
}
/**
* @param {type}
* @return {type}
*/
find(fieldName) {
var _items = this._items;
for (var i = 0; i < _items.length; i++) {
if (_items[i].name == fieldName) {
return _items[i];
}
}
return null;
}
/**
* @return {type}
*/
resetValid() {
for (var i = 0; i < this._items.length; i++) {
this._items[i].informValid(true);
}
}
/**
* @return {type}
*/
validAndAlert() {
var err = [];
var ok = this.valid(err);
if (!ok) this._alert(err);
return ok;
}
_alert(err) {
var errTxt = "";
_.each(err, er => {
errTxt += "• " + er.label + "<br>";
});
M_.Dialog.alert("Erreur", "Merci de corriger les champs en rouge :<br>" + errTxt);
}
validAndSave(modelOrData = null, args = {}, callback = null) {
var err = [];
var ok = this.valid(err);
// console.log("ok", ok);
if (ok) this.save(modelOrData, args, callback);
else this._alert(err);
return ok;
}
/**
* @param {type}
* @return {type}
*/
populate(dataOrModel) {
this.setValues(dataOrModel);
// if (data instanceof M_.Model) {
// data = data.getArray();
// }
// this.resetSate();
// for (var i = 0; i < this.outlets.length; i++) {
// var outlet = this.outlets[i];
// for (var key in data) {
// if (outlet.name == key) {
// if (outlet instanceof M_.Form.Static) {
// if (data[key + "_val"])
// outlet.setValueAndText(data[key], data[key + "_val"]);
// else
// outlet.setValue(data[key]);
// } else if (outlet instanceof M_.Form.MultiCombobox) {
// outlet.reset();
// if ($.isArray(data[key]))
// outlet.setValue(data[key]);
// } else if (outlet instanceof M_.Form.Combobox) {
// if (data[key + "_val"])
// outlet.setValueAndText(data[key], data[key + "_val"]);
// else
// outlet.setValue(data[key]);
// } else {
// outlet.setValue(data[key]);
// }
// }
// }
// }
}
/**
* @param {type}
*/
addItem(item) {
if (!_.isArray(item)) item = [item];
for (let i = 0; i < item.length; i++) {
var optsItem = $.extend({}, this.itemsDefaults, item[i]);
optsItem.form = this;
if (optsItem.autoContainer) {
var jElTemp = $("<div></div>");
optsItem.autoContainer.append(jElTemp);
optsItem.container = jElTemp;
}
if (optsItem.type === M_.Form.Div) new optsItem.type(optsItem);
else {
var item2 = new optsItem.type(optsItem);
this._items.push(item2);
}
}
// console.log("this._items",this._items);
}
/**
* @param {type}
* @return {type}
*/
deleteItem(name) {
var keepedItems = [];
for (var i = 0; i < this._items.length; i++) {
// log("destroy",this._items[i].name,name)
if (this._items[i].name == name) {
this._items[i].destroy();
} else {
keepedItems.push(this._items[i]);
}
}
this._items = keepedItems;
}
/**
* @param {type}
* @return {type}
*/
getItem(name) {
this.find(name);
}
/**
* @return {type}
*/
getItems() {
return this._items;
}
/**
* @param {type}
* @return {type}
*/
getItemsByFakeGroup(fakeGroupName) {
var keepedItems = [];
for (var i = 0; i < this._items.length; i++) {
if (this._items[i].fakeGroup.indexOf(fakeGroupName) >= 0) keepedItems.push(this._items[i]);
}
return keepedItems;
}
getItemsByType(type) {
var keepedItems = [];
for (var i = 0; i < this._items.length; i++) {
if (this._items[i] instanceof type) keepedItems.push(this._items[i]);
}
return keepedItems;
}
/**
* @param {type}
* @return {type}
*/
deleteByFakeGroup(fakeGroupName) {
var keepedItems = [];
for (var i = 0; i < this._items.length; i++) {
// console.log("this._items[i].fakeGroup.indexOf(fakeGroupName)",this._items[i].fakeGroup.indexOf(fakeGroupName),fakeGroupName);
if (this._items[i].fakeGroup.indexOf(fakeGroupName) < 0) keepedItems.push(this._items[i]);
else this._items[i].destroy();
}
this._items = keepedItems;
}
getCurrentModel() {
return this._currentModel;
}
disable() {
for (var i = 0; i < this._items.length; i++) {
if (this._items[i].disable) this._items[i].disable();
}
}
};
/**
* Base class to input form
* @class
* @extends M_.Outlet
* @property {type} name
* @property {type} value
* @property {type} autoContainer
* @property {type} hideContainerIfEmpty
* @property {type} placeholder
* @property {type} form
* @property {type} inputType
* @property {type} editable
* @property {type} clsInput
* @property {type} clsGroup
* @property {type} clsLabel
* @property {type} styleInput
* @property {type} styleGroup
* @property {type} styleLabel
* @property {type} label
* @property {type} labelPosition
* @property {type} labelWidth
* @property {type} labelFocusInput
* @property {type} dontSave
* @property {type} fakeGroup
* @property {type} allowEmpty
* @property {type} minLength
* @property {type} maxLength
* @property {type} disabled
*/
M_.Form.Input = class extends M_.Outlet {
constructor(opts) {
var defaults = {
name: "",
value: "",
autoContainer: null,
// modelValue: '',
// modelKey: '',
hideContainerIfEmpty: false,
placeholder: "",
form: null,
inputType: "hidden",
tabindex: 0,
editable: true,
_inEdit: false,
_clsMore: "",
clsInput: "",
clsGroup: "",
clsLabel: "",
styleInput: "",
styleGroup: "",
styleLabel: "",
label: "",
labelPosition: "top",
labelWidth: 0,
labelFocusInput: true,
dontSave: false,
fakeGroup: [],
allowEmpty: true,
minLength: null,
maxLength: null,
disabled: false,
previousValue: null
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
if (typeof this.fakeGroup === "string") this.fakeGroup = [this.fakeGroup];
if ((this.labelPosition == "left" || this.labelPosition == "right") && this.labelWidth === 0) this.labelWidth = 0.3;
this.setValue(this.value);
}
/**
* @param {type}
*/
setLabel(txt) {
this.container.find("label").html(txt);
}
/**
* @return {type}
*/
create() {
if (M_.Utils.isEmpty(this.id)) this.id = M_.Utils.id();
var v = this.value;
if ($.type(v) !== "string") v = "";
if (this.labelPosition == "left" && this.label !== "") {
this.clsLabel += " M_LabelLeft";
this.clsInput += " M_InputLeft";
}
if (this.labelPosition == "right" && this.label !== "") {
this.clsLabel += " M_LabelRight";
this.clsInput += " M_InputRight";
}
if (
this.labelWidth > 0 &&
this.label !== "" &&
(this.labelPosition == "left" || this.labelPosition == "right") &&
this.inputType != "checkbox"
) {
if (this.labelWidth <= 1) {
this.styleLabel += " width:" + this.labelWidth * 100 + "%;";
this.styleInput += " width:" + (100 - this.labelWidth * 100) + "%;";
} else {
this.styleLabel += " width:" + this.labelWidth + "px;";
this.styleInput += " width:calc(100% - " + this.labelWidth + "px);";
}
}
var html = "";
var readOnly = "";
var forattr = "";
var tabindex = "";
if (this.tabindex) tabindex = 'tabindex="' + this.tabindex + '"';
if (this.labelFocusInput) forattr = 'for="' + this.id + '"';
if (!this.editable) readOnly = "readonly";
if (this.inputType == "hidden") {
html += `<input ${readOnly} id="${this.id}" type="${this.inputType}" class="M_Input" name="${this.name}" value="${v}">`;
} else {
html += `<div style="${this.styleGroup}" class="M_FormGroup ${this.clsGroup}">`;
if (this.label !== "" && (this.labelPosition == "left" || this.labelPosition == "top"))
html += `<label style="${this.styleLabel}" class="${this.clsLabel}" ${forattr}>${this.label}</label>`;
// if (this._addInputGroup) html += `<div class="M_InputGroup">` ;
var multiple = "";
if (this.multiple) multiple = "multiple";
if (this.inputType == "file") {
html += `<input ${tabindex} ${readOnly} id="${this.id}" type="${this.inputType}" style="${this
.styleInput}" ${multiple} class="M_Input M_InputFile ${this.clsInput}" name="${this.name}" value="${v}" placeholder="${this
.placeholder}"><label class='M_InputFileLabel' for="${this.id}">${this.txtChooseFile}</label>`;
} else if (this.inputType == "textarea") {
if (!this.height) this.height = 100;
this.styleInput += " height:" + this.height + "px;";
html += `<textarea ${tabindex} placeholder="${this.placeholder}" ${readOnly} id="${this.id}" class="M_Input ${this
.clsInput}" style="${this.styleInput}" rows="${this.rows}" name="${this.name}">${v}</textarea>`;
} else if (this.inputType == "none") {
html += `<div id="${this.id}"></div>`;
} else {
var incremental = "";
if (this.incremental) incremental = 'incremental="incremental"';
html += `<input ${tabindex} ${readOnly} id="${this.id}" ${incremental} type="${this.inputType}" style="${this
.styleInput}" class="M_Input ${this._clsMore} ${this.clsInput}" name="${this.name}" value="${v}" placeholder="${this
.placeholder}">`;
}
// if (this._addInputGroup) html += `</div>` ;
if (this.label !== "" && (this.labelPosition == "bottom" || this.labelPosition == "right"))
html += `<label style="${this.styleLabel}" class="${this.clsLabel}" ${forattr}">${this.label}</label>`;
html += `</div>`;
}
// log("html",html)
this.container.append(html);
this.jEl = $("#" + this.id);
if (this.disabled) this.jEl.prop("disabled", true);
// this.jEl = this.container.find('.M_Input') ;
// this.jEl.attr('data-m-after', this.placeholder) ;
}
/**
* @return {type}
*/
reset() {
this.setValue("");
}
/**
* @return {type}
*/
disable() {
this.disabled = true;
this.jEl.prop("disabled", true);
}
/**
* @return {type}
*/
enable() {
this.disabled = false;
this.jEl.prop("disabled", false);
}
/**
* @return {type}
*/
valid() {
var ok = true,
val = this.getValue(),
err = "";
if (!this.allowEmpty && M_.Utils.isEmpty(val)) {
ok = false;
err = "Ce champs ne peut pas être vide\n";
}
if (this.minLength !== null && val.length < this.minLength) {
if (val.length === 0 && this.allowEmpty) {
} else {
ok = false;
err = "La longeur minimum est de " + this.minLength + " pour ce champs\n";
}
}
if (this.maxLength !== null && val.length > this.maxLength) {
ok = false;
err = "La longeur maximum est de " + this.maxLength + " pour ce champs\n";
}
// console.log("this.maxLength",this.maxLength);
this.informValid(ok);
return ok;
}
/**
* @param {type}
* @return {type}
*/
informValid(ok) {
if (!ok) this.jEl.addClass("M_Error");
else this.jEl.removeClass("M_Error");
}
/**
* @return {Boolean}
*/
isEmpty() {
// log("isEmpty","'"+this.value+"'")
var v = this.getValue();
if (v === "" || v === null) return true;
return false;
}
/**
* @param {type}
*/
setValue(val) {
// console.log("setValuePrim", val);
this.previousValue = this.value;
this.value = val;
this.jEl.val(val);
}
/**
* @param {Boolean}
* @return {type}
*/
getValue(serialized = false) {
this.value = this.jEl.val();
return this.value;
}
/**
* @return {type}
*/
hide() {
this.container.hide();
}
/**
* @return {type}
*/
show() {
this.container.show();
}
};
M_.Form.Slider = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
placeholder: "",
inputType: "perso",
jElCursor: null,
steps: 2,
value: 0,
labelLeft: "Left",
labelRight: "Right",
colorLeft: "red",
colorRight: "green",
labelWidth: 50
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
}
create() {
if (M_.Utils.isEmpty(this.id)) this.id = M_.Utils.id();
var v = this.value;
var jEl = $(
"<div class='M_Slider'><label class='M_SliderLabelLeft'>" +
this.labelLeft +
"</label><div class='M_SliderBar'><div class='M_SliderCursor'></div></div><label class='M_SliderLabelRight'>" +
this.labelRight +
"</label><div class='M_Clear'></div></div>"
);
var wLabels = 0;
if (this.labelLeft === "") {
jEl.find(".M_SliderLabelLeft").remove();
} else {
wLabels += this.labelWidth;
jEl.find(".M_SliderLabelLeft").width(this.labelWidth);
}
if (this.labelRight === "") {
jEl.find(".M_SliderLabelRight").remove();
} else {
wLabels += this.labelWidth;
jEl.find(".M_SliderLabelRight").width(this.labelWidth);
}
this.container.append(jEl);
this.jEl = jEl.find(".M_SliderBar");
this.jElCursor = jEl.find(".M_SliderCursor");
this._spaceCursor = this.jElCursor.position().left;
this.jEl.css("width", "calc(100% - " + wLabels + "px)");
// this.jElCursor.width((this.jEl.width() - this._spaceCursor*2)/this.steps);
this.jEl.click(evt => {
var pos = 0;
if (this.steps == 2) {
if (!this.getValue()) pos = 1;
this.setValue(pos);
} else {
var parentOffset = $(evt.target)
.closest(".M_Slider")
.offset();
var relX = evt.pageX - parentOffset.left;
var w = this.jEl.width();
// console.log("w % this.steps", Math.ceil(relX / (w/this.steps)));
pos = Math.ceil(relX / (w / this.steps)) - 1;
// if (pos<0) pos = 0 ;
this.setValue(pos);
}
this.trigger("change", this, pos);
});
}
setPosition(pos) {
// console.log("pos",pos);
if (pos === false) pos = 0;
if (pos === true) pos = 1;
var w = this.jEl.width();
var l = pos * (w / this.steps) + 3;
if (pos === 0) {
this.jEl.removeClass("step1");
l += this._spaceCursor;
} else {
this.jEl.addClass("step1");
}
// if (pos===0) this.jEl.css('background-color',this.colorLeft) ;
// else this.jEl.css('background-color',this.colorRight) ;
this.jElCursor.transition({ left: l });
}
setValue(val) {
if (val === "") return;
// console.log("this.name,val",this.name,val);
this.previousValue = this.value;
this.value = val;
this.setPosition(val);
}
/**
* @param {Boolean}
* @return {type}
*/
getValue(serialized = false) {
if (this.steps == 2) {
if (this.value == 1) return true;
return false;
}
return this.value;
}
};
/**
* Hidden input form
* @class
* @extends M_.Form.Input
*/
M_.Form.Hidden = class extends M_.Form.Input {
setValue(val) {
if (_.isPlainObject(val)) val = val[this.name];
super.setValue(val);
}
};
/**
* A multi is a combobox + a list of values
* @class
* @extends M_.Form.Input
* @property {type} inputType
* @property {type} confirmDelete
* @property {type} confirmDeleteMessage
* @property {type} value
* @property {type} onClickBtAdd
*/
M_.Form.Multi = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
inputType: "none",
confirmDelete: false,
confirmDeleteMessage: "Etes-vous certain de vouloir supprimer ce mot clé ?",
value: [],
onClickBtAdd: null,
chooseValues: null,
chooseValuesAreNumbers: true
};
opts._idBtAddKeyword = M_.Utils.id();
if (opts.label) opts.label += " <span id='" + opts._idBtAddKeyword + "' class='fa fa-plus faa-pulse animated-hover'>";
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
if (this.value.length) this.setValue(this.value);
$("#" + this._idBtAddKeyword).click(evt => {
evt.stopPropagation();
if (this.onClickBtAdd) this.onClickBtAdd(this, this.value);
else if (this.chooseValues) this.showChooseValues();
});
}
showChooseValues() {
let html = "";
_.each(this.chooseValues, c => {
let idTemp = M_.Utils.id();
let checked = "";
if (_.indexOf(this.value, c.key) >= 0) checked = "checked";
html += `<div class="M_FormMultiItem"><label for="${idTemp}"><input type='checkbox' id='${idTemp}' name='${idTemp}' data-id="${c.key}" ${checked}>${c.val}</label></div>`;
});
this.dd = new M_.Dropdown({
autoShow: true,
alignTo: $("#" + this._idBtAddKeyword),
html: html
});
this.dd.show();
this.dd.jEl.find(".M_FormMultiItem input").change(evt => {
let ids = [];
this.dd.jEl.find(".M_FormMultiItem input:checked").each((ind, el) => {
let id = $(el).attr("data-id");
let v2 = id;
if (!isNaN(id)) v2 = id * 1;
ids.push(v2);
});
this.setValue(ids);
});
}
/**
* @return {type}
*/
drawContainer() {
// log("this.value",this.value,this.name);
this.jEl.empty();
_.each(this.value, val => {
let valtxt = "",
valid = "";
if (this.chooseValues) {
let v2 = val;
if (!isNaN(val)) v2 = val * 1;
let v = _.find(this.chooseValues, { key: v2 });
if (v) {
valid = v.key;
valtxt = v.val;
} else return;
} else {
valid = valtxt = val;
}
let html = `<div class="M_ComboboxMultiItem selected" data-kw-id="${valid}">${valtxt} <span class="fa fa-trash faa-pulse animated-hover"></span></div>`;
let jEl = $(html);
this.jEl.append(jEl);
jEl.find(".fa-trash").click(evt => {
if (this.confirmDelete) {
M_.Dialog.confirm("Confirmation effacement", this.confirmDeleteMessage, () => {
this.removeValue(
$(evt.target)
.parent()
.attr("data-kw-id")
);
});
} else {
this.removeValue(
$(evt.target)
.parent()
.attr("data-kw-id")
);
}
});
});
}
/**
* @param {type}
* @return {type}
*/
removeValue(valtoremove) {
// _.pull(this.value, val);
let oks = [];
_.each(this.value, val => {
if (val != valtoremove) oks.push(val);
});
this.value = oks;
this.drawContainer();
}
/**
* @param {type}
*/
setValue(val) {
this.previousValue = this.value;
if (val === "") val = [];
this.value = val;
this.drawContainer();
}
/**
* @return {type}
*/
getValue() {
return this.value;
}
/**
* @param {type}
*/
addValue(val) {
this.value.push(val);
}
};
/**
* Display stars to rate
* @class
* @extends M_.Form.Input
* @property {type} inputType
* @property {type} value
* @property {type} nbStars
*/
M_.Form.Rating = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
inputType: "none",
value: -1,
nbStars: 5
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
this.jEl.addClass("M_FormRate");
for (var i = 0; i < this.nbStars; i++) {
var el = $("<div class='M_FormRateItem'>" + i + "</div>");
this.jEl.append(el);
el.mouseenter({ note: i }, evt => {
if (this._toExitStar) window.clearTimeout(this._toExitStar);
// log("mouseover", evt.data.note, $(evt.target))
// $(evt.target).addClass('over') ;
for (var i = 0; i < this.nbStars; i++) {
if (i <= evt.data.note * 1) this.jEl.find(".M_FormRateItem:nth-child(" + (i + 1) + ")").addClass("over");
else this.jEl.find(".M_FormRateItem:nth-child(" + (i + 1) + ")").removeClass("over");
}
});
el.mouseleave({ note: i }, evt => {
this._toExitStar = window.setTimeout(() => {
this._toExitStar = null;
this.setValue(this.value);
}, 300);
});
el.click({ note: i }, evt => {
this.setValue(evt.data.note * 1);
});
}
this.drawContainer();
}
/**
* @return {type}
*/
drawContainer() {
// this.jEl.empty() ;
// this.jEl.removeClass('M_FormRate') ;
for (var i = 0; i < this.nbStars; i++) {
if (i <= this.value * 1) this.jEl.find(".M_FormRateItem:nth-child(" + (i + 1) + ")").addClass("over");
else this.jEl.find(".M_FormRateItem:nth-child(" + (i + 1) + ")").removeClass("over");
}
}
/**
* @param {type}
* @return {type}
*/
removeValue(val) {
_.pull(this.value, val);
this.drawContainer();
}
/**
* @param {type}
*/
setValue(val) {
this.previousValue = this.value;
if (val === "") val = 0;
this.value = val;
this.drawContainer();
}
/**
* @return {type}
*/
getValue() {
return this.value;
}
/**
* @param {type}
*/
addValue(val) {
this.value.push(val);
}
};
/**
* Input file form
* @class
* @extends M_.Form.Input
* @property {type} placeholder
* @property {type} inputType
* @property {type} regxValidChar
*/
M_.Form.File = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
placeholder: "",
inputType: "file",
txtChooseFile: "Choisir un fichier",
multiple: false,
regxValidChar: null
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
this._labelButton = $(this.jEl.get(0).nextElementSibling);
this.jEl.on("change", evt => {
let fileName = "";
if (this.jEl.files && this.jEl.files.length > 1)
fileName = (this.jEl.getAttribute("data-multiple-caption") || "").replace("{count}", this.jEl.files.length);
else fileName = evt.target.value.split("\\").pop();
if (fileName) this._labelButton.html(fileName);
else this._labelButton.html(this.txtChooseFile);
this.trigger("change", this, evt);
});
}
/**
* @param {Boolean}
* @return {type}
*/
getValue(serialized = false) {
this.value = this.jEl.val();
// if (this.jEl.get(0).files.length>0) return this.jEl.get(0).files[0] ;
return this.value;
// return this.value ;
}
/**
* @param {type}
*/
setValue(val) {
this.previousValue = this.value;
this.value = val;
}
setLabelButton(txt) {
this._labelButton.html(txt);
}
reset() {
this._labelButton.html(this.txtChooseFile);
}
// reset() {
// if (!this.jEl) return ;
// var par = this.jEl.parent() ;
// this.jEl.remove() ;
// // this.container.empty() ;
//
// var tabindex = "" ;
// var readOnly = "" ;
// if (this.tabindex) tabindex = 'tabindex="'+this.tabindex+'"' ;
// if (!this.editable) readOnly = "readonly" ;
// var multiple = "" ;
// if (this.multiple) multiple = "multiple" ;
// this.jEl = $(`<input ${tabindex} ${readOnly} id="${this.id}" type="${this.inputType}" style="${this.styleInput}" ${multiple} class="M_Input ${this.clsInput}" name="${this.name}" value="" placeholder="${this.placeholder}">`) ;
// par.append(this.jEl) ;
// this.jEl.on('change', (evt)=> {
// this.trigger("change", this, evt) ;
// }) ;
// }
};
/**
* Input text form
* @class
* @extends M_.Form.Input
* @property {type} placeholder
* @property {type} inputType
* @property {type} regxValidChar
*/
M_.Form.Text = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
placeholder: "",
inputType: "text",
regxValidChar: null,
selectOnFocus: false
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
// this.jEl.keypress((evt)=> {
// // log("evt.which",evt.which)
// if (this.jEl.hasClass('M_editablePlaceholder')) {
// this.jEl.removeClass('M_editablePlaceholder') ;
// this.jEl.empty() ;
// }
// if(evt.which == 13) {
// evt.preventDefault() ;
// this.form.edit(false) ;
// this.jEl.blur() ;
// }
// }) ;
// this.jEl.keydown((evt)=> {
// // log("keydownevt.which",evt.which)
// }) ;
this.jEl.keyup(evt => {
if (evt.which == 13) {
if (this.onEnterInput(evt) !== false) {
this.onKeyup(evt);
}
} else this.onKeyup(evt);
});
this.jEl.keypress(evt => {
this.onKeyPress(evt);
});
if (this.selectOnFocus) {
this.jEl.focus(evt => {
evt.preventDefault();
this.jEl.select();
});
}
}
onEnterInput(evt) {}
/**
* @param {type}
* @return {type}
*/
onKeyup(evt) {
// this.value = this.jEl.val() ;
this.trigger("keyup", this, evt);
this.trigger("update", this, this.getValue());
this.valid();
}
/**
* @param {type}
* @return {type}
*/
onKeyPress(evt) {
this.trigger("keypress", this, evt);
if (evt.which == 13) return true;
if (evt.which == 9) return true;
if (evt.which == 8) return true;
if (!this.validChar(String.fromCharCode(evt.which))) {
evt.preventDefault();
evt.stopPropagation();
evt.stopImmediatePropagation();
// this.informValid(2);
return false;
}
}
/**
* @param {type}
* @return {type}
*/
validChar(txt) {
if (this.regxValidChar === null) return true;
var reg = new RegExp(this.regxValidChar);
var ok = reg.test(txt);
return ok;
}
};
M_.Form.Search = class extends M_.Form.Text {
constructor(opts) {
var defaults = {
inputType: "search",
incremental: true
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
if (M_.Utils.isEventSupported("search")) {
this.jEl.on("search", evt => {
this.trigger("search", this, evt);
});
} else {
this.jEl.on("keyup", evt => {
this.trigger("search", this, evt);
});
}
}
};
/**
* Input textarea form
* @class
* @extends M_.Form.Text
* @property {type} inputType
* @property {type} acceptTabulation
* @property {type} rows
*/
M_.Form.Textarea = class extends M_.Form.Text {
constructor(opts) {
var defaults = {
inputType: "textarea",
acceptTabulation: false,
rows: 4
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
if (this.acceptTabulation) {
this.jEl.on("keydown", e => {
var keyCode = e.keyCode || e.which;
if (keyCode == 9) {
e.preventDefault();
var start = this.jEl.get(0).selectionStart;
var end = this.jEl.get(0).selectionEnd;
this.jEl.val(this.jEl.val().substring(0, start) + "\t" + this.jEl.val().substring(end));
this.jEl.get(0).selectionStart = this.jEl.get(0).selectionEnd = start + 1;
}
});
}
}
};
M_.Form.Password = class extends M_.Form.Text {
constructor(opts) {
var defaults = {
inputType: "password",
checkstrength: true
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
if (this.checkstrength) {
this.jElPass = $("<div class='M_DivPassword'>OK</div>");
this.jElPass.hide();
this.container.append(this.jElPass);
this.container.css("position", "relative");
this.jEl.on("keyup", e => {
var pos = this.jEl.offset();
this.jElPass.show();
var score = this.checkPassword();
this.jElPass.removeClass("bg_col3 bg_col5 bg_col2");
if (score < 2) {
this.jElPass.addClass("bg_col3").html("Mauvais");
} else if (score < 4) {
this.jElPass.addClass("bg_col5").html("Moyen");
} else {
this.jElPass.addClass("bg_col2").html("Bon");
}
this.jElPass
.offset({
top: pos.top,
left: pos.left + this.jEl.outerWidth() - this.jElPass.outerWidth()
})
.outerHeight(this.jEl.outerHeight());
});
}
}
checkPassword() {
var v = this.jEl.val();
var err = [];
var score = 0;
if (v.length < this.minLength) {
err.push(this.minLength + " char minimum");
} else score++;
var re = /[0-9]/;
if (!re.test(v)) {
err.push("password must contain at least one number (0-9)!");
} else score++;
re = /[a-z]/;
if (!re.test(v)) {
err.push("password must contain at least one lowercase letter (a-z)!");
} else score++;
re = /[A-Z]/;
if (!re.test(v)) {
err.push("password must contain at least one uppercase letter (A-Z)!");
} else score++;
return score;
}
};
/**
* A wysiwyg editor
* @class
* @extends M_.Form.Textarea
* @property {type} heightEditor
*/
M_.Form.TextEditor = class extends M_.Form.Textarea {
constructor(opts) {
var defaults = {
heightEditor: 200
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
}
/**
* @return {type}
*/
create() {
super.create();
// var h = this.jEl.height() ;
this.jEl.hide();
var idTemp1 = M_.Utils.id();
var idTemp2 = M_.Utils.id();
this.jEl.after("<div id='" + idTemp1 + "'></div><div><div class='M_FormEditor-Content' id='" + idTemp2 + "'></div></div>");
$("#" + idTemp2).height(this.heightEditor);
this.editor = new M_.Editor({
buttonsContainer: $("#" + idTemp1),
container: $("#" + idTemp2)
});
}
/**
* @param {type}
*/
setValue(val) {
this.previousValue = this.value;
this.value = val;
this.jEl.val(val);
this.editor.container.html(val);
}
/**
* @return {type}
*/
getValue() {
var val = this.editor.container.html();
this.jEl.val(val);
return val;
}
/**
* @return {type}
*/
getEditor() {
return this.editor;
}
};
/**
* A radio buttons group
* @class
* @extends M_.Form.Input
* @property {type} inputType
* @property {type} radioPosition
* @property {type} items
*/
M_.Form.RadioGroup = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
inputType: "radiogroup",
radioPosition: "inline", // inline | col
items: []
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
}
/**
* @return {type}
*/
create() {
// var id1 = M_.Utils.id(),
// id2 = M_.Utils.id() ;
if (M_.Utils.isEmpty(this.id)) this.id = M_.Utils.id();
var v = this.value;
if ($.type(v) !== "string") v = "";
if ((this.labelPosition == "left" || this.labelPosition == "right") && this.label !== "") {
this.clsLabel += " M_LabelLeft";
this.clsInput += " M_InputLeft";
}
if (
this.labelWidth > 0 &&
this.label !== "" &&
(this.labelPosition == "left" || this.labelPosition == "right") &&
this.inputType != "checkbox"
) {
if (this.labelWidth <= 1) {
this.styleLabel += " width:" + this.labelWidth * 100 + "%;";
this.styleInput += " width:" + (100 - this.labelWidth * 100) + "%;";
} else {
this.styleLabel += " width:" + this.labelWidth + "px;";
this.styleInput += " width:calc(100% - " + this.labelWidth + "px);";
}
}
var html = "";
var readOnly = "";
var forattr = "";
if (this.labelFocusInput) forattr = 'for="' + this.id + '"';
if (!this.editable) readOnly = "readonly";
// var html = `<div class='M_HorizontalForm'></div></div>` ;
html += `<div style="${this.styleGroup}" class="M_FormGroup ${this.clsGroup}">`;
if (this.label !== "" && (this.labelPosition == "left" || this.labelPosition == "top"))
html += `<label style="${this.styleLabel}" class="${this.clsLabel}" ${forattr}>${this.label}</label>`;
html += `<div id="${this.id}" type="${this.inputType}" style="${this.styleInput}" class="M_Input ${this.clsInput}">`;
var nameTemp = M_.Utils.id();
_.each(this.items, item => {
let idTemp = M_.Utils.id();
html += `<div class="M_RadioGroupItem ${this
.radioPosition}"><input type='radio' name="${nameTemp}" id="${idTemp}" value="${item.key}" class="M_InputRight"/><label for="${idTemp}" class="M_LabelRight">${item.val}</label></div>`;
});
html += `</div></div>`;
this.container.append(html);
this.jEl = this.container.find(".M_HorizontalForm");
this.container.find("input").click(evt => {
this.trigger("change", this, this.getValue(), evt);
});
}
/**
* @param {type}
*/
setValue(val) {
this.previousValue = this.value;
this.value = val;
this.container.find("input").each((ind, el) => {
if (el.value == this.value) $(el).prop("checked", true);
else $(el).prop("checked", false);
});
}
/**
* @return {type}
*/
getValue() {
var v = this.container.find("input:checked").val();
return v;
}
};
/**
* Input checkbox form
* @class
* @extends M_.Form.Input
* @property {type} inputType
* @property {type} labelWidth
* @property {type} labelFocusInput
* @property {type} labelPosition
*/
M_.Form.Checkbox = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
inputType: "checkbox",
labelWidth: 100,
labelFocusInput: true,
labelPosition: "right",
threeStates: false,
indeterminate: false,
previousValue: false
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
if (this.threeStates) {
var v = 0;
if (this.value) v = 1;
if (this.indeterminate) v = 2;
this.jEl.data("checked", v);
if (v === 2) this.jEl.prop("indeterminate", true);
}
this.jEl.change(evt => {
if (this.indeterminate) {
switch (this.jEl.data("checked")) {
// unchecked, going indeterminate
case 0:
this.jEl.data("checked", 1);
this.jEl.prop("indeterminate", true);
break;
// indeterminate, going checked
case 1:
this.jEl.data("checked", 2);
this.jEl.prop("indeterminate", false);
this.jEl.prop("checked", true);
break;
// checked, going unchecked
default:
this.jEl.data("checked", 0);
this.jEl.prop("indeterminate", false);
this.jEl.prop("checked", false);
}
}
this.trigger("change", this, this.getValue(), evt);
});
if (this.indeterminate) this.jEl.prop("indeterminate", true);
}
/**
* @param {type}
*/
setValue(val) {
// console.log("val", val);
if (val === "" || val === "false" || val === "0" || val === 0) val = false;
if (val === "true" || val === "1" || val === 1) val = true;
this.previousValue = this.value;
this.value = val;
if (val) this.jEl.prop("checked", true);
else this.jEl.prop("checked", false);
if (this.threeStates) {
var v = 0;
if (val) v = 1;
if (val == 2) v = 2;
this.jEl.data("checked", v);
if (v === 2) this.jEl.prop("indeterminate", true);
else this.jEl.prop("indeterminate", false);
}
}
imcheckbox() {
return "ok";
}
/**
* @param {Boolean}
* @return {type}
*/
getValue(serialized = false) {
if (this.threeStates) {
if (this.jEl.prop("indeterminate")) return 2;
}
return this.jEl.is(":checked");
}
};
/**
* Input number form ; only number are accepted
* @class
* @extends M_.Form.Text
* @property {type} decimalLength
* @property {type} decimalSeparator
* @property {type} decimalForced
* @property {type} allowNegative
* @property {type} valueMax
* @property {type} valueMin
* @property {type} startEmpty
* @property {type} allowComparison allow signs : > or < or = or >= or <=
*/
M_.Form.Number = class extends M_.Form.Text {
constructor(opts) {
var defaults = {
// inputType: 'number',
decimalLength: 0,
decimalSeparator: ",",
decimalForced: false,
allowNegative: true,
valueMax: null,
valueMin: null,
startEmpty: false,
allowComparison: false
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
}
// onKeyup(evt) {}
/**
* @param {type}
* @return {type}
*/
validChar(txt) {
var re = "\\d";
if (this.allowNegative) re += "|\\-";
if (this.decimalLength > 0) re += "|\\" + this.decimalSeparator;
if (this.allowComparison) re += "|>|<|=";
this.regxValidChar = re;
var ok = super.validChar(txt);
if (this.maxLength && (this.jEl.val() + "").length >= this.maxLength) ok = false;
return ok;
}
/**
* @param {type}
*/
setValue(v) {
// log("setValue",v)
if (this.startEmpty && (v === "" || v === null || typeof v === "undefined")) return this.jEl.val("");
v = v * 1;
this.value = v;
var v2 = v;
v2 = (v2 + "").replace(".", this.decimalSeparator);
if (this.decimalForced) {
let dec = v % 1,
nbDec = Math.pow(10, this.decimalLength),
dec2 = Math.floor(dec * nbDec);
dec2 = M_.Utils.str_pad(dec2, this.decimalLength, "0", -1);
// log("dec",dec, nbDec, dec2)
v2 = Math.floor(v) + this.decimalSeparator + dec2;
}
this.jEl.val(v2);
}
/**
* @param {Boolean}
* @return {type}
*/
getValue(serialized = false) {
if (this.startEmpty && this.jEl.val() === "") return null;
var v = (this.jEl.val() + "").replace(this.decimalSeparator, ".");
this.value = v * 1;
if (this.allowComparison) this.value = v;
return this.value;
}
};
/**
* Input price form ; only number are accepted
* @class
* @extends M_.Form.Number
* @property {type} decimalLength
* @property {type} decimalSeparator
* @property {type} decimalForced
* @property {type} allowNegative
*/
M_.Form.Price = class extends M_.Form.Number {
constructor(opts) {
var defaults = {
decimalLength: 2,
decimalSeparator: ",",
decimalForced: true,
allowNegative: true
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
}
};
/**
* Input color form ; TO implement !!!!
* @class
* @extends M_.Form.Input
* @property {type} inputType
*/
M_.Form.Color = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
inputType: "color"
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
}
};
/**
* A base class to add a picker to input text form
* @class
* @extends M_.Form.Text
* @property {type} hidePicker
* @property {type} showDropdownOnFocus
* @property {type} icon
* @property {type} containerDropdown
*/
M_.Form.Picker = class extends M_.Form.Text {
constructor(opts) {
var defaults = {
alwaysDropdownBelow: false,
hasDropdown: true,
hidePicker: false,
showDropdownOnFocus: true,
icon: "fa fa-caret-down",
containerDropdown: null,
_clsMore: "M_Combobox",
stylePicker: "",
dropdownOpts: {},
dropdown: null
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
if (!optsTemp.containerDropdown) optsTemp.containerDropdown = optsTemp.container;
optsTemp.containerDropdown = "body";
// log("optsTemp.containerDropdown",optsTemp.containerDropdown,optsTemp.container)
super(optsTemp);
if (this.showDropdownOnFocus && this.hasDropdown) {
this._sameEventEventClick = false;
this.jEl.on("click", evt => {
// focus
// console.log("this._sameEventEventClick", this._sameEventEventClick);
if (!this._sameEventEventClick) this.showDropdown(evt);
this._sameEventEventClick = true;
M_.Utils.delay(
() => {
this._sameEventEventClick = false;
},
200,
"_sameEventEventClick"
);
});
}
}
/**
* @return {type}
*/
createDropdown() {
if (this.dropdown) return;
if (!this.hasDropdown) return;
// console.log("createDropdown");
var optsTemp = $.extend(
{},
{
alignTo: this.jEl,
autoShow: false,
destroyOnHide: true,
container: this.containerDropdown,
alwaysDropdownBelow: this.alwaysDropdownBelow,
listeners: [
[
"hide",
() => {
this.dropdown = null;
}
]
]
},
this.dropdownOpts
);
this.dropdown = new M_.Dropdown(optsTemp);
}
/**
* @return {Boolean}
*/
isDropdownVisible() {
if (!this.dropdown) return false;
return this.dropdown.isVisible();
}
/**
* @param {type}
* @return {type}
*/
showDropdown(evt) {
// log("showDropdown")
M_.Help.hideMHelp();
if (!this.hasDropdown) return;
evt.stopPropagation();
if (!this.dropdown) this.createDropdown();
this.dropdown.show();
}
/**
* @param {type}
* @return {type}
*/
clickPicker(evt) {
this.showDropdown(evt);
this.trigger("clickpicker", this, evt);
}
/**
* @return {type}
*/
create() {
super.create();
if (!this.hidePicker) {
var moreCls = "",
moreStyle = "";
if ((this.labelPosition == "left" || this.labelPosition == "right") && this.label !== "") {
moreCls += " M_InputLeft";
moreStyle += " width:calc(100% - " + this.labelWidth + "px);";
}
this.jEl.outerWidth("100%");
this.jEl.removeClass("M_InputLeft");
this.jEl.wrap("<div class='M_FormInputGroup " + moreCls + "' style='" + moreStyle + "'>");
var htmlCaret = `<div class="M_FormCaret ${this.icon}" style='${this.stylePicker}'></div>`;
this.caret = $(htmlCaret);
this.container.find(".M_FormInputGroup").append(this.caret);
this.caret.click(evt => {
this.clickPicker(evt);
});
}
}
};
/**
* Input date form ; display a date picker and a month view
* @class
* @extends M_.Form.Picker
* @property {type} icon
* @property {type} dateFormat
* @property {type} dateFormatInput
*/
M_.Form.Date = class extends M_.Form.Picker {
constructor(opts) {
var defaults = {
icon: "fa fa-calendar",
dateFormat: "DD/MM/YYYY",
dateFormatInput: "YYYY-MM-DD",
noDays: false,
showWeekNumber: false,
selectWeek: false,
disabledDates: null
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
// if (!optsTemp.value) optsTemp.value = moment() ;
super(optsTemp);
// this.jEl.click((evt)=> {
// evt.stopPropagation() ;
// // if (!this.dropdown) this.createDropdown() ;
// // this.dropdown.show() ;
// })
// this.jEl.on('M_DropdownShow', (evt)=> {
// if (!this.dropdown) this.createDropdown() ;
// this.dropdown.show() ;
// }) ;
}
/**
* @return {type}
*/
createDropdown() {
this.dropdown = new M_.Dropdown({
destroyOnHide: true,
alignTo: this.jEl,
container: this.containerDropdown,
autoSize: false,
alwaysDropdownBelow: this.alwaysDropdownBelow,
listeners: [
[
"destroy",
() => {
this.dropdown = null;
this.calendar.destroy();
}
]
]
});
this.calendar = new M_.CalendarMonth({
controller: this,
container: this.dropdown.jEl,
noDays: this.noDays,
showWeekNumber: this.showWeekNumber,
selectWeek: this.selectWeek,
disabledDates: this.disabledDates,
listeners: [
[
"selected",
(cal, date) => {
// log("date",cal, date)
// this.container.html(date.format('DD/MM/YYYY')) ;
this.setValue(date);
this.trigger("change", this, date);
this.dropdown.hide();
}
],
[
"viewedChanged",
() => {
this.dropdown.realign();
}
]
]
});
}
/**
* @param {type}
* @return {type}
*/
showDropdown(evt) {
super.showDropdown(evt);
if (moment.isMoment(this.value) && this.calendar) {
this.calendar.setDateViewed(this.value);
this.calendar.setDateSelected(this.value);
}
}
/**
* @param {type}
* @return {type}
*/
onKeyup(evt) {
super.onKeyup(evt);
if (this.isDropdownVisible()) {
var m = moment(this.jEl.val(), this.dateFormat);
if (m.isValid()) {
this.value = m;
if (this.calendar) {
this.calendar.setDateViewed(this.value);
this.calendar.setDateSelected(this.value);
}
}
}
}
/**
* @param {type}
*/
setValue(val) {
if (val === "" || !val || val == "0000-00-00" || val == "0000-00-00 00:00:00") val = "";
else if (moment.isMoment(val)) {
if (!val.isValid()) val = "";
// else val = val.format('YYYY-MM-DD') ;
} else val = moment(val, this.dateFormatInput);
this.value = val;
// if (val!=='') log("format",val,val.format(this.dateFormat))
if (val !== "") {
this.jEl.val(val.format(this.dateFormat));
} else this.jEl.val(val);
}
/**
* @param {Boolean}
* @return {type}
*/
getValue(serialized = false) {
var v = this.jEl.val();
if (v !== "") {
v = moment(v, this.dateFormat).startOf("day");
if (!serialized) return v;
v = v.format("YYYY-MM-DD");
}
return v;
}
};
M_.Form.DateWeek = class extends M_.Form.Date {
constructor(opts) {
var defaults = {
// icon: 'fa fa-clock-o',
dateFormat: "YYYY-\\SWW",
dateFormatInput: "YYYY-MM-DD",
showWeekNumber: true,
selectWeek: true
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
}
};
/**
* Input hour form ; display a date picker and a hour view
* @class
* @extends M_.Form.Date
* @property {type} icon
* @property {type} dateFormat
* @property {type} dateFormatInput
* @property {type} incrementHour
* @property {type} incrementMinute
* @property {type} incrementSecond
*/
M_.Form.Hour = class extends M_.Form.Date {
constructor(opts) {
var defaults = {
icon: "fa fa-clock-o",
dateFormat: "HH:mm",
dateFormatInput: "HH:mm",
incrementHour: 1,
incrementMinute: 10,
incrementSecond: 5
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
}
/**
* @return {type}
*/
createDropdown() {
// log("createDropdown")
var html = `
<div class="">
<div class="M_Col6 M_FormHour_hour_up"><span class="fa fa-chevron-up"></span></div>
<div class="M_Col6 M_FormHour_minute_up"><span class="fa fa-chevron-up"></span></div>
<div class="M_Col6 M_FormHour_second_up"><span class="fa fa-chevron-up"></span></div>
<div class="M_Clear"></div>
<div class="M_Col6"><input type="text" class="form-control M_FormHour_hour" placeholder="" maxlength="2"></div>
<div class="M_Col6"><input type="text" class="form-control M_FormHour_minute" placeholder="" maxlength="2"></div>
<div class="M_Col6"><input type="text" class="form-control M_FormHour_second" placeholder="" maxlength="2"></div>
<div class="M_Clear"></div>
<div class="M_Col6 M_FormHour_hour_down"><span class="fa fa-chevron-down"></span></div>
<div class="M_Col6 M_FormHour_minute_down"><span class="fa fa-chevron-down"></span></div>
<div class="M_Col6 M_FormHour_second_down"><span class="fa fa-chevron-down"></span></div>
<div class="M_Clear"></div>
<div>
</div>
`;
this.dropdown = new M_.Dropdown({
destroyOnHide: true,
alignTo: this.jEl,
autoSize: false,
html: html,
listeners: [
[
"destroy",
() => {
this.dropdown = null;
}
]
]
});
this.jElDropdown = this.dropdown.jEl;
this.value = moment();
// log("this.value",this.value)
if (this.jEl.val().length > 0) this.value = moment(this.jEl.val(), this.dateFormat);
this.setToCurrent();
if (this.dateFormat.indexOf("s") < 0) {
this.jElDropdown.find(".M_FormHour_second_up, .M_FormHour_second_down").hide();
this.jElDropdown
.find(".M_FormHour_second")
.parent()
.hide();
this.jElDropdown
.find(".col-lg-4")
.removeClass("col-lg-4")
.addClass("col-lg-6");
this.jElDropdown.find(".dropdown-menu").addClass("short");
}
this.jElDropdown.find(".M_FormHour_hour_up").off("click");
this.jElDropdown.find(".M_FormHour_hour_up").on(
"click",
$.proxy(function(evt) {
// log("this.value",this.value)
var m = this.value.hours() + this.incrementHour;
m = Math.floor(m / this.incrementHour) * this.incrementHour;
this.value.hours(m);
this.setToCurrent();
this.setValue(this.value);
}, this)
);
this.jElDropdown.find(".M_FormHour_minute_up").off("click");
this.jElDropdown.find(".M_FormHour_minute_up").on(
"click",
$.proxy(function(evt) {
var m = this.value.minutes() + this.incrementMinute;
m = Math.floor(m / this.incrementMinute) * this.incrementMinute;
this.value.minutes(m);
this.setToCurrent();
this.setValue(this.value);
}, this)
);
this.jElDropdown.find(".M_FormHour_second_up").off("click");
this.jElDropdown.find(".M_FormHour_second_up").on(
"click",
$.proxy(function(evt) {
var m = this.value.seconds() + this.incrementSecond;
m = Math.floor(m / this.incrementSecond) * this.incrementSecond;
this.value.seconds(m);
this.setToCurrent();
this.setValue(this.value);
}, this)
);
this.jElDropdown.find(".M_FormHour_hour_down").off("click");
this.jElDropdown.find(".M_FormHour_hour_down").on(
"click",
$.proxy(function(evt) {
var m = this.value.hours() - this.incrementHour;
m = Math.floor(m / this.incrementHour) * this.incrementHour;
this.value.hours(m);
this.setToCurrent();
this.setValue(this.value);
}, this)
);
this.jElDropdown.find(".M_FormHour_minute_down").off("click");
this.jElDropdown.find(".M_FormHour_minute_down").on(
"click",
$.proxy(function(evt) {
var m = this.value.minutes() - this.incrementMinute;
m = Math.floor(m / this.incrementMinute) * this.incrementMinute;
this.value.minutes(m);
this.setToCurrent();
this.setValue(this.value);
}, this)
);
this.jElDropdown.find(".M_FormHour_second_down").off("click");
this.jElDropdown.find(".M_FormHour_second_down").on(
"click",
$.proxy(function(evt) {
var m = this.value.seconds() - this.incrementSecond;
m = Math.floor(m / this.incrementSecond) * this.incrementSecond;
this.value.seconds(m);
this.setToCurrent();
this.setValue(this.value);
}, this)
);
this.jElDropdown.find(".dropdown-menu").show();
}
/**
* To document
*/
setToCurrent() {
this.jElDropdown.find(".M_FormHour_hour").val(this.value.format("HH"));
this.jElDropdown.find(".M_FormHour_minute").val(this.value.format("mm"));
this.jElDropdown.find(".M_FormHour_second").val(this.value.format("ss"));
}
getValue() {
var v = this.jEl.val();
var t = v.split(":");
var m = moment()
.hours(t[0])
.minutes(t[1])
.seconds(0);
// console.log("v",v,m,t);
return m;
}
setValue(val) {
var val2;
// console.log("val",val,moment().utcOffset());
if (!val) val = "00:00";
if (typeof val == "string") {
var v = "";
if (val.length > 11) v = val.substring(11, 16);
else v = val;
var t = v.split(":");
val2 = moment()
.hours(t[0])
.minutes(t[1])
.seconds(0); //.add(moment().utcOffset(),'minutes')
} else val2 = moment(val); //.add(moment().utcOffset(),'minutes')
this.jEl.val(val2.format(this.dateFormat));
this.value = val2;
}
};
/**
* Input datehour form ; display 2 pickers for date and hour
* @class
* @extends M_.Form.Input
* @property {type} configDateDef
* @property {type} configHourDef
*/
M_.Form.DateHour = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
configDateDef: {
label: "",
// labelPosition: 'left',
// labelWidth: 3,
name: "",
containerWidth: "64%",
required: true
},
configHourDef: {
label: "",
name: "",
containerWidth: "36%",
incrementMinute: 15,
required: false
}
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
// if ((this.labelPosition=='left' || this.labelPosition=='right') && this.labelWidth===0) this.labelWidth = 0.3 ;
}
/**
* @return {type}
*/
create() {
var id1 = M_.Utils.id(),
id2 = M_.Utils.id();
if (M_.Utils.isEmpty(this.id)) this.id = M_.Utils.id();
var v = this.value;
if ($.type(v) !== "string") v = "";
if ((this.labelPosition == "left" || this.labelPosition == "right") && this.label !== "") {
this.clsLabel += " M_LabelLeft";
this.clsInput += " M_InputLeft";
}
if (
this.labelWidth > 0 &&
this.label !== "" &&
(this.labelPosition == "left" || this.labelPosition == "right") &&
this.inputType != "checkbox"
) {
if (this.labelWidth <= 1) {
this.styleLabel += " width:" + this.labelWidth * 100 + "%;";
this.styleInput += " width:" + (100 - this.labelWidth * 100) + "%;";
} else {
this.styleLabel += " width:" + this.labelWidth + "px;";
this.styleInput += " width:calc(100% - " + this.labelWidth + "px);";
}
}
var html = "";
var readOnly = "";
var forattr = "";
if (this.labelFocusInput) forattr = 'for="' + this.id + '"';
if (!this.editable) readOnly = "readonly";
// var html = `<div class='M_HorizontalForm'></div></div>` ;
html += `<div style="${this.styleGroup}" class="M_FormGroup ${this.clsGroup}">`;
if (this.label !== "" && (this.labelPosition == "left" || this.labelPosition == "top"))
html += `<label style="${this.styleLabel}" class="${this.clsLabel}" ${forattr}>${this.label}</label>`;
html += `<div id="${this.id}" type="${this.inputType}" style="${this.styleInput}" class="M_Input ${this
.clsInput}"><div id='${id1}' class='M_FloatLeft'></div><div id='${id2}' class='M_FloatLeft'></div>`;
html += `</div>`;
this.container.append(html);
this.jEl = this.container.find(".M_HorizontalForm");
var configDate = $.extend({}, this.configDateDef, this.configDate);
var configHour = $.extend({}, this.configHourDef, this.configHour);
configDate.container = $("#" + id1);
configHour.container = $("#" + id2);
$("#" + id1).width(configDate.containerWidth);
$("#" + id2).width(configHour.containerWidth);
this.formDate = new M_.Form.Date(configDate);
this.formHour = new M_.Form.Hour(configHour);
}
/**
* @param {type}
*/
setValue(val) {
this.formDate.setValue(val);
this.formHour.setValue(val);
}
/**
* @param {Boolean}
* @return {type}
*/
getValue(serialized = false) {
var d1 = this.formDate.getValue();
if (d1 !== "") {
var d = moment(d1);
var d2 = this.formHour.getValue();
// console.log("d2",d2,d2.hours(),d2.minutes(),d2.seconds(),d2.format('HH:mm'),d2.utcOffset());
if (d2) {
// d2.local() ;
// log(".utcOffset()",d2.utcOffset())
d.hours(d2.hours());
d.minutes(d2.minutes()); //+d2.utcOffset()
d.seconds(d2.seconds());
}
return d;
}
return d1;
}
};
/**
* Input combobox form ; display a comobobox
* @class
* @extends M_.Form.Picker
* @property {type} store
* @property {type} modelKey
* @property {type} modelValue
* @property {type} mode
* @property {type} editable
* @property {type} useRawValue
* @property {type} useZeroIfEmpty
*/
M_.Form.Combobox = class extends M_.Form.Picker {
constructor(opts) {
var defaults = {
store: null,
modelKey: "key",
modelValue: "val",
mode: "local",
editable: true,
useRawValue: false,
useZeroIfEmpty: false
// inputType: 'text',
// _currentVal: ''
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
if (this.store.url === "") this.mode = "local";
else this.mode = "remote";
if (this.mode == "remote") {
this.store.addListener("load", (store, models) => {
// log("models", models, this.modelValue)
this.createDropdown();
this._createItemsFromStore();
this.dropdown.show();
});
}
// this.jEl.on('M_DropdownShow', (evt)=> {
// if (!this.dropdown) this.createDropdown() ;
// this.showDropdown() ;
// }) ;
if (!this.editable) {
this.jEl.css("cursor", "pointer");
this.jEl.on("click", evt => {
this.showDropdown(evt);
});
}
}
_createItemsFromStore() {
// log("_createItemsFromStore",this.store)
var items = [];
this.store.each(model => {
// var val = "" ;
// if (typeof this.modelValue === 'function') val = this.modelValue(model) ;
// else val = model.get(this.modelValue) ;
items.push({
text: this._getVal(model),
click: $.proxy(this.clickItemDropdown, this, model.get(this.modelKey))
});
});
// if (!this.dropdown)
this.createDropdown();
this.dropdown.setItems(items);
}
/**
* @return {type}
*/
create() {
super.create();
// this.caret = $('<span class="fa fa-caret-down M_caret M_showonedit"></span>') ;
// this.container.find('.M_editable').after(this.caret);
// this.caret.click($.proxy(this.showDropdown, this)) ;
}
/**
* @param {type}
* @return {type}
*/
showDropdown(evt) {
M_.Help.hideMHelp();
if (evt) evt.stopPropagation();
if (this.disabled) return;
// if (!this.dropdown)
if (this.mode == "local") {
this.createDropdown();
this._createItemsFromStore();
this.dropdown.show();
} else if (this.store.load()) {
// this.createDropdown() ;
}
}
/**
* @param {type}
* @return {type}
*/
clickItemDropdown(val) {
this.setValue(val);
let model = this.store.getRow(val);
if (model) {
this.trigger("itemclick", this, model);
} else {
this.trigger("itemclick", this, val);
}
}
/**
* @return {type}
*/
getRawValue() {
// return this.container.find('M_editable').text() ;
return this.jEl.val();
}
/**
* @return {type}
*/
getValue() {
if (this.useRawValue) return this.getRawValue();
if (this.getRawValue() === "") this.value = "";
if (this.value === "" && this.useZeroIfEmpty) return 0;
return this.value;
}
/**
* @return {type}
*/
getValueAndRaw() {
return { key: this.value, val: this.getRawValue() };
}
_getVal(model) {
var val = "";
if (typeof this.modelValue === "function") val = this.modelValue(model);
else val = model.get(this.modelValue);
return val;
}
/**
* @param {type}
*/
setValue(val) {
// log("setValue",this.name, val)
if (val === null) val = "";
if (typeof val === "object") {
let model = new this.store.model({ row: val });
// log("setValue créé",model)
this.setValueAndRawValue(val[this.modelKey], this._getVal(model));
this.trigger("update", this, model);
} else {
this.value = val;
let model = this.store.getRow(val);
if (model) {
this.setValueAndRawValue(val, this._getVal(model));
this.trigger("update", this, model);
} else {
this.setRawValue(val);
this.trigger("update", this, val);
}
}
}
/**
* @param {type}
* @param {type}
*/
setValueAndRawValue(val, rawVal) {
this.value = val;
this.setRawValue(rawVal);
}
/**
* @param {type}
*/
setRawValue(val) {
var v = val;
v = M_.Utils.strip_tags(v);
v = M_.Utils.html_entity_decode(v);
v = M_.Utils.trim(v);
this.jEl.val(v);
}
/**
* @param {type}
* @return {type}
*/
onKeyup(evt) {
// log("this.keyField",this.keyField)
var v = this.getRawValue();
if (this.mode == "local") {
this.showDropdown();
var items = [];
this.store.each(model => {
// log("model",model)
var val = this._getVal(model).toLowerCase();
if (val.indexOf(v.toLowerCase()) >= 0) {
items.push({
text: this._getVal(model),
click: $.proxy(this.clickItemDropdown, this, model.get(this.modelKey))
});
}
});
this.dropdown.setItems(items);
} else {
this.store.load({ query: v.toLowerCase() });
}
this.trigger("keyup", this, evt);
}
};
/**
* Input combobox form ; display a comobobox + a list of values
* @class
* @extends M_.Form.Combobox
*/
M_.Form.ComboboxMulti = class extends M_.Form.Combobox {
constructor(opts) {
var defaults = {
allowCreate: true
};
opts = opts ? opts : {};
var optsTemp = $.extend({}, defaults, opts);
super(optsTemp);
this.jElForMulti = $("<div></div>");
this.container.append(this.jElForMulti);
this.container.append("<div class='M_Clear'></div>");
}
onEnterInput() {
if (this.allowCreate) {
this.addItemMulti("", this.getRawValue());
this.setValueAndRawValue("", "");
}
}
/**
* @param {type}
* @return {type}
*/
clickItemDropdown(val) {
var model = this.store.getRow(val);
var realval = this._getVal(model);
// super.clickItemDropdown(val);
// this.setValueAndRawValue('', '') ;
// this.addItemMulti(val, realval) ;
// this.setValue(val) ;
// let model = this.store.getRow(val) ;
// if (model) {
// this.trigger('itemclick', this, model) ;
// } else {
// this.trigger('itemclick', this, val) ;
// }
this.setRawValue("");
this.addItemMulti(val, realval);
}
/**
* @param {type}
* @param {type}
*/
addItemMulti(val, realval, deletable) {
var idTemp = M_.Utils.id();
var html =
"<div class='M_ComboboxMultiItem selected' data-comboboxmultiitem=\"" + val + '" data-comboboxmultiitemval="' + realval + '">' + realval;
if (deletable !== false) html += " <span class='fa fa-trash faa-pulse animated-hover' id='" + idTemp + "'></span>";
html += " </div>";
this.jElForMulti.append(html);
if (deletable !== false) {
$("#" + idTemp).click(evt => {
$(evt.target)
.closest(".M_ComboboxMultiItem")
.remove();
this.trigger("update", this, val);
});
}
}
/**
* @return {type}
*/
getValue() {
var res = [];
var me = this;
this.jElForMulti.find(".M_ComboboxMultiItem").each(function() {
var obj = {};
obj[me.modelKey] = $(this).attr("data-comboboxmultiitem");
obj[me.modelValue] = $(this).attr("data-comboboxmultiitemval");
res.push(obj);
});
return res;
}
setValue(val) {
this.reset();
_.each(val, v => {
this.addItemMulti(v[this.modelKey], v[this.modelValue], v.deletable);
});
}
/**
* @return {type}
*/
reset() {
if (this.jElForMulti) this.jElForMulti.empty();
}
/**
* @return {type}
*/
getValueAndRaw() {
var res = [];
this.jElForMulti.find(".M_ComboboxMultiItem").each(function() {
res.push({
val: $(this).attr("data-comboboxmultiitemval"),
key: $(this).attr("data-comboboxmultiitem")
});
});
return res;
}
};
/**
* A simple div to use in forms (no label)
* @class
* @property {type} html
*/
M_.Form.Div = class {
constructor(opts) {
var defaults = {
html: ""
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
this.container.append(this.html);
}
};
/**
* A simple div with label
* @class
* @extends M_.Form.Input
* @property {type} inputType
*/
M_.Form.Display = class extends M_.Form.Input {
constructor(opts) {
var defaults = {
inputType: "none"
};
opts = opts ? opts : {};
opts = $.extend({}, defaults, opts);
super(opts);
}
/**
* @param {type}
*/
setValue(val) {
// if (val=="") val=[] ;
this.value = val;
this.jEl.html(val);
}
/**
* @return {type}
*/
getValue() {
return this.value;
}
};
/**
* A wysiwyg editor to use directly or in a textarea
* @class
* @property {type} buttonsContainer
* @property {type} buttons
*/
M_.Editor = class {
constructor(opts) {
var defaults = {
buttonsContainer: null,
buttonsToDisplay: [
"formatBlock",
"bold",
"italic",
"justifyLeft",
"justifyCenter",
"justifyRight",
"justifyFull",
"list",
"removeFormat",
"|",
"createLink",
"table",
"image",
"templates",
"|",
"source"
],
buttons: [
[
"formatBlock",
"Fomater paragraphe",
"fa-font",
"",
"",
[
["formatH1", "H1", "", "execFormatBlock", "H1"],
["formatH2", "H2", "", "execFormatBlock", "H2"],
["formatH3", "H3", "", "execFormatBlock", "H3"],
["formatH4", "H4", "", "execFormatBlock", "H4"],
["formatH5", "H5", "", "execFormatBlock", "H5"],
["formatP", "P", "", "execFormatBlock", "P"],
["formatDIV", "DIV", "", "execFormatBlock", "DIV"]
]
],
["bold", "Gras", "fa-bold", "execCommand", "bold"],
["italic", "Italique", "fa-italic", "execCommand", "italic"],
["justifyLeft", "Justifier à gauche", "fa-align-left", "execJustify", "Left"],
["justifyCenter", "Justifier au centre", "fa-align-center", "execJustify", "Center"],
["justifyRight", "Justifier à droite", "fa-align-right", "execJustify", "Right"],
["justifyFull", "Justifier à gauche et à droite", "fa-align-justify", "execJustify", "Full"],
[
"list",
"Liste numéroté ou avec puces",
"fa-list-ol",
"execCommand",
"",
[
["insertorderedlist", "Liste numéroté", "fa-list-ol", "execCommand", "insertorderedlist"],
["insertunorderedlist", "Liste avec puces", "fa-list-ul", "execCommand", "insertunorderedlist"],
["outdent", "Outdent", "fa-outdent", "execCommand", "outdent"],
["indent", "Indent", "fa-indent", "execCommand", "indent"]
]
],
["removeFormat", "Effacer", "fa-eraser", "execCommand", "removeFormat"],
"|",
[
"createLink",
"Lien",
"fa-link",
"",
"",
[
["insertorderedlist", "Créer un lien", "fa-link", "execLink", ""],
["insertunorderedlist", "Enlever le lien", "fa-unlink", "execCommand", "unlink"]
]
],
[
"table",
"Outils tableau",
"fa-table",
"",
"",
[
["insertTable", "Insérer un tableau", "", "execInsertTable"],
["insertTableColumn", "Nouvelle colonne", "", "execInsertTableColumn"],
["insertTableRow", "Nouvelle rangée", "", "execInsertTableRow"],
["editTable", "Edition tableau", "", "execEditTable"],
["editCell", "Edition céllule", "", "execEditCell"]
]
],
[
"image",
"Images",
"fa-picture-o",
"",
"",
[
["newImage", "Nouvelle image", "fa-picture-o", "execNewImage", ""],
["infoImage", "Information sur l'image", "fa-edit", "execImageInfo", ""],
["imagesLibrary", "Bibliothèque d'images", "fa-windows", "execImageLibrary", ""],
["imagesReplace", "Remplacer image", "fa-magic", "execImageReplace", ""]
]
],
["templates", "Templates", "fa-file", "", ""],
"|",
["source", "Code source", "fa-terminal", "execSource"]
]
};
opts = opts ? opts : {};
$.extend(this, defaults, opts);
this.init();
}
/**
*/
init() {
this.createButtons();
if (this.container) {
this.setContainer(this.container);
this.path = $("<div class='M_FormEditor-Path'> </div>");
this.container.after(this.path);
}
document.execCommand("styleWithCSS", false, false);
document.execCommand("enableObjectResizing", false, false);
}
/**
* @param {type}
* @return {type}
*/
execJustify(what) {
document.execCommand("justify" + what, false, null);
this.caretMove();
}
/**
* @param {type}
* @return {type}
*/
execCommand(comm) {
document.execCommand(comm, false, null);
this.caretMove();
}
/**
* @return {type}
*/
execInsertTable() {
document.execCommand(
"insertHTML",
false,
"<table><tr><td>Cell 1.1</td><td>Cell 1.2</td></tr><tr><td>Cell 2.1</td><td>Cell 2.2</td></tr></table>"
);
this.caretMove();
}
/**
* @param {type}
* @return {type}
*/
execFormatBlock(block) {
document.execCommand("formatBlock", false, block.toLowerCase());
this.caretMove();
}
/**
* @return {type}
*/
execInsertTableColumn() {
var td = $(document.getSelection().getRangeAt(0).commonAncestorContainer).closest("td");
if (td.length && td.closest("#" + this.container.get(0).id).length) {
var n = td
.closest("tr")
.find("td")
.index(td);
td
.closest("table")
.find("tr")
.each(function() {
$(this)
.find("td")
.eq(n)
.after("<td>Nouvelle cellule</td>");
});
}
}
/**
* @return {type}
*/
execInsertTableRow() {
var tr = $(document.getSelection().getRangeAt(0).commonAncestorContainer).closest("tr");
if (tr.length && tr.closest("#" + this.container.get(0).id).length) {
// var n = tr.closest("table").find("tr").index(tr) ;
var countTds = tr.find("td").length;
var newTr = $("<tr></tr>");
for (var i = 0; i < countTds; i++) {
newTr.append("<td>Nouvelle cellule</td>");
}
tr.after(newTr);
}
}
/**
* @return {type}
*/
execLink() {
if (!this.winLink) {
this.winLink = new class extends M_.Window {
constructor() {
super({
modal: true,
width: 400
});
}
create() {
var idBtSave = M_.Utils.id(),
idBtCancel = M_.Utils.id(),
idLink = M_.Utils.id(),
idTarget = M_.Utils.id();
this.html = `<div class="M_WindowContent">
<div class="M_WindowHeader">
<h1>Edition <b>d'un lien</b></h1>
</div>
<div class="M_WindowBody">
<div id="${idLink}"></div>
<div id="${idTarget}"></div>
</div>
<div class="M_WindowFooter">
<div class="M_FloatRight">
<button id="${idBtSave}" type="button" class="M_Button primary">Enregistrer</button>
</div>
<div class="M_FloatLeft">
<button id="${idBtCancel}" type="button" class="M_Button">Annuler</button>
</div>
</div>
</div>`;
super.create();
this.form = new M_.Form.Form({
items: [
{
type: M_.Form.Text,
name: "link",
placeholder: "Lien",
label: "Lien",
labelPosition: "top",
container: $("#" + idLink)
},
{
type: M_.Form.Combobox,
name: "target",
allowEmpty: true,
placeholder: "",
label: "Cible",
labelPosition: "top",
container: $("#" + idTarget),
store: new M_.Store({
controller: this,
model: M_.ModelKeyVal,
rows: [{ key: "", val: "Aucun" }, { key: "_blank", val: "_blank" }, { key: "_self", val: "_self" }]
})
}
]
});
$("#" + idBtSave).click(() => {
M_.Utils.restoreSelection();
document.execCommand("createLink", false, this.form.find("link").getValue());
if (this.form.find("target").getValue() !== "")
$(document.getSelection().getRangeAt(0).commonAncestorContainer)
.closest("a")
.attr("target", this.form.find("target").getValue());
this.hide();
});
$("#" + idBtCancel).click(() => {
M_.Utils.restoreSelection();
this.hide();
});
}
}();
}
this.winLink.show();
}
/**
* @return {type}
*/
execSource() {
if (!this.winSource) {
this.winSource = new class extends M_.Window {
constructor(containerEditor) {
super({
modal: true,
width: 800
});
this.containerEditor = containerEditor;
}
create() {
var idBtSave = M_.Utils.id(),
idBtCancel = M_.Utils.id(),
idCode = M_.Utils.id();
this.html = `<div class="M_WindowContent">
<div class="M_WindowHeader">
<h1>Edition <b>du code source</b></h1>
</div>
<div class="M_WindowBody">
<div id="${idCode}"></div>
</div>
<div class="M_WindowFooter">
<div class="M_FloatRight">
<button id="${idBtSave}" type="button" class="M_Button primary">Enregistrer</button>
</div>
<div class="M_FloatLeft">
<button id="${idBtCancel}" type="button" class="M_Button">Annuler</button>
</div>
</div>
</div>`;
super.create();
this.form = new M_.Form.Form({
items: [
{
type: M_.Form.Textarea,
name: "htmlcode",
acceptTabulation: true,
rows: 30,
container: $("#" + idCode)
}
]
});
$("#" + idBtSave).click(() => {
var html = this.form.find("htmlcode").getValue();
this.containerEditor.html(html);
this.hide();
});
$("#" + idBtCancel).click(() => {
M_.Utils.restoreSelection();
this.hide();
});
}
}(this.container);
}
var html = this.container.html();
this.winSource.form.find("htmlcode").setValue(html);
this.winSource.show();
}
/**
* @return {type}
*/
execEditTable() {
var table = $(document.getSelection().getRangeAt(0).commonAncestorContainer).closest("table");
if (!table.length || !table.closest("#" + this.container.get(0).id).length) return;
if (!this.winEditTable) {
this.winEditTable = new class extends M_.Window {
constructor(containerEditor) {
super({
modal: true,
width: 800,
containerEditor: containerEditor
});
}
create() {
var idBtSave = M_.Utils.id(),
idBtCancel = M_.Utils.id(),
idwidth = M_.Utils.id(),
idcellspacing = M_.Utils.id(),
idcellpadding = M_.Utils.id(),
idborder = M_.Utils.id(),
idstyle = M_.Utils.id(),
idid = M_.Utils.id(),
idalign = M_.Utils.id();
this.html = `<div class="M_WindowContent">
<div class="M_WindowHeader">
<h1>Edition <b>d'un tableau</b></h1>
</div>
<div class="M_WindowBody">
<div id="${idwidth}"></div>
<div id="${idcellspacing}"></div>
<div id="${idcellpadding}"></div>
<div id="${idborder}"></div>
<div id="${idstyle}"></div>
<div id="${idid}"></div>
<div id="${idalign}"></div>
</div>
<div class="M_WindowFooter">
<div class="M_FloatRight">
<button id="${idBtSave}" type="button" class="M_Button primary">Enregistrer</button>
</div>
<div class="M_FloatLeft">
<button id="${idBtCancel}" type="button" class="M_Button">Annuler</button>
</div>
</div>
</div>`;
super.create();
this.form = new M_.Form.Form({
itemsDefaults: {
labelPosition: "left",
labelWidth: 250
},
items: [
{
type: M_.Form.Text,
label: "Largeur (px ou %)",
name: "width",
container: $("#" + idwidth)
},
{
type: M_.Form.Number,
label: "Espacement des cellules",
name: "cellpadding",
container: $("#" + idcellpadding)
},
{
type: M_.Form.Number,
label: "Marge des cellules",
name: "cellspacing",
container: $("#" + idcellspacing)
},
{
type: M_.Form.Number,
label: "Taille de la bordure",
name: "border",
container: $("#" + idborder)
},
{
type: M_.Form.Text,
label: "Style CSS",
name: "style",
container: $("#" + idstyle)
},
{
type: M_.Form.Text,
label: "ID",
name: "id",
container: $("#" + idid)
},
{
type: M_.Form.Combobox,
name: "align",
allowEmpty: true,
placeholder: "",
label: "Alignement",
container: $("#" + idalign),
store: new M_.Store({
controller: this,
model: M_.ModelKeyVal,
rows: [
{ key: "", val: "Aucun" },
{ key: "left", val: "Gauche" },
{ key: "center", val: "Centré" },
{ key: "right", val: "Droite" }
]
})
}
]
});
$("#" + idBtSave).click(() => {
this.currentTable.css({ width: this.form.find("width").getValue() });
this.currentTable.attr("cellpadding", this.form.find("cellpadding").getValue());
this.currentTable.attr("cellspacing", this.form.find("cellspacing").getValue());
this.currentTable.attr("border", this.form.find("border").getValue());
this.currentTable.attr("align", this.form.find("align").getValue());
this.hide();
});
$("#" + idBtCancel).click(() => {
M_.Utils.restoreSelection();
this.hide();
});
}
setCurrentTable(currentTable) {
this.currentTable = currentTable;
var sEl = this.currentTable.get(0).style;
if (sEl.width !== "") this.form.find("width").setValue(this.currentTable.get(0).style.width);
else this.form.find("width").setValue("");
if (this.currentTable.attr("cellpadding")) this.form.find("cellpadding").setValue(this.currentTable.attr("cellpadding"));
else this.form.find("cellpadding").setValue("");
if (this.currentTable.attr("cellspacing")) this.form.find("cellspacing").setValue(this.currentTable.attr("cellspacing"));
else this.form.find("cellspacing").setValue("");
if (this.currentTable.attr("border")) this.form.find("border").setValue(this.currentTable.attr("border"));
else this.form.find("border").setValue("");
if (this.currentTable.attr("align")) this.form.find("align").setValue(this.currentTable.attr("align"));
else this.form.find("align").setValue("");
}
}(this.container);
}
this.winEditTable.setCurrentTable(table);
this.winEditTable.show();
}
/**
* @return {type}
*/
execEditCell() {
var td = $(document.getSelection().getRangeAt(0).commonAncestorContainer).closest("td");
if (!td.length || !td.closest("#" + this.container.get(0).id).length) return;
if (!this.winEditCell) {
this.winEditCell = new class extends M_.Window {
constructor(containerEditor) {
super({
modal: true,
width: 800,
containerEditor: containerEditor
});
}
create() {
var idBtSave = M_.Utils.id(),
idBtCancel = M_.Utils.id(),
idwidth = M_.Utils.id(),
idalign = M_.Utils.id(),
idvalign = M_.Utils.id(),
idcolor = M_.Utils.id();
this.html = `<div class="M_WindowContent">
<div class="M_WindowHeader">
<h1>Edition <b>d'une cellule</b></h1>
</div>
<div class="M_WindowBody">
<div id="${idwidth}"></div>
<div id="${idalign}"></div>
<div id="${idvalign}"></div>
<div id="${idcolor}"></div>
</div>
<div class="M_WindowFooter">
<div class="M_FloatRight">
<button id="${idBtSave}" type="button" class="M_Button primary">Enregistrer</button>
</div>
<div class="M_FloatLeft">
<button id="${idBtCancel}" type="button" class="M_Button">Annuler</button>
</div>
</div>
</div>`;
super.create();
this.form = new M_.Form.Form({
itemsDefaults: {
labelPosition: "left",
labelWidth: 250
},
items: [
{
type: M_.Form.Text,
label: "Largeur (px ou %)",
name: "width",
container: $("#" + idwidth)
},
{
type: M_.Form.Combobox,
name: "align",
allowEmpty: true,
placeholder: "",
label: "Alignement",
container: $("#" + idalign),
store: new M_.Store({
controller: this,
model: M_.ModelKeyVal,
rows: [
{ key: "", val: "Aucun" },
{ key: "left", val: "Gauche" },
{ key: "center", val: "Centré" },
{ key: "right", val: "Droite" }
]
})
},
{
type: M_.Form.Combobox,
name: "valign",
allowEmpty: true,
placeholder: "",
label: "Alignement vertical",
container: $("#" + idvalign),
store: new M_.Store({
controller: this,
model: M_.ModelKeyVal,
rows: [
{ key: "", val: "Aucun" },
{ key: "top", val: "Haut" },
{ key: "middle", val: "Milieu" },
{ key: "bottom", val: "Bas" }
]
})
},
{
type: M_.Form.Text,
label: "Couleur",
name: "color",
container: $("#" + idwidth)
}
]
});
$("#" + idBtSave).click(() => {
this.currentCell.css({ width: this.form.find("width").getValue() });
this.currentCell.attr("align", this.form.find("align").getValue());
this.currentCell.attr("valign", this.form.find("valign").getValue());
this.currentCell.css({
backgroundColor: this.form.find("color").getValue()
});
this.hide();
});
$("#" + idBtCancel).click(() => {
M_.Utils.restoreSelection();
this.hide();
});
}
setCurrentCell(currentCell) {
this.currentCell = currentCell;
var sEl = this.currentCell.get(0).style;
if (sEl.width !== "") this.form.find("width").setValue(this.currentCell.css("width"));
else this.form.find("width").setValue("");
if (this.currentCell.attr("align")) this.form.find("align").setValue(this.currentCell.attr("cellpadding"));
else this.form.find("align").setValue("");
if (this.currentCell.attr("valign")) this.form.find("valign").setValue(this.currentCell.attr("valign"));
else this.form.find("valign").setValue("");
if (sEl.backgroundColor !== "") this.form.find("color").setValue(this.currentCell.css("backgroundColor"));
else this.form.find("color").setValue("");
}
}(this.container);
}
this.winEditCell.setCurrentCell(td);
this.winEditCell.show();
}
/**
* @return {type}
*/
execNewImage() {
if (!this.winNewImage) {
this.winNewImage = new class extends M_.Window {
constructor(containerEditor) {
super({
modal: true,
width: 800,
tabFilesToSend: []
});
this.containerEditor = containerEditor;
}
create() {
var idBtSave = M_.Utils.id(),
idBtCancel = M_.Utils.id(),
idCode = M_.Utils.id();
this.html = `<div class="M_WindowContent">
<div class="M_WindowHeader">
<h1>Envoyer <b>des images</b></h1>
</div>
<div class="M_WindowBody">
<div id="${idCode}"></div>
<div class="m_dropherenewimage" contenteditable><h2>Déposer ici un (ou des) fichier(s) de votre bureau</h2></div>
<h2 style="margin-top:15px;">Ou choisissez les fichiers à envoyer</h2>
<input type="file" class="m_fileinput" multiple />
<div class="M_Clear"></div>
<output class="m_fileoutput"></output>
<div class="M_Clear"></div>
</div>
<div class="M_WindowFooter">
<div class="M_FloatRight">
<button id="${idBtSave}" type="button" class="M_Button primary">Enregistrer</button>
</div>
<div class="M_FloatLeft">
<button id="${idBtCancel}" type="button" class="M_Button">Annuler</button>
</div>
</div>
</div>`;
super.create();
this.jEl.find(".m_dropherenewimage").on(
"dragover",
$.proxy(function(e) {
this.jEl.find(".m_dropherenewimage").css("background-color", "green");
}, this)
);
this.jEl.find(".m_dropherenewimage").on(
"dragleave",
$.proxy(function(e) {
this.jEl.find(".m_dropherenewimage").css("background-color", "white");
}, this)
);
this.jEl.find(".m_dropherenewimage").on(
"drop",
$.proxy(function(e) {
this.jEl.find(".m_dropherenewimage").css("background-color", "white");
e.preventDefault();
var files = e.originalEvent.dataTransfer.files;
if (files.length === 0) return;
for (var i = 0; i < files.length; i++) {
var f = files[i];
this.createThumbnail(f);
}
}, this)
);
$("#" + idBtSave).click(() => {
var formData = new FormData();
for (var i = 0; i < this.tabFilesToSend.length; i++) {
formData.append("file" + i, this.tabFilesToSend[i]);
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "/upload");
var me = this;
xhr.onload = function() {
//console.log(xhr.responseText);
var jsonResponse = JSON.parse(xhr.responseText);
if (me.callbackSave) {
var img = null;
if (jsonResponse.tabRes.length > 0) img = jsonResponse.tabRes[0];
me.callbackSave(img);
} else {
for (var i = 0; i < jsonResponse.tabRes.length; i++) {
document.execCommand("insertImage", false, jsonResponse.tabRes[i]);
}
}
me.hide();
//document.execCommand("insertImage", false, jsonResponse.url) ;
//me.caretMove() ;
};
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
// var complete = (e.loaded / e.total * 100 | 0);
//Mlog("complete",complete)
//updates a <progress> tag to show upload progress
//$('progress').val(complete);
}
};
xhr.send(formData);
});
$("#" + idBtCancel).click(() => {
M_.Utils.restoreSelection();
this.hide();
});
}
resetFileInput() {
var control = this.jEl.find(".m_fileinput");
this.jEl.find(".m_fileinput").replaceWith((control = control.clone(true)));
this.jEl.find(".m_fileinput").on(
"change",
$.proxy(function(e) {
var files = e.target.files;
if (files.length === 0) return;
for (var i = 0; i < files.length; i++) {
var f = files[i];
this.createThumbnail(f);
}
}, this)
);
}
createThumbnail(f) {
this.tabFilesToSend.push(f);
var list = this.jEl.find(".m_fileoutput");
var reader = new FileReader();
var me = this;
reader.onload = (function(theFile) {
return function(e) {
list.append(
['<img style="margin:0 15px 15px 0" src="', e.target.result, '" title="', theFile.name, '" width="100" />'].join("")
);
me.center();
};
})(f);
reader.readAsDataURL(f);
}
}(this.container);
}
this.winNewImage.resetFileInput();
this.winNewImage.show();
}
/**
* @return {type}
*/
caretMove() {
if (document.getSelection().rangeCount > 0) {
var sel = $(document.getSelection().getRangeAt(0).commonAncestorContainer);
var html = "";
var jEls = sel.parentsUntil("#" + this.container.get(0).id);
if (!sel.hasClass("M_FormEditor-Content")) {
var tabTemp = [];
jEls.each(function(ind, el) {
if (el.tagName) tabTemp.push(el.tagName);
});
tabTemp.reverse();
tabTemp.push("text");
html = tabTemp.join(" > ");
}
//html += sel.get(0).tagName ;
html += " ";
this.path.html(html);
}
if (document.queryCommandValue("bold") == "true") this.buttonsContainer.find('button[m_id="bold"]').addClass("active");
else this.buttonsContainer.find('button[m_id="bold"]').removeClass("active");
if (document.queryCommandValue("italic") == "true") this.buttonsContainer.find('button[m_id="italic"]').addClass("active");
else this.buttonsContainer.find('button[m_id="italic"]').removeClass("active");
if (document.queryCommandValue("justifyLeft") == "true") this.buttonsContainer.find('button[m_id="justifyLeft"]').addClass("active");
else this.buttonsContainer.find('button[m_id="justifyLeft"]').removeClass("active");
if (document.queryCommandValue("justifyRight") == "true") this.buttonsContainer.find('button[m_id="justifyRight"]').addClass("active");
else this.buttonsContainer.find('button[m_id="justifyRight"]').removeClass("active");
if (document.queryCommandValue("justifyCenter") == "true") this.buttonsContainer.find('button[m_id="justifyCenter"]').addClass("active");
else this.buttonsContainer.find('button[m_id="justifyCenter"]').removeClass("active");
if (document.queryCommandValue("justifyFull") == "true") this.buttonsContainer.find('button[m_id="justifyFull"]').addClass("active");
else this.buttonsContainer.find('button[m_id="justifyFull"]').removeClass("active");
}
/**
* @return {type}
*/
createButtons() {
var toolbar = $("<div>", { class: "M_Editor", role: "toolbar" });
this.buttonsContainer.append(toolbar);
// var currentGroup = $("<div>", {'class':'btn-group'}) ;
// toolbar.append(currentGroup) ;
for (var i = 0; i < this.buttons.length; i++) {
let bt = this.buttons[i];
if (_.indexOf(this.buttonsToDisplay, bt[0]) < 0) continue;
if (bt[0] === "|") {
// currentGroup = $("<div>", {'class':'btn-group'}) ;
// toolbar.append(currentGroup) ;
} else {
let caret = "";
if (bt[5]) caret = "<span class='fa fa-caret-down'></span>";
let button = $("<button m_id='" + bt[0] + "'><i class='fa " + bt[2] + "'></i> " + caret + "</button>");
toolbar.append(button);
if (bt[5]) {
button.click({ items: bt[5] }, evt => {
evt.stopPropagation();
M_.Utils.saveSelection();
var items = [];
_.each(evt.data.items, item => {
let nameTemp = item[1];
if (item[2] && item[2] !== "") nameTemp = "<i class='fa " + item[2] + "'></i> " + nameTemp;
items.push({
text: nameTemp,
click: evt => {
// evt.stopPropagation() ;
// evt.preventDefault()
M_.Utils.restoreSelection();
this[item[3]](item[4]);
}
});
});
var dropdown = new M_.Dropdown({
destroyOnHide: true,
alignTo: $(evt.target).closest("button"),
items: items
});
dropdown.show();
});
} else {
button.click({ items: bt[5] }, evt => {
this[bt[3]](bt[4]);
});
}
}
}
}
/**
* @param {type}
*/
setContainer(container) {
this.container = container;
this.container.on(
"click",
$.proxy(function(e) {
this.container.prop("contenteditable", true);
this.container.addClass("isEditing");
if (!this.isStartedDrag) {
$("#" + this.container.get(0).id + " .M_todelete").remove();
this.imgDraggable = null;
}
this.caretMove();
}, this)
);
this.container.on(
"keyup",
$.proxy(function(e) {
this.caretMove();
}, this)
);
this.container.on(
"keydown",
$.proxy(function(e) {
this.caretMove();
}, this)
);
this.container.on(
"dragstart",
$.proxy(function(e) {
$("#" + this.container.get(0).id + " .M_todelete").remove();
}, this)
);
$(document).on(
"dblclick",
"#" + this.container.get(0).id + " img",
$.proxy(function(e) {
this.execImageInfo();
}, this)
);
$(document).on(
"drop",
"#" + this.container.get(0).id + " img",
$.proxy(function(e) {
log("drop on image", e);
}, this)
);
$(document).click(
$.proxy(function(e) {
if ($(e.target).closest("#" + this.container.get(0).id + ", #" + this.buttonsContainer.get(0).id).length === 0) {
$("#" + this.container.get(0).id + " .M_todelete").remove();
//this.container.prop("contenteditable", false) ;
this.container.removeClass("isEditing");
}
}, this)
);
this.container.on(
"drop",
$.proxy(function(e) {
var files = e.originalEvent.dataTransfer.files;
if (files.length === 0) return;
e.preventDefault();
if (!this.container.prop("contenteditable")) {
M_.Dialog.alert("Information", "Merci de cliquer d'abord l'endroit où vous souhaitez insérer l'image");
return;
}
this.container.prop("contenteditable", true);
var formData = new FormData();
formData.append("file0", files[0]);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/upload");
var me = this;
xhr.onload = function() {
//console.log(xhr.responseText);
var jsonResponse = JSON.parse(xhr.responseText);
document.execCommand("insertImage", false, jsonResponse.tabRes[0]);
me.caretMove();
};
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
// var complete = (e.loaded / e.total * 100 | 0);
//updates a <progress> tag to show upload progress
//$('progress').val(complete);
}
};
xhr.send(formData);
}, this)
);
}
};
export var M_ = M_;
$.postJSON = function(url, data, callback) {
return $.ajax({
type: "POST",
url: url,
contentType: "application/json",
data: JSON.stringify(data),
dataType: "json",
success: callback
});
};
window.log = function() {
if (window.console) {
console.log.apply(window.console, arguments);
}
};
// (function(factory) {
// if (typeof define === 'function' && define.amd) {
// define([ 'jquery' ], factory);
// }
// else {
// factory(jQuery);
// }
// })(function($) {
// ;;
// var defaults = {
// } ;
// var md = $.m_draggable = { version: "0.1.2" };
// // classic jQuery plugin ; I don't comment
// $.fn.m_draggable = function(options) {
// var args = Array.prototype.slice.call(arguments, 1);
// var res = this;
// this.each(function(i, _element) {
// var element = $(_element);
// var draggable = element.data('m_draggable');
// var singleRes;
// if (typeof options === 'string') {
// if (draggable && $.isFunction(draggable[options])) {
// singleRes = draggable[options].apply(draggable, args);
// if (!i) res = singleRes;
// if (options === 'destroy') element.removeData('m_draggable');
// }
// } else if (!draggable) { // don't initialize twice
// draggable = new m_draggable(element, options);
// element.data('m_draggable', draggable);
// draggable.reload();
// }
// });
// return res;
// };
// // More interesting...
// function m_draggable(element, instanceOptions) {
// var me = this;
// var opts = $.extend(true, {}, defaults, instanceOptions) ;
// // Exports
// // me.render = render ;
// me.sayHello = sayHello ;
// me.reload = reload ;
// me.opts = opts ;
// // local variables
// // for debug
// function log() {
// if (window.console) console.log.apply(window.console, arguments) ;
// }
// function reload() {
// // jElHandle.mousedown({el:el}, $.proxy(function(evt) {
// // this.dragEl = evt.data.el ;
// // this.dragStart = true ;
// // this.dragDiff = {top: evt.pageY-this.dragEl.offset().top, left: evt.pageX-this.dragEl.offset().left}
// // }, this)) ;
// // $(document).mouseup({el:el}, $.proxy(function() {
// // this.dragStart = false ;
// // }, this)) ;
// // $(document).mousemove({el:el}, $.proxy(function(evt) {
// // if (this.dragStart && this.dragEl == evt.data.el) {
// // var top = evt.pageY ;
// // var left = evt.pageX ;
// // this.dragEl.offset({top: top-this.dragDiff.top, left: left-this.dragDiff.left}) ;
// // }
// // }, this)) ;
// }
// // don't create / re-create dom, resize all components
// function redraw() {
// render() ;
// }
// function sayHello() {
// log("hello") ;
// }
// }
// // mg._startDrag = false ;
// ;;
// });
// M_.Draggable = class {
// constructor(el, jElHandle=null, jElHelper=null) {
// //tabDraggable.push({el:el, jElHandle:jElHandle}) ;
// jElHandle.mousedown({el:el}, $.proxy(function(evt) {
// this.dragEl = evt.data.el ;
// this.dragStart = true ;
// this.dragDiff = {top: evt.pageY-this.dragEl.offset().top, left: evt.pageX-this.dragEl.offset().left}
// }, this)) ;
// $(document).mouseup({el:el}, $.proxy(function() {
// this.dragStart = false ;
// }, this)) ;
// $(document).mousemove({el:el}, $.proxy(function(evt) {
// if (this.dragStart && this.dragEl == evt.data.el) {
// var top = evt.pageY ;
// var left = evt.pageX ;
// this.dragEl.offset({top: top-this.dragDiff.top, left: left-this.dragDiff.left}) ;
// }
// }, this)) ;
// }
// changer update en change pour les événements combobox
//// voir http://jsfiddle.net/jhfrench/GpdgF/ pour un treeview
//// mettre un appel bouton "téléphonique reçu" accessible sur toutes les pages
//// excellent : http://dropthebit.com/demos/fancy_input/fancyInput.html
/// pour du scroll : http://johnpolacek.github.io/superscrollorama/
/// peut servir, d'autres trucs bien sur ce site : http://tympanus.net/codrops/2012/01/24/arctext-js-curving-text-with-css3-and-jquery/
/// à utiliser sur un site web : http://www.seanmccambridge.com/tubular/
/// excellent : http://tutorialzine.com/2011/09/jquery-pointpoint-plugin/
/// peut-être pas mal, pour rafraichir les listes : http://usehook.com/
// à étudier : http://www.chartjs.org/docs/#getting-started-creating-a-chart
// à copier ??? http://paulkinzett.github.io/toolbar/
/// http://codecanyon.net/item/fun-captcha-jquery-plugin/6705437?ref=yuzuruishikawa
// Mac OS X: http://blog.coolaj86.com/articles/how-to-create-an-osx-pkg-installer.html
// Ubuntu Linux: http://blog.coolaj86.com/articles/how-to-create-a-debian-installer.html
// Microsoft Windows: http://blog.coolaj86.com/articles/how-to-create-an-innosetup-installer.html
// à annalyser : http://www.corm.fr/demonstration/
// à analyser également (permet de faire des applications locales en nodejs) : https://github.com/rogerwang/node-webkit
// l'utiliser pour la création du PDF ? http://www.jqueryrain.com/?l1iRfaTH
/// ...pas mal du tout, car çà permet de laisser en colonne !!!!