+Фильтр для поля для ввода корректных дат

master
Andrey Pokidov 5 years ago
commit 9eb6cfb1ec

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,327 @@
/*!
* Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus:not(:focus-visible) {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]) {
color: inherit;
text-decoration: none;
}
a:not([href]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg {
overflow: hidden;
vertical-align: middle;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
select {
word-wrap: normal;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button:not(:disabled),
[type="button"]:not(:disabled),
[type="reset"]:not(:disabled),
[type="submit"]:not(:disabled) {
cursor: pointer;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,9 @@
#!/bin/bash
cat /dev/null > ./es6/smart-date.js
for i in AbstractSmartDatePart SmartDateStaticPart AbstractSmartDateNumericPart SmartDateDayPart SmartDateMonthPart SmartDateYearPart SmartDateParseState SmartDateFormat SmartDateField; do
cat ./src-es6/$i.js >> ./es6/smart-date.js
done

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -0,0 +1,997 @@
class AbstractSmartDatePart
{
constructor(placeholder, isStatic)
{
this._state = AbstractSmartDatePart.STATE_NONE;
this._cleanedValue = '';
this._parsedValue = '';
this._scanned = '';
this._placeholder = placeholder;
this._static = isStatic;
}
state()
{
return this._state;
}
isNoState()
{
return this._state === AbstractSmartDatePart.STATE_NONE;
}
isIncomplete()
{
return this._state === AbstractSmartDatePart.STATE_INCOMPLETE;
}
isComplete()
{
return this._state === AbstractSmartDatePart.STATE_COMPLETE;
}
resetState()
{
this._state = AbstractSmartDatePart.STATE_NONE;
}
cleanedValue()
{
return this._cleanedValue;
}
parsedValue()
{
return this._parsedValue;
}
placeHolder()
{
return this._placeholder;
}
isStatic()
{
return this._static;
}
_isNumericSymbol(symbol)
{
return '0' <= symbol && symbol <= '9';
}
/**
* @param {SmartDateCheckState} state
* @returns {Boolean}
*/
check(state)
{
return false;
}
};
AbstractSmartDatePart.STATE_NONE = 0;
AbstractSmartDatePart.STATE_INCOMPLETE = 1;
AbstractSmartDatePart.STATE_COMPLETE = 2;
class SmartDateStaticPart extends AbstractSmartDatePart
{
constructor(placeholder)
{
super(placeholder, true);
this._cleanedValue = placeholder;
}
/**
* @param {SmartDateCheckState} state
* @returns {Boolean}
*/
parse(state)
{
let value = state.value();
let index = state.valueIndex();
while (index < value.length && !this._isNumericSymbol(value[index])) {
index++;
}
this._parsedValue = value.substring(state.valueIndex(), index);
this._state = AbstractSmartDatePart.STATE_COMPLETE;
state.setValueIndex(index);
return true;
}
}
class AbstractSmartDateNumericPart extends AbstractSmartDatePart
{
constructor(placeholder, size)
{
super(placeholder, false);
this._numericValue = 0;
this._size = size;
}
numericValue()
{
return this._numericValue;
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
return '';
}
formatValue(value)
{
let stringValue = String(this._correctValue(value));
if (stringValue === '0') {
return stringValue;
}
if (stringValue.length > this._size) {
return stringValue.substring(0, this._size);
}
if (stringValue.length < this._size) {
return stringValue.padStart(this._size, '0');
}
return stringValue;
}
_correctValue(value)
{
return Math.round(value);
}
/**
* @param {SmartDateCheckState} state
* @returns {String}
*/
scanStringNumber(state)
{
let index = state.valueIndex();
let value = state.value();
while (index < value.length && this._isNumericSymbol(value[index])) {
index++;
}
let stringValue = value.substring(state.valueIndex(), index);
state.setValueIndex(index);
return stringValue;
}
calculateNumericValue(stringValue, maximalNumber)
{
var startIndex = 0;
var endIndex = 0;
var numericValue = 0;
while (startIndex < stringValue.length && stringValue[startIndex] === 0) {
startIndex++;
}
endIndex = startIndex;
while (endIndex < stringValue.length && numericValue < maximalNumber) {
numericValue = numericValue * 10 + Number(stringValue[endIndex]);
endIndex++;
}
if (numericValue > maximalNumber) {
endIndex--;
}
return Number(stringValue.substring(startIndex, endIndex));
}
/**
* @param {SmartDateCheckState} state
* @param {Number} maximalValue
*/
scanValue(state, maximalValue)
{
let index = state.valueIndex();
let value = state.value();
let numericValue = 0;
while (index < value.length && this._isNumericSymbol(value[index]) && numericValue < maximalValue) {
numericValue = numericValue * 10 + Number(value[index]);
index++;
}
if (numericValue > maximalValue) {
index--;
}
let stringValue = value.substring(state.valueIndex(), index);
state.setValueIndex(index);
return stringValue;
}
_defineParsingState(maximalValue, isAtEnd)
{
if (this._numericValue > 0 && (this._parsedValue.length >= this._size || !isAtEnd)) {
this._state = AbstractSmartDatePart.STATE_COMPLETE;
return;
}
if (this._numericValue * 10 > maximalValue) {
this._state = AbstractSmartDatePart.STATE_COMPLETE;
return;
}
this._state = AbstractSmartDatePart.STATE_INCOMPLETE;
}
_defineCleanValue()
{
this._cleanedValue = '';
if (this.isNoState()) {
return;
}
if (this.isComplete()) {
this._cleanedValue = this.formatValue(this._numericValue);
return;
}
if (this._parsedValue.length > 0) {
this._cleanedValue = String(this._numericValue);
}
}
}
class SmartDateDayPart extends AbstractSmartDateNumericPart
{
constructor()
{
super(SmartDateFormat.DAY_PLACEHOLDER, 2);
this.maximalValue = SmartDateDayPart.MAXIMAL_VALUE;
}
resetState()
{
super.resetState();
this.maximalValue = SmartDateDayPart.MAXIMAL_VALUE;
}
setMaximalValue(value)
{
if (value != value /* isNaN */ || value < SmartDateDayPart.MAXIMAL_VALUE_LOW_LIMIT || SmartDateDayPart.MAXIMAL_VALUE_HIGH_LIMIT < value) {
return;
}
this.maximalValue = value;
if (this._numericValue <= this.maximalValue) {
return;
}
this._numericValue = this.maximalValue;
this._defineCleanValue();
}
resetMaximalValue()
{
this.maximalValue = SmartDateDayPart.MAXIMAL_VALUE;
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
return this.formatValue(date.getDate());
}
/**
* @param {SmartDateCheckState} state
*/
parse(state)
{
this._parsedValue = this.scanStringNumber(state);
this._numericValue = this.calculateNumericValue(this._parsedValue, this.maximalValue);
this._defineParsingState(this.maximalValue, state.isOver());
this._defineCleanValue();
}
}
SmartDateDayPart.MAXIMAL_VALUE_LOW_LIMIT = 28;
SmartDateDayPart.MAXIMAL_VALUE_HIGH_LIMIT = 31;
SmartDateDayPart.MAXIMAL_VALUE = SmartDateDayPart.MAXIMAL_VALUE_HIGH_LIMIT;
class SmartDateMonthPart extends AbstractSmartDateNumericPart
{
constructor()
{
super(SmartDateFormat.MONTH_PLACEHOLDER, 2);
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
return this.formatValue(date.getMonth() + 1);
}
/**
* @param {SmartDateCheckState} state
*/
parse(state)
{
this._parsedValue = this.scanStringNumber(state);
this._numericValue = this.calculateNumericValue(this._parsedValue, SmartDateMonthPart.MAXIMAL_VALUE);
this._defineParsingState(SmartDateMonthPart.MAXIMAL_VALUE, state.isOver());
this._defineCleanValue();
}
}
SmartDateMonthPart.MAXIMAL_VALUE = 12;
class SmartDateYearPart extends AbstractSmartDateNumericPart
{
constructor()
{
super(SmartDateFormat.YEAR_PLACEHOLDER, 4);
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
return this.formatValue(date.getFullYear());
}
/**
* @param {SmartDateCheckState} state
* @returns {Boolean}
*/
parse(state)
{
this._parsedValue = this.scanStringNumber(state);
this._numericValue = Number(this._parsedValue);
this._defineParsingState();
if (this._numericValue > SmartDateYearPart.MAXIMAL_VALUE) {
this._numericValue = SmartDateYearPart.MAXIMAL_VALUE;
}
if (this.isComplete() && this._numericValue < SmartDateYearPart.MINIMAL_VALUE) {
this._numericValue = SmartDateYearPart.MINIMAL_VALUE;
}
this._defineCleanValue();
}
_defineParsingState()
{
if (this._numericValue > 0 && this._parsedValue.length >= this._size) {
this._state = AbstractSmartDatePart.STATE_COMPLETE;
return;
}
this._state = AbstractSmartDatePart.STATE_INCOMPLETE;
}
_defineCleanValue()
{
if (this._numericValue > 0) {
this._cleanedValue = String(this._numericValue);
}
else {
this._cleanedValue = '';
}
}
}
SmartDateYearPart.MINIMAL_VALUE = 1000;
SmartDateYearPart.MAXIMAL_VALUE = 9999;
class SmartDateParseState
{
constructor()
{
this._value = '';
this._valueIndex = 0;
}
start(value)
{
this._value = value;
this._valueIndex = 0;
}
current()
{
return this.isOver() ? '' : this._value[this._valueIndex];
}
next()
{
if (this.isOver()) {
return false;
}
this._valueIndex++;
return !this.isOver();
}
isOver()
{
return this._valueIndex >= this._value.length;
}
value()
{
return this._value;
}
valueIndex()
{
return this._valueIndex;
}
setValueIndex(index)
{
if (this._valueIndex < index) {
this._valueIndex = index;
}
}
}
class SmartDateFormat
{
constructor(format)
{
this._day = null;
this._month = null;
this._year = null;
this.loadFormat(format);
this._parseState = new SmartDateParseState();
}
loadFormat(format)
{
let index = 0;
let lowFormat = format.toLowerCase();
this._format = format;
this._parts = [];
while (index < format.length) {
index = this._scanPart(index, format, lowFormat);
}
}
_scanPart(index, format, lowFormat)
{
switch (lowFormat[index]) {
case SmartDateFormat.DAY_ANCHOR:
return this._scanDay(index, lowFormat);
case SmartDateFormat.MONTH_ANCHOR:
return this._scanMonth(index, lowFormat);
case SmartDateFormat.YEAR_ANCHOR:
return this._scanYear(index, lowFormat);
}
return this._scanStatic(index, format, lowFormat);
}
_scanDay(startIndex, lowFormat)
{
this._checkLastIsStatic();
this._day = new SmartDateDayPart();
this._parts.push(this._day);
return this._avoidAnchor(SmartDateFormat.DAY_ANCHOR, startIndex, lowFormat);
}
_scanMonth(startIndex, lowFormat)
{
this._checkLastIsStatic();
this._month = new SmartDateMonthPart();
this._parts.push(this._month);
return this._avoidAnchor(SmartDateFormat.MONTH_ANCHOR, startIndex, lowFormat);
}
_scanYear(startIndex, lowFormat)
{
this._checkLastIsStatic();
this._year = new SmartDateYearPart();
this._parts.push(this._year);
return this._avoidAnchor(SmartDateFormat.YEAR_ANCHOR, startIndex, lowFormat);
}
_avoidAnchor(anchor, startIndex, lowFormat)
{
let endingIndex = startIndex;
while (endingIndex < lowFormat.length && lowFormat[endingIndex] === anchor) {
endingIndex++;
}
return endingIndex;
}
_checkLastIsStatic()
{
if (this._parts.length === 0) {
return;
}
if (!this._parts[this._parts.length - 1].isStatic()) {
throw new Error('Dynamic date parts must be separated by static substrings');
}
}
_scanStatic(startIndex, format, lowFormat)
{
let endingIndex = startIndex;
while (endingIndex < format.length && this._isStaticAnchorSymbol(lowFormat[endingIndex])) {
endingIndex++;
}
this._parts.push(new SmartDateStaticPart(format.substring(startIndex, endingIndex)));
return endingIndex;
}
_isStaticAnchorSymbol(symbol)
{
return symbol !== SmartDateFormat.DAY_ANCHOR
&& symbol !== SmartDateFormat.MONTH_ANCHOR
&& symbol !== SmartDateFormat.YEAR_ANCHOR;
}
getPlaceHolder()
{
let placeHolder = '';
for (let i = 0; i < this._parts.length; i++) {
placeHolder += this._parts[i].placeHolder();
}
return placeHolder;
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
var formattedDate = '';
for (let i = 0; i < this._parts.length; i++) {
if (this._parts[i].isStatic()) {
formattedDate += this._parts[i].placeHolder();
}
else {
formattedDate += this._parts[i].formatDate(date);
}
}
return formattedDate;
}
parse(value, autocomplete)
{
this._resetParseState();
this._parseState.start(value);
this._parseParts(autocomplete);
this._correctDate();
}
_resetParseState()
{
for (let i = 0; i < this._parts.length; i++) {
this._parts[i].resetState();
}
}
_parseParts(autocomplete)
{
let index = 0;
while (index < this._parts.length && (autocomplete || !this._parseState.isOver())) {
this._parts[index].parse(this._parseState);
if (!this._parts[index].isComplete()) {
return;
}
index++;
}
}
_correctDate()
{
if (!this._day.isComplete() || !this._month.isComplete()) {
return;
}
this._day.setMaximalValue(this._getMonthSize(this._month.numericValue() - 1, !this._year.isComplete() || this._isLeaPYear(this._year.numericValue())));
}
getParsedDate()
{
if (!this.isComplete()) {
return null;
}
return new Date(this._year.numericValue(), this._month.numericValue() - 1, this._day.numericValue());
}
_getMonthSize(monthIndex, isLeapYear)
{
if (monthIndex < 0 || monthIndex >= SmartDateMonthPart.MAXIMAL_VALUE) {
return SmartDateMonthPart.MAXIMAL_VALUE;
}
if (monthIndex === 1 && isLeapYear) { // February
return SmartDateFormat.BASIC_MONTH_SIZES[monthIndex] + 1;
}
return SmartDateFormat.BASIC_MONTH_SIZES[monthIndex];
}
_isLeaPYear(year)
{
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
}
getCorrectedValue()
{
let index = 0;
let value = '';
while (index < this._parts.length && !this._parts[index].isNoState()) {
value += this._parts[index].cleanedValue();
index++;
}
return value;
}
isComplete()
{
return this._parts[this._parts.length - 1].isComplete();
}
}
SmartDateFormat.DAY_ANCHOR = 'd';
SmartDateFormat.DAY_PLACEHOLDER = 'ДД';
SmartDateFormat.MONTH_ANCHOR = 'm';
SmartDateFormat.MONTH_PLACEHOLDER = 'ММ';
SmartDateFormat.YEAR_ANCHOR = 'y';
SmartDateFormat.YEAR_PLACEHOLDER = 'ГГГГ';
SmartDateFormat.BASIC_MONTH_SIZES = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
class SmartDateField
{
constructor(field, options)
{
this._dateYmdParser = new RegExp('^([0-9]{4})[,\._\/\-]+([0-9]{1,2})[,\._\/\-]+([0-9]{1,2})$');
this._dateDmyParser = new RegExp('^([0-9]{1,2})[,\._\/\-]+([0-9]{1,2})[,\._\/\-]+([0-9]{4})$');
this._loadOptions(options);
this._formatter = new SmartDateFormat(this._format, this._minimalDate, this._maximalDate);
this._lastCorrectValue = (this._defaultDate === null ? null : this._formatter.formatDate(this._defaultDate));
this._initField(field);
this._subscribeEvents();
}
_loadDefaultOptions()
{
this._format = 'd.m.y';
this._minimalDate = new Date(1900, 0, 1);
this._maximalDate = new Date(2099, 11, 31);
this._defaultDate = null;
}
_loadOptions(options)
{
this._loadDefaultOptions();
if (typeof(options) !== 'object' || options == null) {
return;
}
if (typeof(options.format) === 'string'
&& options.format.indexOf('d') >= 0
&& options.format.indexOf('m') >= 0
&& options.format.indexOf('y') >= 0) {
this._format = options.format;
}
if (typeof(options.minimal) !== 'undefined') {
let date = this._parseDateValue(options.minimal);
this._minimalDate = date;
}
if (typeof(options.maximal) !== 'undefined') {
let date = this._parseDateValue(options.maximal);
this._maximalDate = date;
}
if (typeof(options.default) !== 'undefined') {
let date = this._parseDateValue(options.default);
this._defaultDate = date;
}
this._correctDateOptions();
}
_correctDateOptions()
{
if (this._minimalDate.valueOf() > this._maximalDate.valueOf()) {
let temporaryDate = this._minimalDate;
this._minimalDate = this._maximalDate;
this._maximalDate = temporaryDate;
}
if (this._defaultDate === null) {
return;
}
if (this._defaultDate.valueOf() < this._minimalDate.valueOf()) {
this._defaultDate = this._minimalDate;
}
if (this._defaultDate.valueOf() > this._maximalDate.valueOf()) {
this._defaultDate = this._maximalDate;
}
}
_parseDateValue(date)
{
if (typeof(date) === 'object' && date !== null && date instanceof Date) {
return date;
}
if (typeof(date) !== 'string' || date.trim() === '') {
return null;
}
let parsed = this._dateYmdParser.exec(date);
if (parsed !== null) {
return new Date(Number(parsed[1]), Number(parsed[2]) - 1, Number(parsed[3]));
}
parsed = this._dateDmyParser.exec(date);
if (parsed === null) {
return null;
}
return new Date(Number(parsed[3]), Number(parsed[2]) - 1, Number(parsed[1]));
}
_initField(field)
{
this._field = field;
this._field.placeholder = this._formatter.getPlaceHolder();
if (this._isEmptyValue(this._field.value)) {
this._field.value = (this._defaultDate === null ? '' : this._formatter.formatDate(this._defaultDate));
}
else {
this.correctValue(true);
}
}
_subscribeEvents()
{
let _this = this;
let correct = function (event) {
if (event === null || event.type !== 'keyup') {
_this.correctValue(true);
}
if (_this._isServiceCode(event.keyCode)) {
return;
}
if (event.keyCode === 27) { // Escape
_this.returnLastCorrectValue();
}
_this.correctValue(true);
};
this._field.addEventListener('change', correct);
this._field.addEventListener('keyup', correct);
this._field.addEventListener('blur', function () {
_this.correctValue(true);
_this.returnLastCorrectValue();
});
}
_isServiceCode(code) {
return code === 8 /* backspace */
|| code === 46 /* delete */
|| code === 16 /* shift */
|| code === 17 /* shift */
|| code === 18 /* shift */
|| code === 20 /* caps lock */
|| code === 35 /* end */
|| code === 36 /* home */
|| code === 37 /* left */
|| code === 39 /* right */
|| code === 46 /* delete */;
}
_isEmptyValue(value)
{
return value.trim() === '';
}
getDefaultValue()
{
return this._defaultDate === null ? '' : this._formatter.formatDate(this._defaultDate);
}
correctValue(autocomplete)
{
this._formatter.parse(this._field.value, autocomplete);
let parsedValue = this._getLimitedParsedValue();
if (this._formatter.isComplete()) {
this._lastCorrectValue = parsedValue;
}
if (this._field.value === parsedValue) {
return;
}
this._setValueToFieled(parsedValue);
}
setValue(value)
{
let date = this._parseDateValue(value);
if (date === null || date.valueOf() < this._minimalDate.valueOf() || date.valueOf() > this._maximalDate.valueOf()) {
return;
}
this._lastCorrectValue = this._formatter.formatDate(date);
this._field.value = this._lastCorrectValue;
}
_getLimitedParsedValue()
{
if (!this._formatter.isComplete()) {
return this._formatter.getCorrectedValue();
}
let date = this._formatter.getParsedDate();
if (date.valueOf() < this._minimalDate.valueOf()) {
return this._formatter.formatDate(this._minimalDate);
}
else if (date.valueOf() > this._maximalDate.valueOf()) {
return this._formatter.formatDate(this._maximalDate);
}
return this._formatter.getCorrectedValue();
}
_setValueToFieled(newValue)
{
let position = this._field.selectionStart;
let isAtEnd = (position === this._field.value.length);
this._field.value = newValue;
if (!isAtEnd && position < this._field.value.length) {
this._field.selectionStart = position;
this._field.selectionEnd = position;
}
}
returnLastCorrectValue()
{
if (this._lastCorrectValue !== null) {
this._field.value = this._lastCorrectValue;
}
}
}
SmartDateField.initBy = function (id) {
return SmartDateField.initFor(document.getElementById(id));
};
SmartDateField.initFor = function (field, options) {
if (typeof(field) !== 'object'
|| field === null
|| field.tagName.toLowerCase() !== 'input'
|| field.type.toLowerCase() !== 'text') {
return;
}
if (typeof(field._smartDate) === 'object' && field._smartDate !== null && field._smartDate instanceof SmartDateField) {
return;
}
field._smartDate = new SmartDateField(field, options);
};
jQuery.fn.smartdate = function (options) {
let collection = [];
let field = null;
for (let i = 0; i < this.length; i++) {
field = SmartDateField.initFor(this[i], options);
if (field !== null) {
collection.push(field);
}
}
return jQuery(this);
};
jQuery.fn.setDateValue = function (value) {
for (let i = 0; i < this.length; i++) {
if (typeof(this[i]._smartDate) === 'object' && this[i]._smartDate !== null && this[i]._smartDate instanceof SmartDateField) {
this[i]._smartDate.setValue(value);
}
}
return jQuery(this);
};

File diff suppressed because one or more lines are too long

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script language="JavaScript" src="/jquery/jquery.min.js"></script>
<link href="/css/style.css" type="text/css" rel="stylesheet" />
<link href="/bootstrap/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<script language="JavaScript" src="/bootstrap/js/bootstrap.min.js"></script>
<script language="JavaScript" src="/src-es6/AbstractSmartDatePart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateStaticPart.js"></script>
<script language="JavaScript" src="/src-es6/AbstractSmartDateNumericPart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateDayPart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateMonthPart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateYearPart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateParseState.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateFormat.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateField.js"></script>
</head>
<body>
<br/>
<br/>
123 <input type="text" id="date" value="1.3.2019"/>
<script language="JavaScript">
jQuery('#date').smartdate({
format: 'd.m.y',
minimal: '1900-01-01',
maximal: '2099-12-31',
//default: new Date(),
});
//jQuery('#date').setDateValue('2019-4-5');
</script>
</body>
</html>

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script language="JavaScript" src="/jquery/jquery.min.js"></script>
<link href="/css/style.css" type="text/css" rel="stylesheet" />
<link href="/bootstrap/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<script language="JavaScript" src="/bootstrap/js/bootstrap.min.js"></script>
<script language="JavaScript" src="/es5/smart-date.min.js"></script>
</head>
<body>
<br/>
<br/>
123 <input type="text" id="date" value="1.3.2019"/>
<script language="JavaScript">
jQuery('#date').smartdate({
format: 'd.m.y',
minimal: '1900-01-01',
maximal: '2099-12-31',
//default: new Date(),
});
//jQuery('#date').setDateValue('2019-4-5');
</script>
</body>
</html>

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script language="JavaScript" src="/jquery/jquery.min.js"></script>
<link href="/css/style.css" type="text/css" rel="stylesheet" />
<link href="/bootstrap/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<script language="JavaScript" src="/bootstrap/js/bootstrap.min.js"></script>
<script language="JavaScript" src="/es6/smart-date.js"></script>
</head>
<body>
<br/>
<br/>
123 <input type="text" id="date" value="1.3.2019"/>
<script language="JavaScript">
jQuery('#date').smartdate({
format: 'd.m.y',
minimal: '1900-01-01',
maximal: '2099-12-31',
//default: new Date(),
});
//jQuery('#date').setDateValue('2019-4-5');
</script>
</body>
</html>

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script language="JavaScript" src="/jquery/jquery.min.js"></script>
<link href="/css/style.css" type="text/css" rel="stylesheet" />
<link href="/bootstrap/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<script language="JavaScript" src="/bootstrap/js/bootstrap.min.js"></script>
<script language="JavaScript" src="/src-es6/AbstractSmartDatePart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateStaticPart.js"></script>
<script language="JavaScript" src="/src-es6/AbstractSmartDateNumericPart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateDayPart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateMonthPart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateYearPart.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateParseState.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateFormat.js"></script>
<script language="JavaScript" src="/src-es6/SmartDateField.js"></script>
</head>
<body>
<br/>
<br/>
123 <input type="text" id="date"/>
<script language="JavaScript">
//ETrafficDateField.init(document.getElementById('date'));
jQuery('#date').smartdate({
format: 'd.m.y',
minimal: '1900-01-01',
maximal: '2099-12-31',
default: new Date(),
});
//jQuery('#date').setDateValue('2022-4-5');
</script>
</body>
</html>

@ -0,0 +1,14 @@
jQuery Component
================
Shim [repository](https://github.com/components/jquery) for the [jQuery](http://jquery.com).
If you're looking for jquery-migrate: It got it's [own repository](https://github.com/components/jquery-migrate) since jQuery v3.0.0.
Package Managers
----------------
* [Bower](http://bower.io/): `jquery`
* [Component](https://github.com/component/component): `components/jquery`
* [Composer](http://packagist.org/packages/components/jquery): `components/jquery`
* [spm](http://spmjs.io/package/jquery): `jquery`

@ -0,0 +1,16 @@
{
"name": "jquery",
"version": "3.3.1",
"description": "jQuery component",
"license": "MIT",
"keywords": [
"jquery",
"component"
],
"main": "jquery.js",
"ignore": [
"component.json",
"package.json",
"composer.json"
]
}

@ -0,0 +1,22 @@
{
"name": "jquery",
"repo": "components/jquery",
"version": "3.3.1",
"description": "jQuery component",
"license": "MIT",
"keywords": [
"jquery",
"component"
],
"main": "jquery.js",
"scripts": [
"jquery.js",
"jquery.min.js",
"jquery.slim.js",
"jquery.slim.min.js"
],
"files": [
"jquery.min.map",
"jquery.slim.min.map"
]
}

@ -0,0 +1,34 @@
{
"name": "components/jquery",
"description": "jQuery JavaScript Library",
"type": "component",
"homepage": "http://jquery.com",
"license": "MIT",
"support": {
"irc": "irc://irc.freenode.org/jquery",
"issues": "https://github.com/jquery/jquery/issues",
"forum": "http://forum.jquery.com",
"wiki": "http://docs.jquery.com/",
"source": "https://github.com/jquery/jquery"
},
"authors": [
{
"name": "JS Foundation and other contributors",
"url": "https://github.com/jquery/jquery/blob/master/AUTHORS.txt"
}
],
"extra": {
"component": {
"scripts": [
"jquery.js"
],
"files": [
"jquery.min.js",
"jquery.min.map",
"jquery.slim.js",
"jquery.slim.min.js",
"jquery.slim.min.map"
]
}
}
}

10364
jquery/jquery.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,33 @@
{
"name": "jquery",
"description": "JavaScript library for DOM operations",
"version": "3.3.1",
"homepage": "http://jquery.com",
"author": {
"name": "jQuery Foundation and other contributors",
"url": "https://github.com/jquery/jquery/blob/master/AUTHORS.txt"
},
"repository": {
"type": "git",
"url": "https://github.com/jquery/jquery.git"
},
"keywords": [
"jquery",
"javascript",
"browser",
"library"
],
"bugs": {
"url": "https://github.com/jquery/jquery/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/jquery/jquery/blob/master/LICENSE.txt"
}
],
"spm": {
"main": "jquery.js"
}
}

@ -0,0 +1,11 @@
{
"name": "smart-date",
"version": "0.1.0",
"scripts": {
"build-es6": "bash ./build-es6.sh; minify ./es6/smart-date.js --out-file ./es6/smart-date.min.js",
"build-es5": "babel ./es6/smart-date.js --out-file ./es5/smart-date.js --presets @babel/env; minify ./es6/smart-date.js --out-file ./es6/smart-date.min.js"
},
"devDependencies": {
"babel-cli": "^6.0.0"
}
}

@ -0,0 +1,152 @@
class AbstractSmartDateNumericPart extends AbstractSmartDatePart
{
constructor(placeholder, size)
{
super(placeholder, false);
this._numericValue = 0;
this._size = size;
}
numericValue()
{
return this._numericValue;
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
return '';
}
formatValue(value)
{
let stringValue = String(this._correctValue(value));
if (stringValue === '0') {
return stringValue;
}
if (stringValue.length > this._size) {
return stringValue.substring(0, this._size);
}
if (stringValue.length < this._size) {
return stringValue.padStart(this._size, '0');
}
return stringValue;
}
_correctValue(value)
{
return Math.round(value);
}
/**
* @param {SmartDateCheckState} state
* @returns {String}
*/
scanStringNumber(state)
{
let index = state.valueIndex();
let value = state.value();
while (index < value.length && this._isNumericSymbol(value[index])) {
index++;
}
let stringValue = value.substring(state.valueIndex(), index);
state.setValueIndex(index);
return stringValue;
}
calculateNumericValue(stringValue, maximalNumber)
{
var startIndex = 0;
var endIndex = 0;
var numericValue = 0;
while (startIndex < stringValue.length && stringValue[startIndex] === 0) {
startIndex++;
}
endIndex = startIndex;
while (endIndex < stringValue.length && numericValue < maximalNumber) {
numericValue = numericValue * 10 + Number(stringValue[endIndex]);
endIndex++;
}
if (numericValue > maximalNumber) {
endIndex--;
}
return Number(stringValue.substring(startIndex, endIndex));
}
/**
* @param {SmartDateCheckState} state
* @param {Number} maximalValue
*/
scanValue(state, maximalValue)
{
let index = state.valueIndex();
let value = state.value();
let numericValue = 0;
while (index < value.length && this._isNumericSymbol(value[index]) && numericValue < maximalValue) {
numericValue = numericValue * 10 + Number(value[index]);
index++;
}
if (numericValue > maximalValue) {
index--;
}
let stringValue = value.substring(state.valueIndex(), index);
state.setValueIndex(index);
return stringValue;
}
_defineParsingState(maximalValue, isAtEnd)
{
if (this._numericValue > 0 && (this._parsedValue.length >= this._size || !isAtEnd)) {
this._state = AbstractSmartDatePart.STATE_COMPLETE;
return;
}
if (this._numericValue * 10 > maximalValue) {
this._state = AbstractSmartDatePart.STATE_COMPLETE;
return;
}
this._state = AbstractSmartDatePart.STATE_INCOMPLETE;
}
_defineCleanValue()
{
this._cleanedValue = '';
if (this.isNoState()) {
return;
}
if (this.isComplete()) {
this._cleanedValue = this.formatValue(this._numericValue);
return;
}
if (this._parsedValue.length > 0) {
this._cleanedValue = String(this._numericValue);
}
}
}

@ -0,0 +1,77 @@
class AbstractSmartDatePart
{
constructor(placeholder, isStatic)
{
this._state = AbstractSmartDatePart.STATE_NONE;
this._cleanedValue = '';
this._parsedValue = '';
this._scanned = '';
this._placeholder = placeholder;
this._static = isStatic;
}
state()
{
return this._state;
}
isNoState()
{
return this._state === AbstractSmartDatePart.STATE_NONE;
}
isIncomplete()
{
return this._state === AbstractSmartDatePart.STATE_INCOMPLETE;
}
isComplete()
{
return this._state === AbstractSmartDatePart.STATE_COMPLETE;
}
resetState()
{
this._state = AbstractSmartDatePart.STATE_NONE;
}
cleanedValue()
{
return this._cleanedValue;
}
parsedValue()
{
return this._parsedValue;
}
placeHolder()
{
return this._placeholder;
}
isStatic()
{
return this._static;
}
_isNumericSymbol(symbol)
{
return '0' <= symbol && symbol <= '9';
}
/**
* @param {SmartDateCheckState} state
* @returns {Boolean}
*/
check(state)
{
return false;
}
};
AbstractSmartDatePart.STATE_NONE = 0;
AbstractSmartDatePart.STATE_INCOMPLETE = 1;
AbstractSmartDatePart.STATE_COMPLETE = 2;

@ -0,0 +1,62 @@
class SmartDateDayPart extends AbstractSmartDateNumericPart
{
constructor()
{
super(SmartDateFormat.DAY_PLACEHOLDER, 2);
this.maximalValue = SmartDateDayPart.MAXIMAL_VALUE;
}
resetState()
{
super.resetState();
this.maximalValue = SmartDateDayPart.MAXIMAL_VALUE;
}
setMaximalValue(value)
{
if (value != value /* isNaN */ || value < SmartDateDayPart.MAXIMAL_VALUE_LOW_LIMIT || SmartDateDayPart.MAXIMAL_VALUE_HIGH_LIMIT < value) {
return;
}
this.maximalValue = value;
if (this._numericValue <= this.maximalValue) {
return;
}
this._numericValue = this.maximalValue;
this._defineCleanValue();
}
resetMaximalValue()
{
this.maximalValue = SmartDateDayPart.MAXIMAL_VALUE;
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
return this.formatValue(date.getDate());
}
/**
* @param {SmartDateCheckState} state
*/
parse(state)
{
this._parsedValue = this.scanStringNumber(state);
this._numericValue = this.calculateNumericValue(this._parsedValue, this.maximalValue);
this._defineParsingState(this.maximalValue, state.isOver());
this._defineCleanValue();
}
}
SmartDateDayPart.MAXIMAL_VALUE_LOW_LIMIT = 28;
SmartDateDayPart.MAXIMAL_VALUE_HIGH_LIMIT = 31;
SmartDateDayPart.MAXIMAL_VALUE = SmartDateDayPart.MAXIMAL_VALUE_HIGH_LIMIT;

@ -0,0 +1,283 @@
class SmartDateField
{
constructor(field, options)
{
this._dateYmdParser = new RegExp('^([0-9]{4})[,\._\/\-]+([0-9]{1,2})[,\._\/\-]+([0-9]{1,2})$');
this._dateDmyParser = new RegExp('^([0-9]{1,2})[,\._\/\-]+([0-9]{1,2})[,\._\/\-]+([0-9]{4})$');
this._loadOptions(options);
this._formatter = new SmartDateFormat(this._format, this._minimalDate, this._maximalDate);
this._lastCorrectValue = (this._defaultDate === null ? null : this._formatter.formatDate(this._defaultDate));
this._initField(field);
this._subscribeEvents();
}
_loadDefaultOptions()
{
this._format = 'd.m.y';
this._minimalDate = new Date(1900, 0, 1);
this._maximalDate = new Date(2099, 11, 31);
this._defaultDate = null;
}
_loadOptions(options)
{
this._loadDefaultOptions();
if (typeof(options) !== 'object' || options == null) {
return;
}
if (typeof(options.format) === 'string'
&& options.format.indexOf('d') >= 0
&& options.format.indexOf('m') >= 0
&& options.format.indexOf('y') >= 0) {
this._format = options.format;
}
if (typeof(options.minimal) !== 'undefined') {
let date = this._parseDateValue(options.minimal);
this._minimalDate = date;
}
if (typeof(options.maximal) !== 'undefined') {
let date = this._parseDateValue(options.maximal);
this._maximalDate = date;
}
if (typeof(options.default) !== 'undefined') {
let date = this._parseDateValue(options.default);
this._defaultDate = date;
}
this._correctDateOptions();
}
_correctDateOptions()
{
if (this._minimalDate.valueOf() > this._maximalDate.valueOf()) {
let temporaryDate = this._minimalDate;
this._minimalDate = this._maximalDate;
this._maximalDate = temporaryDate;
}
if (this._defaultDate === null) {
return;
}
if (this._defaultDate.valueOf() < this._minimalDate.valueOf()) {
this._defaultDate = this._minimalDate;
}
if (this._defaultDate.valueOf() > this._maximalDate.valueOf()) {
this._defaultDate = this._maximalDate;
}
}
_parseDateValue(date)
{
if (typeof(date) === 'object' && date !== null && date instanceof Date) {
return date;
}
if (typeof(date) !== 'string' || date.trim() === '') {
return null;
}
let parsed = this._dateYmdParser.exec(date);
if (parsed !== null) {
return new Date(Number(parsed[1]), Number(parsed[2]) - 1, Number(parsed[3]));
}
parsed = this._dateDmyParser.exec(date);
if (parsed === null) {
return null;
}
return new Date(Number(parsed[3]), Number(parsed[2]) - 1, Number(parsed[1]));
}
_initField(field)
{
this._field = field;
this._field.placeholder = this._formatter.getPlaceHolder();
if (this._isEmptyValue(this._field.value)) {
this._field.value = (this._defaultDate === null ? '' : this._formatter.formatDate(this._defaultDate));
}
else {
this.correctValue(true);
}
}
_subscribeEvents()
{
let _this = this;
let correct = function (event) {
if (event === null || event.type !== 'keyup') {
_this.correctValue(true);
}
if (_this._isServiceCode(event.keyCode)) {
return;
}
if (event.keyCode === 27) { // Escape
_this.returnLastCorrectValue();
}
_this.correctValue(true);
};
this._field.addEventListener('change', correct);
this._field.addEventListener('keyup', correct);
this._field.addEventListener('blur', function () {
_this.correctValue(true);
_this.returnLastCorrectValue();
});
}
_isServiceCode(code) {
return code === 8 /* backspace */
|| code === 46 /* delete */
|| code === 16 /* shift */
|| code === 17 /* shift */
|| code === 18 /* shift */
|| code === 20 /* caps lock */
|| code === 35 /* end */
|| code === 36 /* home */
|| code === 37 /* left */
|| code === 39 /* right */
|| code === 46 /* delete */;
}
_isEmptyValue(value)
{
return value.trim() === '';
}
getDefaultValue()
{
return this._defaultDate === null ? '' : this._formatter.formatDate(this._defaultDate);
}
correctValue(autocomplete)
{
this._formatter.parse(this._field.value, autocomplete);
let parsedValue = this._getLimitedParsedValue();
if (this._formatter.isComplete()) {
this._lastCorrectValue = parsedValue;
}
if (this._field.value === parsedValue) {
return;
}
this._setValueToFieled(parsedValue);
}
setValue(value)
{
let date = this._parseDateValue(value);
if (date === null || date.valueOf() < this._minimalDate.valueOf() || date.valueOf() > this._maximalDate.valueOf()) {
return;
}
this._lastCorrectValue = this._formatter.formatDate(date);
this._field.value = this._lastCorrectValue;
}
_getLimitedParsedValue()
{
if (!this._formatter.isComplete()) {
return this._formatter.getCorrectedValue();
}
let date = this._formatter.getParsedDate();
if (date.valueOf() < this._minimalDate.valueOf()) {
return this._formatter.formatDate(this._minimalDate);
}
else if (date.valueOf() > this._maximalDate.valueOf()) {
return this._formatter.formatDate(this._maximalDate);
}
return this._formatter.getCorrectedValue();
}
_setValueToFieled(newValue)
{
let position = this._field.selectionStart;
let isAtEnd = (position === this._field.value.length);
this._field.value = newValue;
if (!isAtEnd && position < this._field.value.length) {
this._field.selectionStart = position;
this._field.selectionEnd = position;
}
}
returnLastCorrectValue()
{
if (this._lastCorrectValue !== null) {
this._field.value = this._lastCorrectValue;
}
}
}
SmartDateField.initBy = function (id) {
return SmartDateField.initFor(document.getElementById(id));
};
SmartDateField.initFor = function (field, options) {
if (typeof(field) !== 'object'
|| field === null
|| field.tagName.toLowerCase() !== 'input'
|| field.type.toLowerCase() !== 'text') {
return;
}
if (typeof(field._smartDate) === 'object' && field._smartDate !== null && field._smartDate instanceof SmartDateField) {
return;
}
field._smartDate = new SmartDateField(field, options);
};
jQuery.fn.smartdate = function (options) {
let collection = [];
let field = null;
for (let i = 0; i < this.length; i++) {
field = SmartDateField.initFor(this[i], options);
if (field !== null) {
collection.push(field);
}
}
return jQuery(this);
};
jQuery.fn.setDateValue = function (value) {
for (let i = 0; i < this.length; i++) {
if (typeof(this[i]._smartDate) === 'object' && this[i]._smartDate !== null && this[i]._smartDate instanceof SmartDateField) {
this[i]._smartDate.setValue(value);
}
}
return jQuery(this);
};

@ -0,0 +1,247 @@
class SmartDateFormat
{
constructor(format)
{
this._day = null;
this._month = null;
this._year = null;
this.loadFormat(format);
this._parseState = new SmartDateParseState();
}
loadFormat(format)
{
let index = 0;
let lowFormat = format.toLowerCase();
this._format = format;
this._parts = [];
while (index < format.length) {
index = this._scanPart(index, format, lowFormat);
}
}
_scanPart(index, format, lowFormat)
{
switch (lowFormat[index]) {
case SmartDateFormat.DAY_ANCHOR:
return this._scanDay(index, lowFormat);
case SmartDateFormat.MONTH_ANCHOR:
return this._scanMonth(index, lowFormat);
case SmartDateFormat.YEAR_ANCHOR:
return this._scanYear(index, lowFormat);
}
return this._scanStatic(index, format, lowFormat);
}
_scanDay(startIndex, lowFormat)
{
this._checkLastIsStatic();
this._day = new SmartDateDayPart();
this._parts.push(this._day);
return this._avoidAnchor(SmartDateFormat.DAY_ANCHOR, startIndex, lowFormat);
}
_scanMonth(startIndex, lowFormat)
{
this._checkLastIsStatic();
this._month = new SmartDateMonthPart();
this._parts.push(this._month);
return this._avoidAnchor(SmartDateFormat.MONTH_ANCHOR, startIndex, lowFormat);
}
_scanYear(startIndex, lowFormat)
{
this._checkLastIsStatic();
this._year = new SmartDateYearPart();
this._parts.push(this._year);
return this._avoidAnchor(SmartDateFormat.YEAR_ANCHOR, startIndex, lowFormat);
}
_avoidAnchor(anchor, startIndex, lowFormat)
{
let endingIndex = startIndex;
while (endingIndex < lowFormat.length && lowFormat[endingIndex] === anchor) {
endingIndex++;
}
return endingIndex;
}
_checkLastIsStatic()
{
if (this._parts.length === 0) {
return;
}
if (!this._parts[this._parts.length - 1].isStatic()) {
throw new Error('Dynamic date parts must be separated by static substrings');
}
}
_scanStatic(startIndex, format, lowFormat)
{
let endingIndex = startIndex;
while (endingIndex < format.length && this._isStaticAnchorSymbol(lowFormat[endingIndex])) {
endingIndex++;
}
this._parts.push(new SmartDateStaticPart(format.substring(startIndex, endingIndex)));
return endingIndex;
}
_isStaticAnchorSymbol(symbol)
{
return symbol !== SmartDateFormat.DAY_ANCHOR
&& symbol !== SmartDateFormat.MONTH_ANCHOR
&& symbol !== SmartDateFormat.YEAR_ANCHOR;
}
getPlaceHolder()
{
let placeHolder = '';
for (let i = 0; i < this._parts.length; i++) {
placeHolder += this._parts[i].placeHolder();
}
return placeHolder;
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
var formattedDate = '';
for (let i = 0; i < this._parts.length; i++) {
if (this._parts[i].isStatic()) {
formattedDate += this._parts[i].placeHolder();
}
else {
formattedDate += this._parts[i].formatDate(date);
}
}
return formattedDate;
}
parse(value, autocomplete)
{
this._resetParseState();
this._parseState.start(value);
this._parseParts(autocomplete);
this._correctDate();
}
_resetParseState()
{
for (let i = 0; i < this._parts.length; i++) {
this._parts[i].resetState();
}
}
_parseParts(autocomplete)
{
let index = 0;
while (index < this._parts.length && (autocomplete || !this._parseState.isOver())) {
this._parts[index].parse(this._parseState);
if (!this._parts[index].isComplete()) {
return;
}
index++;
}
}
_correctDate()
{
if (!this._day.isComplete() || !this._month.isComplete()) {
return;
}
this._day.setMaximalValue(this._getMonthSize(this._month.numericValue() - 1, !this._year.isComplete() || this._isLeaPYear(this._year.numericValue())));
}
getParsedDate()
{
if (!this.isComplete()) {
return null;
}
return new Date(this._year.numericValue(), this._month.numericValue() - 1, this._day.numericValue());
}
_getMonthSize(monthIndex, isLeapYear)
{
if (monthIndex < 0 || monthIndex >= SmartDateMonthPart.MAXIMAL_VALUE) {
return SmartDateMonthPart.MAXIMAL_VALUE;
}
if (monthIndex === 1 && isLeapYear) { // February
return SmartDateFormat.BASIC_MONTH_SIZES[monthIndex] + 1;
}
return SmartDateFormat.BASIC_MONTH_SIZES[monthIndex];
}
_isLeaPYear(year)
{
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
}
getCorrectedValue()
{
let index = 0;
let value = '';
while (index < this._parts.length && !this._parts[index].isNoState()) {
value += this._parts[index].cleanedValue();
index++;
}
return value;
}
isComplete()
{
return this._parts[this._parts.length - 1].isComplete();
}
}
SmartDateFormat.DAY_ANCHOR = 'd';
SmartDateFormat.DAY_PLACEHOLDER = 'ДД';
SmartDateFormat.MONTH_ANCHOR = 'm';
SmartDateFormat.MONTH_PLACEHOLDER = 'ММ';
SmartDateFormat.YEAR_ANCHOR = 'y';
SmartDateFormat.YEAR_PLACEHOLDER = 'ГГГГ';
SmartDateFormat.BASIC_MONTH_SIZES = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

@ -0,0 +1,31 @@
class SmartDateMonthPart extends AbstractSmartDateNumericPart
{
constructor()
{
super(SmartDateFormat.MONTH_PLACEHOLDER, 2);
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
return this.formatValue(date.getMonth() + 1);
}
/**
* @param {SmartDateCheckState} state
*/
parse(state)
{
this._parsedValue = this.scanStringNumber(state);
this._numericValue = this.calculateNumericValue(this._parsedValue, SmartDateMonthPart.MAXIMAL_VALUE);
this._defineParsingState(SmartDateMonthPart.MAXIMAL_VALUE, state.isOver());
this._defineCleanValue();
}
}
SmartDateMonthPart.MAXIMAL_VALUE = 12;

@ -0,0 +1,53 @@
class SmartDateParseState
{
constructor()
{
this._value = '';
this._valueIndex = 0;
}
start(value)
{
this._value = value;
this._valueIndex = 0;
}
current()
{
return this.isOver() ? '' : this._value[this._valueIndex];
}
next()
{
if (this.isOver()) {
return false;
}
this._valueIndex++;
return !this.isOver();
}
isOver()
{
return this._valueIndex >= this._value.length;
}
value()
{
return this._value;
}
valueIndex()
{
return this._valueIndex;
}
setValueIndex(index)
{
if (this._valueIndex < index) {
this._valueIndex = index;
}
}
}

@ -0,0 +1,30 @@
class SmartDateStaticPart extends AbstractSmartDatePart
{
constructor(placeholder)
{
super(placeholder, true);
this._cleanedValue = placeholder;
}
/**
* @param {SmartDateCheckState} state
* @returns {Boolean}
*/
parse(state)
{
let value = state.value();
let index = state.valueIndex();
while (index < value.length && !this._isNumericSymbol(value[index])) {
index++;
}
this._parsedValue = value.substring(state.valueIndex(), index);
this._state = AbstractSmartDatePart.STATE_COMPLETE;
state.setValueIndex(index);
return true;
}
}

@ -0,0 +1,62 @@
class SmartDateYearPart extends AbstractSmartDateNumericPart
{
constructor()
{
super(SmartDateFormat.YEAR_PLACEHOLDER, 4);
}
/**
* @param {Date} date
* @returns {String}
*/
formatDate(date)
{
return this.formatValue(date.getFullYear());
}
/**
* @param {SmartDateCheckState} state
* @returns {Boolean}
*/
parse(state)
{
this._parsedValue = this.scanStringNumber(state);
this._numericValue = Number(this._parsedValue);
this._defineParsingState();
if (this._numericValue > SmartDateYearPart.MAXIMAL_VALUE) {
this._numericValue = SmartDateYearPart.MAXIMAL_VALUE;
}
if (this.isComplete() && this._numericValue < SmartDateYearPart.MINIMAL_VALUE) {
this._numericValue = SmartDateYearPart.MINIMAL_VALUE;
}
this._defineCleanValue();
}
_defineParsingState()
{
if (this._numericValue > 0 && this._parsedValue.length >= this._size) {
this._state = AbstractSmartDatePart.STATE_COMPLETE;
return;
}
this._state = AbstractSmartDatePart.STATE_INCOMPLETE;
}
_defineCleanValue()
{
if (this._numericValue > 0) {
this._cleanedValue = String(this._numericValue);
}
else {
this._cleanedValue = '';
}
}
}
SmartDateYearPart.MINIMAL_VALUE = 1000;
SmartDateYearPart.MAXIMAL_VALUE = 9999;
Loading…
Cancel
Save