diff --git a/extensions/postfix_autoreply/forms.py b/extensions/postfix_autoreply/forms.py
index 2b5cb3b7d..2ad2e10f6 100644
--- a/extensions/postfix_autoreply/forms.py
+++ b/extensions/postfix_autoreply/forms.py
@@ -1,6 +1,7 @@
from django import forms
from models import ARmessage
from django.utils.translation import ugettext as _
+import datetime
class ARmessageForm(forms.ModelForm):
class Meta:
@@ -11,3 +12,8 @@ class ARmessageForm(forms.ModelForm):
super(ARmessageForm, self).__init__(*args, **kwargs)
self.fields['subject'].widget.attrs['size'] = 40
self.fields['content'].widget.attrs['cols'] = 50
+
+ def clean_untildate(self):
+ if self.cleaned_data["untildate"] < datetime.date.today():
+ raise forms.ValidationError(_("This date is over"))
+ return self.cleaned_data['untildate']
diff --git a/extensions/postfix_autoreply/models.py b/extensions/postfix_autoreply/models.py
index 3ad3ddbb5..f7b99ce60 100644
--- a/extensions/postfix_autoreply/models.py
+++ b/extensions/postfix_autoreply/models.py
@@ -12,10 +12,14 @@ class Alias(models.Model):
class ARmessage(models.Model):
mbox = models.ForeignKey(Mailbox)
- subject = models.CharField(_('subject'), max_length=255)
- content = models.TextField(_('content'))
- enabled = models.BooleanField(_('enabled'))
- untildate = models.DateTimeField(_('until'))
+ subject = models.CharField(_('subject'), max_length=255,
+ help_text=_("The subject that will appear in sended emails"))
+ content = models.TextField(_('content'),
+ help_text=_("The content that will appear in sended emails"))
+ enabled = models.BooleanField(_('enabled'),
+ help_text=_("Activate/Deactivate your auto reply"))
+ untildate = models.DateField(_('until'),
+ help_text=_("Activate your auto reply until this date"))
class ARhistoric(models.Model):
armessage = models.ForeignKey(ARmessage)
diff --git a/extensions/postfix_autoreply/templates/autoreply.html b/extensions/postfix_autoreply/templates/autoreply.html
index 6ce9e2e9e..1ee731a23 100644
--- a/extensions/postfix_autoreply/templates/autoreply.html
+++ b/extensions/postfix_autoreply/templates/autoreply.html
@@ -2,9 +2,6 @@
{% load i18n %}
{% block html_head %}
-
@@ -18,7 +15,7 @@
{% endblock %}
diff --git a/extensions/webmail/views.py b/extensions/webmail/views.py
index da3da92f7..d19f4cee0 100644
--- a/extensions/webmail/views.py
+++ b/extensions/webmail/views.py
@@ -149,7 +149,8 @@ def move(request):
@is_not_localadmin()
def delete(request, fdname, mail_id):
mbc = IMAPconnector(user=request.user.username)
- mbc.move(mail_id, fdname, "Trash")
+ mbc.move(mail_id, fdname, parameters.get_user(request.user, "webmail",
+ "TRASH_FOLDER"))
return folder(request, fdname, False)
@login_required
diff --git a/static/js/calendar.js b/static/js/calendar.js
index ab4935248..45befd4b8 100644
--- a/static/js/calendar.js
+++ b/static/js/calendar.js
@@ -1,1344 +1,1104 @@
-/**
- * Calendar
- *
- * version 1.1.2
- * - added option onValuesStart
- * version 1.1.1
- * - added bounds option
- * - fixes chrome toggle bug
- * version 1.1
- * - fix: events in fx not work bug
- * - fix: ie6 iframe bug
- * - added displayHead and displayFooter abstract render function
- * version 1.0
- * - new option dayLabelLength
- * - IE6 Fix works with variable content height
- */
-
-/**
- * Calendar: a Javascript class for Mootools
- *
- * adds accessible and unobtrusive date pickers to your form elements
- * based on a work by Aeron Glemann
- * modifyed by Jens Klose
- *
- * @licence MIT Style License
- */
-var Calendar = new Class({
-
- Implements: [Events,Options],
-
- options: {
- dayLabelLength: 2,
- blocked: [], // blocked dates
- bounds: {start:null , end:null}, //
- classes: {
- calendar:'calendar',
- prev:'prev',
- next:'next',
- month:'month',
- year:'year',
- today:'today',
- invalid:'invalid',
- valid:'valid',
- inactive:'inactive',
- active:'active',
- hover:'hover',
- hilite:'hilite'
- },
- days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // days of the week starting at sunday
- direction: 0, // -1 past, 0 past + future, 1 future
- draggable: true,
- months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
- navigation: 1, // 0 = no nav; 1 = single nav for month; 2 = dual nav for month and year
- offset: 0, // first day of the week: 0 = sunday, 1 = monday, etc..
- onHideStart: $empty,
- onHideComplete: $empty,
- onShowStart: $empty,
- onShowComplete: $empty,
- onValuesStart: $empty,
- pad: 1, // padding between multiple calendars
- tweak: {
- x: 0,
- y: 0
- } // tweak calendar positioning
- },
-
- /**
- * initialize: calendar constructor
- *
- * @param obj (obj) a js object containing the form elements and format strings { id: 'format', id: 'format' etc }
- * @param props (obj) optional properties
- */
- initialize: function(obj, options) {
- // basic error checking
- if (!obj) {
- return false;
- }
-
- this.setOptions(options);
- this.classes = this.options.classes;
-
- // create cal element with css styles required for proper cal functioning
- this.calendar = new Element('div', {
- 'class':this.classes.calendar,
- 'styles': {
- left: '-1000px',
- opacity: 0,
- position: 'absolute',
- top: '-1000px',
- zIndex: 1000
- }
- }).inject(document.body);
-
- this.calendar.coord = this.calendar.getCoordinates();
-
- // iex 6 needs a transparent iframe underneath the calendar in order to not allow select elements to render through
- if (Browser.Engine.trident4) {
- this.iframe = new IFrame({
- 'styles': {
- left: '-1000px',
- position: 'absolute',
- top: '-1000px',
- zIndex: 999
- }
- }).inject(document.body);
- this.iframe.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
- }
-
- // initialize fade method
- this.fx = new Fx.Tween(this.calendar,{
- link : 'chain',
- duration : 'short',
- onStart: function() {
- if (this.calendar.getStyle('opacity') == 0) { // show
- //this.calendar.setStyle('display', 'block');
- this.fireEvent('onShowStart', this.element);
- }
- else { // hide
- this.fireEvent('onHideStart', this.element);
- }
- }.bind(this),
- onComplete: function() {
- if (this.calendar.getStyle('opacity') == 0) { // hidden
- //this.calendar.setStyle('display', 'none');
- if (Browser.Engine.trident4) {
- this.iframe.setStyles({
- left: '-1000px',
- top: '-1000px'
- });
- }
- this.fireEvent('onHideComplete', this.element);
- }
- else { // shown
- this.fireEvent('onShowComplete', this.element);
- }
- }.bind(this)
- });
-
- // initialize drag method
- if (window.Drag && this.options.draggable) {
- this.drag = new Drag.Move(this.calendar, {
- onDrag: function() {
- if (Browser.Engine.trident4) {
- this.iframe.setStyles({
- left: this.calendar.getStyle('left'),
- top: this.calendar.getStyle('top')
- });
- }
- }.bind(this)
- });
- }
-
- // create calendars array
- this.calendars = [];
- var id = 0;
- var d = new Date(); // today
- d.setDate(d.getDate() + this.options.direction.toInt()); // correct today for directional offset
-
- for (var i in obj) {
- var cal = {
- button: new Element('button', {
- 'type': 'button'
- }),
- el: $(i),
- els: [],
- id: id++,
- month: d.getMonth(),
- visible: false,
- year: d.getFullYear()
- };
-
- // fix for bad element (naughty, naughty element!)
- if (!this.element(i, obj[i], cal)) {
- continue;
- }
- // show cal on click
- this.fn = function(e, cal) {
- e.stop();
- this.toggle(cal);
- }.create({
- 'arguments': cal,
- 'bind': this,
- 'event': true
- });
-
- cal.el.addClass(this.classes.calendar);
- // create cal button
- cal.button.addClass(this.classes.calendar)
- .addEvent('click', this.fn)
- .inject(cal.el,'after');
- // read in default value
- cal.val = this.read(cal);
- $extend(cal, this.bounds(cal)); // abs bounds of calendar
- $extend(cal, this.values(cal)); // valid days, months, years
- this.rebuild(cal);
- this.calendars.push(cal); // add to cals array
- }
- return this;
- },
-
- /**
- * blocked: returns an array of blocked days for the month / year
- *
- * @param cal (obj)
- * @returns blocked days (array)
- */
- blocked: function(cal) {
- var blocked = [];
- var offset = new Date(cal.year, cal.month, 1).getDay(); // day of the week (offset)
- var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month
-
- this.options.blocked.each(function(date) {
- var values = date.split(' ');
-
- for (var i = 0; i < 3; i++) {
- if (!values[i]) {
- values[i] = '*';
- } // make sure blocked date contains values for at least d, m and y
- values[i] = values[i].contains(',') ? values[i].split(',') : new Array(values[i]); // split multiple values
- }
-
- if (values[2].contains(cal.year + '') || values[2].contains('*')) {
- if (values[1].contains(cal.month + 1 + '') || values[1].contains('*')) {
- values[0].each(function(val) { // if blocked value indicates this month / year
- if (val > 0) {
- blocked.push(val.toInt());
- } // add date to blocked array
- });
-
- if (values[3]) { // optional value for day of week
- values[3] = values[3].contains(',') ? values[3].split(',') : new Array(values[3]);
-
- for (var i = 0; i < last; i++) {
- var day = (i + offset) % 7;
-
- if (values[3].contains(day + '')) {
- blocked.push(i + 1); // add every date that corresponds to the blocked day of the week to the blocked array
- }
- }
- }
- }
- }
- }, this);
-
- return blocked;
- },
-
- /**
- * bounds: returns the start / end bounds of the calendar
- *
- * @param cal (obj)
- * @returns obj
- */
- bounds: function(cal) {
- // check external settings
- if ($defined(this.options.bounds.start)&&$defined(this.options.bounds.end)) {
- if ($type(this.options.bounds.end) == 'date' &&
- $type(this.options.bounds.start) == 'date' &&
- this.options.bounds.end>=this.options.bounds.start) {
- return {
- 'start': this.options.bounds.start,
- 'end': this.options.bounds.end
- }
- }
- }
-
- // 1. first we assume the calendar has no bounds (or a thousand years in either direction)
-
- // by default the calendar will accept a millennium in either direction
- var start = new Date(1000, 0, 1); // jan 1, 1000
- var end = new Date(2999, 11, 31); // dec 31, 2999
-
- // 2. but if the cal is one directional we adjust accordingly
- var date = new Date().getDate() + this.options.direction.toInt();
-
- if (this.options.direction > 0) {
- start = new Date();
- start.setDate(date + this.options.pad * cal.id);
- }
-
- if (this.options.direction < 0) {
- end = new Date();
- end.setDate(date - this.options.pad * (this.calendars.length - cal.id - 1));
- }
-
- // 3. then we can further filter the limits by using the pre-existing values in the selects
- cal.els.each(function(el) {
- if (el.get('tag') == 'select') {
- if (el.retrieve('format').test('(y|Y)')) { // search for a year select
- var years = [];
-
- el.getChildren().each(function(option) { // get options
- var values = this.unformat(option.get('value'), el.retrieve('format'));
-
- if (!years.contains(values[0])) {
- years.push(values[0]);
- } // add to years array
- }, this);
-
- years.sort(this.sort);
-
- if (years[0] > start.getFullYear()) {
- d = new Date(years[0], start.getMonth() + 1, 0); // last day of new month
-
- if (start.getDate() > d.getDate()) {
- start.setDate(d.getDate());
- }
-
- start.setYear(years[0]);
- }
-
- if (years.getLast() < end.getFullYear()) {
- d = new Date(years.getLast(), end.getMonth() + 1, 0); // last day of new month
-
- if (end.getDate() > d.getDate()) {
- end.setDate(d.getDate());
- }
-
- end.setYear(years.getLast());
- }
- }
-
- if (el.retrieve('format').test('(F|m|M|n)')) { // search for a month select
- var months_start = [];
- var months_end = [];
-
- el.getChildren().each(function(option) { // get options
- var values = this.unformat(option.get('value'), el.retrieve('format'));
-
- if ($type(values[0]) != 'number' || values[0] == years[0]) { // if it's a year / month combo for curr year, or simply a month select
- if (!months_start.contains(values[1])) {
- months_start.push(values[1]);
- } // add to months array
- }
-
- if ($type(values[0]) != 'number' || values[0] == years.getLast()) { // if it's a year / month combo for curr year, or simply a month select
- if (!months_end.contains(values[1])) {
- months_end.push(values[1]);
- } // add to months array
- }
- }, this);
-
- months_start.sort(this.sort);
- months_end.sort(this.sort);
-
- if (months_start[0] > start.getMonth()) {
- d = new Date(start.getFullYear(), months_start[0] + 1, 0); // last day of new month
-
- if (start.getDate() > d.getDate()) {
- start.setDate(d.getDate());
- }
-
- start.setMonth(months_start[0]);
- }
-
- if (months_end.getLast() < end.getMonth()) {
- d = new Date(start.getFullYear(), months_end.getLast() + 1, 0); // last day of new month
-
- if (end.getDate() > d.getDate()) {
- end.setDate(d.getDate());
- }
-
- end.setMonth(months_end.getLast());
- }
- }
- }
- }, this);
-
- return {
- 'start': start,
- 'end': end
- };
- },
-
- /**
- * caption: returns the caption element with header and navigation
- *
- * @param cal (obj)
- * @returns caption (element)
- */
- caption: function(cal) {
- // start by assuming navigation is allowed
- var navigation = {
- prev: {
- 'month': true,
- 'year': true
- },
- next: {
- 'month': true,
- 'year': true
- }
- };
-
- // if we're in an out of bounds year
- if (cal.year == cal.start.getFullYear()) {
- navigation.prev.year = false;
- if (cal.month == cal.start.getMonth() && this.options.navigation == 1) {
- navigation.prev.month = false;
- }
- }
- if (cal.year == cal.end.getFullYear()) {
- navigation.next.year = false;
- if (cal.month == cal.end.getMonth() && this.options.navigation == 1) {
- navigation.next.month = false;
- }
- }
-
- // special case of improved navigation but months array with only 1 month we can disable all month navigation
- if ($type(cal.months) == 'array') {
- if (cal.months.length == 1 && this.options.navigation == 2) {
- navigation.prev.month = navigation.next.month = false;
- }
- }
-
- var caption = new Element('caption');
-
- var prev = new Element('a').addClass(this.classes.prev).appendText('\x3c'); // <
- var next = new Element('a').addClass(this.classes.next).appendText('\x3e'); // >
-
- if (this.options.navigation == 2) {
- var month = new Element('span').addClass(this.classes.month).inject(caption);
-
- if (navigation.prev.month) {
- prev.clone().addEvent('click', function(cal) {
- this.navigate(cal, 'm', -1);
- }.pass(cal, this)).inject(month);
- }
-
- month.adopt(new Element('span').appendText(this.options.months[cal.month]));
-
- if (navigation.next.month) {
- next.clone().addEvent('click', function(cal) {
- this.navigate(cal, 'm', 1);
- }.pass(cal, this)).inject(month);
- }
-
- var year = new Element('span').addClass(this.classes.year).inject(caption);
-
- if (navigation.prev.year) {
- prev.clone().addEvent('click', function(cal) {
- this.navigate(cal, 'y', -1);
- }.pass(cal, this)).inject(year);
- }
-
- year.adopt(new Element('span').appendText(cal.year));
-
- if (navigation.next.year) {
- next.clone().addEvent('click', function(cal) {
- this.navigate(cal, 'y', 1);
- }.pass(cal, this)).inject(year);
- }
- }
- else { // 1 or 0
- if (navigation.prev.month && this.options.navigation) {
- prev.clone().addEvent('click', function(cal) {
- this.navigate(cal, 'm', -1);
- }.pass(cal, this)).inject(caption);
- }
-
- caption.adopt(new Element('span').addClass(this.classes.month).appendText(this.options.months[cal.month]));
-
- caption.adopt(new Element('span').addClass(this.classes.year).appendText(cal.year));
-
- if (navigation.next.month && this.options.navigation) {
- next.clone().addEvent('click', function(cal) {
- this.navigate(cal, 'm', 1);
- }.pass(cal, this)).inject(caption);
- }
-
- }
-
- return caption;
- },
-
- /**
- * changed: run when a select value is changed
- *
- * @param cal (obj)
- * @returns caption (element)
- */
- changed: function(cal) {
- cal.val = this.read(cal); // update calendar val from inputs
-
- $extend(cal, this.values(cal)); // update bounds - based on curr month
-
- this.rebuild(cal); // rebuild days select
-
- if (!cal.val) {
- return;
- } // in case the same date was clicked the cal has no set date we should exit
-
- if (cal.val.getDate() < cal.days[0]) {
- cal.val.setDate(cal.days[0]);
- }
- if (cal.val.getDate() > cal.days.getLast()) {
- cal.val.setDate(cal.days.getLast());
- }
-
- cal.els.each(function(el) { // then we can set the value to the field
- el.set('value',this.format(cal.val, el.retrieve('format')));
- }, this);
-
- this.check(cal); // checks other cals
-
- this.calendars.each(function(kal) { // update cal graphic if visible
- if (kal.visible) {
- this.display(kal);
- }
- }, this);
- },
-
- /**
- * check: checks other calendars to make sure no overlapping values
- *
- * @param cal (obj)
- */
- check: function(cal) {
- this.calendars.each(function(kal, i) {
- if (kal.val) { // if calendar has value set
- var change = false;
-
- if (i < cal.id) { // preceding calendar
- var bound = new Date(Date.parse(cal.val));
-
- bound.setDate(bound.getDate() - (this.options.pad * (cal.id - i)));
-
- if (bound < kal.val) {
- change = true;
- }
- }
- if (i > cal.id) { // following calendar
- var bound = new Date(Date.parse(cal.val));
-
- bound.setDate(bound.getDate() + (this.options.pad * (i - cal.id)));
-
- if (bound > kal.val) {
- change = true;
- }
- }
-
- if (change) {
- if (kal.start > bound) {
- bound = kal.start;
- }
- if (kal.end < bound) {
- bound = kal.end;
- }
-
- kal.month = bound.getMonth();
- kal.year = bound.getFullYear();
-
- $extend(kal, this.values(kal));
-
- // TODO - IN THE CASE OF SELECT MOVE TO NEAREST VALID VALUE
- // IN THE CASE OF INPUT DISABLE
-
- // if new date is not valid better unset cal value
- // otherwise it would mean incrementally checking to find the nearest valid date which could be months / years away
- kal.val = kal.days.contains(bound.getDate()) ? bound : null;
-
- this.write(kal);
-
- if (kal.visible) {
- this.display(kal);
- } // update cal graphic if visible
- }
- }
- }, this);
- },
-
- /**
- * clicked: run when a valid day is clicked in the calendar
- *
- * @param td (obj)
- * @param day (int)
- * @param cal (obj)
- */
- clicked: function(td, day, cal) {
- cal.val = (this.value(cal) == day) ? null : new Date(cal.year, cal.month, day); // set new value - if same then disable
-
- this.write(cal);
-
- // ok - in the special case that it's all selects and there's always a date no matter what (at least as far as the form is concerned)
- // we can't let the calendar undo a date selection - it's just not possible!!
- if (!cal.val) {
- cal.val = this.read(cal);
- }
-
- if (cal.val) {
- this.check(cal); // checks other cals
- this.toggle(cal); // hide cal
- }
- else { // remove active class and replace with valid
- td.addClass(this.classes.valid);
- td.removeClass(this.classes.active);
- }
- },
-
- /**
- * display: create calendar element
- *
- * @param cal (obj)
- */
- display: function(cal) {
- // 1. header and navigation
- this.calendar.empty(); // init div
- this.calendar.className = this.classes.calendar + ' ' + this.options.months[cal.month].toLowerCase();
- // render optional head content
- var extra = this.displayHead(cal);
- if ($defined(extra) && $defined(extra.inject)) extra.inject(this.calendar);
- //this.displayHead(cal).inject(this.calendar, 'top');
-
- var div = new Element('div').inject(this.calendar); // a wrapper div to help correct browser css problems with the caption element
- var table = new Element('table').inject(div).adopt(this.caption(cal));
- // 2. day names
- var thead = new Element('thead').inject(table);
- var tr = new Element('tr').inject(thead);
-
- for (var i = 0; i <= 6; i++) {
- var th = this.options.days[(i + this.options.offset) % 7];
-
- tr.adopt(new Element('th', {
- 'title': th
- }).appendText(th.substr(0, this.options.dayLabelLength)));
- }
-
- // 3. day numbers
- var tbody = new Element('tbody').inject(table);
- var tr = new Element('tr').inject(tbody);
- var d = new Date(cal.year, cal.month, 1);
- var offset = ((d.getDay() - this.options.offset) + 7) % 7; // day of the week (offset)
- var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month
- var prev = new Date(cal.year, cal.month, 0).getDate(); // last day of previous month
- var active = this.value(cal); // active date (if set and within curr month)
- var valid = cal.days; // valid days for curr month
- var inactive = []; // active dates set by other calendars
- var hilited = [];
- this.calendars.each(function(kal, i) {
- if (kal != cal && kal.val) {
- if (cal.year == kal.val.getFullYear() && cal.month == kal.val.getMonth()) {
- inactive.push(kal.val.getDate());
- }
- if (cal.val) {
- for (var day = 1; day <= last; day++) {
- d.setDate(day);
- if ((i < cal.id && d > kal.val && d < cal.val) || (i > cal.id && d > cal.val && d < kal.val)) {
- if (!hilited.contains(day)) {
- hilited.push(day);
- }
- }
- }
- }
- }
- }, this);
- var d = new Date();
- var today = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); // today obv
-
- for (var i = 1; i < 43; i++) { // 1 to 42 (6 x 7 or 6 weeks)
- if ((i - 1) % 7 == 0) {
- tr = new Element('tr').inject(tbody);
- } // each week is it's own table row
- var td = new Element('td').inject(tr);
- var day = i - offset;
- var date = new Date(cal.year, cal.month, day);
- var cls = '';
-
- if (day === active) {
- cls = this.classes.active;
- } // active
- else if (inactive.contains(day)) {
- cls = this.classes.inactive;
- } // inactive
- else if (valid.contains(day)) {
- cls = this.classes.valid;
- } // valid
- else if (day >= 1 && day <= last) {
- cls = this.classes.invalid;
- } // invalid
-
- if (date.getTime() == today) {
- cls = cls + ' ' + this.classes.today;
- } // adds class for today
- if (hilited.contains(day)) {
- cls = cls + ' ' + this.classes.hilite;
- } // adds class if hilited
- td.addClass(cls);
-
- if (valid.contains(day)) { // if it's a valid - clickable - day we add interaction
- td.set('title', this.format(date, 'D M jS Y'));
- td.addEvents({
- 'click': function(td, day, cal) {
- this.clicked(td, day, cal);
- }.pass([td, day, cal], this),
- 'mouseover': function(td, cls) {
- td.addClass(cls);
- }.pass([td, this.classes.hover]),
- 'mouseout': function(td, cls) {
- td.removeClass(cls);
- }.pass([td, this.classes.hover])
- });
- }
-
- // pad calendar with last days of prev month and first days of next month
- if (day < 1) {
- day = prev + day;
- }
- else if (day > last) {
- day = day - last;
- }
- td.appendText(day);
- }
- // render optional footer content
- extra = this.displayFooter(cal)
- if ($defined(extra) && $defined(extra.inject)) extra.inject(this.calendar);
-
- this.calendar.coord = this.calendar.getCoordinates();
- },
-
- /**
- * displayHead
- *
- * @abstract
- */
- displayHead: function(cal) {},
-
- /**
- * displayFooter
- *
- * @abstract
- */
- displayFooter: function(cal) {},
-
- /**
- * element: helper function
- *
- * @param el (string) element id
- * @param f (string) format string
- * @param cal (obj)
- */
- element: function(el, f, cal) {
- if ($type(f) == 'object') { // in the case of multiple inputs per calendar
- for (var i in f) {
- if (!this.element(i, f[i], cal)) {
- return false;
- }
- }
-
- return true;
- }
-
- el = $(el);
-
- if (!el) {
- return false;
- }
-
- el.store('format',f);
-
- if (el.get('tag') == 'select') { // select elements allow the user to manually set the date via select option
- el.addEvent('change', function(cal) {
- this.changed(cal);
- }.pass(cal, this));
- }
- else { // input (type text) elements restrict the user to only setting the date via the calendar
- el.set('readonly','readonly');
- el.addEvent('focus', function(cal) {
- this.toggle(cal);
- }.pass(cal, this));
- }
-
- cal.els.push(el);
-
- return true;
- },
-
- /**
- * format: formats a date object according to passed in instructions
- *
- * @param date (obj)
- * @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y
- * @returns string
- */
- format: function(date, f) {
- var g = '';
-
- if (date) {
- var d = date.getDate(); // 1 - 31
- var day = this.options.days[date.getDay()]; // Sunday - Saturday
- var m = date.getMonth() + 1; // 1 - 12
- var month = this.options.months[date.getMonth()]; // January - December
- var y = date.getFullYear() + ''; // 19xx - 20xx
-
- for (var i = 0; i < f.length; i++) {
- var c = f.charAt(i); // format char
- switch(c) {
- // year cases
- case 'y': // xx - xx
- y = y.substr(2);
- case 'Y': // 19xx - 20xx
- g += y;
- break;
-
- // month cases
- case 'm': // 01 - 12
- if (m < 10) {
- m = '0' + m;
- }
- case 'n': // 1 - 12
- g += m;
- break;
-
- case 'M': // Jan - Dec
- month = month.substr(0, 3);
- case 'F': // January - December
- g += month;
- break;
-
- // day cases
- case 'd': // 01 - 31
- if (d < 10) {
- d = '0' + d;
- }
- case 'j': // 1 - 31
- g += d;
- break;
-
- case 'D': // Sun - Sat
- day = day.substr(0, 3);
- case 'l': // Sunday - Saturday
- g += day;
- break;
-
- case 'S': // st, nd, rd or th (works well with j)
- if (d % 10 == 1 && d != '11') {
- g += 'st';
- }
- else if (d % 10 == 2 && d != '12') {
- g += 'nd';
- }
- else if (d % 10 == 3 && d != '13') {
- g += 'rd';
- }
- else {
- g += 'th';
- }
- break;
-
- default:
- g += c;
- }
- }
- }
- return g; // return format with values replaced
- },
-
- /**
- * navigate: calendar navigation
- *
- * @param cal (obj)
- * @param type (str) m or y for month or year
- * @param n (int) + or - for next or prev
- */
- navigate: function(cal, type, n) {
- switch (type) {
- case 'm': // month
- if ($type(cal.months) == 'array') {
- var i = cal.months.indexOf(cal.month) + n; // index of current month
-
- if (i < 0 || i == cal.months.length) { // out of range
- if (this.options.navigation == 1) { // if type 1 nav we'll need to increment the year
- this.navigate(cal, 'y', n);
- }
-
- i = (i < 0) ? cal.months.length - 1 : 0;
- }
-
- cal.month = cal.months[i];
- }
- else {
- var i = cal.month + n;
-
- if (i < 0 || i == 12) {
- if (this.options.navigation == 1) {
- this.navigate(cal, 'y', n);
- }
-
- i = (i < 0) ? 11 : 0;
- }
-
- cal.month = i;
- }
- break;
-
- case 'y': // year
- if ($type(cal.years) == 'array') {
- var i = cal.years.indexOf(cal.year) + n;
-
- cal.year = cal.years[i];
- }
- else {
- cal.year += n;
- }
- break;
- }
- $extend(cal, this.values(cal));
- if ($type(cal.months) == 'array') { // if the calendar has a months select
- var i = cal.months.indexOf(cal.month); // and make sure the curr months exists for the new year
-
- if (i < 0) {
- cal.month = cal.months[0];
- } // otherwise we'll reset the month
- }
- this.display(cal);
- },
-
- /**
- * read: compiles cal value based on array of inputs passed in
- *
- * @param cal (obj)
- * @returns date (obj) or (null)
- */
- read: function(cal) {
- var arr = [null, null, null];
- cal.els.each(function(el) {
- // returns an array which may contain empty values
- var values = this.unformat(el.get('value'), el.retrieve('format'));
-
- values.each(function(val, i) {
- if ($type(val) == 'number') {
- arr[i] = val;
- }
- });
- }, this);
- // we can update the cals month and year values
- if ($type(arr[0]) == 'number') {
- cal.year = arr[0];
- }
- if ($type(arr[1]) == 'number') {
- cal.month = arr[1];
- }
- var val = null;
- if (arr.every(function(i) {
- return $type(i) == 'number';
- })) { // if valid date
- var last = new Date(arr[0], arr[1] + 1, 0).getDate(); // last day of month
-
- if (arr[2] > last) {
- arr[2] = last;
- } // make sure we stay within the month (ex in case default day of select is 31 and month is feb)
- val = new Date(arr[0], arr[1], arr[2]);
- }
- return (cal.val == val) ? null : val; // if new date matches old return null (same date clicked twice = disable)
- },
-
- /**
- * rebuild: rebuilds days + months selects
- *
- * @param cal (obj)
- */
- rebuild: function(cal) {
- cal.els.each(function(el) {
- /*
- if (el.getTag() == 'select' && el.format.test('^(F|m|M|n)$')) { // special case for months-only select
- if (!cal.options) { cal.options = el.clone(); } // clone a copy of months select
-
- var val = (cal.val) ? cal.val.getMonth() : el.value.toInt();
-
- el.empty(); // initialize select
-
- cal.months.each(function(month) {
- // create an option element
- var option = new Element('option', {
- 'selected': (val == month),
- 'value': this.format(new Date(1, month, 1), el.format);
- }).appendText(day).injectInside(el);
- }, this);
- }
- */
- if (el.get('tag') == 'select' && el.retrieve('format').test('^(d|j)$')) { // special case for days-only select
- var d = this.value(cal);
-
- if (!d) {
- d = el.get('value').toInt();
- } // if the calendar doesn't have a set value, try to use value from select
-
- el.empty(); // initialize select
-
- cal.days.each(function(day) {
- // create an option element
- var option = new Element('option', {
- 'value': ((el.retrieve('format') == 'd' && day < 10) ? '0' + day : day)
- }).appendText(day).inject(el);
- if(d == day){
- option.set('selected','selected');
- }
- }, this);
- }
- }, this);
- },
-
- /**
- * sort: helper function for numerical sorting
- *
- * @param a (int)
- * @param b (int)
- */
- sort: function(a, b) {
- return a - b;
- },
-
- /**
- * toggle: show / hide calendar
- *
- * @param cal (obj)
- */
- toggle: function(cal) {
- document.removeEvent('mousedown', this.fn); // always remove the current mousedown script first
-
- if (cal.visible) { // simply hide curr cal
- cal.visible = false;
- cal.button.removeClass(this.classes.active); // active
- this.fx.start('opacity', 0); //*RAL - Fx.Tween.start([property], from, [to])
- }
- else { // otherwise show (may have to hide others)
- // hide cal on out-of-bounds click
- this.fn = function(e, cal) {
- var event = new Event(e);
- var el = $(event.target);
- var stop = false;
-
- while (el != document.body && el.nodeType == 1) {
- if (el == this.calendar) {
- stop = true;
- }
- this.calendars.each(function(kal) {
- if (kal.button == el || kal.els.contains(el)) {
- stop = true;
- }
- });
-
- if (stop) {
- e.stop();
- return false;
- }
- else {
- el = el.getParent();
- }
- }
-
- this.toggle(cal);
- }.create({
- 'arguments': cal,
- 'bind': this,
- 'event': true
- });
-
- document.addEvent('mousedown', this.fn);
-
- this.calendars.each(function(kal) {
- if (kal == cal) {
- kal.visible = true;
- kal.button.addClass(this.classes.active); // css c-icon-active
- }
- else {
- kal.visible = false;
- kal.button.removeClass(this.classes.active); // css c-icon-active
- }
- }, this);
-
- this.display(cal);
-
- var size = window.getScrollSize();
- var coord = cal.button.getCoordinates();
- var x = coord.right + this.options.tweak.x;
- var y = coord.top + this.options.tweak.y;
-
- // make sure the calendar doesn't open off screen (does't work in safari!?)
- if (x + this.calendar.coord.width > size.x) {
- x -= (x + this.calendar.coord.width - size.x);
- }
- if (y + this.calendar.coord.height > size.y) {
- y -= (y + this.calendar.coord.height - size.y);
- }
-
- this.calendar.setStyles({
- left: x + 'px',
- top: y + 'px'
- });
- // IE6 Iframe Fix
- if (Browser.Engine.trident4) {
- this.iframe.setStyles({
- left: x + 'px',
- top: y + 'px',
- height: this.calendar.coord.height + 'px',
- width: this.calendar.coord.width + 'px'
- });
- }
- this.fx.start('opacity', 1); //*RAL - Fx.Tween.start([property], from, [to])
- }
- },
-
- /**
- * unformat: takes a value from an input and parses the d, m and y elements
- *
- * @param val (string)
- * @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y
- * @returns array
- */
- unformat: function(val, f) {
- f = f.escapeRegExp();
-
- var re = {
- d: '([0-9]{2})',
- j: '([0-9]{1,2})',
- D: '(' + this.options.days.map(function(day) {
- return day.substr(0, 3);
- }).join('|') + ')',
- l: '(' + this.options.days.join('|') + ')',
- S: '(st|nd|rd|th)',
- F: '(' + this.options.months.join('|') + ')',
- m: '([0-9]{2})',
- M: '(' + this.options.months.map(function(month) {
- return month.substr(0, 3);
- }).join('|') + ')',
- N: '([0-9]{1,2})',
- n: '([0-9]{1,2})',
- Y: '([0-9]{4})',
- y: '([0-9]{2})'
- };
-
- var arr = []; // array of indexes
- var g = '';
-
- // convert our format string to regexp
- for (var i = 0; i < f.length; i++) {
- var c = f.charAt(i);
- if (re[c]) {
- arr.push(c);
- g += re[c];
- }
- else {
- g += c;
- }
- }
-
- // match against date
- var matches = val.match('^' + g + '$');
- var dates = new Array(3);
- if (matches) {
- matches = matches.slice(1); // remove first match which is the date
- arr.each(function(c, i) {
- i = matches[i];
-
- switch(c) {
- // year cases
- case 'y':
- i = '19' + i; // 2 digit year assumes 19th century (same as JS)
- case 'Y':
- dates[0] = i.toInt();
- break;
-
- // month cases
- case 'F':
- i = i.substr(0, 3);
- case 'M':
- i = this.options.months.map(function(month) {
- return month.substr(0, 3);
- }).indexOf(i) + 1;
- case 'm':
- case 'n':
- dates[1] = i.toInt() - 1;
- break;
-
- // day cases
- case 'd':
- case 'j':
- dates[2] = i.toInt();
- break;
- }
- }, this);
- }
- return dates;
- },
-
- /**
- * value: returns day value of calendar if set
- *
- * @param cal (obj)
- * @returns day (int) or null
- */
- value: function(cal) {
- var day = null;
- if (cal.val) {
- if (cal.year == cal.val.getFullYear() && cal.month == cal.val.getMonth()) {
- day = cal.val.getDate();
- }
- }
- return day;
- },
-
- /**
- * values: returns the years, months (for curr year) and days (for curr month and year) for the calendar
- *
- * @param cal (obj)
- * @returns obj
- */
- values: function(cal) {
- // make it possible
- this.fireEvent('onValuesStart', this.element);
- var years, months, days;
-
- cal.els.each(function(el) {
- if (el.get('tag') == 'select') {
- if (el.retrieve('format').test('(y|Y)')) { // search for a year select
- years = [];
-
- el.getChildren().each(function(option) { // get options
- var values = this.unformat(option.get('value'), el.retrieve('format'));
-
- if (!years.contains(values[0])) {
- years.push(values[0]);
- } // add to years array
- }, this);
-
- years.sort(this.sort);
- }
-
- if (el.retrieve('format').test('(F|m|M|n|N)')) { // search for a month select
- months = []; // 0 - 11 should be
-
- el.getChildren().each(function(option) { // get options
- var values = this.unformat(option.get('value'), el.retrieve('format'));
-
- if ($type(values[0]) != 'number' || values[0] == cal.year) { // if it's a year / month combo for curr year, or simply a month select
- if (!months.contains(values[1])) {
- months.push(values[1]);
- } // add to months array
- }
- }, this);
-
- months.sort(this.sort);
- }
-
- if (el.retrieve('format').test('(d|j)') && !el.retrieve('format').test('^(d|j)$')) { // search for a day select, but NOT a days only select
- days = []; // 1 - 31
-
- el.getChildren().each(function(option) { // get options
- var values = this.unformat(option.get('value'), el.retrieve('format'));
-
- // in the special case of days we dont want the value if its a days only select
- // otherwise that will screw up the options rebuilding
- // we will take the values if they are exact dates though
- if (values[0] == cal.year && values[1] == cal.month) {
- if (!days.contains(values[2])) {
- days.push(values[2]);
- } // add to days array
- }
- }, this);
- }
- }
- }, this);
-
- // we start with what would be the first and last days were there no restrictions
- var first = 1;
- var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of the month
-
- // if we're in an out of bounds year
- if (cal.year == cal.start.getFullYear()) {
- // in the special case of improved navigation but no months array, we'll need to construct one
- if (months == null && this.options.navigation == 2) {
- months = [];
-
- for (var i = 0; i < 12; i ++) {
- if (i >= cal.start.getMonth()) {
- months.push(i);
- }
- }
- }
-
- // if we're in an out of bounds month
- if (cal.month == cal.start.getMonth()) {
- first = cal.start.getDate(); // first day equals day of bound
- }
- }
- if (cal.year == cal.end.getFullYear()) {
- // in the special case of improved navigation but no months array, we'll need to construct one
- if (months == null && this.options.navigation == 2) {
- months = [];
-
- for (var i = 0; i < 12; i ++) {
- if (i <= cal.end.getMonth()) {
- months.push(i);
- }
- }
- }
-
- if (cal.month == cal.end.getMonth()) {
- last = cal.end.getDate(); // last day equals day of bound
- }
- }
-
- // let's get our invalid days
- var blocked = this.blocked(cal);
-
- // finally we can prepare all the valid days in a neat little array
- if ($type(days) == 'array') { // somewhere there was a days select
- days = days.filter(function(day) {
- if (day >= first && day <= last && !blocked.contains(day)) {
- return day;
- }
- });
- }
- else { // no days select we'll need to construct a valid days array
- days = [];
-
- for (var i = first; i <= last; i++) {
- if (!blocked.contains(i)) {
- days.push(i);
- }
- }
- }
-
- days.sort(this.sort); // sorting our days will give us first and last of month
-
- return {
- 'days': days,
- 'months': months,
- 'years': years
- };
- },
-
- /**
- * write: sets calendars value to form elements
- *
- * @param cal (obj)
- */
- write: function(cal) {
- this.rebuild(cal); // in the case of options, we'll need to make sure we have the correct number of days available
-
- cal.els.each(function(el) { // then we can set the value to the field
- el.set('value',this.format(cal.val, el.retrieve('format')));
- }, this);
- }
-});
\ No newline at end of file
+// Calendar: a Javascript class for Mootools that adds accessible and unobtrusive date pickers to your form elements
+// Calendar RC4, Copyright (c) 2007 Aeron Glemann , MIT Style License.
+// Mootools 1.2 compatibility by Davorin Ĺ ego
+
+var Calendar = new Class({
+
+ Implements: Options,
+
+ options: {
+ blocked: [], // blocked dates
+ classes: [], // ['calendar', 'prev', 'next', 'month', 'year', 'today', 'invalid', 'valid', 'inactive', 'active', 'hover', 'hilite']
+ days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // days of the week starting at sunday
+ direction: 0, // -1 past, 0 past + future, 1 future
+ draggable: true,
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+ navigation: 1, // 0 = no nav; 1 = single nav for month; 2 = dual nav for month and year
+ offset: 0, // first day of the week: 0 = sunday, 1 = monday, etc..
+ onHideStart: Class.empty,
+ onHideComplete: Class.empty,
+ onShowStart: Class.empty,
+ onShowComplete: Class.empty,
+ pad: 1, // padding between multiple calendars
+ tweak: {x: 0, y: 0} // tweak calendar positioning
+ },
+
+ // initialize: calendar constructor
+ // @param obj (obj) a js object containing the form elements and format strings { id: 'format', id: 'format' etc }
+ // @param props (obj) optional properties
+
+ initialize: function(obj, options) {
+ // basic error checking
+ if (!obj) { return false; }
+
+ this.setOptions(options);
+
+ // create our classes array
+ var keys = ['calendar', 'prev', 'next', 'month', 'year', 'today', 'invalid', 'valid', 'inactive', 'active', 'hover', 'hilite'];
+
+ var values = keys.map(function(key, i) {
+ if (this.options.classes[i]) {
+ if (this.options.classes[i].length) { key = this.options.classes[i]; }
+ }
+ return key;
+ }, this);
+
+ this.classes = values.associate(keys);
+
+ // create cal element with css styles required for proper cal functioning
+ this.calendar = new Element('div', {
+ 'styles': { left: '-1000px', opacity: 0, position: 'absolute', top: '-1000px', zIndex: 1000 }
+ }).addClass(this.classes.calendar).injectInside(document.body);
+
+ // iex 6 needs a transparent iframe underneath the calendar in order to not allow select elements to render through
+ if (window.ie6) {
+ this.iframe = new Element('iframe', {
+ 'styles': { left: '-1000px', position: 'absolute', top: '-1000px', zIndex: 999 }
+ }).injectInside(document.body);
+ this.iframe.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
+ }
+
+ // initialize fade method
+ this.fx = new Fx.Tween(this.calendar, {
+ onStart: function() {
+ if (this.calendar.getStyle('opacity') == 0) { // show
+ if (window.ie6) { this.iframe.setStyle('display', 'block'); }
+ this.calendar.setStyle('display', 'block');
+ this.fireEvent('onShowStart', this.element);
+ }
+ else { // hide
+ this.fireEvent('onHideStart', this.element);
+ }
+ }.bind(this),
+ onComplete: function() {
+ if (this.calendar.getStyle('opacity') == 0) { // hidden
+ this.calendar.setStyle('display', 'none');
+ if (window.ie6) { this.iframe.setStyle('display', 'none'); }
+ this.fireEvent('onHideComplete', this.element);
+ }
+ else { // shown
+ this.fireEvent('onShowComplete', this.element);
+ }
+ }.bind(this)
+ });
+
+ // initialize drag method
+ if (window.Drag && this.options.draggable) {
+ this.drag = new Drag.Move(this.calendar, {
+ onDrag: function() {
+ if (window.ie6) { this.iframe.setStyles({ left: this.calendar.style.left, top: this.calendar.style.top }); }
+ }.bind(this)
+ });
+ }
+
+ // create calendars array
+ this.calendars = [];
+
+ var id = 0;
+ var d = new Date(); // today
+
+ d.setDate(d.getDate() + this.options.direction.toInt()); // correct today for directional offset
+
+ for (var i in obj) {
+ var cal = {
+ button: new Element('button', { 'type': 'button' }),
+ el: $(i),
+ els: [],
+ id: id++,
+ month: d.getMonth(),
+ visible: false,
+ year: d.getFullYear()
+ };
+
+ // fix for bad element (naughty, naughty element!)
+ if (!this.element(i, obj[i], cal)) { continue; }
+
+ cal.el.addClass(this.classes.calendar);
+
+ // create cal button
+ cal.button.addClass(this.classes.calendar).addEvent('click', function(cal) { this.toggle(cal); }.pass(cal, this)).injectAfter(cal.el);
+
+ // read in default value
+ cal.val = this.read(cal);
+
+ $extend(cal, this.bounds(cal)); // abs bounds of calendar
+
+ $extend(cal, this.values(cal)); // valid days, months, years
+
+ this.rebuild(cal);
+
+ this.calendars.push(cal); // add to cals array
+ }
+ },
+
+
+ // blocked: returns an array of blocked days for the month / year
+ // @param cal (obj)
+ // @returns blocked days (array)
+
+ blocked: function(cal) {
+ var blocked = [];
+ var offset = new Date(cal.year, cal.month, 1).getDay(); // day of the week (offset)
+ var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month
+
+ this.options.blocked.each(function(date){
+ var values = date.split(' ');
+
+ // preparation
+ for (var i = 0; i <= 3; i++){
+ if (!values[i]){ values[i] = (i == 3) ? '' : '*'; } // make sure blocked date contains values for at least d, m and y
+ values[i] = values[i].contains(',') ? values[i].split(',') : new Array(values[i]); // split multiple values
+ var count = values[i].length - 1;
+ for (var j = count; j >= 0; j--){
+ if (values[i][j].contains('-')){ // a range
+ var val = values[i][j].split('-');
+ for (var k = val[0]; k <= val[1]; k++){
+ if (!values[i].contains(k)){ values[i].push(k + ''); }
+ }
+ values[i].splice(j, 1);
+ }
+ }
+ }
+
+ // execution
+ if (values[2].contains(cal.year + '') || values[2].contains('*')){
+ if (values[1].contains(cal.month + 1 + '') || values[1].contains('*')){
+ values[0].each(function(val){ // if blocked value indicates this month / year
+ if (val > 0){ blocked.push(val.toInt()); } // add date to blocked array
+ });
+
+ if (values[3]){ // optional value for day of week
+ for (var i = 0; i < last; i++){
+ var day = (i + offset) % 7;
+
+ if (values[3].contains(day + '')){
+ blocked.push(i + 1); // add every date that corresponds to the blocked day of the week to the blocked array
+ }
+ }
+ }
+ }
+ }
+ }, this);
+
+ return blocked;
+ },
+
+
+ // bounds: returns the start / end bounds of the calendar
+ // @param cal (obj)
+ // @returns obj
+
+ bounds: function(cal) {
+ // 1. first we assume the calendar has no bounds (or a thousand years in either direction)
+
+ // by default the calendar will accept a millennium in either direction
+ var start = new Date(1000, 0, 1); // jan 1, 1000
+ var end = new Date(2999, 11, 31); // dec 31, 2999
+
+ // 2. but if the cal is one directional we adjust accordingly
+ var date = new Date().getDate() + this.options.direction.toInt();
+
+ if (this.options.direction > 0) {
+ start = new Date();
+ start.setDate(date + this.options.pad * cal.id);
+ }
+
+ if (this.options.direction < 0) {
+ end = new Date();
+ end.setDate(date - this.options.pad * (this.calendars.length - cal.id - 1));
+ }
+
+ // 3. then we can further filter the limits by using the pre-existing values in the selects
+ cal.els.each(function(el) {
+ if (el.get('tag') == 'select') {
+ if (el.format.test('(y|Y)')) { // search for a year select
+ var years = [];
+
+ el.getChildren().each(function(option) { // get options
+ var values = this.unformat(option.value, el.format);
+
+ if (!years.contains(values[0])) { years.push(values[0]); } // add to years array
+ }, this);
+
+ years.sort(this.sort);
+
+ if (years[0] > start.getFullYear()) {
+ d = new Date(years[0], start.getMonth() + 1, 0); // last day of new month
+
+ if (start.getDate() > d.getDate()) { start.setDate(d.getDate()); }
+
+ start.setYear(years[0]);
+ }
+
+ if (years.getLast() < end.getFullYear()) {
+ d = new Date(years.getLast(), end.getMonth() + 1, 0); // last day of new month
+
+ if (end.getDate() > d.getDate()) { end.setDate(d.getDate()); }
+
+ end.setYear(years.getLast());
+ }
+ }
+
+ if (el.format.test('(F|m|M|n)')) { // search for a month select
+ var months_start = [];
+ var months_end = [];
+
+ el.getChildren().each(function(option) { // get options
+ var values = this.unformat(option.value, el.format);
+
+ if ($type(values[0]) != 'number' || values[0] == years[0]) { // if it's a year / month combo for curr year, or simply a month select
+ if (!months_start.contains(values[1])) { months_start.push(values[1]); } // add to months array
+ }
+
+ if ($type(values[0]) != 'number' || values[0] == years.getLast()) { // if it's a year / month combo for curr year, or simply a month select
+ if (!months_end.contains(values[1])) { months_end.push(values[1]); } // add to months array
+ }
+ }, this);
+
+ months_start.sort(this.sort);
+ months_end.sort(this.sort);
+
+ if (months_start[0] > start.getMonth()) {
+ d = new Date(start.getFullYear(), months_start[0] + 1, 0); // last day of new month
+
+ if (start.getDate() > d.getDate()) { start.setDate(d.getDate()); }
+
+ start.setMonth(months_start[0]);
+ }
+
+ if (months_end.getLast() < end.getMonth()) {
+ d = new Date(start.getFullYear(), months_end.getLast() + 1, 0); // last day of new month
+
+ if (end.getDate() > d.getDate()) { end.setDate(d.getDate()); }
+
+ end.setMonth(months_end.getLast());
+ }
+ }
+ }
+ }, this);
+
+ return { 'start': start, 'end': end };
+ },
+
+
+ // caption: returns the caption element with header and navigation
+ // @param cal (obj)
+ // @returns caption (element)
+
+ caption: function(cal) {
+ // start by assuming navigation is allowed
+ var navigation = {
+ prev: { 'month': true, 'year': true },
+ next: { 'month': true, 'year': true }
+ };
+
+ // if we're in an out of bounds year
+ if (cal.year == cal.start.getFullYear()) {
+ navigation.prev.year = false;
+ if (cal.month == cal.start.getMonth() && this.options.navigation == 1) {
+ navigation.prev.month = false;
+ }
+ }
+ if (cal.year == cal.end.getFullYear()) {
+ navigation.next.year = false;
+ if (cal.month == cal.end.getMonth() && this.options.navigation == 1) {
+ navigation.next.month = false;
+ }
+ }
+
+ // special case of improved navigation but months array with only 1 month we can disable all month navigation
+ if ($type(cal.months) == 'array') {
+ if (cal.months.length == 1 && this.options.navigation == 2) {
+ navigation.prev.month = navigation.next.month = false;
+ }
+ }
+
+ var caption = new Element('caption');
+
+ var prev = new Element('a').addClass(this.classes.prev).appendText('\x3c'); // <
+ var next = new Element('a').addClass(this.classes.next).appendText('\x3e'); // >
+
+ if (this.options.navigation == 2) {
+ var month = new Element('span').addClass(this.classes.month).injectInside(caption);
+
+ if (navigation.prev.month) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', -1); }.pass(cal, this)).injectInside(month); }
+
+ month.adopt(new Element('span').appendText(this.options.months[cal.month]));
+
+ if (navigation.next.month) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', 1); }.pass(cal, this)).injectInside(month); }
+
+ var year = new Element('span').addClass(this.classes.year).injectInside(caption);
+
+ if (navigation.prev.year) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'y', -1); }.pass(cal, this)).injectInside(year); }
+
+ year.adopt(new Element('span').appendText(cal.year));
+
+ if (navigation.next.year) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'y', 1); }.pass(cal, this)).injectInside(year); }
+ }
+ else { // 1 or 0
+ if (navigation.prev.month && this.options.navigation) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', -1); }.pass(cal, this)).injectInside(caption); }
+
+ caption.adopt(new Element('span').addClass(this.classes.month).appendText(this.options.months[cal.month]));
+
+ caption.adopt(new Element('span').addClass(this.classes.year).appendText(cal.year));
+
+ if (navigation.next.month && this.options.navigation) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', 1); }.pass(cal, this)).injectInside(caption); }
+
+ }
+
+ return caption;
+ },
+
+
+ // changed: run when a select value is changed
+ // @param cal (obj)
+
+ changed: function(cal) {
+ cal.val = this.read(cal); // update calendar val from inputs
+
+ $extend(cal, this.values(cal)); // update bounds - based on curr month
+
+ this.rebuild(cal); // rebuild days select
+
+ if (!cal.val) { return; } // in case the same date was clicked the cal has no set date we should exit
+
+ if (cal.val.getDate() < cal.days[0]) { cal.val.setDate(cal.days[0]); }
+ if (cal.val.getDate() > cal.days.getLast()) { cal.val.setDate(cal.days.getLast()); }
+
+ cal.els.each(function(el) { // then we can set the value to the field
+ el.value = this.format(cal.val, el.format);
+ }, this);
+
+ this.check(cal); // checks other cals
+
+ this.calendars.each(function(kal) { // update cal graphic if visible
+ if (kal.visible) { this.display(kal); }
+ }, this);
+ },
+
+
+ // check: checks other calendars to make sure no overlapping values
+ // @param cal (obj)
+
+ check: function(cal) {
+ this.calendars.each(function(kal, i) {
+ if (kal.val) { // if calendar has value set
+ var change = false;
+
+ if (i < cal.id) { // preceding calendar
+ var bound = new Date(Date.parse(cal.val));
+
+ bound.setDate(bound.getDate() - (this.options.pad * (cal.id - i)));
+
+ if (bound < kal.val) { change = true; }
+ }
+ if (i > cal.id) { // following calendar
+ var bound = new Date(Date.parse(cal.val));
+
+ bound.setDate(bound.getDate() + (this.options.pad * (i - cal.id)));
+
+ if (bound > kal.val) { change = true; }
+ }
+
+ if (change) {
+ if (kal.start > bound) { bound = kal.start; }
+ if (kal.end < bound) { bound = kal.end; }
+
+ kal.month = bound.getMonth();
+ kal.year = bound.getFullYear();
+
+ $extend(kal, this.values(kal));
+
+ // TODO - IN THE CASE OF SELECT MOVE TO NEAREST VALID VALUE
+ // IN THE CASE OF INPUT DISABLE
+
+ // if new date is not valid better unset cal value
+ // otherwise it would mean incrementally checking to find the nearest valid date which could be months / years away
+ kal.val = kal.days.contains(bound.getDate()) ? bound : null;
+
+ this.write(kal);
+
+ if (kal.visible) { this.display(kal); } // update cal graphic if visible
+ }
+ }
+ else {
+ kal.month = cal.month;
+ kal.year = cal.year;
+ }
+ }, this);
+ },
+
+
+ // clicked: run when a valid day is clicked in the calendar
+ // @param cal (obj)
+
+ clicked: function(td, day, cal) {
+ cal.val = (this.value(cal) == day) ? null : new Date(cal.year, cal.month, day); // set new value - if same then disable
+
+ this.write(cal);
+
+ // ok - in the special case that it's all selects and there's always a date no matter what (at least as far as the form is concerned)
+ // we can't let the calendar undo a date selection - it's just not possible!!
+ if (!cal.val) { cal.val = this.read(cal); }
+
+ if (cal.val) {
+ this.check(cal); // checks other cals
+ this.toggle(cal); // hide cal
+ }
+ else { // remove active class and replace with valid
+ td.addClass(this.classes.valid);
+ td.removeClass(this.classes.active);
+ }
+ },
+
+
+ // display: create calendar element
+ // @param cal (obj)
+
+ display: function(cal) {
+ // 1. header and navigation
+ this.calendar.empty(); // init div
+
+ this.calendar.className = this.classes.calendar + ' ' + this.options.months[cal.month].toLowerCase();
+
+ var div = new Element('div').injectInside(this.calendar); // a wrapper div to help correct browser css problems with the caption element
+
+ var table = new Element('table').injectInside(div).adopt(this.caption(cal));
+
+ // 2. day names
+ var thead = new Element('thead').injectInside(table);
+
+ var tr = new Element('tr').injectInside(thead);
+
+ for (var i = 0; i <= 6; i++) {
+ var th = this.options.days[(i + this.options.offset) % 7];
+
+ tr.adopt(new Element('th', { 'title': th }).appendText(th.substr(0, 1)));
+ }
+
+ // 3. day numbers
+ var tbody = new Element('tbody').injectInside(table);
+ var tr = new Element('tr').injectInside(tbody);
+
+ var d = new Date(cal.year, cal.month, 1);
+ var offset = ((d.getDay() - this.options.offset) + 7) % 7; // day of the week (offset)
+ var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month
+ var prev = new Date(cal.year, cal.month, 0).getDate(); // last day of previous month
+ var active = this.value(cal); // active date (if set and within curr month)
+ var valid = cal.days; // valid days for curr month
+ var inactive = []; // active dates set by other calendars
+ var hilited = [];
+ this.calendars.each(function(kal, i) {
+ if (kal != cal && kal.val) {
+ if (cal.year == kal.val.getFullYear() && cal.month == kal.val.getMonth()) { inactive.push(kal.val.getDate()); }
+
+ if (cal.val) {
+ for (var day = 1; day <= last; day++) {
+ d.setDate(day);
+
+ if ((i < cal.id && d > kal.val && d < cal.val) || (i > cal.id && d > cal.val && d < kal.val)) {
+ if (!hilited.contains(day)) { hilited.push(day); }
+ }
+ }
+ }
+ }
+ }, this);
+ var d = new Date();
+ var today = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); // today obv
+
+ for (var i = 1; i < 43; i++) { // 1 to 42 (6 x 7 or 6 weeks)
+ if ((i - 1) % 7 == 0) { tr = new Element('tr').injectInside(tbody); } // each week is it's own table row
+
+ var td = new Element('td').injectInside(tr);
+
+ var day = i - offset;
+ var date = new Date(cal.year, cal.month, day);
+
+ var cls = '';
+
+ if (day === active) { cls = this.classes.active; } // active
+ else if (inactive.contains(day)) { cls = this.classes.inactive; } // inactive
+ else if (valid.contains(day)) { cls = this.classes.valid; } // valid
+ else if (day >= 1 && day <= last) { cls = this.classes.invalid; } // invalid
+
+ if (date.getTime() == today) { cls = cls + ' ' + this.classes.today; } // adds class for today
+
+ if (hilited.contains(day)) { cls = cls + ' ' + this.classes.hilite; } // adds class if hilited
+
+ td.addClass(cls);
+
+ if (valid.contains(day)) { // if it's a valid - clickable - day we add interaction
+ td.setProperty('title', this.format(date, 'D M jS Y'));
+
+ td.addEvents({
+ 'click': function(td, day, cal) {
+ this.clicked(td, day, cal);
+ }.pass([td, day, cal], this),
+ 'mouseover': function(td, cls) {
+ td.addClass(cls);
+ }.pass([td, this.classes.hover]),
+ 'mouseout': function(td, cls) {
+ td.removeClass(cls);
+ }.pass([td, this.classes.hover])
+ });
+ }
+
+ // pad calendar with last days of prev month and first days of next month
+ if (day < 1) { day = prev + day; }
+ else if (day > last) { day = day - last; }
+
+ td.appendText(day);
+ }
+ },
+
+
+ // element: helper function
+ // @param el (string) element id
+ // @param f (string) format string
+ // @param cal (obj)
+
+ element: function(el, f, cal) {
+ if ($type(f) == 'object') { // in the case of multiple inputs per calendar
+ for (var i in f) {
+ if (!this.element(i, f[i], cal)) { return false; }
+ }
+
+ return true;
+ }
+
+ el = $(el);
+
+ if (!el) { return false; }
+
+ el.format = f;
+
+ if (el.get('tag') == 'select') { // select elements allow the user to manually set the date via select option
+ el.addEvent('change', function(cal) { this.changed(cal); }.pass(cal, this));
+ }
+ else { // input (type text) elements restrict the user to only setting the date via the calendar
+ el.readOnly = true;
+ el.addEvent('focus', function(cal) { this.toggle(cal); }.pass(cal, this));
+ }
+
+ cal.els.push(el);
+
+ return true;
+ },
+
+
+ // format: formats a date object according to passed in instructions
+ // @param date (obj)
+ // @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y
+ // @returns string
+
+ format: function(date, format) {
+ var str = '';
+
+ if (date) {
+ var j = date.getDate(); // 1 - 31
+ var w = date.getDay(); // 0 - 6
+ var l = this.options.days[w]; // Sunday - Saturday
+ var n = date.getMonth() + 1; // 1 - 12
+ var f = this.options.months[n - 1]; // January - December
+ var y = date.getFullYear() + ''; // 19xx - 20xx
+
+ for (var i = 0, len = format.length; i < len; i++) {
+ var cha = format.charAt(i); // format char
+
+ switch(cha) {
+ // year cases
+ case 'y': // xx - xx
+ y = y.substr(2);
+ case 'Y': // 19xx - 20xx
+ str += y;
+ break;
+
+ // month cases
+ case 'm': // 01 - 12
+ if (n < 10) { n = '0' + n; }
+ case 'n': // 1 - 12
+ str += n;
+ break;
+
+ case 'M': // Jan - Dec
+ f = f.substr(0, 3);
+ case 'F': // January - December
+ str += f;
+ break;
+
+ // day cases
+ case 'd': // 01 - 31
+ if (j < 10) { j = '0' + j; }
+ case 'j': // 1 - 31
+ str += j;
+ break;
+
+ case 'D': // Sun - Sat
+ l = l.substr(0, 3);
+ case 'l': // Sunday - Saturday
+ str += l;
+ break;
+
+ case 'N': // 1 - 7
+ w += 1;
+ case 'w': // 0 - 6
+ str += w;
+ break;
+
+ case 'S': // st, nd, rd or th (works well with j)
+ if (j % 10 == 1 && j != '11') { str += 'st'; }
+ else if (j % 10 == 2 && j != '12') { str += 'nd'; }
+ else if (j % 10 == 3 && j != '13') { str += 'rd'; }
+ else { str += 'th'; }
+ break;
+
+ default:
+ str += cha;
+ }
+ }
+ }
+
+ return str; // return format with values replaced
+ },
+
+
+ // navigate: calendar navigation
+ // @param cal (obj)
+ // @param type (str) m or y for month or year
+ // @param n (int) + or - for next or prev
+
+ navigate: function(cal, type, n) {
+ switch (type) {
+ case 'm': // month
+ if ($type(cal.months) == 'array') {
+ var i = cal.months.indexOf(cal.month) + n; // index of current month
+
+ if (i < 0 || i == cal.months.length) { // out of range
+ if (this.options.navigation == 1) { // if type 1 nav we'll need to increment the year
+ this.navigate(cal, 'y', n);
+ }
+
+ i = (i < 0) ? cal.months.length - 1 : 0;
+ }
+
+ cal.month = cal.months[i];
+ }
+ else {
+ var i = cal.month + n;
+
+ if (i < 0 || i == 12) {
+ if (this.options.navigation == 1) {
+ this.navigate(cal, 'y', n);
+ }
+
+ i = (i < 0) ? 11 : 0;
+ }
+
+ cal.month = i;
+ }
+ break;
+
+ case 'y': // year
+ if ($type(cal.years) == 'array') {
+ var i = cal.years.indexOf(cal.year) + n;
+
+ cal.year = cal.years[i];
+ }
+ else {
+ cal.year += n;
+ }
+ break;
+ }
+
+ $extend(cal, this.values(cal));
+
+ if ($type(cal.months) == 'array') { // if the calendar has a months select
+ var i = cal.months.indexOf(cal.month); // and make sure the curr months exists for the new year
+
+ if (i < 0) { cal.month = cal.months[0]; } // otherwise we'll reset the month
+ }
+
+
+ this.display(cal);
+ },
+
+
+ // read: compiles cal value based on array of inputs passed in
+ // @param cal (obj)
+ // @returns date (obj) or (null)
+
+ read: function(cal) {
+ var arr = [null, null, null];
+
+ cal.els.each(function(el) {
+ // returns an array which may contain empty values
+ var values = this.unformat(el.value, el.format);
+
+ values.each(function(val, i) {
+ if ($type(val) == 'number') { arr[i] = val; }
+ });
+ }, this);
+
+ // we can update the cals month and year values
+ if ($type(arr[0]) == 'number') { cal.year = arr[0]; }
+ if ($type(arr[1]) == 'number') { cal.month = arr[1]; }
+
+ var val = null;
+
+ if (arr.every(function(i) { return $type(i) == 'number'; })) { // if valid date
+ var last = new Date(arr[0], arr[1] + 1, 0).getDate(); // last day of month
+
+ if (arr[2] > last) { arr[2] = last; } // make sure we stay within the month (ex in case default day of select is 31 and month is feb)
+
+ val = new Date(arr[0], arr[1], arr[2]);
+ }
+
+ return (cal.val == val) ? null : val; // if new date matches old return null (same date clicked twice = disable)
+ },
+
+
+ // rebuild: rebuilds days + months selects
+ // @param cal (obj)
+
+ rebuild: function(cal) {
+ cal.els.each(function(el) {
+ /*
+ if (el.get('tag') == 'select' && el.format.test('^(F|m|M|n)$')) { // special case for months-only select
+ if (!cal.options) { cal.options = el.clone(); } // clone a copy of months select
+
+ var val = (cal.val) ? cal.val.getMonth() : el.value.toInt();
+
+ el.empty(); // initialize select
+
+ cal.months.each(function(month) {
+ // create an option element
+ var option = new Element('option', {
+ 'selected': (val == month),
+ 'value': this.format(new Date(1, month, 1), el.format);
+ }).appendText(day).injectInside(el);
+ }, this);
+ }
+ */
+
+ if (el.get('tag') == 'select' && el.format.test('^(d|j)$')) { // special case for days-only select
+ var d = this.value(cal);
+
+ if (!d) { d = el.value.toInt(); } // if the calendar doesn't have a set value, try to use value from select
+
+ el.empty(); // initialize select
+
+ cal.days.each(function(day) {
+ // create an option element
+ var option = new Element('option', {
+ 'selected': (d == day),
+ 'value': ((el.format == 'd' && day < 10) ? '0' + day : day)
+ }).appendText(day).injectInside(el);
+ }, this);
+ }
+ }, this);
+ },
+
+
+ // sort: helper function for numerical sorting
+
+ sort: function(a, b) {
+ return a - b;
+ },
+
+
+ // toggle: show / hide calendar
+ // @param cal (obj)
+
+ toggle: function(cal) {
+ document.removeEvent('mousedown', this.fn); // always remove the current mousedown script first
+
+ if (cal.visible) { // simply hide curr cal
+ cal.visible = false;
+ cal.button.removeClass(this.classes.active); // active
+
+ this.fx.start('opacity', 1, 0);
+ }
+ else { // otherwise show (may have to hide others)
+ // hide cal on out-of-bounds click
+ this.fn = function(e, cal) {
+ var e = new Event(e);
+
+ var el = e.target;
+
+ var stop = false;
+
+ while (el != document.body && el.nodeType == 1) {
+ if (el == this.calendar) { stop = true; }
+ this.calendars.each(function(kal) {
+ if (kal.button == el || kal.els.contains(el)) { stop = true; }
+ });
+
+ if (stop) {
+ e.stop();
+ return false;
+ }
+ else { el = el.parentNode; }
+ }
+
+ this.toggle(cal);
+ }.create({ 'arguments': cal, 'bind': this, 'event': true });
+
+ document.addEvent('mousedown', this.fn);
+
+ this.calendars.each(function(kal) {
+ if (kal == cal) {
+ kal.visible = true;
+ kal.button.addClass(this.classes.active); // css c-icon-active
+ }
+ else {
+ kal.visible = false;
+ kal.button.removeClass(this.classes.active); // css c-icon-active
+ }
+ }, this);
+
+ var size = window.getScrollSize();
+
+ var coord = cal.button.getCoordinates();
+
+ var x = coord.right + this.options.tweak.x;
+ var y = coord.top + this.options.tweak.y;
+
+ // make sure the calendar doesn't open off screen
+ if (!this.calendar.coord) { this.calendar.coord = this.calendar.getCoordinates(); }
+
+ if (x + this.calendar.coord.width > size.x) { x -= (x + this.calendar.coord.width - size.x); }
+ if (y + this.calendar.coord.height > size.y) { y -= (y + this.calendar.coord.height - size.y); }
+
+ this.calendar.setStyles({ left: x + 'px', top: y + 'px' });
+
+ if (window.ie6) {
+ this.iframe.setStyles({ height: this.calendar.coord.height + 'px', left: x + 'px', top: y + 'px', width: this.calendar.coord.width + 'px' });
+ }
+
+ this.display(cal);
+
+ this.fx.start('opacity', 0, 1);
+ }
+ },
+
+
+ // unformat: takes a value from an input and parses the d, m and y elements
+ // @param val (string)
+ // @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y
+ // @returns array
+
+ unformat: function(val, f) {
+ f = f.escapeRegExp();
+
+ var re = {
+ d: '([0-9]{2})',
+ j: '([0-9]{1,2})',
+ D: '(' + this.options.days.map(function(day) { return day.substr(0, 3); }).join('|') + ')',
+ l: '(' + this.options.days.join('|') + ')',
+ S: '(st|nd|rd|th)',
+ F: '(' + this.options.months.join('|') + ')',
+ m: '([0-9]{2})',
+ M: '(' + this.options.months.map(function(month) { return month.substr(0, 3); }).join('|') + ')',
+ n: '([0-9]{1,2})',
+ Y: '([0-9]{4})',
+ y: '([0-9]{2})'
+ }
+
+ var arr = []; // array of indexes
+
+ var g = '';
+
+ // convert our format string to regexp
+ for (var i = 0; i < f.length; i++) {
+ var c = f.charAt(i);
+
+ if (re[c]) {
+ arr.push(c);
+
+ g += re[c];
+ }
+ else {
+ g += c;
+ }
+ }
+
+ // match against date
+ var matches = val.match('^' + g + '$');
+
+ var dates = new Array(3);
+
+ if (matches) {
+ matches = matches.slice(1); // remove first match which is the date
+
+ arr.each(function(c, i) {
+ i = matches[i];
+
+ switch(c) {
+ // year cases
+ case 'y':
+ i = '19' + i; // 2 digit year assumes 19th century (same as JS)
+ case 'Y':
+ dates[0] = i.toInt();
+ break;
+
+ // month cases
+ case 'F':
+ i = i.substr(0, 3);
+ case 'M':
+ i = this.options.months.map(function(month) { return month.substr(0, 3); }).indexOf(i) + 1;
+ case 'm':
+ case 'n':
+ dates[1] = i.toInt() - 1;
+ break;
+
+ // day cases
+ case 'd':
+ case 'j':
+ dates[2] = i.toInt();
+ break;
+ }
+ }, this);
+ }
+
+ return dates;
+ },
+
+
+ // value: returns day value of calendar if set
+ // @param cal (obj)
+ // @returns day (int) or null
+
+ value: function(cal) {
+ var day = null;
+
+ if (cal.val) {
+ if (cal.year == cal.val.getFullYear() && cal.month == cal.val.getMonth()) { day = cal.val.getDate(); }
+ }
+
+ return day;
+ },
+
+
+ // values: returns the years, months (for curr year) and days (for curr month and year) for the calendar
+ // @param cal (obj)
+ // @returns obj
+
+ values: function(cal) {
+ var years, months, days;
+
+ cal.els.each(function(el) {
+ if (el.get('tag') == 'select') {
+ if (el.format.test('(y|Y)')) { // search for a year select
+ years = [];
+
+ el.getChildren().each(function(option) { // get options
+ var values = this.unformat(option.value, el.format);
+
+ if (!years.contains(values[0])) { years.push(values[0]); } // add to years array
+ }, this);
+
+ years.sort(this.sort);
+ }
+
+ if (el.format.test('(F|m|M|n)')) { // search for a month select
+ months = []; // 0 - 11 should be
+
+ el.getChildren().each(function(option) { // get options
+ var values = this.unformat(option.value, el.format);
+
+ if ($type(values[0]) != 'number' || values[0] == cal.year) { // if it's a year / month combo for curr year, or simply a month select
+ if (!months.contains(values[1])) { months.push(values[1]); } // add to months array
+ }
+ }, this);
+
+ months.sort(this.sort);
+ }
+
+ if (el.format.test('(d|j)') && !el.format.test('^(d|j)$')) { // search for a day select, but NOT a days only select
+ days = []; // 1 - 31
+
+ el.getChildren().each(function(option) { // get options
+ var values = this.unformat(option.value, el.format);
+
+ // in the special case of days we dont want the value if its a days only select
+ // otherwise that will screw up the options rebuilding
+ // we will take the values if they are exact dates though
+ if (values[0] == cal.year && values[1] == cal.month) {
+ if (!days.contains(values[2])) { days.push(values[2]); } // add to days array
+ }
+ }, this);
+ }
+ }
+ }, this);
+
+ // we start with what would be the first and last days were there no restrictions
+ var first = 1;
+ var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of the month
+
+ // if we're in an out of bounds year
+ if (cal.year == cal.start.getFullYear()) {
+ // in the special case of improved navigation but no months array, we'll need to construct one
+ if (months == null && this.options.navigation == 2) {
+ months = [];
+
+ for (var i = 0; i < 12; i ++) {
+ if (i >= cal.start.getMonth()) { months.push(i); }
+ }
+ }
+
+ // if we're in an out of bounds month
+ if (cal.month == cal.start.getMonth()) {
+ first = cal.start.getDate(); // first day equals day of bound
+ }
+ }
+ if (cal.year == cal.end.getFullYear()) {
+ // in the special case of improved navigation but no months array, we'll need to construct one
+ if (months == null && this.options.navigation == 2) {
+ months = [];
+
+ for (var i = 0; i < 12; i ++) {
+ if (i <= cal.end.getMonth()) { months.push(i); }
+ }
+ }
+
+ if (cal.month == cal.end.getMonth()) {
+ last = cal.end.getDate(); // last day equals day of bound
+ }
+ }
+
+ // let's get our invalid days
+ var blocked = this.blocked(cal);
+
+ // finally we can prepare all the valid days in a neat little array
+ if ($type(days) == 'array') { // somewhere there was a days select
+ days = days.filter(function(day) {
+ if (day >= first && day <= last && !blocked.contains(day)) { return day; }
+ });
+ }
+ else { // no days select we'll need to construct a valid days array
+ days = [];
+
+ for (var i = first; i <= last; i++) {
+ if (!blocked.contains(i)) { days.push(i); }
+ }
+ }
+
+ days.sort(this.sort); // sorting our days will give us first and last of month
+
+ return { 'days': days, 'months': months, 'years': years };
+ },
+
+
+ // write: sets calendars value to form elements
+ // @param cal (obj)
+
+ write: function(cal) {
+ this.rebuild(cal); // in the case of options, we'll need to make sure we have the correct number of days available
+
+ cal.els.each(function(el) { // then we can set the value to the field
+ el.value = this.format(cal.val, el.format);
+ }, this);
+ }
+});
+
+Calendar.implement(new Events, new Options);
\ No newline at end of file
diff --git a/static/js/form.js b/static/js/form.js
index 9a6fee9f9..36d391f24 100644
--- a/static/js/form.js
+++ b/static/js/form.js
@@ -1,11 +1,14 @@
window.addEvent("domready", function() {
- sqbsubmit = function(obj, dest, id, callback) {
+ sqbsubmit = function(obj, dest, id, callback, onerror) {
obj.set('send', {
onSuccess: function(responseText) {
var decode = JSON.decode(responseText);
if (decode.status == "ko") {
$(dest).set("html", decode.content);
$(id).addEvent("submit", callback);
+ if ($defined(onerror)) {
+ onerror();
+ }
} else {
if (decode.url != "")
parent.location.href = decode.url;
@@ -16,9 +19,9 @@ window.addEvent("domready", function() {
obj.send();
}
- generic_submit = function(obj, event, id, callback) {
+ generic_submit = function(obj, event, id, callback, onerror) {
event.stop();
- sqbsubmit(obj, "content", id, callback);
+ sqbsubmit(obj, "content", id, callback, onerror);
}
domain_submit = function(event) {
generic_submit(this, event, "domform", domain_submit);
@@ -35,8 +38,14 @@ window.addEvent("domready", function() {
chpassword_submit = function(event) {
generic_submit(this, event, "chpwdform", chpassword_submit);
}
+
+ arm_onerror = function() {
+ new Calendar({id_untildate: "Y-m-d"}, {
+ tweak: {x: -100, y:-100}
+ });
+ }
arm_submit = function(event) {
- generic_submit(this, event, "armform", arm_submit);
+ generic_submit(this, event, "armform", arm_submit, arm_onerror);
}
if ($("domform"))
diff --git a/static/js/preferences.js b/static/js/preferences.js
index 6f948c92a..c82a1a837 100644
--- a/static/js/preferences.js
+++ b/static/js/preferences.js
@@ -11,8 +11,4 @@ window.addEvent('domready', function() {
}
}, $('accordion'));
- new DatePicker("#untildate", {
- pickerClass: "datepicker_vista",
- allowEmpty: true
- });
});