PageSourceSearch

https://65050.ru/bitrix/js/main/core/core_date.js

js 65050.ru collected 2026-10-02 02:07:05 UTC 53,208 bytes, 1,979 lines download raw bytes

1;(function(){
2
3if (BX.date)
4	return;
5
6BX.date = {};
7
8
9BX.date.format = function(format, timestamp, now, utc)
10{
11	/*
12	PHP to Javascript:
13		time() = new Date()
14		mktime(...) = new Date(...)
15		gmmktime(...) = new Date(Date.UTC(...))
16		mktime(0,0,0, 1, 1, 1970) != 0          new Date(1970,0,1).getTime() != 0
17		gmmktime(0,0,0, 1, 1, 1970) == 0        new Date(Date.UTC(1970,0,1)).getTime() == 0
18		date("d.m.Y H:i:s") = BX.date.format("d.m.Y H:i:s")
19		gmdate("d.m.Y H:i:s") = BX.date.format("d.m.Y H:i:s", null, null, true);
20	*/
21	var date = BX.type.isDate(timestamp) ? new Date(timestamp.getTime()) : BX.type.isNumber(timestamp) ? new Date(timestamp * 1000) : new Date();
22	var nowDate = BX.type.isDate(now) ? new Date(now.getTime()) : BX.type.isNumber(now) ? new Date(now * 1000) : new Date();
23	var isUTC = !!utc;
24
25	if (BX.type.isArray(format))
26		return _formatDateInterval(format, date, nowDate, isUTC);
27	else if (!BX.type.isNotEmptyString(format))
28		return "";
29
30	var formatRegex = /\\?(sago|iago|isago|Hago|dago|mago|Yago|sdiff|idiff|Hdiff|ddiff|mdiff|Ydiff|yesterday|today|tommorow|[a-z])/gi;
31
32	var dateFormats = {
33		d : function() {
34			// Day of the month 01 to 31
35			return BX.util.str_pad_left(getDate(date).toString(), 2, "0");
36		},
37
38		D : function() {
39			//Mon through Sun
40			return BX.message("DOW_" + getDay(date));
41		},
42
43		j : function() {
44			//Day of the month 1 to 31
45			return getDate(date);
46		},
47
48		l : function() {
49			//Sunday through Saturday
50			return BX.message("DAY_OF_WEEK_" + getDay(date));
51		},
52
53		N : function() {
54			//1 (for Monday) through 7 (for Sunday)
55			return getDay(date) || 7;
56		},
57
58		S : function() {
59			//st, nd, rd or th. Works well with j
60			if (getDate(date) % 10 == 1 && getDate(date) != 11)
61				return "st";
62			else if (getDate(date) % 10 == 2 && getDate(date) != 12)
63				return "nd";
64			else if (getDate(date) % 10 == 3 && getDate(date) != 13)
65				return "rd";
66			else
67				return "th";
68		},
69
70		w : function() {
71			//0 (for Sunday) through 6 (for Saturday)
72			return getDay(date);
73		},
74
75		z : function() {
76			//0 through 365
77			var firstDay = new Date(getFullYear(date), 0, 1);
78			var currentDay = new Date(getFullYear(date), getMonth(date), getDate(date));
79			return Math.ceil( (currentDay - firstDay) / (24 * 3600 * 1000) );
80		},
81
82		W : function() {
83			//ISO-8601 week number of year
84			var newDate  = new Date(date.getTime());
85		    var dayNumber   = (getDay(date) + 6) % 7;
86			setDate(newDate, getDate(newDate) - dayNumber + 3);
87		    var firstThursday = newDate.getTime();
88			setMonth(newDate, 0, 1);
89		    if (getDay(newDate) != 4)
90				setMonth(newDate, 0, 1 + ((4 - getDay(newDate)) + 7) % 7);
91			var weekNumber = 1 + Math.ceil((firstThursday - newDate) / (7 * 24 * 3600 * 1000));
92		    return BX.util.str_pad_left(weekNumber.toString(), 2, "0");
93		},
94
95		F : function() {
96			//January through December
97			return BX.message("MONTH_" + (getMonth(date) + 1) + "_S");
98		},
99
100		f : function() {
101			//January through December
102			return BX.message("MONTH_" + (getMonth(date) + 1));
103		},
104
105		m : function() {
106			//Numeric representation of a month 01 through 12
107			return BX.util.str_pad_left((getMonth(date) + 1).toString(), 2, "0");
108		},
109
110		M : function() {
111			//A short textual representation of a month, three letters Jan through Dec
112			return BX.message("MON_" + (getMonth(date) + 1));
113		},
114
115		n : function() {
116			//Numeric representation of a month 1 through 12
117			return getMonth(date) + 1;
118		},
119
120		t : function() {
121			//Number of days in the given month 28 through 31
122			var lastMonthDay = isUTC ? new Date(Date.UTC(getFullYear(date), getMonth(date) + 1, 0)) : new Date(getFullYear(date), getMonth(date) + 1, 0);
123			return getDate(lastMonthDay);
124		},
125
126		L : function() {
127			//1 if it is a leap year, 0 otherwise.
128			var year = getFullYear(date);
129			return (year % 4 == 0 && year % 100 != 0 || year % 400 == 0 ? 1 : 0);
130		},
131
132		o : function() {
133			//ISO-8601 year number
134			var correctDate  = new Date(date.getTime());
135			setDate(correctDate, getDate(correctDate) - ((getDay(date) + 6) % 7) + 3);
136   			return getFullYear(correctDate);
137		},
138
139		Y : function() {
140			//A full numeric representation of a year, 4 digits
141			return getFullYear(date);
142		},
143
144		y : function() {
145			//A two digit representation of a year
146			return getFullYear(date).toString().slice(2);
147		},
148
149		a : function() {
150			//am or pm
151			return getHours(date) > 11 ? "pm" : "am";
152		},
153
154		A : function() {
155			//AM or PM
156			return getHours(date) > 11 ? "PM" : "AM";
157		},
158
159		B : function() {
160			//000 through 999
161			var swatch = ((date.getUTCHours() + 1) % 24) + date.getUTCMinutes() / 60 + date.getUTCSeconds() / 3600;
162			return BX.util.str_pad_left(Math.floor(swatch * 1000 / 24).toString(), 3, "0");
163		},
164
165		g : function() {
166			//12-hour format of an hour without leading zeros 1 through 12
167			return getHours(date) % 12 || 12;
168		},
169
170		G : function() {
171			//24-hour format of an hour without leading zeros 0 through 23
172			return getHours(date);
173		},
174
175		h : function() {
176			//12-hour format of an hour with leading zeros 01 through 12
177			return BX.util.str_pad_left((getHours(date) % 12 || 12).toString(), 2, "0");
178		},
179
180		H : function() {
181			//24-hour format of an hour with leading zeros 00 through 23
182			return BX.util.str_pad_left(getHours(date).toString(), 2, "0");
183		},
184
185		i : function() {
186			//Minutes with leading zeros 00 to 59
187			return BX.util.str_pad_left(getMinutes(date).toString(), 2, "0");
188		},
189
190		s : function() {
191			//Seconds, with leading zeros 00 through 59
192			return BX.util.str_pad_left(getSeconds(date).toString(), 2, "0");
193		},
194
195		u : function() {
196			//Microseconds
197			return BX.util.str_pad_left((getMilliseconds(date) * 1000).toString(), 6, "0");
198		},
199
200		e : function() {
201			if (isUTC)
202				return "UTC";
203			return "";
204		},
205
206		I : function() {
207			if (isUTC)
208				return 0;
209
210			//Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise
211			var firstJanuary = new Date(getFullYear(date), 0, 1);
212			var firstJanuaryUTC = Date.UTC(getFullYear(date), 0, 1);
213			var firstJuly = new Date(getFullYear(date), 6, 0);
214			var firstJulyUTC = Date.UTC(getFullYear(date), 6, 0);
215			return 0 + ((firstJanuary - firstJanuaryUTC) !== (firstJuly - firstJulyUTC));
216		},
217
218		O : function() {
219			if (isUTC)
220				return "+0000";
221
222			//Difference to Greenwich time (GMT) in hours +0200
223			var timezoneOffset = date.getTimezoneOffset();
224			var timezoneOffsetAbs = Math.abs(timezoneOffset);
225			return (timezoneOffset > 0 ? "-" : "+") + BX.util.str_pad_left((Math.floor(timezoneOffsetAbs / 60) * 100 + timezoneOffsetAbs % 60).toString(), 4, "0");
226		},
227
228		P : function() {
229			if (isUTC)
230				return "+00:00";
231
232			//Difference to Greenwich time (GMT) with colon between hours and minutes +02:00
233			var difference = this.O();
234			return difference.substr(0, 3) + ":" + difference.substr(3);
235		},
236
237		Z : function() {
238			if (isUTC)
239				return 0;
240			//Timezone offset in seconds. The offset for timezones west of UTC is always negative,
241			//and for those east of UTC is always positive.
242			return -date.getTimezoneOffset() * 60;
243		},
244
245		c : function() {
246			//ISO 8601 date
247			return "Y-m-d\\TH:i:sP".replace(formatRegex, _replaceDateFormat);
248		},
249
250		r : function() {
251			//RFC 2822 formatted date
252			return "D, d M Y H:i:s O".replace(formatRegex, _replaceDateFormat);
253		},
254
255		U : function() {
256			//Seconds since the Unix Epoch
257			return Math.floor(date.getTime() / 1000);
258		},
259
260		sago : function() {
261			return _formatDateMessage(intval((nowDate - date) / 1000), {
262				"0" : "FD_SECOND_AGO_0",
263				"1" : "FD_SECOND_AGO_1",
264				"10_20" : "FD_SECOND_AGO_10_20",
265				"MOD_1" : "FD_SECOND_AGO_MOD_1",
266				"MOD_2_4" : "FD_SECOND_AGO_MOD_2_4",
267				"MOD_OTHER" : "FD_SECOND_AGO_MOD_OTHER"
268			});
269		},
270
271		sdiff : function() {
272			return _formatDateMessage(intval((nowDate - date) / 1000), {
273				"0" : "FD_SECOND_DIFF_0",
274				"1" : "FD_SECOND_DIFF_1",
275				"10_20" : "FD_SECOND_DIFF_10_20",
276				"MOD_1" : "FD_SECOND_DIFF_MOD_1",
277				"MOD_2_4" : "FD_SECOND_DIFF_MOD_2_4",
278				"MOD_OTHER" : "FD_SECOND_DIFF_MOD_OTHER"
279			});
280		},
281
282		iago : function() {
283			return _formatDateMessage(intval((nowDate - date) / 60 / 1000), {
284				"0" : "FD_MINUTE_AGO_0",
285				"1" : "FD_MINUTE_AGO_1",
286				"10_20" : "FD_MINUTE_AGO_10_20",
287				"MOD_1" : "FD_MINUTE_AGO_MOD_1",
288				"MOD_2_4" : "FD_MINUTE_AGO_MOD_2_4",
289				"MOD_OTHER" : "FD_MINUTE_AGO_MOD_OTHER"
290			});
291		},
292
293		idiff : function() {
294			return _formatDateMessage(intval((nowDate - date) / 60 / 1000), {
295				"0" : "FD_MINUTE_DIFF_0",
296				"1" : "FD_MINUTE_DIFF_1",
297				"10_20" : "FD_MINUTE_DIFF_10_20",
298				"MOD_1" : "FD_MINUTE_DIFF_MOD_1",
299				"MOD_2_4" : "FD_MINUTE_DIFF_MOD_2_4",
300				"MOD_OTHER" : "FD_MINUTE_DIFF_MOD_OTHER"
301			});
302		},
303
304		isago : function() {
305			var minutesAgo = intval((nowDate - date) / 60 / 1000);
306			var result = _formatDateMessage(minutesAgo, {
307				"0" : "FD_MINUTE_0",
308				"1" : "FD_MINUTE_1",
309				"10_20" : "FD_MINUTE_10_20",
310				"MOD_1" : "FD_MINUTE_MOD_1",
311				"MOD_2_4" : "FD_MINUTE_MOD_2_4",
312				"MOD_OTHER" : "FD_MINUTE_MOD_OTHER"
313			});
314
315			result += " ";
316
317			var secondsAgo = intval((nowDate - date) / 1000) - (minutesAgo * 60);
318			result += _formatDateMessage(secondsAgo, {
319				"0" : "FD_SECOND_AGO_0",
320				"1" : "FD_SECOND_AGO_1",
321				"10_20" : "FD_SECOND_AGO_10_20",
322				"MOD_1" : "FD_SECOND_AGO_MOD_1",
323				"MOD_2_4" : "FD_SECOND_AGO_MOD_2_4",
324				"MOD_OTHER" : "FD_SECOND_AGO_MOD_OTHER"
325			});
326			return result;
327		},
328
329		Hago : function() {
330			return _formatDateMessage(intval((nowDate - date) / 60 / 60 / 1000), {
331				"0" : "FD_HOUR_AGO_0",
332				"1" : "FD_HOUR_AGO_1",
333				"10_20" : "FD_HOUR_AGO_10_20",
334				"MOD_1" : "FD_HOUR_AGO_MOD_1",
335				"MOD_2_4" : "FD_HOUR_AGO_MOD_2_4",
336				"MOD_OTHER" : "FD_HOUR_AGO_MOD_OTHER"
337			});
338		},
339
340		Hdiff : function() {
341			return _formatDateMessage(intval((nowDate - date) / 60 / 60 / 1000), {
342				"0" : "FD_HOUR_DIFF_0",
343				"1" : "FD_HOUR_DIFF_1",
344				"10_20" : "FD_HOUR_DIFF_10_20",
345				"MOD_1" : "FD_HOUR_DIFF_MOD_1",
346				"MOD_2_4" : "FD_HOUR_DIFF_MOD_2_4",
347				"MOD_OTHER" : "FD_HOUR_DIFF_MOD_OTHER"
348			});
349		},
350
351		yesterday : function() {
352			return BX.message("FD_YESTERDAY");
353		},
354
355		today : function() {
356			return BX.message("FD_TODAY");
357		},
358
359		tommorow : function() {
360			return BX.message("FD_TOMORROW");
361		},
362
363		dago : function() {
364			return _formatDateMessage(intval((nowDate - date) / 60 / 60 / 24 / 1000), {
365				"0" : "FD_DAY_AGO_0",
366				"1" : "FD_DAY_AGO_1",
367				"10_20" : "FD_DAY_AGO_10_20",
368				"MOD_1" : "FD_DAY_AGO_MOD_1",
369				"MOD_2_4" : "FD_DAY_AGO_MOD_2_4",
370				"MOD_OTHER" : "FD_DAY_AGO_MOD_OTHER"
371			});
372		},
373
374		ddiff : function() {
375			return _formatDateMessage(intval((nowDate - date) / 60 / 60 / 24 / 1000), {
376				"0" : "FD_DAY_DIFF_0",
377				"1" : "FD_DAY_DIFF_1",
378				"10_20" : "FD_DAY_DIFF_10_20",
379				"MOD_1" : "FD_DAY_DIFF_MOD_1",
380				"MOD_2_4" : "FD_DAY_DIFF_MOD_2_4",
381				"MOD_OTHER" : "FD_DAY_DIFF_MOD_OTHER"
382			});
383		},
384
385		mago : function() {
386			return _formatDateMessage(intval((nowDate - date) / 60 / 60 / 24 / 31 / 1000), {
387				"0" : "FD_MONTH_AGO_0",
388				"1" : "FD_MONTH_AGO_1",
389				"10_20" : "FD_MONTH_AGO_10_20",
390				"MOD_1" : "FD_MONTH_AGO_MOD_1",
391				"MOD_2_4" : "FD_MONTH_AGO_MOD_2_4",
392				"MOD_OTHER" : "FD_MONTH_AGO_MOD_OTHER"
393			});
394		},
395
396		mdiff : function() {
397			return _formatDateMessage(intval((nowDate - date) / 60 / 60 / 24 / 31 / 1000), {
398				"0" : "FD_MONTH_DIFF_0",
399				"1" : "FD_MONTH_DIFF_1",
400				"10_20" : "FD_MONTH_DIFF_10_20",
401				"MOD_1" : "FD_MONTH_DIFF_MOD_1",
402				"MOD_2_4" : "FD_MONTH_DIFF_MOD_2_4",
403				"MOD_OTHER" : "FD_MONTH_DIFF_MOD_OTHER"
404			});
405		},
406
407		Yago : function() {
408			return _formatDateMessage(intval((nowDate - date) / 60 / 60 / 24 / 365 / 1000), {
409				"0" : "FD_YEARS_AGO_0",
410				"1" : "FD_YEARS_AGO_1",
411				"10_20" : "FD_YEARS_AGO_10_20",
412				"MOD_1" : "FD_YEARS_AGO_MOD_1",
413				"MOD_2_4" : "FD_YEARS_AGO_MOD_2_4",
414				"MOD_OTHER" : "FD_YEARS_AGO_MOD_OTHER"
415			});
416		},
417
418		Ydiff : function() {
419			return _formatDateMessage(intval((nowDate - date) / 60 / 60 / 24 / 365 / 1000), {
420				"0" : "FD_YEARS_DIFF_0",
421				"1" : "FD_YEARS_DIFF_1",
422				"10_20" : "FD_YEARS_DIFF_10_20",
423				"MOD_1" : "FD_YEARS_DIFF_MOD_1",
424				"MOD_2_4" : "FD_YEARS_DIFF_MOD_2_4",
425				"MOD_OTHER" : "FD_YEARS_DIFF_MOD_OTHER"
426			});
427		},
428
429		x : function() {
430			return BX.date.format([
431				["tommorow", "tommorow, H:i"],
432				["-", BX.date.convertBitrixFormat(BX.message("FORMAT_DATETIME")).replace(/:s$/g, "")],
433				["s", "sago"],
434				["i", "iago"],
435				["today", "today, H:i"],
436				["yesterday", "yesterday, H:i"],
437				["", BX.date.convertBitrixFormat(BX.message("FORMAT_DATETIME")).replace(/:s$/g, "")]
438			], date, nowDate, isUTC);
439		},
440
441		X : function() {
442			var day = BX.date.format([
443				["tommorow", "tommorow"],
444				["-", BX.date.convertBitrixFormat(BX.message("FORMAT_DATE"))],
445				["today", "today"],
446				["yesterday", "yesterday"],
447				["", BX.date.convertBitrixFormat(BX.message("FORMAT_DATE"))]
448			], date, nowDate, isUTC);
449
450			var time = BX.date.format([
451				["tommorow", "H:i"],
452				["today", "H:i"],
453				["yesterday", "H:i"],
454				["", ""]
455			], date, nowDate, isUTC);
456
457			if (time.length > 0)
458				return BX.message("FD_DAY_AT_TIME").replace(/#DAY#/g, day).replace(/#TIME#/g, time);
459			else
460				return day;
461		},
462
463		Q : function() {
464			var daysAgo = intval((nowDate - date) / 60 / 60 / 24 / 1000);
465			if(daysAgo == 0)
466				return BX.message("FD_DAY_DIFF_1").replace(/#VALUE#/g, 1);
467			else
468				return BX.date.format([ ["d", "ddiff"], ["m", "mdiff"], ["", "Ydiff"] ], date, nowDate);
469		}
470	};
471
472	var cutZeroTime = false;
473	if (format[0] && format[0] == "^")
474	{
475		cutZeroTime = true;
476		format = format.substr(1);
477	}
478
479	var result = format.replace(formatRegex, _replaceDateFormat);
480
481	if (cutZeroTime)
482	{
483		/* 	15.04.12 13:00:00 => 15.04.12 13:00
484			00:01:00 => 00:01
485			4 may 00:00:00 => 4 may
486			01-01-12 00:00 => 01-01-12
487		*/
488
489		result = result.replace(/\s*00:00:00\s*/g, "").
490						replace(/(\d\d:\d\d)(:00)/g, "$1").
491						replace(/(\s*00:00\s*)(?!:)/g, "");
492	}
493
494	return result;
495
496	function _formatDateInterval(formats, date, nowDate, isUTC)
497	{
498		var secondsAgo = intval((nowDate - date) / 1000);
499		for (var i = 0; i < formats.length; i++)
500		{
501			var formatInterval = formats[i][0];
502			var formatValue = formats[i][1];
503			var match = null;
504			if (formatInterval == "s")
505			{
506				if (secondsAgo < 60)
507					return BX.date.format(formatValue, date, nowDate, isUTC);
508			}
509			else if ((match = /^s(\d+)/.exec(formatInterval)) != null)
510			{
511				if (secondsAgo < match[1])
512					return BX.date.format(formatValue, date, nowDate, isUTC);
513			}
514			else if (formatInterval == "i")
515			{
516				if (secondsAgo < 60 * 60)
517					return BX.date.format(formatValue, date, nowDate, isUTC);
518			}
519			else if ((match = /^i(\d+)/.exec(formatInterval)) != null)
520			{
521				if (secondsAgo < match[1]*60)
522					return BX.date.format(formatValue, date, nowDate, isUTC);
523			}
524			else if (formatInterval == "H")
525			{
526				if (secondsAgo < 24 * 60 * 60)
527					return BX.date.format(formatValue, date, nowDate, isUTC);
528			}
529			else if ((match = /^H(\d+)/.exec(formatInterval)) != null)
530			{
531				if (secondsAgo < match[1] * 60 * 60)
532					return BX.date.format(formatValue, date, nowDate, isUTC);
533			}
534			else if (formatInterval == "d")
535			{
536				if (secondsAgo < 31 *24 * 60 * 60)
537					return BX.date.format(formatValue, date, nowDate, isUTC);
538			}
539			else if ((match = /^d(\d+)/.exec(formatInterval)) != null)
540			{
541				if (secondsAgo < match[1] * 60 * 60)
542					return BX.date.format(formatValue, date, nowDate, isUTC);
543			}
544			else if (formatInterval == "m")
545			{
546				if (secondsAgo < 365 * 24 * 60 * 60)
547					return BX.date.format(formatValue, date, nowDate, isUTC);
548			}
549			else if ((match = /^m(\d+)/.exec(formatInterval)) != null)
550			{
551				if (secondsAgo < match[1] * 31 * 24 * 60 * 60)
552					return BX.date.format(formatValue, date, nowDate, isUTC);
553			}
554			else if (formatInterval == "today")
555			{
556				var year = getFullYear(nowDate), month = getMonth(nowDate), day = getDate(nowDate);
557				var todayStart = isUTC ? new Date(Date.UTC(year, month, day, 0, 0, 0, 0)) : new Date(year, month, day, 0, 0, 0, 0);
558				var todayEnd = isUTC ? new Date(Date.UTC(year, month, day+1, 0, 0, 0, 0)) : new Date(year, month, day+1, 0, 0, 0, 0);
559				if (date >= todayStart && date < todayEnd)
560					return BX.date.format(formatValue, date, nowDate, isUTC);
561			}
562			else if (formatInterval == "yesterday")
563			{
564				year = getFullYear(nowDate); month = getMonth(nowDate); day = getDate(nowDate);
565				var yesterdayStart = isUTC ? new Date(Date.UTC(year, month, day-1, 0, 0, 0, 0)) : new Date(year, month, day-1, 0, 0, 0, 0);
566				var yesterdayEnd = isUTC ? new Date(Date.UTC(year, month, day, 0, 0, 0, 0)) : new Date(year, month, day, 0, 0, 0, 0);
567				if (date >= yesterdayStart && date < yesterdayEnd)
568					return BX.date.format(formatValue, date, nowDate, isUTC);
569			}
570			else if (formatInterval == "tommorow")
571			{
572				year = getFullYear(nowDate); month = getMonth(nowDate); day = getDate(nowDate);
573				var tommorowStart = isUTC ? new Date(Date.UTC(year, month, day+1, 0, 0, 0, 0)) : new Date(year, month, day+1, 0, 0, 0, 0);
574				var tommorowEnd = isUTC ? new Date(Date.UTC(year, month, day+2, 0, 0, 0, 0)) : new Date(year, month, day+2, 0, 0, 0, 0);
575				if (date >= tommorowStart && date < tommorowEnd)
576					return BX.date.format(formatValue, date, nowDate, isUTC);
577			}
578			else if (formatInterval == "-")
579			{
580				if (secondsAgo < 0)
581					return BX.date.format(formatValue, date, nowDate, isUTC);
582			}
583		}
584
585		return formats.length > 0 ? BX.date.format(formats.pop()[1], date, nowDate, isUTC) : "";
586	}
587
588
589	function getFullYear(date) { return isUTC ? date.getUTCFullYear() : date.getFullYear(); }
590	function getDate(date) { return isUTC ? date.getUTCDate() : date.getDate(); }
591	function getMonth(date) { return isUTC ? date.getUTCMonth() : date.getMonth(); }
592	function getHours(date) { return isUTC ? date.getUTCHours() : date.getHours(); }
593	function getMinutes(date) { return isUTC ? date.getUTCMinutes() : date.getMinutes(); }
594	function getSeconds(date) { return isUTC ? date.getUTCSeconds() : date.getSeconds(); }
595	function getMilliseconds(date) { return isUTC ? date.getUTCMilliseconds() : date.getMilliseconds(); }
596	function getDay(date) { return isUTC ? date.getUTCDay() : date.getDay(); }
597	function setDate(date, dayValue) { return isUTC ? date.setUTCDate(dayValue) : date.setDate(dayValue); }
598	function setMonth(date, monthValue, dayValue) { return isUTC ? date.setUTCMonth(monthValue, dayValue) : date.setMonth(monthValue, dayValue); }
599
600	function _formatDateMessage(value, messages)
601	{
602		var val = value < 100 ? Math.abs(value) : Math.abs(value % 100);
603		var dec = val % 10;
604		var message = "";
605
606		if(val == 0)
607			message = BX.message(messages["0"]);
608		else if (val == 1)
609			message = BX.message(messages["1"]);
610		else if (val >= 10 && val <= 20)
611			message = BX.message(messages["10_20"]);
612		else if (dec == 1)
613			message = BX.message(messages["MOD_1"]);
614		else if (2 <= dec && dec <= 4)
615			message = BX.message(messages["MOD_2_4"]);
616		else
617			message = BX.message(messages["MOD_OTHER"]);
618
619		return message.replace(/#VALUE#/g, value);
620	}
621
622	function _replaceDateFormat(match, matchFull)
623	{
624		if (dateFormats[match])
625			return dateFormats[match]();
626		else
627			return matchFull;
628	}
629
630	function intval(number)
631	{
632		return number >= 0 ? Math.floor(number) : Math.ceil(number);
633	}
634};
635
636BX.date.convertBitrixFormat = function(format)
637{
638	if (!BX.type.isNotEmptyString(format))
639		return "";
640
641	return format.replace("YYYY", "Y")	// 1999
642				 .replace("MMMM", "F")	// January - December
643				 .replace("MM", "m")	// 01 - 12
644				 .replace("M", "M")	// Jan - Dec
645				 .replace("DD", "d")	// 01 - 31
646				 .replace("G", "g")	//  1 - 12
647				 .replace(/GG/i, "G")	//  0 - 23
648				 .replace("H", "h")	// 01 - 12
649				 .replace(/HH/i, "H")	// 00 - 24
650				 .replace("MI", "i")	// 00 - 59
651				 .replace("SS", "s")	// 00 - 59
652				 .replace("TT", "A")	// AM - PM
653				 .replace("T", "a");	// am - pm
654};
655
656BX.date.convertToUTC = function(date)
657{
658	if (!BX.type.isDate(date))
659		return null;
660	return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()));
661};
662
663/*
664 function creates and returns Javascript Date() object from server timestamp regardless of local browser (system) timezone.
665 For example can be used to convert timestamp from some exact date on server to the JS Date object with the same value.
666
667 params: {
668 timestamp: timestamp in seconds
669 }
670 */
671BX.date.getNewDate = function(timestamp)
672{
673	return new Date(BX.date.getBrowserTimestamp(timestamp));
674};
675
676/*
677 function transforms server timestamp (in sec) to javascript timestamp (calculated depend on local browser timezone offset). Returns timestamp in milliseconds.
678 Also see BX.date.getNewDate description.
679
680 params: {
681 timestamp: timestamp in seconds
682 }
683 */
684BX.date.getBrowserTimestamp = function(timestamp)
685{
686	timestamp = parseInt(timestamp, 10);
687	var browserOffset = new Date(timestamp * 1000).getTimezoneOffset() * 60;
688	return (parseInt(timestamp, 10) + parseInt(BX.message('SERVER_TZ_OFFSET')) + browserOffset) * 1000;
689};
690
691/*
692 function transforms local browser timestamp (in ms) to server timestamp (calculated depend on local browser timezone offset). Returns timestamp in seconds.
693
694 params: {
695 timestamp: timestamp in milliseconds
696 }
697 */
698BX.date.getServerTimestamp = function(timestamp)
699{
700	timestamp = parseInt(timestamp, 10);
701	var browserOffset = new Date(timestamp).getTimezoneOffset() * 60;
702	return Math.round(timestamp / 1000 - (parseInt(BX.message('SERVER_TZ_OFFSET'), 10) + parseInt(browserOffset, 10)));
703};
704
705/************************************** calendar class **********************************/
706
707var obCalendarSingleton = null;
708
709/*
710params: {
711	node: bind element || document.body
712
713	value - start value in site format (using 'field' param if 'value' does not exist)
714	callback - date check handler. can return false to prevent calendar closing.
715	callback_after - another handler, called after date picking
716
717	field - field to read/write data
718
719	bTime = true - whether to enable time control
720	bHideTime = false - whether to hide time control by default
721
722	currentTime - current UTC time()
723
724}
725*/
726
727
728BX.calendar = function(params)
729{
730	return BX.calendar.get().Show(params);
731};
732
733BX.calendar.get = function()
734{
735	if (!obCalendarSingleton)
736		obCalendarSingleton = new BX.JCCalendar();
737
738	return obCalendarSingleton;
739};
740
741// simple func for compatibility with the oldies
742BX.calendar.InsertDaysBack = function(input, days)
743{
744	if (days != '')
745	{
746		var d = new Date();
747		if(days > 0)
748		{
749			d.setTime(d.valueOf() - days*86400000);
750		}
751
752		input.value = BX.date.format(BX.date.convertBitrixFormat(BX.message('FORMAT_DATE')), d, null);
753	}
754	else
755	{
756		input.value = '';
757	}
758};
759
760BX.calendar.ValueToString = function(value, bTime, bUTC)
761{
762	return BX.date.format(
763		BX.date.convertBitrixFormat(BX.message(bTime ? 'FORMAT_DATETIME' : 'FORMAT_DATE')),
764		value,
765		null,
766		!!bUTC
767	);
768};
769
770
771BX.CalendarPeriod =
772{
773	Init: function(inputFrom, inputTo, selPeriod)
774	{
775		if((inputFrom.value != "" || inputTo.value != "") && selPeriod.value == "")
776			selPeriod.value = "interval";
777
778		selPeriod.onchange();
779	},
780
781	ChangeDirectOpts: function(peroidValue, selPParent) // "week" || "others"
782	{
783		var selDirect = BX.findChild(selPParent, {'className':'adm-select adm-calendar-direction'}, true);
784
785		if(peroidValue == "week")
786		{
787			selDirect.options[0].text = BX.message('JSADM_CALEND_PREV_WEEK');
788			selDirect.options[1].text = BX.message('JSADM_CALEND_CURR_WEEK');
789			selDirect.options[2].text = BX.message('JSADM_CALEND_NEXT_WEEK');
790		}
791		else
792		{
793			selDirect.options[0].text = BX.message('JSADM_CALEND_PREV');
794			selDirect.options[1].text = BX.message('JSADM_CALEND_CURR');
795			selDirect.options[2].text = BX.message('JSADM_CALEND_NEXT');
796		}
797	},
798
799	SaveAndClearInput: function(oInput)
800	{
801		if(!window.SavedPeriodValues)
802			window.SavedPeriodValues = {};
803
804		window.SavedPeriodValues[oInput.id] = oInput.value;
805		oInput.value="";
806	},
807
808	RestoreInput: function(oInput)
809	{
810		if(!window.SavedPeriodValues || !window.SavedPeriodValues[oInput.id])
811			return;
812
813		oInput.value = window.SavedPeriodValues[oInput.id];
814		delete(window.SavedPeriodValues[oInput.id]);
815	},
816
817	OnChangeP: function(sel)
818	{
819		var selPParent = sel.parentNode.parentNode;
820		var bShowFrom, bShowTo, bShowDirect, bShowSeparate;
821		bShowFrom = bShowTo = bShowDirect = bShowSeparate = false;
822
823		var inputFromWrap = BX.findChild(selPParent, {'className':'adm-input-wrap adm-calendar-inp adm-calendar-first'});
824		var inputToWrap = BX.findChild(selPParent, {'className':'adm-input-wrap adm-calendar-second'});
825		var selDirectWrap = BX.findChild(selPParent, {'className':'adm-select-wrap adm-calendar-direction'});
826		var separator = BX.findChild(selPParent, {'className':'adm-calendar-separate'});
827		var inputFrom = BX.findChild(selPParent, {'className':'adm-input adm-calendar-from'},true);
828		var inputTo = BX.findChild(selPParent, {'className':'adm-input adm-calendar-to'},true);
829
830		// define who must be shown
831		switch (sel.value)
832		{
833			case "day":
834			case "week":
835			case "month":
836			case "quarter":
837			case "year":
838				bShowDirect=true;
839				BX.CalendarPeriod.OnChangeD(selDirectWrap.children[0]);
840				break;
841
842			case "before":
843				bShowTo = true;
844				break;
845
846			case "after":
847				bShowFrom = true;
848				break;
849
850			case "exact":
851				bShowFrom= true;
852				break;
853
854			case "interval":
855				bShowFrom = bShowTo = bShowSeparate = true;
856				BX.CalendarPeriod.RestoreInput(inputFrom);
857				BX.CalendarPeriod.RestoreInput(inputTo);
858
859				break;
860
861			case "":
862				BX.CalendarPeriod.SaveAndClearInput(inputFrom);
863				BX.CalendarPeriod.SaveAndClearInput(inputTo);
864				break;
865
866			default:
867				break;
868
869		}
870
871		BX.CalendarPeriod.ChangeDirectOpts(sel.value, selPParent);
872
873		inputFromWrap.style.display = (bShowFrom? 'inline-block':'none');
874		inputToWrap.style.display = (bShowTo? 'inline-block':'none');
875		selDirectWrap.style.display = (bShowDirect? 'inline-block':'none');
876		separator.style.display = (bShowSeparate? 'inline-block':'none');
877	},
878
879
880	OnChangeD: function(sel)
881	{
882		var selPParent = sel.parentNode.parentNode;
883		var inputFrom = BX.findChild(selPParent, {'className':'adm-input adm-calendar-from'},true);
884		var inputTo = BX.findChild(selPParent, {'className':'adm-input adm-calendar-to'},true);
885		var selPeriod = BX.findChild(selPParent, {'className':'adm-select adm-calendar-period'},true);
886
887		var offset=0;
888
889		switch (sel.value)
890		{
891			case "previous":
892				offset = -1;
893				break;
894
895			case "next":
896				offset = 1;
897				break;
898
899			case "current":
900			default:
901				break;
902
903		}
904
905		var from = false;
906		var to = false;
907
908		var today = new Date();
909		var year = today.getFullYear();
910		var month = today.getMonth();
911		var day = today.getDate();
912		var dayW = today.getDay();
913
914		if (dayW == 0)
915				dayW = 7;
916
917		switch (selPeriod.value)
918		{
919			case "day":
920				from = new Date(year, month, day+offset, 0, 0, 0);
921				to = new Date(year, month, day+offset, 23, 59, 59);
922				break;
923
924			case "week":
925				from = new Date(year, month, day-dayW+1+offset*7, 0, 0, 0);
926				to = new Date(year, month, day+(7-dayW)+offset*7, 23, 59, 59);
927				break;
928
929			case "month":
930				from = new Date(year, month+offset, 1, 0, 0, 0);
931				to = new Date(year, month+1+offset, 0, 23, 59, 59);
932				break;
933
934			case "quarter":
935				var quarterNum = Math.floor((month/3))+offset;
936				from = new Date(year, 3*(quarterNum), 1, 0, 0, 0);
937				to = new Date(year, 3*(quarterNum+1), 0, 23, 59, 59);
938				break;
939
940			case "year":
941				from = new Date(year+offset, 0, 1, 0, 0, 0);
942				to = new Date(year+1+offset, 0, 0, 23, 59, 59);
943				break;
944
945			default:
946				break;
947		}
948
949		var format = window[inputFrom.name+"_bTime"] ? BX.message('FORMAT_DATETIME') : BX.message('FORMAT_DATE');
950
951		if(from)
952		{
953			inputFrom.value = BX.formatDate(from, format);
954			BX.addClass(inputFrom,"adm-calendar-inp-setted");
955		}
956
957		if(to)
958		{
959			inputTo.value = BX.formatDate(to, format);
960			BX.addClass(inputTo,"adm-calendar-inp-setted");
961		}
962	}
963};
964
965
966BX.JCCalendar = function()
967{
968	this.params = {};
969
970	this.bAmPm = BX.isAmPmMode();
971
972	this.popup = null;
973	this.popup_month = null;
974	this.popup_year = null;
975
976	this.value = null;
977
978	this.control_id = Math.random();
979
980	this._layers = {};
981	this._current_layer = null;
982
983	this.DIV = null;
984	this.PARTS = {};
985
986	this.weekStart = 0;
987	this.numRows = 6;
988
989	this._create = function(params)
990	{
991		this.popup = new BX.PopupWindow('calendar_popup_' + this.control_id, params.node, {
992			closeByEsc: true,
993			autoHide: false,
994			content: this._get_content(),
995			zIndex: 3000,
996			bindOptions: {forceBindPosition: true}
997		});
998
999		BX.bind(this.popup.popupContainer, 'click', this.popup.cancelBubble);
1000	};
1001
1002	this._auto_hide_disable = function()
1003	{
1004		BX.unbind(document, 'click', BX.proxy(this._auto_hide, this));
1005	};
1006
1007	this._auto_hide_enable = function()
1008	{
1009		BX.bind(document, 'click', BX.proxy(this._auto_hide, this));
1010	};
1011
1012	this._auto_hide = function(e)
1013	{
1014		this._auto_hide_disable();
1015		this.popup.close();
1016	};
1017
1018	this._get_content = function()
1019	{
1020		var _layer_onclick = BX.delegate(function(e) {
1021			e = e||window.event;
1022			this.SetDate(new Date(parseInt(BX.proxy_context.getAttribute('data-date'))), e.type=='dblclick')
1023		}, this);
1024
1025		this.DIV = BX.create('DIV', {
1026			props: {className: 'bx-calendar'},
1027			children: [
1028				BX.create('DIV', {
1029					props: {
1030						className: 'bx-calendar-header'
1031					},
1032					children: [
1033						BX.create('A', {
1034							attrs: {href: 'javascript:void(0)'},
1035							props: {className: 'bx-calendar-left-arrow'},
1036							events: {click: BX.proxy(this._prev, this)}
1037						}),
1038
1039						BX.create('SPAN', {
1040							props: {className: 'bx-calendar-header-content'},
1041							children: [
1042								(this.PARTS.MONTH = BX.create('A', {
1043									attrs: {href: 'javascript:void(0)'},
1044									props: {className: 'bx-calendar-top-month'},
1045									events: {click: BX.proxy(this._menu_month, this)}
1046								})),
1047
1048								(this.PARTS.YEAR = BX.create('A', {
1049									attrs: {href: 'javascript:void(0)'},
1050									props: {className: 'bx-calendar-top-year'},
1051									events: {click: BX.proxy(this._menu_year, this)}
1052								}))
1053							]
1054						}),
1055
1056						BX.create('A', {
1057							attrs: {href: 'javascript:void(0)'},
1058							props: {className: 'bx-calendar-right-arrow'},
1059							events: {click: BX.proxy(this._next, this)}
1060						})
1061					]
1062				}),
1063
1064				(this.PARTS.WEEK = BX.create('DIV', {
1065					props: {
1066						className: 'bx-calendar-name-day-wrap'
1067					}
1068				})),
1069
1070				(this.PARTS.LAYERS = BX.create('DIV', {
1071					props: {
1072						className: 'bx-calendar-cell-block'
1073					},
1074					events: {
1075						click: BX.delegateEvent({className: 'bx-calendar-cell'}, _layer_onclick),
1076						dblclick: BX.delegateEvent({className: 'bx-calendar-cell'}, _layer_onclick)
1077					}
1078				})),
1079
1080				(this.PARTS.TIME = BX.create('DIV', {
1081					props: {
1082						className: 'bx-calendar-set-time-wrap'
1083					},
1084					events: {
1085						click: BX.delegateEvent(
1086							{attr: 'data-action'},
1087							BX.delegate(this._time_actions, this)
1088						)
1089					},
1090					html: '<a href="javascript:void(0)" data-action="time_show" class="bx-calendar-set-time"><i></i>'+BX.message('CAL_TIME_SET')+'</a><div class="bx-calendar-form-block"><span class="bx-calendar-form-text">'+BX.message('CAL_TIME')+'</span><span class="bx-calendar-form"><input type="text" class="bx-calendar-form-input" maxwidth="2" onkeyup="BX.calendar.get()._check_time()" /><span class="bx-calendar-form-separator"></span><input type="text" class="bx-calendar-form-input" maxwidth="2" onkeyup="BX.calendar.get()._check_time()" />'+(this.bAmPm?'<span class="bx-calendar-AM-PM-block"><span class="bx-calendar-AM-PM-text" data-action="time_ampm"></span><span class="bx-calendar-form-arrow-r"><a href="javascript:void(0)" class="bx-calendar-form-arrow-top" data-action="time_ampm_up"><i></i></a><a href="javascript:void(0)" class="bx-calendar-form-arrow-bottom" data-action="time_ampm_down"><i></i></a></span></span>':'')+'</span><a href="javascript:void(0)" data-action="time_hide" class="bx-calendar-form-close"><i></i></a></div>'
1091				})),
1092
1093				BX.create('DIV', {
1094					props: {className: 'bx-calendar-button-block'},
1095					events: {
1096						click: BX.delegateEvent(
1097							{attr: 'data-action'},
1098							BX.delegate(this._button_actions, this)
1099						)
1100					},
1101					html: '<a href="javascript:void(0)" class="bx-calendar-button bx-calendar-button-select" data-action="submit"><span class="bx-calendar-button-left"></span><span class="bx-calendar-button-text">'+BX.message('CAL_BUTTON')+'</span><span class="bx-calendar-button-right"></span></a><a href="javascript:void(0)" class="bx-calendar-button bx-calendar-button-cancel" data-action="cancel"><span class="bx-calendar-button-left"></span><span class="bx-calendar-button-text">'+BX.message('JS_CORE_WINDOW_CLOSE')+'</span><span class="bx-calendar-button-right"></span></a>'
1102				})
1103			]
1104		});
1105
1106		this.PARTS.TIME_INPUT_H = BX.findChild(this.PARTS.TIME, {tag: 'INPUT'}, true);
1107		this.PARTS.TIME_INPUT_M = this.PARTS.TIME_INPUT_H.nextSibling.nextSibling;
1108
1109		if (this.bAmPm)
1110			this.PARTS.TIME_AMPM = this.PARTS.TIME_INPUT_M.nextSibling.firstChild;
1111
1112		var spinner = (new BX.JCSpinner({
1113			input: this.PARTS.TIME_INPUT_H,
1114			callback_change: BX.proxy(this._check_time, this),
1115			bSaveValue: false
1116		})).Show();
1117		spinner.className = 'bx-calendar-form-arrow-l';
1118		this.PARTS.TIME_INPUT_H.parentNode.insertBefore(spinner, this.PARTS.TIME_INPUT_H);
1119
1120		spinner = (new BX.JCSpinner({
1121			input: this.PARTS.TIME_INPUT_M,
1122			callback_change: BX.proxy(this._check_time, this),
1123			bSaveValue: true
1124		})).Show();
1125		spinner.className = 'bx-calendar-form-arrow-r';
1126		if (!this.PARTS.TIME_INPUT_M.nextSibling)
1127			this.PARTS.TIME_INPUT_M.parentNode.appendChild(spinner);
1128		else
1129			this.PARTS.TIME_INPUT_M.parentNode.insertBefore(spinner, this.PARTS.TIME_INPUT_M.nextSibling);
1130
1131		for (var i = 0; i < 7; i++)
1132		{
1133			this.PARTS.WEEK.appendChild(BX.create('SPAN', {
1134				props: {
1135					className: 'bx-calendar-name-day'
1136				},
1137				text: BX.message('DOW_' + ((i + this.weekStart) % 7))
1138			}));
1139		}
1140
1141		return this.DIV;
1142	};
1143
1144	this._time_actions = function()
1145	{
1146		switch (BX.proxy_context.getAttribute('data-action'))
1147		{
1148			case 'time_show':
1149				BX.addClass(this.PARTS.TIME, 'bx-calendar-set-time-opened');
1150				this.popup.adjustPosition();
1151			break;
1152			case 'time_hide':
1153				BX.removeClass(this.PARTS.TIME, 'bx-calendar-set-time-opened');
1154				this.popup.adjustPosition();
1155			break;
1156			case 'time_ampm':
1157				this.PARTS.TIME_AMPM.innerHTML = this.PARTS.TIME_AMPM.innerHTML == 'AM' ? 'PM' : 'AM';
1158			break;
1159			case 'time_ampm_up':
1160				this._check_time({bSaveValue: false}, null, 12);
1161				return;
1162			break;
1163			case 'time_ampm_down':
1164				this._check_time({bSaveValue: false}, null, -12);
1165				return;
1166			break;
1167		}
1168
1169		this._check_time();
1170	};
1171
1172	this._button_actions = function()
1173	{
1174		switch (BX.proxy_context.getAttribute('data-action'))
1175		{
1176			case 'submit':
1177				this.SaveValue();
1178			break;
1179			case 'cancel':
1180				this.Close();
1181			break;
1182		}
1183	};
1184
1185	this._check_time = function(params, value, direction)
1186	{
1187		var h = parseInt(this.PARTS.TIME_INPUT_H.value.substring(0,5),10)||0,
1188			m = parseInt(this.PARTS.TIME_INPUT_M.value.substring(0,5),10)||0,
1189			bChanged = false;
1190
1191		if (!!params && !params.bSaveValue)
1192		{
1193			this.value.setUTCHours(this.value.getUTCHours() + direction);
1194		}
1195		else if (!isNaN(h))
1196		{
1197			if (this.bAmPm)
1198			{
1199				if (h != 12 && this.PARTS.TIME_AMPM.innerHTML == 'PM')
1200				{
1201					h += 12;
1202				}
1203			}
1204
1205			bChanged = true;
1206			this.value.setUTCHours(h % 24);
1207		}
1208
1209		if (!isNaN(m))
1210		{
1211			bChanged = true;
1212			this.value.setUTCMinutes(m % 60);
1213		}
1214
1215		if (bChanged)
1216		{
1217			this.SetValue(this.value);
1218		}
1219	};
1220
1221	this._set_layer = function()
1222	{
1223		var layerId = parseInt(this.value.getUTCFullYear() + '' + BX.util.str_pad_left(this.value.getUTCMonth()+'', 2, "0"));
1224
1225		if (!this._layers[layerId])
1226		{
1227			this._layers[layerId] = this._create_layer();
1228			this._layers[layerId].BXLAYERID = layerId;
1229		}
1230
1231		if (this._current_layer)
1232		{
1233			var v = new Date(this.value.valueOf());
1234			v.setUTCHours(0); v.setUTCMinutes(0);
1235
1236			var cur_value = BX.findChild(this._layers[layerId], {
1237					tag: 'A',
1238					className: 'bx-calendar-active'
1239				}, true),
1240				new_value = BX.findChild(this._layers[layerId], {
1241					tag: 'A',
1242					attr: {
1243						'data-date' : v.valueOf() + ''
1244					}
1245				}, true);
1246
1247			if (cur_value)
1248			{
1249				BX.removeClass(cur_value, 'bx-calendar-active');
1250			}
1251
1252			if (new_value)
1253			{
1254				BX.addClass(new_value, 'bx-calendar-active');
1255			}
1256
1257			this._replace_layer(this._current_layer, this._layers[layerId]);
1258		}
1259		else
1260		{
1261			this.PARTS.LAYERS.appendChild(this._layers[layerId]);
1262		}
1263
1264		this._current_layer = this._layers[layerId];
1265	};
1266
1267	this._replace_layer = function(old_layer, new_layer)
1268	{
1269		if (old_layer != new_layer)
1270		{
1271			if (!BX.browser.IsIE() || BX.browser.IsDoctype())
1272			{
1273				var dir = old_layer.BXLAYERID > new_layer.BXLAYERID ? 1 : -1;
1274
1275				var old_top = 0;
1276				var new_top = -dir * old_layer.offsetHeight;
1277
1278				old_layer.style.position = 'relative';
1279				old_layer.style.top = "0px";
1280				old_layer.style.zIndex = 5;
1281
1282				new_layer.style.position = 'absolute';
1283				new_layer.style.top = new_top + 'px';
1284				new_layer.style.zIndex = 6;
1285
1286				this.PARTS.LAYERS.appendChild(new_layer);
1287
1288				var delta = 15;
1289
1290				var f;
1291				(f = function() {
1292					new_top += dir * delta;
1293					old_top += dir * delta;
1294
1295					if (dir * new_top < 0)
1296					{
1297						old_layer.style.top = old_top + 'px';
1298						new_layer.style.top = new_top + 'px';
1299						setTimeout(f, 10);
1300					}
1301					else
1302					{
1303						old_layer.parentNode.removeChild(old_layer);
1304
1305						new_layer.style.top = "0px";
1306						new_layer.style.position = 'static';
1307						new_layer.style.zIndex = 0;
1308					}
1309				})();
1310			}
1311			else
1312			{
1313				this.PARTS.LAYERS.replaceChild(new_layer, old_layer);
1314			}
1315		}
1316	};
1317
1318	this._create_layer = function()
1319	{
1320		var l = BX.create('DIV', {
1321			props: {
1322				className: 'bx-calendar-layer'
1323			}
1324		});
1325
1326		var month_start = new Date(this.value);
1327		month_start.setUTCHours(0);
1328		month_start.setUTCMinutes(0);
1329
1330		month_start.setUTCDate(1);
1331
1332		if (month_start.getUTCDay() != this.weekStart)
1333		{
1334			var d = month_start.getUTCDay() - this.weekStart;
1335			d += d < 0 ? 7 : 0;
1336			month_start.setUTCDate(month_start.getUTCDate()-d);
1337		}
1338
1339		var cur_month = this.value.getUTCMonth(),
1340			cur_day = this.value.getUTCDate(),
1341			s = '';
1342		for (var i = 0; i < this.numRows; i++)
1343		{
1344			s += '<div class="bx-calendar-range'
1345				+(i == this.numRows-1 ? ' bx-calendar-range-noline' : '')
1346				+'">';
1347
1348			for (var j = 0; j < 7; j++)
1349			{
1350				d = month_start.getUTCDate();
1351				var wd = month_start.getUTCDay();
1352				var className = 'bx-calendar-cell';
1353
1354				if (cur_month != month_start.getUTCMonth())
1355					className += ' bx-calendar-date-hidden';
1356				else if (cur_day == d)
1357					className += ' bx-calendar-active';
1358
1359
1360				if (wd == 0 || wd == 6)
1361					className += ' bx-calendar-weekend';
1362
1363				s += '<a href="javascript:void(0)" class="'+className+'" data-date="' + month_start.valueOf() + '">' + d + '</a>';
1364
1365				month_start.setUTCDate(month_start.getUTCDate()+1);
1366			}
1367			s += '</div>';
1368		}
1369
1370		l.innerHTML = s;
1371
1372		return l;
1373	};
1374
1375	this._prev = function()
1376	{
1377		this.SetMonth(this.value.getUTCMonth()-1);
1378	};
1379
1380	this._next = function()
1381	{
1382		this.SetMonth(this.value.getUTCMonth()+1);
1383	};
1384
1385	this._menu_month_content = function()
1386	{
1387		var months = '', cur_month = this.value.getMonth(), i;
1388		for (i=0; i<12; i++)
1389		{
1390			months += '<a href="javascript:void(0)" class="bx-calendar-month'+(i == cur_month ? ' bx-calendar-month-active' : '')+'" onclick="BX.calendar.get().SetMonth('+i+')">'+BX.message('MONTH_' + (i+1))+'</a>';
1391		}
1392
1393		return '<div class="bx-calendar-month-popup"><div class="bx-calendar-month-title" onclick="BX.calendar.get().popup_month.close();">'+BX.message('MONTH_' + (this.value.getUTCMonth()+1))+'</div><div class="bx-calendar-month-content">'+months+'</div></div>';
1394	};
1395
1396	this._menu_month = function()
1397	{
1398		if (!this.popup_month)
1399		{
1400			this.popup_month = BX.PopupWindowManager.create(
1401				'calendar_popup_month_' + this.control_id, this.PARTS.MONTH,
1402				{
1403					content: this._menu_month_content(),
1404					zIndex: 3001,
1405					closeByEsc: true,
1406					autoHide: true,
1407					offsetTop: -29,
1408					offsetLeft: -1
1409				}
1410			);
1411
1412			this.popup_month.BXMONTH = this.value.getUTCMonth();
1413		}
1414		else if (this.popup_month.BXMONTH != this.value.getUTCMonth())
1415		{
1416			this.popup_month.setContent(this._menu_month_content());
1417			this.popup_month.BXMONTH = this.value.getUTCMonth();
1418		}
1419
1420		this.popup_month.show();
1421	};
1422
1423	this._menu_year_content = function()
1424	{
1425		var s = '<div class="bx-calendar-year-popup"><div class="bx-calendar-year-title" onclick="BX.calendar.get().popup_year.close();">'+this.value.getUTCFullYear()+'</div><div class="bx-calendar-year-content" id="bx-calendar-year-content">';
1426
1427		for (var i=-3; i <= 3; i++)
1428		{
1429			s += '<a href="javascript:void(0)" class="bx-calendar-year-number'+(i==0?' bx-calendar-year-active':'')+'" onclick="BX.calendar.get().SetYear('+(this.value.getUTCFullYear()-i)+')">'+(this.value.getUTCFullYear()-i)+'</a>';
1430		}
1431
1432		s += '</div><input type="text" class="bx-calendar-year-input" onkeyup="if(this.value>=1900&&this.value<=2100)BX.calendar.get().SetYear(this.value);" maxlength="4" /></div>';
1433
1434		return s;
1435	};
1436
1437	this._menu_year = function()
1438	{
1439		if (!this.popup_year)
1440		{
1441			this.popup_year = BX.PopupWindowManager.create(
1442				'calendar_popup_year_' + this.control_id, this.PARTS.YEAR,
1443				{
1444					content: this._menu_year_content(),
1445					zIndex: 3001,
1446					closeByEsc: true,
1447					autoHide: true,
1448					offsetTop: -29,
1449					offsetLeft: -1
1450				}
1451			);
1452
1453			this.popup_year.BXYEAR = this.value.getUTCFullYear();
1454		}
1455		else if (this.popup_year.BXYEAR != this.value.getUTCFullYear())
1456		{
1457			this.popup_year.setContent(this._menu_year_content());
1458			this.popup_year.BXYEAR = this.value.getUTCFullYear();
1459		}
1460
1461		this.popup_year.show();
1462	};
1463
1464	this._check_date = function(v)
1465	{
1466		var res = v;
1467
1468		if (BX.type.isString(v))
1469		{
1470			res = BX.parseDate(v, true);
1471		}
1472
1473		if (!BX.type.isDate(res) || isNaN(res.valueOf()))
1474		{
1475			res = BX.date.convertToUTC(new Date());
1476			if (this.params.bHideTime)
1477			{
1478				res.setUTCHours(0);
1479				res.setUTCMinutes(0);
1480			}
1481		}
1482
1483		res.setUTCMilliseconds(0);
1484		res.setUTCSeconds(0);
1485
1486		res.BXCHECKED = true;
1487
1488		return res;
1489	};
1490};
1491
1492BX.JCCalendar.prototype.Show = function(params)
1493{
1494	if (!BX.isReady)
1495	{
1496		BX.ready(BX.delegate(function() {this.Show(params)}, this));
1497		return;
1498	}
1499
1500	params.node = params.node||document.body;
1501
1502	if (BX.type.isNotEmptyString(params.node))
1503	{
1504		var n = BX(params.node);
1505		if (!n)
1506		{
1507			n = document.getElementsByName(params.node);
1508			if (n && n.length > 0)
1509			{
1510				n = n[0]
1511			}
1512		}
1513		params.node = n;
1514	}
1515
1516	if (!params.node)
1517		return;
1518
1519	if (!!params.field)
1520	{
1521		if (BX.type.isString(params.field))
1522		{
1523			n = BX(params.field);
1524			if (!!n)
1525			{
1526				params.field = n;
1527			}
1528			else
1529			{
1530				if (params.form)
1531				{
1532					if (BX.type.isString(params.form))
1533					{
1534						params.form = document.forms[params.form];
1535					}
1536				}
1537
1538				if (BX.type.isDomNode(params.form) && !!params.form[params.field])
1539				{
1540					params.field = params.form[params.field];
1541				}
1542				else
1543				{
1544					n = document.getElementsByName(params.field);
1545					if (n && n.length > 0)
1546					{
1547						n = n[0];
1548						params.field = n;
1549					}
1550				}
1551			}
1552
1553			if (BX.type.isString(params.field))
1554			{
1555				params.field = BX(params.field);
1556			}
1557		}
1558	}
1559
1560	var bShow = !this.popup || !this.popup.isShown() || this.params.node != params.node;
1561
1562	this.params = params;
1563
1564	this.params.bTime = typeof this.params.bTime == 'undefined' ? true : !!this.params.bTime;
1565	this.params.bHideTime = typeof this.params.bHideTime == 'undefined' ? true : !!this.params.bHideTime;
1566
1567	this.weekStart = parseInt(this.params.weekStart || this.params.weekStart || BX.message('WEEK_START'));
1568	if (isNaN(this.weekStart))
1569		this.weekStart = 1;
1570
1571	if (!this.popup)
1572	{
1573		this._create(this.params);
1574	}
1575	else
1576	{
1577		this.popup.setBindElement(this.params.node);
1578	}
1579
1580	var bHideTime = !!this.params.bHideTime;
1581	if (this.params.value)
1582	{
1583		this.SetValue(this.params.value);
1584		bHideTime = this.value.getUTCHours() <= 0 && this.value.getUTCMinutes() <= 0;
1585	}
1586	else if (this.params.field)
1587	{
1588		this.SetValue(this.params.field.value);
1589		bHideTime = this.value.getUTCHours() <= 0 && this.value.getUTCMinutes() <= 0;
1590	}
1591	else if (!!this.params.currentTime)
1592	{
1593		this.SetValue(this.params.currentTime);
1594	}
1595	else
1596	{
1597		this.SetValue();
1598	}
1599
1600	if (!!this.params.bTime)
1601		BX.removeClass(this.DIV, 'bx-calendar-time-disabled');
1602	else
1603		BX.addClass(this.DIV, 'bx-calendar-time-disabled');
1604
1605	if (!!bHideTime)
1606		BX.removeClass(this.PARTS.TIME, 'bx-calendar-set-time-opened');
1607	else
1608		BX.addClass(this.PARTS.TIME, 'bx-calendar-set-time-opened');
1609
1610	if (bShow)
1611	{
1612		this._auto_hide_disable();
1613		this.popup.show();
1614		setTimeout(BX.proxy(this._auto_hide_enable, this), 0);
1615	}
1616
1617	this.params.bSetFocus = typeof this.params.bSetFocus == 'undefined' ? true : !!this.params.bSetFocus;
1618	if(this.params.bSetFocus)
1619		params.node.blur();
1620
1621	return this;
1622};
1623
1624BX.JCCalendar.prototype.SetDay = function(d)
1625{
1626	this.value.setUTCDate(d);
1627	return this.SetValue(this.value);
1628};
1629
1630BX.JCCalendar.prototype.SetMonth = function(m)
1631{
1632	if (this.popup_month)
1633		this.popup_month.close();
1634
1635	this.value.setUTCMonth(m);
1636
1637	if(m < 0)
1638		m += 12;
1639	else if (m >= 12)
1640		m -= 12;
1641
1642	while(this.value.getUTCMonth() > m)
1643	{
1644		this.value.setUTCDate(this.value.getUTCDate()-1);
1645	}
1646
1647	return this.SetValue(this.value);
1648};
1649
1650BX.JCCalendar.prototype.SetYear = function(y)
1651{
1652	if (this.popup_year)
1653		this.popup_year.close();
1654	this.value.setUTCFullYear(y);
1655	return this.SetValue(this.value);
1656};
1657
1658BX.JCCalendar.prototype.SetDate = function(v, bSet)
1659{
1660	v = this._check_date(v);
1661	v.setUTCHours(this.value.getUTCHours());
1662	v.setUTCMinutes(this.value.getUTCMinutes());
1663	v.setUTCSeconds(this.value.getUTCSeconds());
1664
1665	if (this.params.bTime && !bSet)
1666	{
1667		return this.SetValue(v);
1668	}
1669	else
1670	{
1671		this.SetValue(v);
1672		this.SaveValue();
1673	}
1674};
1675
1676BX.JCCalendar.prototype.SetValue = function(v)
1677{
1678	this.value = (v && v.BXCHECKED) ? v : this._check_date(v);
1679
1680	this.PARTS.MONTH.innerHTML = BX.message('MONTH_' + (this.value.getUTCMonth()+1));
1681	this.PARTS.YEAR.innerHTML = this.value.getUTCFullYear();
1682
1683	if (!!this.params.bTime)
1684	{
1685		var h = this.value.getUTCHours();
1686		if (this.bAmPm)
1687		{
1688			if (h >= 12)
1689			{
1690				this.PARTS.TIME_AMPM.innerHTML = 'PM';
1691
1692				if (h != 12)
1693					h -= 12;
1694			}
1695			else
1696			{
1697				this.PARTS.TIME_AMPM.innerHTML = 'AM';
1698
1699				if (h == 0)
1700					h = 12;
1701			}
1702		}
1703
1704		this.PARTS.TIME_INPUT_H.value = BX.util.str_pad_left(h.toString(), 2, "0");
1705		this.PARTS.TIME_INPUT_M.value = BX.util.str_pad_left(this.value.getUTCMinutes().toString(), 2, "0");
1706	}
1707
1708	this._set_layer();
1709
1710	return this;
1711};
1712
1713BX.JCCalendar.prototype.SaveValue = function()
1714{
1715	if (this.popup_month)
1716		this.popup_month.close();
1717	if (this.popup_year)
1718		this.popup_year.close();
1719
1720	var bSetValue = true;
1721	if (!!this.params.callback)
1722	{
1723		var res = this.params.callback.apply(this, [new Date(this.value.valueOf()+this.value.getTimezoneOffset()*60000)]);
1724		if (res === false)
1725			bSetValue = false;
1726	}
1727
1728	if (bSetValue)
1729	{
1730		var bTime = !!this.params.bTime && BX.hasClass(this.PARTS.TIME, 'bx-calendar-set-time-opened');
1731
1732		if (this.params.field)
1733		{
1734			this.params.field.value = BX.calendar.ValueToString(this.value, bTime, true);
1735			BX.fireEvent(this.params.field, 'change');
1736		}
1737
1738		this.popup.close();
1739
1740		if (!!this.params.callback_after)
1741		{
1742			this.params.callback_after.apply(this, [new Date(this.value.valueOf()+this.value.getTimezoneOffset()*60000), bTime]);
1743		}
1744	}
1745
1746	return this;
1747};
1748
1749BX.JCCalendar.prototype.Close = function()
1750{
1751	if (!!this.popup)
1752		this.popup.close();
1753
1754	return this;
1755};
1756
1757BX.JCSpinner = function(params)
1758{
1759	params = params || {};
1760	this.params = {
1761		input: params.input || null,
1762
1763		delta: params.delta || 1,
1764
1765		timeout_start: params.timeout_start || 1000,
1766		timeout_cont: params.timeout_cont || 150,
1767
1768		callback_start: params.callback_start || null,
1769		callback_change: params.callback_change || null,
1770		callback_finish: params.callback_finish || null,
1771
1772		bSaveValue: typeof params.bSaveValue == 'undefined' ? !!params.input : !!params.bSaveValue
1773	};
1774
1775	this.mousedown = false;
1776	this.direction = 1;
1777};
1778
1779BX.JCSpinner.prototype.Show = function()
1780{
1781	this.node = BX.create('span', {
1782		events: {
1783			mousedown: BX.delegateEvent(
1784				{attr: 'data-dir'},
1785				BX.delegate(this.Start, this)
1786			)
1787		},
1788		html: '<a href="javascript:void(0)" class="bx-calendar-form-arrow bx-calendar-form-arrow-top" data-dir="1"><i></i></a><a href="javascript:void(0)" class="bx-calendar-form-arrow bx-calendar-form-arrow-bottom" data-dir="-1"><i></i></a>'
1789	});
1790	return this.node;
1791};
1792
1793BX.JCSpinner.prototype.Start = function()
1794{
1795	this.mousedown = true;
1796	this.direction = BX.proxy_context.getAttribute('data-dir') > 0 ? 1 : -1;
1797	BX.bind(document, "mouseup", BX.proxy(this.MouseUp, this));
1798	this.ChangeValue(true);
1799};
1800
1801BX.JCSpinner.prototype.ChangeValue = function(bFirst)
1802{
1803	if(!this.mousedown)
1804		return;
1805
1806	if (this.params.input)
1807	{
1808		var v = parseInt(this.params.input.value, 10) + this.params.delta * this.direction;
1809
1810		if (this.params.bSaveValue)
1811			this.params.input.value = v;
1812
1813		if (!!bFirst && this.params.callback_start)
1814			this.params.callback_start(this.params, v, this.direction);
1815
1816		if (this.params.callback_change)
1817			this.params.callback_change(this.params, v, this.direction);
1818
1819		setTimeout(
1820			BX.proxy(this.ChangeValue, this),
1821			!!bFirst ? this.params.timeout_start : this.params.timeout_cont
1822		);
1823	}
1824};
1825
1826BX.JCSpinner.prototype.MouseUp = function()
1827{
1828	this.mousedown = false;
1829	BX.unbind(document, "mouseup", BX.proxy(this.MouseUp, this));
1830
1831	if (this.params.callback_finish)
1832		this.params.callback_finish(this.params, this.params.input.value);
1833};
1834
1835/**************** compatibility hacks ***************************/
1836
1837window.jsCalendar = {
1838	Show: function(obj, field, fieldFrom, fieldTo, bTime, serverTime, form_name, bHideTimebar)
1839	{
1840		return BX.calendar({
1841			node: obj, field: field, form: form_name, bTime: !!bTime, currentTime: serverTime, bHideTimebar: !!bHideTimebar
1842		});
1843	},
1844
1845	ValueToString: BX.calendar.ValueToString
1846};
1847
1848
1849/************ clock popup transferred from timeman **************/
1850
1851BX.CClockSelector = function(params)
1852{
1853	this.params = params;
1854
1855	this.params.popup_buttons = this.params.popup_buttons || [
1856		new BX.PopupWindowButton({
1857			text : BX.message('CAL_BUTTON'),
1858			className : "popup-window-button-create",
1859			events : {click : BX.proxy(this.setValue, this)}
1860		})
1861	];
1862
1863	this.isReady = false;
1864
1865	this.WND = new BX.PopupWindow(
1866		this.params.popup_id || 'clock_selector_popup',
1867		this.params.node,
1868		this.params.popup_config || {
1869			titleBar: {content: BX.create('SPAN', {text: BX.message('CAL_TIME')})},
1870			offsetLeft: -45,
1871			offsetTop: -135,
1872			autoHide: true,
1873			closeIcon: true,
1874			closeByEsc: true,
1875			zIndex: this.params.zIndex
1876		}
1877	);
1878
1879	this.SHOW = false;
1880	BX.addCustomEvent(this.WND, "onPopupClose", BX.delegate(this.onPopupClose, this));
1881
1882	this.obClocks = {};
1883	this.CLOCK_ID = this.params.clock_id || 'clock_selector';
1884};
1885
1886BX.CClockSelector.prototype.Show = function()
1887{
1888	if (!this.isReady)
1889	{
1890		//BX.timeman.showWait(this.parent.DIV);
1891
1892		BX.addCustomEvent('onClockRegister', BX.proxy(this.onClockRegister, this));
1893		return BX.ajax.get('/bitrix/tools/clock_selector.php', {start_time: this.params.start_time, clock_id: this.CLOCK_ID, sessid: BX.bitrix_sessid()}, BX.delegate(this.Ready, this));
1894	}
1895
1896	this.WND.setButtons(this.params.popup_buttons);
1897	this.WND.show();
1898
1899	this.SHOW = true;
1900
1901	if (window['bxClock_' + this.obClocks[this.CLOCK_ID]])
1902	{
1903		setTimeout("window['bxClock_" + this.obClocks[this.CLOCK_ID] + "'].CalculateCoordinates()", 40);
1904	}
1905
1906	return true;
1907};
1908
1909BX.CClockSelector.prototype.onClockRegister = function(obClocks)
1910{
1911	if (obClocks[this.CLOCK_ID])
1912	{
1913		this.obClocks[this.CLOCK_ID] = obClocks[this.CLOCK_ID];
1914		BX.removeCustomEvent('onClockRegister', BX.proxy(this.onClockRegister, this));
1915	}
1916};
1917
1918BX.CClockSelector.prototype.Ready = function(data)
1919{
1920	this.content = this.CreateContent(data);
1921	this.WND.setContent(this.content);
1922
1923	this.isReady = true;
1924	//BX.timeman.closeWait();
1925
1926	setTimeout(BX.proxy(this.Show, this), 30);
1927};
1928
1929BX.CClockSelector.prototype.CreateContent = function(data)
1930{
1931	return BX.create('DIV', {
1932		events: {click: BX.PreventDefault},
1933		html:
1934			'<div class="bx-tm-popup-clock">' + data + '</div>'
1935	});
1936};
1937
1938BX.CClockSelector.prototype.setValue = function(e)
1939{
1940	if (this.params.callback)
1941	{
1942		var input = BX.findChild(this.content, {tagName: 'INPUT'}, true);
1943		this.params.callback.apply(this.params.node, [input.value]);
1944	}
1945
1946	return BX.PreventDefault(e);
1947};
1948
1949BX.CClockSelector.prototype.closeWnd = function(e)
1950{
1951	this.WND.close();
1952	return (e || window.event) ? BX.PreventDefault(e) : true;
1953};
1954
1955BX.CClockSelector.prototype.setNode = function(node)
1956{
1957	this.WND.setBindElement(node);
1958};
1959
1960BX.CClockSelector.prototype.setTime = function(timestamp)
1961{
1962	this.params.start_time = timestamp;
1963	if (window['bxClock_' + this.obClocks[this.CLOCK_ID]])
1964	{
1965		window['bxClock_' +  this.obClocks[this.CLOCK_ID]].SetTime(parseInt(timestamp/3600), parseInt((timestamp%3600)/60));
1966	}
1967};
1968
1969BX.CClockSelector.prototype.setCallback = function(cb)
1970{
1971	this.params.callback = cb;
1972};
1973
1974BX.CClockSelector.prototype.onPopupClose = function()
1975{
1976	this.SHOW = false;
1977};
1978
1979})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.