PageSourceSearch

https://incubadoradesondeos.es/components/com_chronoforms/js/datepicker/datepicker.js

js incubadoradesondeos.es collected 2026-09-26 02:33:47 UTC 24,875 bytes, 736 lines download raw bytes

1/**
2 * datepicker.js - MooTools Datepicker class
3 * @version 1.17
4 * 
5 * by MonkeyPhysics.com
6 *
7 * Source/Documentation available at:
8 * http://www.monkeyphysics.com/mootools/script/2/datepicker
9 * 
10 * --
11 * 
12 * Smoothly animating, very configurable and easy to install.
13 * No Ajax, pure Javascript. 4 skins available out of the box.
14 * 
15 * --
16 *
17 * Some Rights Reserved
18 * http://creativecommons.org/licenses/by-sa/3.0/
19 * 
20 */
21
22var DatePicker = new Class({
23	
24	Implements: Options,
25	
26	// working date, which we will keep modifying to render the calendars
27	d: '',
28	
29	// just so that we need not request it over and over
30	today: '',
31	
32	// current user-choice in date object format
33	choice: {}, 
34	
35	// size of body, used to animate the sliding
36	bodysize: {}, 
37	
38	// to check availability of next/previous buttons
39	limit: {}, 
40	
41	// element references:
42	attachTo: null,    // selector for target inputs
43	picker: null,      // main datepicker container
44	slider: null,      // slider that contains both oldContents and newContents, used to animate between 2 different views
45	oldContents: null, // used in animating from-view to new-view
46	newContents: null, // used in animating from-view to new-view
47	input: null,       // original input element (used for input/output)
48	visual: null,      // visible input (used for rendering)
49	
50	options: { 
51		pickerClass: 'datepicker',
52		days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
53		months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
54		dayShort: 2,
55		monthShort: 3,
56		startDay: 1, // Sunday (0) through Saturday (6) - be aware that this may affect your layout, since the days on the right might have a different margin
57		timePicker: false,
58		timePickerOnly: false,
59		yearPicker: true,
60		yearsPerPage: 20,
61		format: 'd-m-Y',
62		allowEmpty: false,
63		inputOutputFormat: 'U', // default to unix timestamp
64		animationDuration: 400,
65		useFadeInOut: !Browser.Engine.trident, // dont animate fade-in/fade-out for IE
66		startView: 'month', // allowed values: {time, month, year, decades}
67		positionOffset: { x: 0, y: 0 },
68		minDate: null, // { date: '[date-string]', format: '[date-string-interpretation-format]' }
69		maxDate: null, // same as minDate
70		debug: false,
71		toggleElements: null,
72		
73		// and some event hooks:
74		onShow: $empty,   // triggered when the datepicker pops up
75		onClose: $empty,  // triggered after the datepicker is closed (destroyed)
76		onSelect: $empty  // triggered when a date is selected
77	},
78	
79	initialize: function(attachTo, options) {
80		this.attachTo = attachTo;
81		this.setOptions(options).attach();
82		if (this.options.timePickerOnly) {
83			this.options.timePicker = true;
84			this.options.startView = 'time';
85		}
86		this.formatMinMaxDates();
87		document.addEvent('mousedown', this.close.bind(this));
88	},
89	
90	formatMinMaxDates: function() {
91		if (this.options.minDate && this.options.minDate.format) {
92			this.options.minDate = this.unformat(this.options.minDate.date, this.options.minDate.format);
93		}
94		if (this.options.maxDate && this.options.maxDate.format) {
95			this.options.maxDate = this.unformat(this.options.maxDate.date, this.options.maxDate.format);
96			this.options.maxDate.setHours(23);
97			this.options.maxDate.setMinutes(59);
98			this.options.maxDate.setSeconds(59);
99		}
100	},
101	
102	attach: function() {
103		// toggle the datepicker through a separate element?
104		if ($chk(this.options.toggleElements)) {
105			var togglers = $$(this.options.toggleElements);
106			document.addEvents({
107				'keydown': function(e) {
108					if (e.key == "tab") {
109						this.close(null, true);
110					}
111				}.bind(this)
112			});
113		};
114		
115		// attach functionality to the inputs		
116		$$(this.attachTo).each(function(item, index) {
117			
118			// never double attach
119			if (item.retrieve('datepicker')) return;
120			
121			// determine starting value(s)
122			if ($chk(item.get('value'))) {
123				var init_clone_val = this.format(new Date(this.unformat(item.get('value'), this.options.inputOutputFormat)), this.options.format);
124			} else if (!this.options.allowEmpty) {
125				var init_clone_val = this.format(new Date(), this.options.format);
126				//max hack
127				var init_orig_val = this.format(new Date(), this.options.inputOutputFormat);
128				item.set('value', init_orig_val);
129				//end max hack
130			} else {
131				var init_clone_val = '';
132			}
133			
134			// create clone
135			var display = item.getStyle('display');
136			//max hack for date validation
137			if($chk(item.get('id'))){
138				var new_id = item.get('id')+'_clone_id';
139			}else{
140				var new_id = item.get('name')+'_clone_id';
141			}
142			//max hack for date validation
143			var display = item.getStyle('display');
144			var clone = item
145			.setStyle('display', this.options.debug ? display : 'none')
146			.store('datepicker', true) // to prevent double attachment...
147			.clone()
148			.set('id', new_id)//max hack for date validation
149			.store('datepicker', true) // ...even for the clone (!)
150			.removeProperty('name')    // secure clean (form)submission
151			.setStyle('display', display)
152			.set('value', init_clone_val)
153			.inject(item, 'after');
154			
155			// events
156			if ($chk(this.options.toggleElements)) {
157				togglers[index]
158					.setStyle('cursor', 'pointer')
159					.addEvents({
160						'click': function(e) {
161							this.onFocus(item, clone);
162						}.bind(this)
163					});
164				clone.addEvents({
165					'blur': function() {
166						item.set('value', clone.get('value'));
167					}
168				});
169			} else {
170				clone.addEvents({
171					'keydown': function(e) {
172						if (this.options.allowEmpty && (e.key == "delete" || e.key == "backspace")) {
173							item.set('value', '');
174							e.target.set('value', '');
175							this.close(null, true);
176						} else if (e.key == "tab") {
177							this.close(null, true);
178						} else {
179							e.stop();
180						}
181					}.bind(this),
182					'focus': function(e) {
183						this.onFocus(item, clone);
184					}.bind(this)
185				});
186			}
187		}.bind(this));
188	},
189	
190	onFocus: function(original_input, visual_input) {
191		var init_visual_date, d = visual_input.getCoordinates();
192		
193		if ($chk(original_input.get('value'))) {
194			init_visual_date = this.unformat(original_input.get('value'), this.options.inputOutputFormat).valueOf();
195		} else {
196			init_visual_date = new Date();
197			if ($chk(this.options.maxDate) && init_visual_date.valueOf() > this.options.maxDate.valueOf()) {
198				init_visual_date = new Date(this.options.maxDate.valueOf());
199			}
200			if ($chk(this.options.minDate) && init_visual_date.valueOf() < this.options.minDate.valueOf()) {
201				init_visual_date = new Date(this.options.minDate.valueOf());
202			}
203		}
204		
205		this.show({ left: d.left + this.options.positionOffset.x, top: d.top + d.height + this.options.positionOffset.y }, init_visual_date);
206		this.input = original_input;
207		this.visual = visual_input;
208		this.options.onShow();
209	},
210	
211	dateToObject: function(d) {
212		return {
213			year: d.getFullYear(),
214			month: d.getMonth(),
215			day: d.getDate(),
216			hours: d.getHours(),
217			minutes: d.getMinutes(),
218			seconds: d.getSeconds()
219		};
220	},
221	
222	dateFromObject: function(values) {
223		var d = new Date();
224		d.setDate(1);
225		['year', 'month', 'day', 'hours', 'minutes', 'seconds'].each(function(type) {
226			var v = values[type];
227			if (!$chk(v)) return;
228			switch (type) {
229				case 'day': d.setDate(v); break;
230				case 'month': d.setMonth(v); break;
231				case 'year': d.setFullYear(v); break;
232				case 'hours': d.setHours(v); break;
233				case 'minutes': d.setMinutes(v); break;
234				case 'seconds': d.setSeconds(v); break;
235			}
236		});
237		return d;
238	},
239	
240	show: function(position, timestamp) {
241		this.formatMinMaxDates();
242		if ($chk(timestamp)) {
243			this.d = new Date(timestamp);
244		} else {
245			this.d = new Date();
246		}
247		this.today = new Date();
248		this.choice = this.dateToObject(this.d);
249		this.mode = (this.options.startView == 'time' && !this.options.timePicker) ? 'month' : this.options.startView;
250		this.render();
251		this.picker.setStyles(position);
252	},
253	
254	render: function(fx) {
255		if (!$chk(this.picker)) {
256			this.constructPicker();
257		} else {
258			// swap contents so we can fill the newContents again and animate
259			var o = this.oldContents;
260			this.oldContents = this.newContents;
261			this.newContents = o;
262			this.newContents.empty();
263		}
264		
265		// remember current working date
266		var startDate = new Date(this.d.getTime());
267		
268		// intially assume both left and right are allowed
269		this.limit = { right: false, left: false };
270		
271		// render! booty!
272		if (this.mode == 'decades') {
273			this.renderDecades();
274		} else if (this.mode == 'year') {
275			this.renderYear();
276		} else if (this.mode == 'time') {
277			this.renderTime();
278			this.limit = { right: true, left: true }; // no left/right in timeview
279		} else {
280			this.renderMonth();
281		}
282		
283		this.picker.getElement('.previous').setStyle('visibility', this.limit.left ? 'hidden' : 'visible');
284		this.picker.getElement('.next').setStyle('visibility', this.limit.right ? 'hidden' : 'visible');
285		this.picker.getElement('.titleText').setStyle('cursor', this.allowZoomOut() ? 'pointer' : 'default');
286		
287		// restore working date
288		this.d = startDate;
289		
290		// if ever the opacity is set to '0' it was only to have us fade it in here
291		// refer to the constructPicker() function, which instantiates the picker at opacity 0 when fading is desired
292		if (this.picker.getStyle('opacity') == 0) {
293			this.picker.tween('opacity', 0, 1);
294		}
295		
296		// animate
297		if ($chk(fx)) this.fx(fx);
298	},
299	
300	fx: function(fx) {
301		if (fx == 'right') {
302			this.oldContents.setStyles({ left: 0, opacity: 1 });
303			this.newContents.setStyles({ left: this.bodysize.x, opacity: 1 });
304			this.slider.setStyle('left', 0).tween('left', 0, -this.bodysize.x);
305		} else if (fx == 'left') {
306			this.oldContents.setStyles({ left: this.bodysize.x, opacity: 1 });
307			this.newContents.setStyles({ left: 0, opacity: 1 });
308			this.slider.setStyle('left', -this.bodysize.x).tween('left', -this.bodysize.x, 0);
309		} else if (fx == 'fade') {
310			this.slider.setStyle('left', 0);
311			this.oldContents.setStyle('left', 0).set('tween', { duration: this.options.animationDuration / 2 }
311).tween('opacity', 1, 0);
312			this.newContents.setStyles({ opacity: 0, left: 0}).set('tween', { duration: this.options.animationDuration }).tween('opacity', 0, 1);
313		}
314	},
315	
316	constructPicker: function() {
317		this.picker = new Element('div', { 'class': this.options.pickerClass }).inject(document.body);
318		if (this.options.useFadeInOut) {
319			this.picker.setStyle('opacity', 0).set('tween', { duration: this.options.animationDuration });
320		}
321		
322		var h = new Element('div', { 'class': 'header' }).inject(this.picker);
323		var titlecontainer = new Element('div', { 'class': 'title' }).inject(h);
324		new Element('div', { 'class': 'previous' }).addEvent('click', this.previous.bind(this)).set('text', '«').inject(h);
325		new Element('div', { 'class': 'next' }).addEvent('click', this.next.bind(this)).set('text', '»').inject(h);
326		new Element('div', { 'class': 'closeButton' }).addEvent('click', this.close.bindWithEvent(this, true)).set('text', 'x').inject(h);
327		new Element('span', { 'class': 'titleText' }).addEvent('click', this.zoomOut.bind(this)).inject(titlecontainer);
328		
329		var b = new Element('div', { 'class': 'body' }).inject(this.picker);
330		this.bodysize = b.getSize();
331		this.slider = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: 2 * this.bodysize.x, height: this.bodysize.y }})
332					.set('tween', { duration: this.options.animationDuration, transition: Fx.Transitions.Quad.easeInOut }).inject(b);
333		this.oldContents = new Element('div', { styles: { position: 'absolute', top: 0, left: this.bodysize.x, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider);
334		this.newContents = new Element('div', { styles: { position: 'absolute', top: 0, left: 0, width: this.bodysize.x, height: this.bodysize.y }}).inject(this.slider);
335	},
336	
337	renderTime: function() {
338		var container = new Element('div', { 'class': 'time' }).inject(this.newContents);
339		
340		if (this.options.timePickerOnly) {
341			this.picker.getElement('.titleText').set('text', 'Select a time');
342		} else {
343			this.picker.getElement('.titleText').set('text', this.format(this.d, 'j M, Y'));
344		}
345		
346		new Element('input', { type: 'text', 'class': 'hour' })
347			.set('value', this.leadZero(this.d.getHours()))
348			.addEvents({
349				mousewheel: function(e) {
350					var i = e.target, v = i.get('value').toInt();
351					i.focus();
352					if (e.wheel > 0) {
353						v = (v < 23) ? v + 1 : 0;
354					} else {
355						v = (v > 0) ? v - 1 : 23;
356					}
357					i.set('value', this.leadZero(v));
358					e.stop();
359				}.bind(this)
360			})
361			.set('maxlength', 2)
362			.inject(container);
363			
364		new Element('input', { type: 'text', 'class': 'minutes' })
365			.set('value', this.leadZero(this.d.getMinutes()))
366			.addEvents({
367				mousewheel: function(e) {
368					var i = e.target, v = i.get('value').toInt();
369					i.focus();
370					if (e.wheel > 0) {
371						v = (v < 59) ? v + 1 : 0;
372					} else {
373						v = (v > 0) ? v - 1 : 59;
374					}
375					i.set('value', this.leadZero(v));
376					e.stop();
377				}.bind(this)
378			})
379			.set('maxlength', 2)
380			.inject(container);
381		
382		new Element('div', { 'class': 'separator' }).set('text', ':').inject(container);
383		
384		new Element('input', { type: 'submit', value: 'OK', 'class': 'ok' })
385			.addEvents({
386				click: function(e) {
387					e.stop();
388					this.select($merge(this.dateToObject(this.d), { hours: this.picker.getElement('.hour').get('value').toInt(), minutes: this.picker.getElement('.minutes').get('value').toInt() }));
389				}.bind(this)
390			})
391			.set('maxlength', 2)
392			.inject(container);
393	},
394	
395	renderMonth: function() {
396		var month = this.d.getMonth();
397		
398		this.picker.getElement('.titleText').set('text', this.options.months[month] + ' ' + this.d.getFullYear());
399		
400		this.d.setDate(1);
401		while (this.d.getDay() != this.options.startDay) {
402			this.d.setDate(this.d.getDate() - 1);
403		}
404		
405		var container = new Element('div', { 'class': 'days' }).inject(this.newContents);
406		var titles = new Element('div', { 'class': 'titles' }).inject(container);
407		var d, i, classes, e, weekcontainer;
408
409		for (d = this.options.startDay; d < (this.options.startDay + 7); d++) {
410			new Element('div', { 'class': 'title day day' + (d % 7) }).set('text', this.options.days[(d % 7)].substring(0,this.options.dayShort)).inject(titles);
411		}
412		
413		var available = false;
414		var t = this.today.toDateString();
415		var currentChoice = this.dateFromObject(this.choice).toDateString();
416		
417		for (i = 0; i < 42; i++) {
418			classes = [];
419			classes.push('day');
420			classes.push('day'+this.d.getDay());
421			if (this.d.toDateString() == t) classes.push('today');
422			if (this.d.toDateString() == currentChoice) classes.push('selected');
423			if (this.d.getMonth() != month) classes.push('otherMonth');
424			
425			if (i % 7 == 0) {
426				weekcontainer = new Element('div', { 'class': 'week week'+(Math.floor(i/7)) }).inject(container);
427			}
428			
429			e = new Element('div', { 'class': classes.join(' ') }).set('text', this.d.getDate()).inject(weekcontainer);
430			if (this.limited('date')) {
431				e.addClass('unavailable');
432				if (available) {
433					this.limit.right = true;
434				} else if (this.d.getMonth() == month) {
435					this.limit.left = true;
436				}
437			} else {
438				available = true;
439				e.addEvent('click', function(e, d) {
440					if (this.options.timePicker) {
441						this.d.setDate(d.day);
442						this.d.setMonth(d.month);
443						this.mode = 'time';
444						this.render('fade');
445					} else {
446						this.select(d);
447					}
448				}.bindWithEvent(this, { day: this.d.getDate(), month: this.d.getMonth(), year: this.d.getFullYear() }));
449			}
450			this.d.setDate(this.d.getDate() + 1);
451		}
452		if (!available) this.limit.right = true;
453	},
454	
455	renderYear: function() {
456		var month = this.today.getMonth();
457		var thisyear = this.d.getFullYear() == this.today.getFullYear();
458		var selectedyear = this.d.getFullYear() == this.choice.year;
459		
460		this.picker.getElement('.titleText').set('text', this.d.getFullYear());
461		this.d.setMonth(0);
462		
463		var i, e;
464		var available = false;
465		var container = new Element('div', { 'class': 'months' }).inject(this.newContents);
466		
467		for (i = 0; i <= 11; i++) {
468			e = new Element('div', { 'class': 'month month'+(i+1)+(i == month && thisyear ? ' today' : '')+(i == this.choice.month && selectedyear ? ' selected' : '') })
469			.set('text', this.options.monthShort ? this.options.months[i].substring(0, this.options.monthShort) : this.options.months[i]).inject(container);
470			
471			if (this.limited('month')) {
472				e.addClass('unavailable');
473				if (available) {
474					this.limit.right = true;
475				} else {
476					this.limit.left = true;
477				}
478			} else {
479				available = true;
480				e.addEvent('click', function(e, d) {
481					this.d.setDate(1);
482					this.d.setMonth(d);
483					this.mode = 'month';
484					this.render('fade');
485				}.bindWithEvent(this, i));
486			}
487			this.d.setMonth(i);
488		}
489		if (!available) this.limit.right = true;
490	},
491	
492	renderDecades: function() {
493		// start neatly at interval (eg. 1980 instead of 1987)
494		while (this.d.getFullYear() % this.options.yearsPerPage > 0) {
495			this.d.setFullYear(this.d.getFullYear() - 1);
496		}
497
498		this.picker.getElement('.titleText').set('text', this.d.getFullYear() + '-' + (this.d.getFullYear() + this.options.yearsPerPage - 1));
499		
500		var i, y, e;
501		var available = false;
502		var container = new Element('div', { 'class': 'years' }).inject(this.newContents);
503		
504		if ($chk(this.options.minDate) && this.d.getFullYear() <= this.options.minDate.getFullYear()) {
505			this.limit.left = true;
506		}
507		
508		for (i = 0; i < this.options.yearsPerPage; i++) {
509			y = this.d.getFullYear();
510			e = new Element('div', { 'class': 'year year' + i + (y == this.today.getFullYear() ? ' today' : '') + (y == this.choice.year ? ' selected' : '') }).set('text', y).inject(container);
511			
512			if (this.limited('year')) {
513				e.addClass('unavailable');
514				if (available) {
515					this.limit.right = true;
516				} else {
517					this.limit.left = true;
518				}
519			} else {
520				available = true;
521				e.addEvent('click', function(e, d) {
522					this.d.setFullYear(d);
523					this.mode = 'year';
524					this.render('fade');
525				}.bindWithEvent(this, y));
526			}
527			this.d.setFullYear(this.d.getFullYear() + 1);
528		}
529		if (!available) {
530			this.limit.right = true;
531		}
532		if ($chk(this.options.maxDate) && this.d.getFullYear() >= this.options.maxDate.getFullYear()) {
533			this.limit.right = true;
534		}
535	},
536	
537	limited: function(type) {
538		var cs = $chk(this.options.minDate);
539		var ce = $chk(this.options.maxDate);
540		if (!cs && !ce) return false;
541		
542		switch (type) {
543			case 'year':
544				return (cs && this.d.getFullYear() < this.options.minDate.getFullYear()) || (ce && this.d.getFullYear() > this.options.maxDate.getFullYear());
545				
546			case 'month':
547				// todo: there has got to be an easier way...?
548				var ms = ('' + this.d.getFullYear() + this.leadZero(this.d.getMonth())).toInt();
549				return cs && ms < ('' + this.options.minDate.getFullYear() + this.leadZero(this.options.minDate.getMonth())).toInt()
550					|| ce && ms > ('' + this.options.maxDate.getFullYear() + this.leadZero(this.options.maxDate.getMonth())).toInt()
551				
552			case 'date':
553				return (cs && this.d < this.options.minDate) || (ce && this.d > this.options.maxDate);
554		}
555	},
556	
557	allowZoomOut: function() {
558		if (this.mode == 'time' && this.options.timePickerOnly) return false;
559		if (this.mode == 'decades') return false;
560		if (this.mode == 'year' && !this.options.yearPicker) return false;
561		return true;
562	},
563	
564	zoomOut: function() {
565		if (!this.allowZoomOut()) return;
566		if (this.mode == 'year') {
567			this.mode = 'decades';
568		} else if (this.mode == 'time') {
569			this.mode = 'month';
570		} else {
571			this.mode = 'year';
572		}
573		this.render('fade');
574	},
575	
576	previous: function() {
577		if (this.mode == 'decades') {
578			this.d.setFullYear(this.d.getFullYear() - this.options.yearsPerPage);
579		} else if (this.mode == 'year') {
580			this.d.setFullYear(this.d.getFullYear() - 1);
581		} else if (this.mode == 'month') {
582			this.d.setDate(1);
583			this.d.setMonth(this.d.getMonth() - 1);
584		}
585		this.render('left');
586	},
587	
588	next: function() {
589		if (this.mode == 'decades') {
590			this.d.setFullYear(this.d.getFullYear() + this.options.yearsPerPage);
591		} else if (this.mode == 'year') {
592			this.d.setFullYear(this.d.getFullYear() + 1);
593		} else if (this.mode == 'month') {
594			this.d.setDate(1);
595			this.d.setMonth(this.d.getMonth() + 1);
596		}
597		this.render('right');
598	},
599	
600	close: function(e, force) {
601		if (!$(this.picker)) return;
602		var clickOutside = ($chk(e) && e.target != this.picker && !this.picker.hasChild(e.target) && e.target != this.visual);
603		if (force || clickOutside) {
604			if (this.options.useFadeInOut) {
605				this.picker.set('tween', { duration: this.options.animationDuration / 2, onComplete: this.destroy.bind(this) }).tween('opacity', 1, 0);
606			} else {
607				this.destroy();
608			}
609		}
610	},
611	
612	destroy: function() {
613		this.picker.destroy();
614		this.picker = null;
615		this.options.onClose();
616	},
617	
618	select: function(values) {
619		this.choice = $merge(this.choice, values);
620		var d = this.dateFromObject(this.choice);
621		this.input.set('value', this.format(d, this.options.inputOutputFormat));
622		this.visual.set('value', this.format(d, this.options.format));
623		this.options.onSelect(d);
624		this.close(null, true);
625	},
626	
627	leadZero: function(v) {
628		return v < 10 ? '0'+v : v;
629	},
630	
631	format: function(t, format) {
632		var f = '';
633		var h = t.getHours();
634		var m = t.getMonth();
635		
636		for (var i = 0; i < format.length; i++) {
637			switch(format.charAt(i)) {
638				case '\\': i++; f+= format.charAt(i); break;
639				case 'y': f += (t.getFullYear() + '').substring(2); break;
640				case 'Y': f += t.getFullYear(); break;
641				case 'm': f += this.leadZero(m + 1); break;
642				case 'n': f += (m + 1); break;
643				case 'M': f += this.options.months[m].substring(0,this.options.monthShort); break;
644				case 'F': f += this.options.months[m]; break;
645				case 'd': f += this.leadZero(t.getDate()); break;
646				case 'j': f += t.getDate(); break;
647				case 'D': f += this.options.days[t.getDay()].substring(0,this.options.dayShort); break;
648				case 'l': f += this.options.days[t.getDay()]; break;
649				case 'G': f += h; break;
650				case 'H': f += this.leadZero(h); break;
651				case 'g': f += (h % 12 ? h % 12 : 12); break;
652				case 'h': f += this.leadZero(h % 12 ? h % 12 : 12); break;
653				case 'a': f += (h > 11 ? 'pm' : 'am'); break;
654				case 'A': f += (h > 11 ? 'PM' : 'AM'); break;
655				case 'i': f += this.leadZero(t.getMinutes()); break;
656				case 's': f += this.leadZero(t.getSeconds()); break;
657				case 'U': f += Math.floor(t.valueOf() / 1000); break;
658				default:  f += format.charAt(i);
659			}
660		}
661		return f;
662	},
663	
664	unformat: function(t, format) {
665		var d = new Date();
666		d.setMonth(0);
667		d.setDate(1);
668		var a = {};
669		var c, m;
670		t = t.toString();
671		
672		for (var i = 0; i < format.length; i++) {
673			c = format.charAt(i);
674			switch(c) {
675				case '\\': r = null; i++; break;
676				case 'y': r = '[0-9]{2}'; break;
677				case 'Y': r = '[0-9]{4}'; break;
678				case 'm': r = '0[1-9]|1[012]'; break;
679				case 'n': r = '[1-9]|1[012]'; break;
680				case 'M': r = '[A-Za-z]{'+this.options.monthShort+'}'; break;
681				case 'F': r = '[A-Za-z]+'; break;
682				case 'd': r = '0[1-9]|[12][0-9]|3[01]'; break;
683				case 'j': r = '[12][0-9]|3[01]|[1-9]'; break;
684				case 'D': r = '[A-Za-z]{'+this.options.dayShort+'}'; break;
685				case 'l': r = '[A-Za-z]+'; break;
686				case 'G': 
687				case 'H': 
688				case 'g': 
689				case 'h': r = '[0-9]{1,2}'; break;
690				case 'a': r = '(am|pm)'; break;
691				case 'A': r = '(AM|PM)'; break;
692				case 'i': 
693				case 's': r = '[012345][0-9]'; break;
694				case 'U': r = '-?[0-9]+$'; break;
695				default:  r = null;
696			}
697			
698			if ($chk(r)) {
699				m = t.match('^'+r);
700				if ($chk(m)) {
701					a[c] = m[0];
702					t = t.substring(a[c].length);
703				} else {
704					if (this.options.debug) alert("Fatal Error in DatePicker\n\nUnexpected format at: '"+t+"' expected format character '"+c+"' (pattern '"+r+"')");
705					return d;
706				}
707			} else {
708				t = t.substring(1);
709			}
710		}
711		
712		for (c in a) {
713			var v = a[c];
714			switch(c) {
715				case 'y': d.setFullYear(v < 30 ? 2000 + v.toInt() : 1900 + v.toInt()); break; // assume between 1930 - 2029
716				case 'Y': d.setFullYear(v); break;
717				case 'm':
718				case 'n': d.setMonth(v - 1); break;
719				// FALL THROUGH NOTICE! "M" has no break, because "v" now is the full month (eg. 'February'), which will work with the next format "F":
720				case 'M': v = this.options.months.filter(function(item, index) { return item.substring(0,this.options.monthShort) == v }.bind(this))[0];
721				case 'F': d.setMonth(this.options.months.indexOf(v)); break;
722				case 'd':
723				case 'j': d.setDate(v); break;
724				case 'G': 
725				case 'H': d.setHours(v); break;
726				case 'g': 
727				case 'h': if (a['a'] == 'pm' || a['A'] == 'PM') { d.setHours(v == 12 ? 0 : v.toInt() + 12); } else { d.setHours(v); } break;
728				case 'i': d.setMinutes(v); break;
729				case 's': d.setSeconds(v); break;
730				case 'U': d = new Date(v.toInt() * 1000);
731			}
732		};
733		
734		return d;
735	}
736});

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.