1. 1 : function html_helper(option, html, optional_classes = "", attribs = null){
  2. 2 :
  3. 3 : if (!!attribs)
  4. 4 : {
  5. 5 : const tmp = attribs;
  6. 6 : attribs = '';
  7. 7 :
  8. 8 : for (const key in tmp) {
  9. 9 : if (Object.hasOwnProperty.call(tmp, key)) {
  10. 10 : const element = tmp[key];
  11. 11 : attribs += `${key}="${element}" `;
  12. 12 : }
  13. 13 : }
  14. 14 :
  15. 15 : }
  16. 16 : else attribs = '';
  17. 17 :
  18. 18 : switch (option) {
  19. 19 : case html_helper.options["block"]:
  20. 20 : return `<div class="square_div ${optional_classes}" ${attribs}><div class=contents><div class=square-contents>${html}</div></div></div>`;
  21. 21 : case html_helper.options.create_button:
  22. 22 :
  23. 23 : let onclick = "";
  24. 24 : let classes = 'mel-button create mel-before-remover btn btn-secondary';
  25. 25 : let id = "";
  26. 26 :
  27. 27 : if (typeof optional_classes !== "string" && optional_classes !== null && optional_classes !== undefined)
  28. 28 : {
  29. 29 : if (optional_classes.onclick !== undefined)
  30. 30 : onclick = `onclick="${optional_classes.onclick}"`;
  31. 31 :
  32. 32 : if (optional_classes.new_classes !== undefined)
  33. 33 : classes = `${optional_classes.new_classes}`
  34. 34 :
  35. 35 : if (optional_classes.additional_classes !== undefined)
  36. 36 : classes += `${optional_classes.additional_classes}`
  37. 37 :
  38. 38 : if (optional_classes.id !== undefined)
  39. 39 : id = `id="${optional_classes.id}"`;
  40. 40 : }
  41. 41 :
  42. 42 : if (classes !== "")
  43. 43 : classes = `class="${classes}"`;
  44. 44 :
  45. 45 : return `<button ${id} ${classes} ${onclick} ${attribs} >${html}</button>`;
  46. 46 : default:
  47. 47 : return html;
  48. 48 : }
  49. 49 : }
  50. 50 :
  51. 51 : html_helper.options = {
  52. 52 : "block":Symbol("block"),
  53. 53 : "create_button":Symbol("create_button")
  54. 54 : }
  55. 55 :
  56. 56 : /**
  57. 57 : * Renvoie vrai si le string est vide ou vaut null
  58. 58 : * @param {string} string String à tester
  59. 59 : */
  60. 60 : html_helper.is_null_or_empty = function(string)
  61. 61 : {
  62. 62 : return string === null || string == "";
  63. 63 : }
  64. 64 :
  65. 65 : html_helper.JSON = {
  66. 66 : parse:(string) => {
  67. 67 : if (string === null || string === undefined)
  68. 68 : return string;
  69. 69 : return JSON.parse(string.replaceAll('¤¤¤¤¤¤¤¤', '"'));
  70. 70 : },
  71. 71 : stringify:(item) => {
  72. 72 : if (item === null)
  73. 73 : item = "null";
  74. 74 : else if (item === undefined)
  75. 75 : item = "undefined";
  76. 76 : return JSON.stringify(item).replaceAll('"', "¤¤¤¤¤¤¤¤");
  77. 77 : }
  78. 78 : }
  79. 79 :
  80. 80 : /**
  81. 81 : * Récupère le html pour la liste des tâches de l'utilisateur. Récupère les données depuis le stockage local.
  82. 82 : * @param {*} e Element qui contiendra le html
  83. 83 : * @param {*} e_news Element qui contiendra le nombre de tâches (facultatif)
  84. 84 : * @param {JSON} tabs Liste des onglets
  85. 85 : * @param {String} title Titre du block
  86. 86 : */
  87. 87 : html_helper.TasksAsync = async function (tabs, e = null, e_news = null,title = null)
  88. 88 : {
  89. 89 : let storage = await mel_metapage.Storage.check(mel_metapage.Storage.tasks).wait();
  90. 90 :
  91. 91 : if (html_helper.tasks_updates === undefined)
  92. 92 : {
  93. 93 : html_helper.tasks_updates = new Mel_Update(mel_metapage.EventListeners.tasks_updated.after, "tasks.update", async () => {
  94. 94 : $('.html-tasks').each(async (i,e) => {
  95. 95 : e = $(e);
  96. 96 : const tabs = html_helper.JSON.parse(e.data('task-tabs'));
  97. 97 : const title = e.data('task-title');
  98. 98 : e[0].outerHTML = await html_helper.TasksAsync(tabs, null, null, title);
  99. 99 : });
  100. 100 : });
  101. 101 : }
  102. 102 :
  103. 103 : return html_helper.Tasks(storage, tabs, e, e_news, title);
  104. 104 : }
  105. 105 :
  106. 106 : /**
  107. 107 : * Récupère le html pour la liste des tâches de l'utilisateur.
  108. 108 : * @param {*} e Element qui contiendra le html
  109. 109 : * @param {Array} datas Liste des tâches
  110. 110 : * @param {*} e_news Element qui contiendra le nombre de tâches (facultatif)
  111. 111 : * @param {JSON} tabs Liste des noms des onglets
  112. 112 : * @param {String} title Titre du block
  113. 113 : */
  114. 114 : html_helper.Tasks = function (datas, tabs, e = null, e_news = null,title = null)
  115. 115 : {
  116. 116 : let html = ''
  117. 117 : html += '<div class="html-tasks" data-task-tabs="'+html_helper.JSON.stringify(tabs)+'" data-task-title="'+title+'">';
  118. 118 : if (!html_helper.is_null_or_empty(title))
  119. 119 : {
  120. 120 : html += "<div class=task-title>";
  121. 121 : html += "<span>" + title + "</span>";
  122. 122 : html += "</div>";
  123. 123 : }
  124. 124 : // html = '<div id=dwp-tadk-urg class="tab-task-dwp mel-tab mel-tabheader">Tâches urgentes</div>';
  125. 125 : //html += '<center><div id=dwp-tadk-all class="tab-task-dwp mel-tab mel-tabheader active last">'+tabs["right"]+'</div></center>';
  126. 126 :
  127. 127 : html += '<div style="margin:10px 0;"><div id="nb-waiting-task" class="nb-task wsp-task-classik tab-task mel-tab-content" style=""><span class="icon-mel-time roundbadge large clear"></span><span><span class="waiting-task"></span><span class="nb-waiting-task nb font-size-large">'+datas.length+'</span> tâches en cours</span></div></div>';
  128. 128 :
  129. 129 : datas = Enumerable.from(datas).orderBy((x) => x.order).thenBy((x) => (x._hasdate === 1 ? x.datetime : Number.MAX_VALUE )).toArray();
  130. 130 : let date;
  131. 131 :
  132. 132 : html += `<ul class="ignore-bullet">`;
  133. 133 :
  134. 134 : for (let index = 0; index < datas.length; index++) {
  135. 135 : const element = datas[index];
  136. 136 : date = moment(parseInt(element.created + "000"));
  137. 137 : html += "<li>";
  138. 138 : html += "<div class=row style=margin-bottom:15px;margin-right:15px;>";
  139. 139 :
  140. 140 : if (date._isValid)
  141. 141 : html += `<div class=col-md-10><a href=# class="element-block mel-not-link mel-focus" onclick="open_task('${element.id}')"><span class="element-title default-text bold element-block">${element.title}</span><span class="element-desc secondary-text element-block">Créée le ${date.format("DD/MM/YYYY")} à ${date.format("hh:mm")}</span></a></div>`;//html += "<div class=col-md-12><span class=element-title>" + element.title + "</span><br/><span class=element-desc>Créée le " + date.format("DD/MM/YYYY") + " à " + date.format("hh:mm") +"</span></div>";
  142. 142 : else
  143. 143 : html += "<div class=col-md-12></div>";
  144. 144 :
  145. 145 : //html += '<div class=col-md-2><a style=display:none; onclick="add_task_to_completed(`'+element.id+'`)" class="roundbadge large hover tick ' + (element.mel_metapage.order == 0 ? "icon-mel-warning warning" : "icon-mel-time clear") + '"></a></div>'
  146. 146 : html += "</div>";
  147. 147 : html += "</li>";
  148. 148 : }
  149. 149 : html += "</ul>";
  150. 150 : html += "</div>";
  151. 151 :
  152. 152 : if (e !== null)
  153. 153 : e.html(html);
  154. 154 :
  155. 155 : if (e_news !== null)
  156. 156 : {
  157. 157 : if (datas.length > 0)
  158. 158 : {
  159. 159 : e_news.html(datas.length);
  160. 160 : e_news.removeClass("hidden");
  161. 161 : }
  162. 162 : else
  163. 163 : e_news.addClass("hidden");
  164. 164 : }
  165. 165 :
  166. 166 : return html;
  167. 167 : }
  168. 168 :
  169. 169 : html_helper.CalendarsAsync = async function(config = {
  170. 170 : add_day_navigation:false,
  171. 171 : add_create:false,
  172. 172 : create_function:null,
  173. 173 : add_see_all:false,
  174. 174 : next_when_empty_today_function:null,
  175. 175 : }, e = null, e_number = null, _date = moment())
  176. 176 : {
  177. 177 : const Loader = (await loadJsModule('mel_metapage', 'calendar_loader', '/js/lib/calendar/')).CalendarLoader.Instance;
  178. 178 : let storage = Loader.get_next_events_day(_date, {});
  179. 179 :
  180. 180 : const KEY = 'HTML_CALENDAR_TOP_FUNCTION';
  181. 181 : const Top = (await loadJsModule('mel_metapage', 'top')).Top;
  182. 182 :
  183. 183 : if (!Top.has(KEY)) {
  184. 184 : Top.add(KEY, true);
  185. 185 :
  186. 186 : Loader.add_event_listener(mel_metapage.EventListeners.calendar_updated.after, function() {
  187. 187 :
  188. 188 : Loader.select('iframe.mm-frame').each((index, element) => {
  189. 189 : if (!$(element).hasClass('discussion-frame')) {
  190. 190 : element.contentWindow.$('.html-calendar').each(async (i, calendar) => {
  191. 191 : calendar = $(calendar);
  192. 192 :
  193. 193 : let config = html_helper.JSON.parse(calendar.data('config'));
  194. 194 : const date = moment(calendar.data('date'));
  195. 195 :
  196. 196 : if (config.add_create) delete config.add_create;
  197. 197 :
  198. 198 : if (!(config.add_day_navigation && moment(calendar.find(".mm-agenda-date").data("current-date")).startOf("day").format() !== date.startOf("day").format())) {
  199. 199 : e[0].outerHTML = await html_helper.CalendarsAsync(config, null, null, date);
  200. 200 : }
  201. 201 : });
  202. 202 : }
  203. 203 : });
  204. 204 :
  205. 205 : }, { callback_key:KEY});
  206. 206 : }
  207. 207 :
  208. 208 : if (moment().startOf('day').format() === moment(_date).startOf('day').format()) storage.where(x => moment(x.end) > moment());
  209. 209 :
  210. 210 : return await html_helper.Calendars({datas:storage.toArray(), config:config, e:e, e_number:e_number, _date:_date});
  211. 211 : }
  212. 212 :
  213. 213 : html_helper.Calendars = async function ({datas, config = {
  214. 214 : add_day_navigation:false,
  215. 215 : add_create:false,
  216. 216 : create_function:null,
  217. 217 : add_see_all:false,
  218. 218 : next_when_empty_today_function:null
  219. 219 : }, e = null, e_number = null, _date = moment(), get_only_body = false} = {})
  220. 220 : {
  221. 221 : const Loader = (await loadJsModule('mel_metapage', 'calendar_loader', '/js/lib/calendar/')).CalendarLoader.Instance;
  222. 222 : const html_events = (await loadJsModule('mel_metapage', 'html_events', '/js/lib/html/')).html_events;
  223. 223 : const html_li = (await loadJsModule('mel_metapage', 'html', '/js/lib/html/')).html_li;
  224. 224 :
  225. 225 : let html = ''
  226. 226 : if (!get_only_body)
  227. 227 : html += '<div class="html-calendar" data-config="'+html_helper.JSON.stringify(config)+'" data-date="'+_date.format()+'">';
  228. 228 : if (config.add_day_navigation === true)
  229. 229 : {
  230. 230 : const count = Enumerable.from(datas).where(x => x.free_busy !== "free").count();
  231. 231 : let nav_click = "rcube_calendar.change_calendar_date($('.mm-agenda-date'), ¤¤¤)";
  232. 232 : html += '<div class="row">';
  233. 233 : html += '<div class="col-2"><span class="icon-mel-calendar mm-agenda-icon"><span class="notif roundbadge lightgreen edited" '+(typeof datas === "string" || count === 0 ? "style=display:none;" : "")+'>'+count+'</span></span></div>';
  234. 234 : html += '<div class="col-6"><span class="mm-agenda-date">'+rcube_calendar.mel_metapage_misc.GetDate(_date)+'</span></div>';
  235. 235 : html += '<div class="col-4"><div class="row">';
  236. 236 : html += '<div class="col-6"><button class="btn-mel-invisible btn-arrow btn btn-secondary" onclick="'+nav_click.replace("¤¤¤", "-1")+'"> <span class="icon-mel-arrow-left"><span class="sr-only">'+rcmail.gettext("last_day", "mel_metapage")+'</span></span> </button></div>';
  237. 237 : html += '<div class="col-6"><button class="btn-mel-invisible btn-arrow btn btn-secondary" onclick="'+nav_click.replace("¤¤¤", "1")+'"> <span class="icon-mel-arrow-right"><span class="sr-only">'+rcmail.gettext("next_day", "mel_metapage")+'</span></span> </button></div>';
  238. 238 : html += "</div></div></div>"
  239. 239 : }
  240. 240 : if (!get_only_body)
  241. 241 : html += '<ul class="block-body ignore-bullet">';
  242. 242 :
  243. 243 : //let icon;
  244. 244 : if (typeof datas === "string")
  245. 245 : html += "<div>" + datas + "</div>";
  246. 246 : else {
  247. 247 :
  248. 248 : if (datas.length > 0)
  249. 249 : {
  250. 250 : var $jquery_array = $('');
  251. 251 : let li;
  252. 252 : for (let index = 0; index < datas.length; index++) {
  253. 253 : const element = datas[index];
  254. 254 :
  255. 255 : li = new html_li({});
  256. 256 : new html_events(element, {'data-ignore-date':true}, _date).appendTo(li);
  257. 257 : li = li.generate();
  258. 258 : $jquery_array = $.merge($jquery_array, li);
  259. 259 : html += html_events.$_toString(li);
  260. 260 : }
  261. 261 : html_helper.Calendars.$jquery_array = $jquery_array;
  262. 262 : li = null;
  263. 263 : }
  264. 264 : else
  265. 265 : {
  266. 266 : const now = moment();
  267. 267 : const raw_storage = Loader.load_all_events();
  268. 268 : const storage = Enumerable.from(config.next_when_empty_today_function !== null && typeof config.next_when_empty_today_function === "function" ? config.next_when_empty_today_function(raw_storage) : raw_storage).where(x => moment(x.start) > now);
  269. 269 : const storage_count = storage.count();
  270. 270 : if (storage_count > 0)
  271. 271 : {
  272. 272 : const storage_first = storage.first();
  273. 273 : const value = !!storage_first.value && !!storage_first.value[0] ? storage_first.value[0] : storage_first;
  274. 274 : const all_day = value.allDay ? "_all_day" : "";
  275. 275 : html += `<li><span class="element-title element-no default-text bold element-block">${rcmail.gettext('mel_portal.no_event_today')}</span>
  276. 276 : <a href=# class="element-block mel-not-link mel-focus" onclick="${html_helper.Calendars.generate_link(value)}">
  277. 277 : <span class="element-title default-text bold element-block">${rcmail.gettext(`mel_portal.next_agenda_event${all_day}`).replace('{date}', moment(value.start).format('DD/MM/YYYY')).replace('{horaire}', moment(value.start).format('HH:mm'))}</span>
  278. 278 : <span class="element-desc secondary-text element-block">${value.title}</span>
  279. 279 : </a>
  280. 280 : </li>`;
  281. 281 : }
  282. 282 : else html += `<li>Pas d'évènements aujourd'hui ainsi que dans les 7 prochains jours !</li>`;
  283. 283 : }
  284. 284 : }
  285. 285 :
  286. 286 : if (!get_only_body)
  287. 287 : {
  288. 288 : html += "</ul>";
  289. 289 : html += "</div>";
  290. 290 : }
  291. 291 : else
  292. 292 : html = `<ul class="ignore-bullet">${html}</ul>`;
  293. 293 :
  294. 294 : if (config.add_create === true)
  295. 295 : {
  296. 296 : const data_text = 'Créer <span class="icon-mel-plus plus"></span>';
  297. 297 : if (!html.includes(data_text))
  298. 298 : html += html_helper(html_helper.options.create_button, data_text, {
  299. 299 : onclick:config.create_function === undefined || config.create_function === null ? "html_helper.Calendars.create()" : config.create_function
  300. 300 : });
  301. 301 : }
  302. 302 :
  303. 303 : if (e !== null) e.html(html);
  304. 304 : if (e_number !== null)
  305. 305 : {
  306. 306 : if (datas.length > 0)
  307. 307 : {
  308. 308 : e_number.html(datas.length);
  309. 309 : e_number.removeClass("hidden");
  310. 310 : }
  311. 311 : else
  312. 312 : e_number.addClass("hidden");
  313. 313 : }
  314. 314 :
  315. 315 : if (!!e && !!$jquery_array) {
  316. 316 : e.find('ul').html($jquery_array);
  317. 317 : }
  318. 318 :
  319. 319 : return html;
  320. 320 : }
  321. 321 :
  322. 322 : html_helper.Calendars.create = function(config = {
  323. 323 : date:moment(),
  324. 324 : selector:null
  325. 325 : })
  326. 326 : {
  327. 327 :
  328. 328 : let date;
  329. 329 :
  330. 330 : if (config.selector !== undefined && config.selector !== null)
  331. 331 : date = $(config.selector).data("current-date");
  332. 332 : else if (config.date !== undefined && config.date !== null)
  333. 333 : date = config.date;
  334. 334 : else
  335. 335 : date = moment();
  336. 336 :
  337. 337 : const e = {
  338. 338 : target:null
  339. 339 : };
  340. 340 :
  341. 341 : let event = {
  342. 342 : start:moment(`${moment(date).format("YYYY-MM-DD")} ${moment().format("HH:mm")}`),
  343. 343 : end:null//moment().add(1, "h")
  344. 344 : };
  345. 345 :
  346. 346 : event.end = moment(event.start).add(1, "h");
  347. 347 : rcmail.local_storage_set_item("tmp_calendar_event", event);
  348. 348 : //console.log("[html_helper.Calendars.create]", event, date, config);
  349. 349 :
  350. 350 : return rcmail.commands['add-event-from-shortcut'] ? rcmail.command('add-event-from-shortcut', '', e.target, e) : rcmail.command('addevent', '', e.target, e);
  351. 351 : }
  352. 352 :
  353. 353 : html_helper.Calendars.generate_link = function(event)
  354. 354 : {
  355. 355 : let link = "";
  356. 356 :
  357. 357 : if (SearchResultCalendar && SearchResultCalendar.CreateOrOpen)
  358. 358 : link = `SearchResultCalendar.CreateOrOpen('${JSON.stringify(event).replaceAll('"', '£¤£').replaceAll("'", "µ¤¤µ")}')`;
  359. 359 :
  360. 360 : return link;
  361. 361 : }
  362. 362 :
  363. 363 : /**
  364. 364 : * Classe qui permet de générer du html
  365. 365 : */
  366. 366 : class mel_html{
  367. 367 : /**
  368. 368 : * Constructeur de la classe
  369. 369 : * @param {string} tag Balise (exemple : div, li, ul etc...)
  370. 370 : * @param {Object} attribs Attributs de la balise
  371. 371 : * @param {string} content Contenu de la balise
  372. 372 : */
  373. 373 : constructor(tag, attribs = {}, content = '')
  374. 374 : {
  375. 375 : this.tag = tag.toLowerCase();
  376. 376 : this.attribs = attribs;
  377. 377 : this.content = content;
  378. 378 : this.onclick = new MelEvent();
  379. 379 : this.onkeydown = new MelEvent();
  380. 380 : this.onmouseover = new MelEvent();
  381. 381 : this.onmouseout = new MelEvent();
  382. 382 : this.aftergenerate = new MelEvent();
  383. 383 : }
  384. 384 :
  385. 385 : /**
  386. 386 : * Actions à faire avant de générer l'élément jquery
  387. 387 : * @abstract
  388. 388 : * @private Cette fonction est privée
  389. 389 : */
  390. 390 : _before_generate() {}
  391. 391 :
  392. 392 : /**
  393. 393 : * Récupère le jquery de ces données html
  394. 394 : * @param {Object} additionnal_attribs Attributs additionnels
  395. 395 : * @returns {$}
  396. 396 : */
  397. 397 : generate(additionnal_attribs = {})
  398. 398 : {
  399. 399 : this._before_generate();
  400. 400 :
  401. 401 : let multi_balise = true;
  402. 402 :
  403. 403 : switch (this.tag) {
  404. 404 : case CONST_HTML_IMG:
  405. 405 : case CONST_HTML_INPUT:
  406. 406 : case CONST_HTML_BR:
  407. 407 : multi_balise = false;
  408. 408 : break;
  409. 409 :
  410. 410 : default:
  411. 411 : break;
  412. 412 : }
  413. 413 :
  414. 414 : if (multi_balise && !!this.attribs[mel_html.ATTRIB_NO_MULTI_BALISE]) multi_balise = false;
  415. 415 :
  416. 416 : let $html = $(`${CONST_BALISE_START}${this.tag} ${(!multi_balise ? CONST_BALISE_CLOSE_END : CONST_BALISE_END)}${(multi_balise ? `${CONST_BALISE_CLOSE_START}${this.tag}${CONST_BALISE_END}` : EMPTY_STRING)}`);
  417. 417 :
  418. 418 : for (const iterator of Enumerable.from(this.attribs).concat(additionnal_attribs).where(x => 0 === x || !!(x || null))) {
  419. 419 : switch (iterator.key) {
  420. 420 : case CONST_ATTRIB_CLASS:
  421. 421 : $html.addClass(Array.isArray(iterator.value) ? iterator.value.join(' ') : iterator.value);
  422. 422 : break;
  423. 423 : case mel_html.EVENT_ON:
  424. 424 : for (const key in iterator.value) {
  425. 425 : if (Object.hasOwnProperty.call(iterator.value, key)) {
  426. 426 : const element = iterator.value[key];
  427. 427 : $html.on(key, element);
  428. 428 : }
  429. 429 : }
  430. 430 : break;
  431. 431 : case 'style':
  432. 432 : iterator.value = this._getStyle();
  433. 433 : default:
  434. 434 : $html.attr(iterator.key, iterator.value);
  435. 435 : break;
  436. 436 : }
  437. 437 : }
  438. 438 :
  439. 439 : let generated = this._generateContent($html, this.content);
  440. 440 : generated = this.bind_events(generated);
  441. 441 :
  442. 442 : if (this.aftergenerate.count() > 0) {
  443. 443 : this.aftergenerate.call(generated);
  444. 444 : }
  445. 445 :
  446. 446 : return generated;
  447. 447 : }
  448. 448 :
  449. 449 : /**
  450. 450 : * Ajoute les évènements de l'objet à un élement jquery
  451. 451 : * @param {$} $element
  452. 452 : * @returns {$} Elément avec les actions
  453. 453 : */
  454. 454 : bind_events($element) {
  455. 455 : if (this.onclick.haveEvents()) $element.on(CONST_EVENT_ACTION_CLICK, (event) => {
  456. 456 : this.onclick.call(event);
  457. 457 : });
  458. 458 :
  459. 459 : if (this.onkeydown.haveEvents()) $element.on('keydown', (event) => {
  460. 460 : this.onkeydown.call(event);
  461. 461 : });
  462. 462 :
  463. 463 : if (this.onmouseover.haveEvents()) $element.on('mouseover', (event) => {
  464. 464 : this.onmouseover.call(event);
  465. 465 : });
  466. 466 :
  467. 467 : if (this.onmouseout.haveEvents()) $element.on('mouseout', (event) => {
  468. 468 : this.onmouseout.call(event);
  469. 469 : });
  470. 470 :
  471. 471 : return $element;
  472. 472 : }
  473. 473 :
  474. 474 : /**
  475. 475 : * Ajoute une classe à la liste des classes de cet élément html
  476. 476 : * @param {string} classes Classe à ajouter
  477. 477 : * @returns Chaînage
  478. 478 : */
  479. 479 : addClass(classes) {
  480. 480 : if (!this.attribs) this.attribs = {};
  481. 481 : if (!(this.attribs[CONST_ATTRIB_CLASS] || null)) this.attribs[CONST_ATTRIB_CLASS] = [];
  482. 482 : else if (STRING === typeof this.attribs[CONST_ATTRIB_CLASS]) this.attribs[CONST_ATTRIB_CLASS] = [this.attribs[CONST_ATTRIB_CLASS]];
  483. 483 :
  484. 484 : this.attribs[CONST_ATTRIB_CLASS].push(classes);
  485. 485 : return this;
  486. 486 : }
  487. 487 :
  488. 488 : /**
  489. 489 : * Vérifie si l'élément possède une classe en particulier
  490. 490 : * @param {string} html_class Classe à vérifier
  491. 491 : * @returns {boolean}
  492. 492 : */
  493. 493 : hasClass(html_class){
  494. 494 : if (!!this.attribs && !!this.attribs[CONST_ATTRIB_CLASS]) {
  495. 495 : if (STRING === typeof this.attribs[CONST_ATTRIB_CLASS]) return html_class === this.attribs[CONST_ATTRIB_CLASS];
  496. 496 : else return this.attribs[CONST_ATTRIB_CLASS].includes(html_class);
  497. 497 : }
  498. 498 :
  499. 499 : return false;
  500. 500 : }
  501. 501 :
  502. 502 : /**
  503. 503 : * Ajoute du css initial à l'élément
  504. 504 : * @param {string} key Propriété css (ex: display)
  505. 505 : * @param {string} value Valeur css (ex : none)
  506. 506 : * @returns Chaînage
  507. 507 : */
  508. 508 : css(key, value) {
  509. 509 : if (!this.attribs) this.attribs = {};
  510. 510 : if (!(this.attribs['style'] || null)) this.attribs['style'] = {};
  511. 511 : else if (STRING === typeof this.attribs['style']) {
  512. 512 : const splited = this.attribs['style'].split(CONST_CSS_SEPARATOR);
  513. 513 : this.attribs['style'] = {};
  514. 514 : for (let index = 0, len = splited.length; index < len; ++index) {
  515. 515 : const element = splited[index].split(CONST_CSS_ASSIGN);
  516. 516 : this.attribs['style'][element[0]] = element[1];
  517. 517 : }
  518. 518 : }
  519. 519 :
  520. 520 : this.attribs['style'][key] = value;
  521. 521 : return this;
  522. 522 : }
  523. 523 :
  524. 524 : /**
  525. 525 : * Ajoute un attribut à l'élément
  526. 526 : * @param {string} key Nom de l'attribut
  527. 527 : * @param {string | number | Boolean} value Valeur de l'attribut
  528. 528 : * @returns Chaînage
  529. 529 : */
  530. 530 : setAttr(key, value) {
  531. 531 : if (!this.attribs) this.attribs = {};
  532. 532 :
  533. 533 : this.attribs[key] = value;
  534. 534 : return this;
  535. 535 : }
  536. 536 :
  537. 537 : /**
  538. 538 : * Met un id à l'élément
  539. 539 : * @param {string} id
  540. 540 : * @returns Chaînage
  541. 541 : */
  542. 542 : setId(id) {
  543. 543 : return this.setAttr('id', id);
  544. 544 : }
  545. 545 :
  546. 546 : toString() {
  547. 547 : return this.generate()[0].outerHTML;
  548. 548 : }
  549. 549 :
  550. 550 : _getStyle() {
  551. 551 : if (!this.attribs['style'] || STRING === typeof this.attribs['style']) return this.attribs['style'] || EMPTY_STRING;
  552. 552 : else {
  553. 553 : let array = [];
  554. 554 : for (let keys = Object.keys(this.attribs['style']), index = 0, len = keys.length; index < len; ++index) {
  555. 555 : const key = keys[index];
  556. 556 : const value = this.attribs['style'][key];
  557. 557 : array.push([key, value].join(CONST_CSS_ASSIGN));
  558. 558 : }
  559. 559 :
  560. 560 : array = array.join(CONST_CSS_SEPARATOR);
  561. 561 :
  562. 562 : return (EMPTY_STRING === array ? EMPTY_STRING : (array + CONST_CSS_SEPARATOR));
  563. 563 : }
  564. 564 : }
  565. 565 :
  566. 566 : /**
  567. 567 : * Génère un élément jquery à partir des données de cet élément.
  568. 568 : *
  569. 569 : * L'ajoute ensuite à un élément parent.
  570. 570 : * @param {$} $parent Elément parent qui contiendra l'objet
  571. 571 : * @param {Object} additionnal_attribs Attributs additionnels
  572. 572 : * @returns {$}
  573. 573 : */
  574. 574 : create($parent, additionnal_attribs = [])
  575. 575 : {
  576. 576 : return this.generate(additionnal_attribs).appendTo($parent);
  577. 577 : }
  578. 578 :
  579. 579 : /**
  580. 580 : * Ajoute le html à l'élément jquery générer
  581. 581 : * @param {$} $html Elément jquery
  582. 582 : * @param {string} content html générer
  583. 583 : * @returns {$}
  584. 584 : * @private
  585. 585 : */
  586. 586 : _generateContent($html, content) {
  587. 587 : return $html.html(content);
  588. 588 : }
  589. 589 :
  590. 590 : /**
  591. 591 : * Récupère un mel_html div
  592. 592 : * @param {Object} attribs Attributs de l'élément
  593. 593 : * @param {string} content Contenue de l'élément
  594. 594 : * @returns {mel_html}
  595. 595 : */
  596. 596 : static div(attribs = {}, content = '') {
  597. 597 : return new mel_html(CONST_HTML_DIV, attribs, content);
  598. 598 : }
  599. 599 : }
  600. 600 :
  601. 601 : Object.defineProperty(mel_html, 'EVENT_ON', {
  602. 602 : enumerable: false,
  603. 603 : configurable: false,
  604. 604 : writable: false,
  605. 605 : value:CONST_EVENT_ON
  606. 606 : });
  607. 607 :
  608. 608 : Object.defineProperty(mel_html, 'ATTRIB_NO_MULTI_BALISE', {
  609. 609 : enumerable: false,
  610. 610 : configurable: false,
  611. 611 : writable: false,
  612. 612 : value:CONST_ATTRIB_NO_MULTI_BALISE
  613. 613 : });
  614. 614 :
  615. 615 : Object.defineProperty(mel_html, 'select_html', {
  616. 616 : enumerable: false,
  617. 617 : configurable: false,
  618. 618 : writable: false,
  619. 619 : value:() => $(CONST_HTML_HTML)
  620. 620 : });
  621. 621 :
  622. 622 : /**
  623. 623 : * Classe qui permet de générer du html.
  624. 624 : *
  625. 625 : * Accepte plusieurs mel_html enfants pour génrer son html
  626. 626 : */
  627. 627 : class mel_html2 extends mel_html {
  628. 628 : /**
  629. 629 : * Constructeur de la classe
  630. 630 : * @param {string} tag Balise de l'élément
  631. 631 : * @param {Object} options - Les options du constructeur.
  632. 632 : * @param {Object} options.attribs - Attributs de l'élément
  633. 633 : * @param {[] | mel_html | string} options.contents - Eléments enfants
  634. 634 : */
  635. 635 : constructor(tag, {attribs={}, contents=[]}) {
  636. 636 : super(tag, attribs, EMPTY_STRING);
  637. 637 : this._init()._setup(contents);
  638. 638 : }
  639. 639 :
  640. 640 : _init() {
  641. 641 : this.jcontents = [];
  642. 642 : return this;
  643. 643 : }
  644. 644 :
  645. 645 : _setup(contents = []) {
  646. 646 : if (!Array.isArray(contents)) {
  647. 647 : if ('string' === typeof contents) contents = [new mel_html('span', {}, contents)];
  648. 648 : else contents = [contents];
  649. 649 : }
  650. 650 :
  651. 651 : this.jcontents = contents;
  652. 652 : return this;
  653. 653 : }
  654. 654 :
  655. 655 : /**
  656. 656 : * Ajoute un élément enfant
  657. 657 : * @param {mel_html} mel_html Elément à ajouter
  658. 658 : * @returns Chaînage
  659. 659 : */
  660. 660 : addContent(mel_html) {
  661. 661 : this.jcontents.push(mel_html);
  662. 662 : mel_html.parent = this;
  663. 663 : return this;
  664. 664 : }
  665. 665 :
  666. 666 : _generateContent($html) {
  667. 667 : for (let index = 0, len = this.jcontents.length; index < len; ++index) {
  668. 668 : const element = this.jcontents[index];
  669. 669 : element.create($html);
  670. 670 : }
  671. 671 :
  672. 672 : return $html;
  673. 673 : }
  674. 674 :
  675. 675 : /**
  676. 676 : * Ajoute cet élément à un html parent
  677. 677 : * @param {mel_html2} mel_html2
  678. 678 : * @returns Chaînage
  679. 679 : */
  680. 680 : appendTo(mel_html2) {
  681. 681 : mel_html2.addContent(this);
  682. 682 : return this;
  683. 683 : }
  684. 684 :
  685. 685 : /**
  686. 686 : * Taille des éléments enfants
  687. 687 : * @returns {number}
  688. 688 : */
  689. 689 : count() {
  690. 690 : return this.jcontents.length;
  691. 691 : }
  692. 692 :
  693. 693 : /**
  694. 694 : * Récupère un élément enfant par son id
  695. 695 : * @param {string} id Id de l'élément à retrouver
  696. 696 : * @returns {mel_html | null}
  697. 697 : */
  698. 698 : find_by_id(id) {
  699. 699 : return Enumerable.from(this.jcontents).where(x => x.attribs['id'] === id).firstOrDefault();
  700. 700 : }
  701. 701 :
  702. 702 : /**
  703. 703 : * Récupère des éléments enfant par une classe
  704. 704 : * @param {string} html_class Classe que l'on recherche
  705. 705 : * @returns {Enumerable<mel_html>}
  706. 706 : */
  707. 707 : find_by_class(html_class) {
  708. 708 : return Enumerable.from(this.jcontents).where(x => x.hasClass(html_class));
  709. 709 : }
  710. 710 :
  711. 711 : /**
  712. 712 : * Récupère le premier élément enfant
  713. 713 : * @returns {mel_html}
  714. 714 : */
  715. 715 : first() {
  716. 716 : return this.jcontents[0];
  717. 717 : }
  718. 718 :
  719. 719 : /**
  720. 720 : * Récupère le premier élément enfant ou une valeur par défaut
  721. 721 : * @param {* | null} _default Renvoyer si first n'éxiste pas
  722. 722 : * @returns {mel_html | * | null}
  723. 723 : */
  724. 724 : firstOrDefault(_default = null) {
  725. 725 : try {
  726. 726 : return this.first();
  727. 727 : } catch (error) {
  728. 728 : return _default;
  729. 729 : }
  730. 730 : }
  731. 731 :
  732. 732 : /**
  733. 733 : * Créer un mel_html2 qui représente une div
  734. 734 : * @param {Object} options - Les options du constructeur.
  735. 735 : * @param {Object} options.attribs - Attributs de l'élément
  736. 736 : * @param {[] | mel_html | string} options.contents - Eléments enfants
  737. 737 : * @returns {mel_html2}
  738. 738 : */
  739. 739 : static div({attribs={}, contents=[]}) {
  740. 740 : return new mel_html2('div', {attribs, contents});
  741. 741 : }
  742. 742 : }
  743. 743 :
  744. 744 : class mel_option extends mel_html{
  745. 745 : constructor(value, text, attribs = [])
  746. 746 : {
  747. 747 : super(CONST_HTML_SELECT, attribs, text);
  748. 748 : this.attribs[CONST_ATTRIB_VALUE] = value;
  749. 749 : }
  750. 750 : }
  751. 751 :
  752. 752 : class amel_form_item extends mel_html {
  753. 753 : constructor(tag, attribs = {}, content = EMPTY_STRING, ignore_default_action = false)
  754. 754 : {
  755. 755 : super(tag, attribs, content);
  756. 756 :
  757. 757 : if (!ignore_default_action) {
  758. 758 : if (!this.attribs[CONST_ATTRIB_CLASS]) this.attribs[CONST_ATTRIB_CLASS] = EMPTY_STRING;
  759. 759 :
  760. 760 : if (!this.attribs[CONST_ATTRIB_VALUE]) {
  761. 761 : this.attribs[CONST_ATTRIB_VALUE] = `${amel_form_item.CLASS_FORM_BASE} ${amel_form_item.CLASS_FORM_MEL}`;
  762. 762 : }
  763. 763 :
  764. 764 : if (!this.attribs[CONST_ATTRIB_CLASS].includes(amel_form_item.CLASS_FORM_BASE))
  765. 765 : {
  766. 766 : this.attribs[CONST_ATTRIB_CLASS] += ` ${amel_form_item.CLASS_FORM_BASE}`;
  767. 767 : }
  768. 768 :
  769. 769 : if (!this.attribs[CONST_ATTRIB_CLASS].includes(amel_form_item.CLASS_FORM_MEL)) this.attribs[CONST_ATTRIB_CLASS] += ` ${amel_form_item.CLASS_FORM_MEL}`;
  770. 770 : }
  771. 771 :
  772. 772 : this.onfocusout = new MelEvent();
  773. 773 : this.onfocus = new MelEvent();
  774. 774 : this.onchange = new MelEvent();
  775. 775 : this.oninput = new MelEvent();
  776. 776 : }
  777. 777 :
  778. 778 : toFloatingLabel(label) {
  779. 779 : let $label = new mel_html(CONST_HTML_DIV, {class:amel_form_item.CLASS_FORM_FLOATING}, this.generate({required:CONST_ATTRIB_REQUIRED})).generate();
  780. 780 : return $label.append(new mel_html(CONST_HTML_LABEL, {for:this.attribs[CONST_ATTRIB_ID]}, label).generate());
  781. 781 : }
  782. 782 :
  783. 783 : generate(value, additionnal_attribs = {})
  784. 784 : {
  785. 785 : let generated = super.generate(additionnal_attribs).val(value);
  786. 786 :
  787. 787 : if (this.onfocus.haveEvents()) {
  788. 788 : generated.on(CONST_EVENT_ACTION_FOCUS, (event) => {
  789. 789 : this.onfocus.call(event);
  790. 790 : });
  791. 791 : }
  792. 792 :
  793. 793 : if (this.onfocusout.haveEvents()) {
  794. 794 : generated.on(CONST_EVENT_ACTION_BLUR, (event) => {
  795. 795 : this.onfocusout.call(event);
  796. 796 : });
  797. 797 : }
  798. 798 :
  799. 799 : if (this.oninput.haveEvents()) {
  800. 800 : generated.on(CONST_EVENT_ACTION_INPUT, (event) => {
  801. 801 : this.oninput.call(event);
  802. 802 : });
  803. 803 : }
  804. 804 :
  805. 805 : if (this.onchange.haveEvents()) {
  806. 806 : generated.on(CONST_EVENT_ACTION_CHANGE, (event) => {
  807. 807 : this.onchange.call(event);
  808. 808 : });
  809. 809 : }
  810. 810 :
  811. 811 : return generated;
  812. 812 : }
  813. 813 : }
  814. 814 :
  815. 815 : Object.defineProperty(amel_form_item, 'CLASS_FORM_BASE', {
  816. 816 : enumerable: false,
  817. 817 : configurable: false,
  818. 818 : writable: false,
  819. 819 : value:CONST_CLASS_INPUT_FORM_BASE
  820. 820 : });
  821. 821 :
  822. 822 : Object.defineProperty(amel_form_item, 'CLASS_FORM_MEL', {
  823. 823 : enumerable: false,
  824. 824 : configurable: false,
  825. 825 : writable: false,
  826. 826 : value:CONST_CLASS_INPUT_FORM_MEL
  827. 827 : });
  828. 828 :
  829. 829 : Object.defineProperty(amel_form_item, 'CLASS_FORM_FLOATING', {
  830. 830 : enumerable: false,
  831. 831 : configurable: false,
  832. 832 : writable: false,
  833. 833 : value:CONST_CLASS_INPUT_FORM_FLOATING
  834. 834 : });
  835. 835 :
  836. 836 : Object.defineProperty(amel_form_item, 'CLASS_FORM_FLOATING_FOCUS', {
  837. 837 : enumerable: false,
  838. 838 : configurable: false,
  839. 839 : writable: false,
  840. 840 : value:CONST_CLASS_INPUT_FORM_FLOATING_FOCUS
  841. 841 : });
  842. 842 :
  843. 843 : Object.defineProperty(amel_form_item, 'CLASS_INPUT_FORM_FLOATING_NOT_EMPTY', {
  844. 844 : enumerable: false,
  845. 845 : configurable: false,
  846. 846 : writable: false,
  847. 847 : value:CONST_CLASS_INPUT_FORM_FLOATING_NOT_EMPTY
  848. 848 : });
  849. 849 :
  850. 850 : class mel_field extends amel_form_item {
  851. 851 : constructor(tag, attribs = {})
  852. 852 : {
  853. 853 : super(tag, attribs, '', true);
  854. 854 : }
  855. 855 : }
  856. 856 :
  857. 857 :
  858. 858 : class mel_select extends amel_form_item{
  859. 859 : constructor(attribs = {}, options = [])
  860. 860 : {
  861. 861 : super(CONST_HTML_SELECT, attribs, options);
  862. 862 : }
  863. 863 :
  864. 864 : _generateContent($html, content) {
  865. 865 : for (const iterator of content) {
  866. 866 : iterator.create($html);
  867. 867 : }
  868. 868 :
  869. 869 : return $html;
  870. 870 : }
  871. 871 : }
  872. 872 :
  873. 873 : class mel_input extends amel_form_item
  874. 874 : {
  875. 875 : constructor(attribs = {})
  876. 876 : {
  877. 877 : super(CONST_HTML_INPUT, attribs, EMPTY_STRING);
  878. 878 : }
  879. 879 :
  880. 880 : static togglePasswordShowed(element) {
  881. 881 : const INPUT_DATA = CONST_ATTRIB_FOR;
  882. 882 : const IS_SHOWED_DATA = 'isShowed';
  883. 883 : const DATA_VALID = 'yes';
  884. 884 : const DATA_INVALID = 'no';
  885. 885 : const ATTR = CONST_ATTRIB_TYPE;
  886. 886 : const ATTR_PASSWORD = CONST_ATTRIB_TYPE_PASSWORD;
  887. 887 : const ATTR_TEXT = CONST_ATTRIB_TYPE_TEXT;
  888. 888 : element = $(element);
  889. 889 : let $input = $(`${CONST_JQUERY_SELECTOR_ID}${element.data(INPUT_DATA)}`);
  890. 890 :
  891. 891 : if (0 === $input.length) $input = top.$(`${CONST_JQUERY_SELECTOR_ID}${element.data(INPUT_DATA)}`);
  892. 892 :
  893. 893 : if (0 < $input.length)
  894. 894 : {
  895. 895 : if (DATA_VALID === $input.data(IS_SHOWED_DATA)) {
  896. 896 : $input.attr(ATTR, ATTR_PASSWORD);
  897. 897 : $input.data(IS_SHOWED_DATA, DATA_INVALID);
  898. 898 : }
  899. 899 : else {
  900. 900 : $input.attr(ATTR, ATTR_TEXT);
  901. 901 : $input.data(IS_SHOWED_DATA, DATA_VALID);
  902. 902 : }
  903. 903 : }
  904. 904 :
  905. 905 : mel_input.togglePasswordShowed.updateButton(element);
  906. 906 : }
  907. 907 :
  908. 908 : static floatingSetFocusClass(element, isOut = false) {
  909. 909 : const DIV = CONST_ATTRIB_FOR;
  910. 910 : const CLASS = amel_form_item.CLASS_FORM_FLOATING_FOCUS;
  911. 911 : element = $(element);
  912. 912 : let $div = $(`${CONST_JQUERY_SELECTOR_ID}${element.data(DIV)}`);
  913. 913 :
  914. 914 : if (0 === $div.length) $div = top.$(`${CONST_JQUERY_SELECTOR_ID}${element.data(DIV)}`);
  915. 915 :
  916. 916 : if ($div.length > 0) {
  917. 917 : if (isOut) {
  918. 918 : $div.removeClass(CLASS);
  919. 919 : }
  920. 920 : else {
  921. 921 : $div.addClass(CLASS);
  922. 922 : }
  923. 923 : }
  924. 924 : }
  925. 925 :
  926. 926 : static floatingSetInput(element) {
  927. 927 : const DIV = CONST_ATTRIB_FOR;
  928. 928 : const CLASS = amel_form_item.CLASS_INPUT_FORM_FLOATING_NOT_EMPTY;
  929. 929 : element = $(element);
  930. 930 : let $div = $(`${CONST_JQUERY_SELECTOR_ID}${element.data(DIV)}`);
  931. 931 :
  932. 932 : if ($div.length === 0) $div = top.$(`${CONST_JQUERY_SELECTOR_ID}${element.data(DIV)}`);
  933. 933 :
  934. 934 : if ($div.length > 0) {
  935. 935 : if (EMPTY_STRING !== element.val()) $div.addClass(CLASS);
  936. 936 : else $div.removeClass(CLASS);
  937. 937 : }
  938. 938 : }
  939. 939 : }
  940. 940 :
  941. 941 : class mel_label_input extends mel_input {
  942. 942 : constructor(id, type, label, attribs = {}) {
  943. 943 : super(attribs);
  944. 944 :
  945. 945 : this.id = id;
  946. 946 : this.type = type;
  947. 947 : this.label = label;
  948. 948 : }
  949. 949 :
  950. 950 : _before_generate() {
  951. 951 : this.setId(this.id);
  952. 952 : this.attribs.type = this.type;
  953. 953 : }
  954. 954 :
  955. 955 : generate(value, attribs = {}, parent_attribs = {}) {
  956. 956 : let $generated = super.generate(value, attribs);
  957. 957 :
  958. 958 : const html_label = new mel_html('label', {for:this.id}, this.label);
  959. 959 : let $parent_div = new mel_html2('div', {
  960. 960 : attribs:parent_attribs,
  961. 961 : contents:html_label
  962. 962 : }).generate();
  963. 963 :
  964. 964 : $parent_div.append($generated);
  965. 965 : $generated = null;
  966. 966 :
  967. 967 : return $parent_div;
  968. 968 : }
  969. 969 : }
  970. 970 :
  971. 971 : class mel_password extends mel_input {
  972. 972 : constructor(attribs = {})
  973. 973 : {
  974. 974 : super(attribs);
  975. 975 : this.attribs[CONST_ATTRIB_TYPE] = CONST_ATTRIB_TYPE_PASSWORD;
  976. 976 : }
  977. 977 : }
  978. 978 :
  979. 979 : mel_input.togglePasswordShowed.updateButton = function ($event) {
  980. 980 : const DATA_SHOW = 'icon-show';
  981. 981 : const DATA_HIDE = 'icon-hide';
  982. 982 : const BALISE = CONST_HTML_SPAN;
  983. 983 :
  984. 984 : const icon_show = $event.data(DATA_SHOW);
  985. 985 :
  986. 986 : if (!!icon_show) {
  987. 987 : const icon_hide = $event.data(DATA_HIDE);
  988. 988 :
  989. 989 : if (!!icon_hide) {
  990. 990 : let $span = $event.find(BALISE);
  991. 991 :
  992. 992 : if ($span.hasClass(icon_show)) {
  993. 993 : $span.removeClass(icon_show).addClass(icon_hide);
  994. 994 : } else {
  995. 995 : $span.removeClass(icon_hide).addClass(icon_show);
  996. 996 : }
  997. 997 : }
  998. 998 : }
  999. 999 :
  1000. 1000 :
  1001. 1001 : }
  1002. 1002 :
  1003. 1003 : class mel_password_with_button extends mel_password{
  1004. 1004 : constructor(id, input_id, attribs = {}, attribsOnParent = {}, attribsOnButton = {}) {
  1005. 1005 : super(attribs);
  1006. 1006 : this.id = id;
  1007. 1007 : this._id = input_id;
  1008. 1008 : this.main = new mel_html('div', attribsOnParent);
  1009. 1009 : this.button = new mel_button(attribsOnButton);
  1010. 1010 : this.button_span = new mel_html('span', {class:'icon-mel-eye'});
  1011. 1011 :
  1012. 1012 : this.onfocus.push(function (event) {
  1013. 1013 : mel_input.floatingSetFocusClass(event.currentTarget, false);
  1014. 1014 : });
  1015. 1015 :
  1016. 1016 : this.onfocusout.push(function (event) {
  1017. 1017 : mel_input.floatingSetFocusClass(event.currentTarget, true);
  1018. 1018 : });
  1019. 1019 :
  1020. 1020 : this.oninput.push(function (event) {
  1021. 1021 : mel_input.floatingSetInput(event.currentTarget);
  1022. 1022 : });
  1023. 1023 :
  1024. 1024 : this.button.onclick.push(function (event) {
  1025. 1025 : mel_input.togglePasswordShowed(event.currentTarget);
  1026. 1026 : });
  1027. 1027 : }
  1028. 1028 :
  1029. 1029 : generate(value, label = 'Mot de passe', additionnal_attribs = {})
  1030. 1030 : {
  1031. 1031 : const DATA_FOR = 'data-for';
  1032. 1032 : const CLASS_INPUT_GROUP = 'input-group';
  1033. 1033 : const CLASS_RETURN = 'form-floating pixel-correction';
  1034. 1034 : const ATTR_ID = 'id';
  1035. 1035 : const ATTR_REQUIRED = 'required';
  1036. 1036 : const BALISE_LABEL = 'label';
  1037. 1037 : const BALISE_DIV = 'div';
  1038. 1038 :
  1039. 1039 : const button_config = {
  1040. 1040 : 'data-for':this._id,
  1041. 1041 : 'data-icon-show':mel_password_with_button.password_show_button,
  1042. 1042 : 'data-icon-hide':mel_password_with_button.password_hide_button,
  1043. 1043 : };
  1044. 1044 : const main_config = {class:CLASS_INPUT_GROUP};
  1045. 1045 : const return_config = {id:this.id, class:CLASS_RETURN};
  1046. 1046 : const label_config = {for:button_config[DATA_FOR]};
  1047. 1047 :
  1048. 1048 : additionnal_attribs[DATA_FOR] = return_config.id;
  1049. 1049 : additionnal_attribs[ATTR_ID] = button_config[DATA_FOR];
  1050. 1050 : additionnal_attribs[ATTR_REQUIRED] = ATTR_REQUIRED;
  1051. 1051 : additionnal_attribs['class'] = 'input-mel';
  1052. 1052 :
  1053. 1053 : let $input = super.generate(value, additionnal_attribs);
  1054. 1054 : let $button = new mel_html(BALISE_DIV, {class:'input-group-append'}).generate().append(this.button.generate(button_config).append(this.button_span.generate()));
  1055. 1055 : let $main = this.main.generate(main_config).append($input).append($button);
  1056. 1056 : let $label = new mel_html(BALISE_LABEL, label_config, label);
  1057. 1057 :
  1058. 1058 : return new mel_html(BALISE_DIV).generate(return_config).append($main).append($label.generate());
  1059. 1059 : }
  1060. 1060 : }
  1061. 1061 :
  1062. 1062 : Object.defineProperty(mel_password_with_button, 'password_show_button', {
  1063. 1063 : enumerable: false,
  1064. 1064 : configurable: false,
  1065. 1065 : writable: false,
  1066. 1066 : value:CONST_ICON_EYE
  1067. 1067 : });
  1068. 1068 :
  1069. 1069 : Object.defineProperty(mel_password_with_button, 'password_hide_button', {
  1070. 1070 : enumerable: false,
  1071. 1071 : configurable: false,
  1072. 1072 : writable: false,
  1073. 1073 : value:CONST_ICON_EYE_CROSSED
  1074. 1074 : });
  1075. 1075 :
  1076. 1076 : class mel_button extends mel_html {
  1077. 1077 : constructor(attribs = {}, content = EMPTY_STRING)
  1078. 1078 : {
  1079. 1079 : super(CONST_HTML_BUTTON, attribs, content);
  1080. 1080 : this.attribs[CONST_ATTRIB_CLASS] = mel_button.html_base_class_full;//'mel-button btn btn-secondary no-button-margin'
  1081. 1081 : }
  1082. 1082 : }
  1083. 1083 :
  1084. 1084 : {
  1085. 1085 : let item = {};
  1086. 1086 : let bootstrap = {};
  1087. 1087 : Object.defineProperty(item, 'base', {
  1088. 1088 : enumerable: false,
  1089. 1089 : configurable: false,
  1090. 1090 : writable: false,
  1091. 1091 : value:CONST_CLASS_BUTTON_MEL
  1092. 1092 : });
  1093. 1093 :
  1094. 1094 : Object.defineProperty(bootstrap, 'base', {
  1095. 1095 : enumerable: false,
  1096. 1096 : configurable: false,
  1097. 1097 : writable: false,
  1098. 1098 : value:CONST_CLASS_BUTTON_BASE
  1099. 1099 : });
  1100. 1100 :
  1101. 1101 : Object.defineProperty(bootstrap, 'state', {
  1102. 1102 : enumerable: false,
  1103. 1103 : configurable: false,
  1104. 1104 : writable: false,
  1105. 1105 : value:CONST_CLASS_BUTTON_SECONDARY
  1106. 1106 : });
  1107. 1107 :
  1108. 1108 : Object.defineProperty(item, 'bootstrap', {
  1109. 1109 : enumerable: false,
  1110. 1110 : configurable: false,
  1111. 1111 : writable: false,
  1112. 1112 : value:bootstrap
  1113. 1113 : });
  1114. 1114 :
  1115. 1115 : Object.defineProperty(mel_button, 'html_base_class', {
  1116. 1116 : enumerable: false,
  1117. 1117 : configurable: false,
  1118. 1118 : writable: false,
  1119. 1119 : value:item
  1120. 1120 : });
  1121. 1121 : }
  1122. 1122 :
  1123. 1123 : Object.defineProperty(mel_button, 'html_base_class_no_margin', {
  1124. 1124 : enumerable: false,
  1125. 1125 : configurable: false,
  1126. 1126 : writable: false,
  1127. 1127 : value:CONST_CLASS_BUTTON_MEL_NO_MARGIN
  1128. 1128 : });
  1129. 1129 :
  1130. 1130 : Object.defineProperty(mel_button, 'html_base_class_success', {
  1131. 1131 : enumerable: false,
  1132. 1132 : configurable: false,
  1133. 1133 : writable: false,
  1134. 1134 : value:CONST_CLASS_BUTTON_SUCCESS
  1135. 1135 : });
  1136. 1136 :
  1137. 1137 : Object.defineProperty(mel_button, 'html_base_class_danger', {
  1138. 1138 : enumerable: false,
  1139. 1139 : configurable: false,
  1140. 1140 : writable: false,
  1141. 1141 : value:CONST_CLASS_BUTTON_DANGER
  1142. 1142 : });
  1143. 1143 :
  1144. 1144 : Object.defineProperty(mel_button, 'html_base_class_full', {
  1145. 1145 : enumerable: false,
  1146. 1146 : configurable: false,
  1147. 1147 : writable: false,
  1148. 1148 : value:`${mel_button.html_base_class.base} ${mel_button.html_base_class_no_margin} ${mel_button.html_base_class.bootstrap.base} ${mel_button.html_base_class.bootstrap.state}`
  1149. 1149 : });
  1150. 1150 :
  1151. 1151 : class mel_tab extends mel_button{
  1152. 1152 : constructor(namespace, id, attribs={}, text) {
  1153. 1153 : super(attribs, text);
  1154. 1154 : this._init()._setup(id);
  1155. 1155 : }
  1156. 1156 :
  1157. 1157 : _init() {
  1158. 1158 : this.namespace = EMPTY_STRING;
  1159. 1159 : this.id = EMPTY_STRING;
  1160. 1160 : /**
  1161. 1161 : * @type {mel_tabpanel}
  1162. 1162 : */
  1163. 1163 : this.control = null;
  1164. 1164 : return this;
  1165. 1165 : }
  1166. 1166 :
  1167. 1167 : _setup(namespace, id){
  1168. 1168 : this.id = id;
  1169. 1169 : this.namespace = namespace;
  1170. 1170 : return this;
  1171. 1171 : }
  1172. 1172 :
  1173. 1173 : generate(attribs={}) {
  1174. 1174 : attribs['id'] = id;
  1175. 1175 : attribs['tabindex'] = -1;
  1176. 1176 : attribs['aria-controls'] = this.control?.attribs?.['id'];
  1177. 1177 : attribs['role'] = 'tab';
  1178. 1178 : attribs['type'] = 'button';
  1179. 1179 : attribs['aria-selected'] = attribs?.['aria-selected'] ?? false;
  1180. 1180 : attribs['class'] = this.namespace + ' mel-html-tab';
  1181. 1181 : attribs['data-tabnamespace'] = this.namespace;
  1182. 1182 :
  1183. 1183 : this.onclick.push((e) => {
  1184. 1184 : e = $(e.currentTarget);
  1185. 1185 : MelAsync.forof($(`button.mel-html-tab.${e.data('tabnamespace')}`), async (iterator) => {
  1186. 1186 : $(iterator).removeClass('selected').attr('aria-selected', false);
  1187. 1187 : }, false).then(() => {
  1188. 1188 : e.addClass('selected').attr('aria-selected', true);
  1189. 1189 : });
  1190. 1190 :
  1191. 1191 : MelAsync.forof($(`mel-html-tabpanel.mel-html-tab.${e.data('tabnamespace')}`), async (iterator) => {
  1192. 1192 : $(iterator).css('display', 'none');
  1193. 1193 : }, false).then(() => {
  1194. 1194 : $(`mel-html-tabpanel.mel-html-tab.${e.data('tabnamespace')}.${this.id}`);
  1195. 1195 : });
  1196. 1196 : });
  1197. 1197 :
  1198. 1198 : this.onkeydown.push((event) => {
  1199. 1199 : let tabs = $(`button.mel-html-tab.${e.data('tabnamespace')}`);
  1200. 1200 : const key = event.keyCode;
  1201. 1201 :
  1202. 1202 : let direction = 0;
  1203. 1203 : switch (key) {
  1204. 1204 : case this.keys.left:
  1205. 1205 : direction = -1;
  1206. 1206 : break;
  1207. 1207 : case this.keys.right:
  1208. 1208 : direction = 1;
  1209. 1209 : break;
  1210. 1210 :
  1211. 1211 : case this.keys.home:
  1212. 1212 : $(tabs[0]).focus().click();
  1213. 1213 : break;
  1214. 1214 : case this.keys.end:
  1215. 1215 : $(tabs[tabs.length-1]).focus().click();
  1216. 1216 : break;
  1217. 1217 :
  1218. 1218 : default:
  1219. 1219 : break;
  1220. 1220 : }
  1221. 1221 :
  1222. 1222 : if (direction !== 0)
  1223. 1223 : {
  1224. 1224 : for (let index = 0; index < tabs.length; ++index) {
  1225. 1225 : const element = $(tabs[index]);
  1226. 1226 :
  1227. 1227 : if (element.hasClass("selected") || element.hasClass("active"))
  1228. 1228 : {
  1229. 1229 : let id;
  1230. 1230 : if (index + direction < 0)
  1231. 1231 : id = tabs.length - 1;
  1232. 1232 : else if (index + direction >= tabs.length)
  1233. 1233 : id = 0;
  1234. 1234 : else
  1235. 1235 : id = index + direction;
  1236. 1236 :
  1237. 1237 : $(tabs[id]).focus().click();
  1238. 1238 :
  1239. 1239 : break;
  1240. 1240 : }
  1241. 1241 : }
  1242. 1242 : }
  1243. 1243 : });
  1244. 1244 :
  1245. 1245 : return super.generate(attribs);
  1246. 1246 : }
  1247. 1247 :
  1248. 1248 : select(selected) {
  1249. 1249 : attribs['aria-selected'] = selected;
  1250. 1250 : return this;
  1251. 1251 : }
  1252. 1252 :
  1253. 1253 : setControl(controler) {
  1254. 1254 : this.control = controler;
  1255. 1255 : return this;
  1256. 1256 : }
  1257. 1257 :
  1258. 1258 : }
  1259. 1259 : class mel_tablist extends mel_html {
  1260. 1260 : constructor(namespace, id, {
  1261. 1261 : attribs={},
  1262. 1262 : tabs=[],
  1263. 1263 : label=EMPTY_STRING
  1264. 1264 : }) {
  1265. 1265 : super('div', attribs);
  1266. 1266 : this._init()._setup(namespace, id, {tabs, label});
  1267. 1267 : }
  1268. 1268 :
  1269. 1269 : _init() {
  1270. 1270 : /**
  1271. 1271 : * @type {mel_tab[]}
  1272. 1272 : */
  1273. 1273 : this.tabs = [];
  1274. 1274 : this.id = EMPTY_STRING;
  1275. 1275 : this.label = EMPTY_STRING;
  1276. 1276 : this.namespace = EMPTY_STRING;
  1277. 1277 : return this;
  1278. 1278 : }
  1279. 1279 :
  1280. 1280 : _setup(namespace, id, {tabs=[], label=EMPTY_STRING})
  1281. 1281 : {
  1282. 1282 : this.id = id;
  1283. 1283 : this.tabs = tabs;
  1284. 1284 : this.label = label;
  1285. 1285 : this.namespace = namespace;
  1286. 1286 : return this;
  1287. 1287 : }
  1288. 1288 :
  1289. 1289 : generate(attribs={}){
  1290. 1290 : attribs['role'] = 'tablist';
  1291. 1291 : let $tablist = super.generate(attribs);
  1292. 1292 :
  1293. 1293 : for (let index = 0, len = this.tabs.length; index < len; ++index) {
  1294. 1294 : this.tabs[index].generate().appendTo($tablist);
  1295. 1295 : }
  1296. 1296 :
  1297. 1297 : new mel_html('label', {for:this.id, class:'sr-only'}).generate().appendTo($tablist);
  1298. 1298 :
  1299. 1299 : return $tablist;
  1300. 1300 : }
  1301. 1301 :
  1302. 1302 : /**
  1303. 1303 : *
  1304. 1304 : * @returns {mel_tab}
  1305. 1305 : */
  1306. 1306 : getSelectedTab() {
  1307. 1307 : return Enumerable.from(this.tabs).where(x => x.attribs['aria-selected'] === true).firstOrDefault();
  1308. 1308 : }
  1309. 1309 :
  1310. 1310 : /**
  1311. 1311 : *
  1312. 1312 : * @param {mel_tab} tab
  1313. 1313 : */
  1314. 1314 : addTab(tab) {
  1315. 1315 : this.tabs.push(tab.setControl(this.pannel));
  1316. 1316 : return this;
  1317. 1317 : }
  1318. 1318 : }
  1319. 1319 :
  1320. 1320 : class mel_tabpanel extends mel_html {
  1321. 1321 : constructor(namespace, tab, {attribs={}, contents=EMPTY_STRING, jquery_content = null}) {
  1322. 1322 : super('div', attribs, contents);
  1323. 1323 : this._init()._setup(tab, {jquery_content});
  1324. 1324 : }
  1325. 1325 :
  1326. 1326 : _init() {
  1327. 1327 : this.namespace = EMPTY_STRING;
  1328. 1328 : /**
  1329. 1329 : * @type {mel_tab}
  1330. 1330 : */
  1331. 1331 : this.tab = null;
  1332. 1332 : /**
  1333. 1333 : * @type {mel_html[]}
  1334. 1334 : */
  1335. 1335 : this.pannels = [];
  1336. 1336 : this.jcontent = null;
  1337. 1337 : return this;
  1338. 1338 : }
  1339. 1339 :
  1340. 1340 : _setup(namespace, tab, {jquery_content = null}) {
  1341. 1341 : this.tab = tab;
  1342. 1342 : this.jcontent = jquery_content;
  1343. 1343 : this.namespace = namespace;
  1344. 1344 : return this;
  1345. 1345 : }
  1346. 1346 :
  1347. 1347 : generate(attribs={}){
  1348. 1348 : attribs['aria-labelledby'] = this.tab.id;
  1349. 1349 : attribs['tabindex'] = 0;
  1350. 1350 : attribs['class'] = `${this.tab.id} ${this.namespace} mel-html-tabpanel`;
  1351. 1351 : let $generated = super.generate(attribs);
  1352. 1352 :
  1353. 1353 : if (!!this.jcontent) this.jcontent.appendTo($generated);
  1354. 1354 :
  1355. 1355 : return $generated;
  1356. 1356 : }
  1357. 1357 : }
  1358. 1358 :
  1359. 1359 : class mel_tabs extends mel_html {
  1360. 1360 : constructor(id, label, {attribs={}, tablist_attribs={}}) {
  1361. 1361 : super('div', attribs);
  1362. 1362 : this._init()._setup(id, label, {tablist_attribs});
  1363. 1363 : }
  1364. 1364 :
  1365. 1365 : _init() {
  1366. 1366 : /**
  1367. 1367 : * @type {mel_tablist}
  1368. 1368 : */
  1369. 1369 : this.tabs = null;
  1370. 1370 : /**
  1371. 1371 : * @type {mel_tabpanel[]}
  1372. 1372 : */
  1373. 1373 : this.contents = null;
  1374. 1374 : return this;
  1375. 1375 : }
  1376. 1376 :
  1377. 1377 : _setup(id, label, {tablist_attribs={}}) {
  1378. 1378 : this.tabs = new mel_tablist(id, {label, attribs:tablist_attribs})
  1379. 1379 : this.contents = [];
  1380. 1380 : return this;
  1381. 1381 : }
  1382. 1382 :
  1383. 1383 : add(id, tabtext, pannel, selectedTab = false) {
  1384. 1384 : let tab = new mel_tab(id, {}, tabtext);
  1385. 1385 : let mel_pannel = new mel_tabpanel(tab, {jquery_content:pannel});
  1386. 1386 : this.tabs.addTab(tab.select(selectedTab).setControl(mel_pannel));
  1387. 1387 : this.contents.push(mel_pannel);
  1388. 1388 : tab = (mel_pannel = null, null);
  1389. 1389 : return this;
  1390. 1390 : }
  1391. 1391 :
  1392. 1392 : generate({attribs={}, tablist_attribs={}}) {
  1393. 1393 : let $generated = super.generate(attribs);
  1394. 1394 : this.tabs.generate(tablist_attribs).appendTo($generated);
  1395. 1395 :
  1396. 1396 : const selected_tab = this.tabs.getSelectedTab();
  1397. 1397 :
  1398. 1398 : for (let index = 0, len = this.contents.length, content; index < len; ++index) {
  1399. 1399 : const element = this.contents[index];
  1400. 1400 : content = this.contents.generate();
  1401. 1401 :
  1402. 1402 : if (!!selected_tab && selected_tab.id === element.tab.id) content.css('display', '');
  1403. 1403 : else content.css('display', 'none');
  1404. 1404 :
  1405. 1405 : content.appendTo($generated);
  1406. 1406 : }
  1407. 1407 :
  1408. 1408 : return $generated;
  1409. 1409 :
  1410. 1410 : }
  1411. 1411 :
  1412. 1412 : }
  1413. 1413 :
  1414. 1414 : class mel_iframe extends mel_html {
  1415. 1415 : constructor(src, attribs = {}) {
  1416. 1416 : super('iframe', attribs);
  1417. 1417 : this.src = src;
  1418. 1418 : this.onload = new MelEvent();
  1419. 1419 : }
  1420. 1420 :
  1421. 1421 : generate(extra_attribs = {}) {
  1422. 1422 : if (Array.isArray(extra_attribs)) extra_attribs = {};
  1423. 1423 :
  1424. 1424 : extra_attribs[mel_html.ATTRIB_NO_MULTI_BALISE] = true;
  1425. 1425 : extra_attribs.src = this.src;
  1426. 1426 : return super.generate(extra_attribs).on('load', (e) => {
  1427. 1427 : this.onload.call(e);
  1428. 1428 : });
  1429. 1429 : }
  1430. 1430 : }