![]() System : Linux absol.cf 5.4.0-198-generic #218-Ubuntu SMP Fri Sep 27 20:18:53 UTC 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.33 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, Directory : /var/www/html/libs/absol-acomp/out/ |
Upload File : |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: DateInput2.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: DateInput2.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>import '../css/dateinput.css'; import ACore from "../ACore"; import { daysInMonth, beginOfDay, compareDate, formatDateString, parseDateString, formatDateTime, parseDateTime, DATE_TIME_TOKEN_RGX, weekIndexOf, prevDate } from "absol/src/Time/datetime"; import ChromeCalendar from "./ChromeCalendar"; import OOP from "absol/src/HTML5/OOP"; import DateInput from "./DateInput"; import AElement from "absol/src/HTML5/AElement"; import DomSignal from "absol/src/HTML5/DomSignal"; import DateTimeInput from "./DateTimeInput"; import { zeroPadding } from "./utils"; import { hitElement } from "absol/src/HTML5/EventEmitter"; var STATE_NEW = 1; var STATE_EDITED = 2; var STATE_NONE = 0; var _ = ACore._; var $ = ACore.$; /** * @extends AElement * @constructor */ function DateInput2() { this._lastValue = null; this._value = null; this._format = 'dd/MM/yyyy'; this.$input = $('input', this); this._editingData = {}; this.startDayOfWeek = 1; this.$text = this.$input; this.$text.on('mousedown', this.eventHandler.mouseDownInput) .on('mouseup', this.eventHandler.mouseUpInput) .on('dblclick', this.eventHandler.dblclickInput) .on('keydown', this.eventHandler.keydown) .on('blur', this.eventHandler.inputBlur) .on('contextmenu', function (event) { event.preventDefault(); }); this.$domSignal = _('attachhook').addTo(this); this.domSignal = new DomSignal(this.$domSignal); this.domSignal.on('request_auto_select', this._autoSelect.bind(this)); this._min = new Date(1890, 0, 1, 0, 0, 0, 0); this._max = new Date(2090, 0, 1, 0, 0, 0, 0); this.$calendarBtn = $('.as-date-input-icon-ctn', this) .on('click', this.eventHandler.clickCalendarBtn); this.$clearBtn = $('button.as-date-input-clear-btn', this) .on('click', this.clear.bind(this)); this.value = this._value; this.format = this._format; this.notNull = false; OOP.drillProperty(this, this, 'minLimitDate', 'min'); OOP.drillProperty(this, this, 'minDateLimit', 'min'); OOP.drillProperty(this, this, 'maxLimitDate', 'max'); OOP.drillProperty(this, this, 'maxDateLimit', 'max'); } DateInput2.tag = 'dateinput'; DateInput2.render = function () { //only support dd/mm/yyyy return _({ class: 'as-date-input', extendEvent: ['change'], child: [{ tag: 'input', class: 'as-date-input-text', props: { value: '__/__/____' } }, { tag: 'button', class: 'as-date-input-clear-btn', child: 'span.mdi.mdi-close-circle' }, { tag: 'button', class: 'as-date-input-icon-ctn', child: 'span.mdi.mdi-calendar' }] }); }; /** * @param {String} text */ DateInput2.prototype._verifyFormat = function (text) { var regex = /([,.\-\/])|([a-zA-Z0-9]+)/g; var tokens = text.match(regex); var count = [['dd', 'd'], ['M', 'MM'], ['yy', 'yyyy']].map(function (list) { return list.reduce(function (ac, cr) { if (tokens.indexOf(cr) >= 0) return ac + 1; return ac; }, 0); }); return count[0] <= count[1] && count[1] <= count[2] && count[2] === 1; }; DateInput2.prototype._notifyIfChange = function (event) { if (!this._lastValue !== !this._value || (this._lastValue && compareDate(this._lastValue, this._value)) !== 0) { this._lastValue = this._value; this.emit('change', { type: 'change', target: this, value: this._value, originEvent: event }, this); } }; DateInput2.prototype.notifyChange = function () { this._lastValue = this._value; this.emit('change', { type: 'change', target: this, value: this._value }, this); }; DateInput2.prototype.focus = function () { this.$input.focus(); }; DateInput2.prototype.blur = function () { this.$input.blur(); }; DateInput2.prototype.clear = function () { this._applyValue(null); this._notifyIfChange(); }; /*** * * @param {Date|null} value */ DateInput2.prototype._applyValue = function (value) { this._value = value; if (!value) { this.$input.value = this.format; } else { this.$input.value = formatDateTime(this._value, this._format); } this._updateNullClass(); }; DateInput2.prototype._updateNullClass = function () { var value = this._value; if (!value) { this.addClass('as-value-null'); } else { this.removeClass('as-value-null'); } }; DateInput2.prototype.tokenRegex = DateTimeInput.prototype.tokenRegex; DateInput2.prototype._autoSelect = DateTimeInput.prototype._autoSelect; DateInput2.prototype._tokenAt = DateTimeInput.prototype._tokenAt; DateInput2.prototype._editNextToken = DateTimeInput.prototype._editNextToken; DateInput2.prototype._editPrevToken = DateTimeInput.prototype._editPrevToken; DateInput2.prototype._makeTokenDict = DateTimeInput.prototype._makeTokenDict; DateInput2.prototype._correctingInput = function () { var tkDict = this._makeTokenDict(this.$text.value); var min = this._min; var max = this._max; var equalMin; var equalMax; if (!isNaN(tkDict.y.value)) { tkDict.y.value = Math.max(min.getFullYear(), Math.min(max.getFullYear(), tkDict.y.value)); equalMin = tkDict.y.value === min.getFullYear(); equalMax = tkDict.y.value === max.getFullYear(); } else { equalMin = false; equalMax = false; } if (tkDict.M && !isNaN(tkDict.M.value)) { tkDict.M.value = Math.max(1, Math.min(12, tkDict.M.value)); if (equalMin) { tkDict.M.value = Math.max(min.getMonth() + 1, tkDict.M.value); equalMin = tkDict.M.value === min.getMonth() + 1; } if (equalMax) { tkDict.M.value = Math.min(max.getMonth() + 1, tkDict.M.value); equalMax = max.getMonth() + 1; } } else { equalMin = false; equalMax = false; } if (tkDict.d && !isNaN(tkDict.d.value)) { tkDict.d.value = Math.max(1, Math.min(31, tkDict.d.value)); if (!isNaN(tkDict.M.value)) { tkDict.d.value = Math.min(tkDict.d.value, daysInMonth(isNaN(tkDict.y.value) ? 2020 : tkDict.y.value, tkDict.M.value - 1)); } if (equalMin) { tkDict.d.value = Math.max(min.getDate(), tkDict.d.value); } if (equalMax) { tkDict.d.value = Math.min(max.getDate(), tkDict.d.value); } } if (tkDict.w && !isNaN(tkDict.w.value)) { if (!isNaN(tkDict.y.value)) { tkDict.w.value = Math.max(1, Math.min(tkDict.w.value, 1 + weekIndexOf(prevDate(new Date(tkDict.y.value + 1, 0, 1)), false, this._startDayOfWeek))); } } this.$text.value = this._applyTokenDict(this._format, tkDict); } DateInput2.prototype._correctingCurrentToken = function () { var token = this._tokenAt(this.$text.selectionStart); if (!token) return; var value; value = parseInt(token.text); var rqMin = { d: 1, dd: 1, M: 1, MM: 1, y: 1890, yyyy: 1890, w: 1, ww: 1 }[token.ident]; var rqMax = { d: 31, dd: 31, M: 12, MM: 12, y: 2089, yyyy: 2089, w: 54, ww: 54 }[token.ident]; if (rqMin !== undefined) { if (!isNaN(value)) { if ((value < rqMin || value > rqMin)) { value = Math.max(rqMin, Math.min(rqMax, value)); token.replace(zeroPadding(value, token.ident.length), false); } } else if (this.notNull) { if (token.ident.startsWith('y')) { value = new Date().getFullYear(); } else { value = rqMin; } token.replace(zeroPadding(value, token.ident.length), false); } else if (token.text !== token.ident) { token.replace(token.ident, false); } } }; /*** * * @param {Date|string|null}date * @return {Date|null} */ DateInput2.prototype._normalizeValue = function (date) { var temp; if (date === null || date === undefined || date === false) { return null; } if (typeof date === 'string') { temp = new Date(date); if (isNaN(temp.getTime())) { temp = parseDateTime(date, this._format); } date = temp; } else if (typeof date === 'number') { date = new Date(date); } if (date.getTime && date.getHours) { if (isNaN(date.getTime())) { return null; } else { return beginOfDay(date); } } else { return null; } }; DateInput2.prototype._loadValueFromInput = function () { var tkDict = this._makeTokenDict(this.$text.value); var y = tkDict.y ? tkDict.y.value : new Date().getFullYear(); var m = tkDict.M ? tkDict.M.value - 1 : 0; var d = tkDict.d ? tkDict.d.value : 1; var date = new Date(y, m, d); if (isNaN(date.getTime())) { this._value = null; } else { this._value = date; } this._updateNullClass(); }; DateInput2.prototype._applyTokenDict = function (format, dict, debug) { var rgx = new RegExp(this.tokenRegex.source, 'g'); var tokenMap = this.tokenMap; var res = format.replace(rgx, function (full, g1, g2, sourceText) { if (g1 && tokenMap[g1]) { var ident = tokenMap[g1]; if (dict[ident] && !isNaN(dict[ident].value)) { return zeroPadding(dict[ident].value, g1.length); } else { return full; } } else return full; }); return res; }; DateInput2.prototype.focus = function () { this.$text.focus(); this.$text.select(); }; DateInput2.prototype.tokenMap = { d: 'd', dd: 'd', M: 'M', MM: 'M', y: 'y', yyyy: 'y', ww: 'w' } /** * @type {DateInput2} */ DateInput2.eventHandler = {}; DateInput2.eventHandler.keydown = function (event) { if (this.readOnly) { if (!event.ctrlKey || event.key !== 'c') { event.preventDefault(); } return; } var token = this._tokenAt(this.$text.selectionStart); var endToken = this._tokenAt(this.$text.selectionEnd); if (!token) { if (event.key === 'Enter') { this._correctingInput(); this._loadValueFromInput(); this._notifyIfChange(event); } return; } var newTokenText; var value; if (event.key.startsWith('Arrow') || event.key.match(/^[\-/,\s]$/)) { event.preventDefault(); switch (event.key) { case 'ArrowLeft': this._editPrevToken(); break; case 'ArrowRight': case '-': case ',': case '/': case ' ': this._editNextToken(); break; case 'ArrowUp': case 'ArrowDown': switch (token.ident) { case 'dd': case 'd': value = parseInt(token.text); if (isNaN(value)) { this._editingData.d = event.key === 'ArrowUp' ? 1 : 31; } else { this._editingData.d = 1 + (value + (event.key === 'ArrowUp' ? 0 : 29)) % 31; } newTokenText = '' + this._editingData.d; while (newTokenText.length < token.ident.length) newTokenText = '0' + newTokenText; token.replace(newTokenText, true); break; case 'w': case 'ww': value = parseInt(token.text); if (isNaN(value)) { this._editingData.w = event.key === 'ArrowUp' ? 1 : 54; } else { this._editingData.w = 1 + (value + (event.key === 'ArrowUp' ? 0 : 52)) % 54; } newTokenText = zeroPadding(this._editingData.w, token.ident.length); token.replace(newTokenText, true); break; case 'MM': case 'M': value = parseInt(token.text) - 1; if (isNaN(value)) { this._editingData.M = event.key === 'ArrowUp' ? 0 : 11; } else { this._editingData.M = (value + (event.key === 'ArrowUp' ? 1 : 11)) % 12; } newTokenText = '' + (this._editingData.M + 1); while (newTokenText.length < token.ident.length) newTokenText = '0' + newTokenText; token.replace(newTokenText, true); break; case 'yyyy': value = parseInt(token.text); if (isNaN(value)) { this._editingData.y = new Date().getFullYear(); } else { this._editingData.y = Math.max(1890, Math.min(2089, value + (event.key === 'ArrowUp' ? 1 : -1))); } newTokenText = this._editingData.y + ''; while (newTokenText.length < token.ident.length) newTokenText = '0' + newTokenText; token.replace(newTokenText, true); break; } } } else if (event.key === "Delete" || event.key === 'Backspace') { event.preventDefault(); if (endToken.idx !== token.idx) { if (this.notNull) { this.$text.value = formatDateTime(new Date(Math.min(this.max.getTime(), Math.max(this.min.getTime(), new Date().getTime()))), this._format); } else { this.$text.value = this._format; } this.$text.select(); } else { if (this.notNull) { switch (token.ident) { case 'y': case 'yyyy': token.replace(zeroPadding(new Date().getFullYear(), token.ident.length), true); break; case 'w': case 'ww': token.replace(zeroPadding(1, token.ident.length), true); break; case 'M': case 'MM': case 'd': case 'dd': token.replace(zeroPadding(1, token.ident.length), true); break; default: token.replace(token.ident, true); } } else { token.replace(token.ident, true); } if (event.key === "Delete") this._editNextToken(); else this._editPrevToken(); } } else if (event.key === "Enter" || event.key === 'Tab') { this._correctingInput(); this._loadValueFromInput(); this._notifyIfChange(event); } else if (event.ctrlKey) { switch (event.key) { case 'a': case 'A': break; case 'c': case 'C': break; case 'x': case 'X': this.domSignal.once('clear_value', function () { this.$text.value = this._format; this.$text.select(); }.bind(this)); this.domSignal.emit('clear_value'); break; default: event.preventDefault(); } } else if (event.key.match(/^[0-9]$/g)) { event.preventDefault(); var dVal = parseInt(event.key); if (this._editingData.state === STATE_NEW) { switch (token.ident) { case 'dd': case 'd': token.replace(zeroPadding(dVal, token.ident.length), true); this._editingData.state = STATE_EDITED; this._editingData.d = dVal; if (dVal > 3) { this._editNextToken(); } break; case 'w': case 'ww': token.replace(zeroPadding(dVal, token.ident.length), true); this._editingData.state = STATE_EDITED; this._editingData.d = dVal; if (dVal > 6) { this._editNextToken(); } break; case 'MM': case 'M': token.replace(zeroPadding(dVal, token.ident.length), true); this._editingData.state = STATE_EDITED; this._editingData.M = dVal; if (dVal > 1) { this._editNextToken(); } break; case 'yyyy': case 'y': token.replace(zeroPadding(dVal, token.ident.length), true); this._editingData.state = STATE_EDITED; this._editingData.state_num = 1; break; } } else { switch (token.ident) { case 'dd': case 'd': dVal = (parseInt(token.text.split('').pop()) || 0) * 10 + dVal; dVal = Math.max(1, Math.min(31, dVal)); this._editingData.d = dVal; token.replace(zeroPadding(dVal, token.ident.length), true); this._editNextToken(); break; case 'ww': case 'w': dVal = (parseInt(token.text.split('').pop()) || 0) * 10 + dVal; dVal = Math.max(1, Math.min(54, dVal)); this._editingData.d = dVal; token.replace(zeroPadding(dVal, token.ident.length), true); this._editNextToken(); break; case 'MM': case 'M': dVal = (parseInt(token.text.split('').pop()) || 0) * 10 + dVal; dVal = Math.max(1, Math.min(12, dVal)); this._editingData.M = dVal - 1; token.replace(zeroPadding(dVal, token.ident.length), true); this._editNextToken(); break; case 'yyyy': case 'y': dVal = (parseInt(token.text.replace(/^./, '')) || 0) * 10 + dVal; this._editingData.state_num++; if (this._editingData.state_num >= 4) { // dVal = Math.max(1890, Math.min(2089, dVal)); token.replace(zeroPadding(dVal, token.ident.length), true); this._editNextToken(); } else { token.replace(zeroPadding(dVal, token.ident.length), true); } break; } } } else { event.preventDefault(); } }; DateInput2.eventHandler.mouseUpInput = DateTimeInput.eventHandler.mouseUpInput; DateInput2.eventHandler.mouseDownInput = DateTimeInput.eventHandler.mouseDownInput; DateInput2.eventHandler.dblclickInput = DateTimeInput.eventHandler.dblclickInput; DateInput2.eventHandler.inputBlur = DateTimeInput.eventHandler.inputBlur; DateInput2.eventHandler.calendarSelect = function (value) { this.value = value; this.notifyChange(); }; DateInput2.eventHandler.clickCalendarBtn = function () { if (this.readOnly) return; this._attachCalendar(); }; DateInput2.eventHandler.clickOut = function (event) { if (hitElement(this.share.$calendar, event)) return; this._releaseCalendar(); }; DateInput2.eventHandler.calendarPick = function (event) { this._applyValue(event.value); this._notifyIfChange(event.originEvent || event); this._releaseCalendar(); }; DateInput2.property = {}; DateInput2.property.value = { set: function (value) { value = this._normalizeValue(value); if (!value && this.notNull) value = beginOfDay(new Date()); this._lastValue = value; this._applyValue(value); }, get: function () { return this._value; } }; /** * not support MMM, MMMM, support number only * @type {DateInput2} */ DateInput2.property.format = { set: function (value) { value = value || 'dd/MM/yyyy'; value = value.replace(/m/g, 'M'); value = value.replace(/MM([M]+)/, 'MM'); if (!this._verifyFormat(value)) { value = 'dd/MM/yyyy'; console.error("Invalid date format: " + value); } this._format = value; this._formatTokens = this._format.match(new RegExp(DATE_TIME_TOKEN_RGX.source, 'g')) || []; this.value = this.value;//update }, get: function () { return this._format; } }; DateInput2.property.disabled = { set: function (value) { value = !!value; this.$input.disabled = value; if (value) this.addClass('as-disabled'); else this.removeClass('as-disabled'); this.$text.disabled = value; }, get: function () { return this.$input.disabled; } }; DateInput2.property.readOnly = { set: function (value) { if (value) { this.addClass('as-read-only'); this.$input.readOnly = true; } else { this.removeClass('as-read-only'); this.$input.readOnly = false; } }, get: function () { return this.hasClass('as-read-only'); } }; DateInput2.property.text = { get: function () { return this.$input.value; } }; /*** * @memberOf DateInput2 * @name calendarLevel * @type {number} */ DateInput2.property.calendarLevel = { get: function () { if (this._formatTokens.indexOf('d') >= 0 || this._formatTokens.indexOf('dd') >= 0) return 'day'; if (this._formatTokens.indexOf('w') >= 0 || this._formatTokens.indexOf('ww') >= 0) return 'week'; if (this._formatTokens.indexOf('M') >= 0 || this._formatTokens.indexOf('MM') >= 0) return 'month'; return 'year'; } }; DateInput2.property.min = { set: function (value) { this._min = this._normalizeValue(value) || new Date(1890, 0, 1); }, get: function () { return this._min; } }; DateInput2.property.max = { set: function (value) { this._max = this._normalizeValue(value) || new Date(2090, 0, 1); }, get: function () { return this._max; } }; DateInput2.property.notNull = { set: function (value) { if (value) { this.addClass('as-must-not-null'); if (!this.value) this.value = new Date(); } else { this.removeClass('as-must-not-null'); } }, get: function () { return this.hasClass('as-must-not-null'); } }; DateInput2.prototype.share = { /*** * @type {ChromeCalendar} */ $calendar: null, /*** * @type {Follower} */ $follower: null, /*** * @type {DateInput2} */ $holdingInput: null }; DateInput2.prototype._prepareCalendar = function () { if (this.share.$calendar) return; this.share.$calendar = _({ tag: 'chromecalendar', class: ['as-date-input-calendar', 'as-dropdown-box-common-style'] }); this.share.$follower = _({ tag: 'follower', class: 'as-date-input-follower', child: this.share.$calendar }); }; DateInput2.prototype._attachCalendar = function () { this._prepareCalendar(); if (this.share.$holdingInput) this.share.$holdingInput._releaseCalendar(); this.share.$follower.addTo(document.body); this.share.$follower.addStyle('visibility', 'hidden'); this.share.$holdingInput = this; this.share.$follower.followTarget = this; this.share.$calendar.level = this.calendarLevel; this.share.$calendar.startDayOfWeek = this.startDayOfWeek || 0; this.share.$calendar.min = this._min; this.share.$calendar.max = this._max; this.share.$calendar.on('pick', this.eventHandler.calendarPick); this.share.$calendar.selectedDates = this.value ? [this.value] : []; this.share.$calendar.viewDate = this.value ? this.value : new Date(Math.max(this._min.getTime(), Math.min(this._max.getTime(), new Date().getTime()))); setTimeout(function () { document.body.addEventListener('click', this.eventHandler.clickOut); this.share.$follower.removeStyle('visibility'); }.bind(this), 5); this.$calendarBtn.off('click', this.eventHandler.clickCalendarBtn); }; DateInput2.prototype._releaseCalendar = function () { if (this.share.$holdingInput !== this) return; this.share.$calendar.off('pick', this.eventHandler.calendarPick); this.share.$follower.remove(); document.body.removeEventListener('click', this.eventHandler.clickOut); setTimeout(function () { this.$calendarBtn.on('click', this.eventHandler.clickCalendarBtn); }.bind(this), 5) this.share.$holdingInput = null; }; ACore.install(DateInput2); export default DateInput2;</code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="CalendarInput.html">CalendarInput</a></li><li><a href="CandyBoxButton.html">CandyBoxButton</a></li><li><a href="ChromeCalendar.html">ChromeCalendar</a></li><li><a href="CircleSectionLabel.html">CircleSectionLabel</a></li><li><a href="Cluster.html">Cluster</a></li><li><a href="ClusterIconInfo.html">ClusterIconInfo</a></li><li><a href="ClusterIconStyle.html">ClusterIconStyle</a></li><li><a href="ContextCaptor.html">ContextCaptor</a></li><li><a href="DateInput.html">DateInput</a></li><li><a href="DateInput2.html">DateInput2</a></li><li><a href="DropPanel.html">DropPanel</a></li><li><a href="DropPanelStack.html">DropPanelStack</a></li><li><a href="FlexiconButton.html">FlexiconButton</a></li><li><a href="MarkerClusterer.html">MarkerClusterer</a></li><li><a href="MarkerClustererOptions.html">MarkerClustererOptions</a></li><li><a href="SearchTextInput.html">SearchTextInput</a></li><li><a href="Sprite.html">Sprite</a></li><li><a href="Time24Input.html">Time24Input</a></li></ul><h3>Events</h3><ul><li><a href="MarkerClusterer.html#event:click">click</a></li><li><a href="MarkerClusterer.html#event:clusteringbegin">clusteringbegin</a></li><li><a href="MarkerClusterer.html#event:clusteringend">clusteringend</a></li><li><a href="MarkerClusterer.html#event:mouseout">mouseout</a></li><li><a href="MarkerClusterer.html#event:mouseover">mouseover</a></li></ul><h3>Global</h3><ul><li><a href="global.html#$windowTitleText">$windowTitleText</a></li><li><a href="global.html#AddIcon">AddIcon</a></li><li><a href="global.html#calcMinHMTime">calcMinHMTime</a></li><li><a href="global.html#cleanMenuItemProperty">cleanMenuItemProperty</a></li><li><a href="global.html#preventNotNumberInput">preventNotNumberInput</a></li><li><a href="global.html#vScrollIntoView">vScrollIntoView</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.0</a> on Wed Jan 04 2023 18:18:58 GMT+0700 (Indochina Time) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>