public
Description: Click-draggable. Range-makeable. A better calendar.
Homepage: http://stephencelis.com/projects/timeframe
Clone URL: git://github.com/stephencelis/timeframe.git
Search Repo:
Click here to lend your support to: timeframe and make a donation at www.pledgie.com !
Remove whitespace. Fix Timeframe.prototype.reset().
stephencelis (author)
Sat May 10 09:11:56 -0700 2008
commit  a465a8bee1fc2e8239e68783c2ea81b086c2465c
tree    787a06b1e9e58343d269daeffc86ce5926f3381d
parent  0f0468025ebe4f9e8bd85444f4a6cb4f02509d03
...
63
64
65
66
 
67
68
69
...
63
64
65
 
66
67
68
69
0
@@ -63,7 +63,7 @@
0
     </h2>
0
     <ul>
0
       <li>A little bit slow in IE. Optimizations forthcoming.</li>
0
- <li>Try <a href="http://apple.com/safari">Safari</a> for the best experience.
0
+ <li>Try <a href="http://apple.com/safari">Safari</a> for the best experience.</li>
0
     </ul>
0
     <div id="example">
0
       <h4>Please select a date range below:</h4>
...
19
20
21
22
 
23
24
25
 
26
27
28
29
 
30
31
32
33
34
 
35
36
37
38
39
40
41
42
...
39
40
41
42
 
43
44
45
46
47
48
 
 
49
50
51
 
52
53
54
 
55
56
 
57
58
59
60
 
61
62
63
...
67
68
69
70
 
71
72
73
74
75
76
77
...
78
79
80
81
 
82
83
84
85
86
87
 
88
89
90
91
92
93
 
94
95
96
97
 
98
99
100
101
 
102
103
104
...
107
108
109
110
 
111
112
113
114
115
...
117
118
119
120
 
121
122
123
124
 
125
126
127
128
129
 
130
131
132
...
145
146
147
148
 
149
150
151
152
...
163
164
165
166
 
167
168
 
169
170
171
172
...
175
176
177
178
 
179
180
181
182
 
183
184
185
...
187
188
189
190
 
191
192
193
194
...
195
196
197
198
 
199
200
 
201
202
203
...
213
214
215
216
 
217
218
219
220
...
225
226
227
228
 
229
 
230
231
232
233
234
...
236
237
238
239
 
240
241
 
242
243
244
245
246
247
248
 
249
250
251
...
257
258
259
260
 
261
262
263
264
...
271
272
273
274
 
275
276
277
278
279
280
281
 
282
283
284
...
291
292
293
294
 
295
296
297
...
304
305
306
307
 
308
309
310
...
314
315
316
317
 
318
319
320
...
326
327
328
329
 
330
331
332
...
340
341
342
343
 
344
345
346
347
...
355
356
357
358
 
359
360
361
362
 
363
364
365
...
397
398
399
400
 
401
402
403
...
418
419
420
421
 
422
423
424
...
19
20
21
 
22
23
24
 
25
26
27
28
 
29
30
31
32
33
 
34
35
36
37
38
39
40
41
42
...
39
40
41
 
42
43
44
45
46
 
 
47
48
49
50
 
51
52
53
 
54
55
 
56
57
58
59
 
60
61
62
63
...
67
68
69
 
70
71
72
73
74
75
76
77
...
78
79
80
 
81
82
83
84
85
86
 
87
88
89
90
91
92
 
93
94
95
96
 
97
98
99
100
 
101
102
103
104
...
107
108
109
 
110
111
112
113
114
115
...
117
118
119
 
120
121
122
123
 
124
125
126
127
128
 
129
130
131
132
...
145
146
147
 
148
149
150
151
152
...
163
164
165
 
166
167
 
168
169
170
171
172
...
175
176
177
 
178
179
180
181
 
182
183
184
185
...
187
188
189
 
190
191
192
193
194
...
195
196
197
 
198
199
 
200
201
202
203
...
213
214
215
 
216
217
218
219
220
...
225
226
227
 
228
229
230
231
232
233
234
235
...
237
238
239
 
240
241
 
242
243
244
245
246
247
248
 
249
250
251
252
...
258
259
260
 
261
262
263
264
265
...
272
273
274
 
275
276
277
278
279
280
281
 
282
283
284
285
...
292
293
294
 
295
296
297
298
...
305
306
307
 
308
309
310
311
...
315
316
317
 
318
319
320
321
...
327
328
329
 
330
331
332
333
...
341
342
343
 
344
345
346
347
348
...
356
357
358
 
359
360
361
362
 
363
364
365
366
...
398
399
400
 
401
402
403
404
...
419
420
421
 
422
423
424
425
0
@@ -19,19 +19,19 @@
0
 
0
 var Timeframe = Class.create({
0
   Version: '0.2',
0
-
0
+
0
   initialize: function(element, options) {
0
     Timeframes.push(this);
0
-
0
+
0
     this.element = $(element);
0
     this.options = $H({ months: 2 }).merge(options || {});;
0
     this.months = this.options.get('months');
0
-
0
+
0
     this.weekdayNames = Locale.get('dayNames');
0
     this.monthNames = Locale.get('monthNames');
0
     this.format = this.options.get('format') || Locale.get('format');
0
     this.weekOffset = this.options.get('weekOffset') || Locale.get('weekOffset');
0
-
0
+
0
     this.buttons = $H({
0
       previous: $H({ label: '&larr;', element: $(this.options.get('previousButton')) }),
0
       today: $H({ label: 'T', element: $(this.options.get('todayButton')) }),
0
0
0
0
0
0
@@ -39,25 +39,25 @@
0
       next: $H({ label: '&rarr;', element: $(this.options.get('nextButton')) })
0
     })
0
     this.fields = $H({ start: $(this.options.get('startField')), end: $(this.options.get('endField')) });
0
-
0
+
0
     this.range = $H({});
0
     this._buildButtons()._buildFields();
0
     this.earliest = Date.parseToObject(this.options.get('earliest'));
0
     this.latest = Date.parseToObject(this.options.get('latest'));
0
-
0
- this.calendars = [];
0
+
0
+ this.calendars = [];
0
     this.element.insert(new Element('div', { id: 'calendars_container' }));
0
     this.months.times(function(month) { this.createCalendar(month) }.bind(this));
0
-
0
+
0
     this.register().populate().refreshRange();
0
   },
0
-
0
+
0
   // Scaffolding
0
-
0
+
0
   createCalendar: function() {
0
     var calendar = new Element('table', { id: 'calendar_' + this.calendars.length, border: 0, cellspacing: 0, cellpadding: 5 });
0
     calendar.insert(new Element('caption'));
0
-
0
+
0
     var head = new Element('thead');
0
     var row = new Element('tr');
0
     this.weekdayNames.length.times(function(column) {
0
@@ -67,7 +67,7 @@
0
     }.bind(this));
0
     head.insert(row);
0
     calendar.insert(head);
0
-
0
+
0
     var body = new Element('tbody');
0
     (6).times(function(rowNumber) {
0
       var row = new Element('tr');
0
0
0
0
0
@@ -78,27 +78,27 @@
0
       body.insert(row);
0
     }.bind(this));
0
     calendar.insert(body);
0
-
0
+
0
     this.element.down('div#calendars_container').insert(calendar);
0
     this.calendars.push(calendar);
0
     this.months = this.calendars.length;
0
     return this;
0
   },
0
-
0
+
0
   destroyCalendar: function() {
0
     this.calendars.pop().remove();
0
     this.months = this.calendars.length;
0
     return this;
0
   },
0
-
0
+
0
   populate: function() {
0
     var month = this.date.neutral();
0
     month.setDate(1);
0
- this.months.times(function(n) {
0
+ this.months.times(function(n) {
0
       var calendar = $('calendar_' + n);
0
       var caption = calendar.select('caption').first();
0
       caption.update(this.monthNames[month.getMonth()] + ' ' + month.getFullYear());
0
-
0
+
0
       var iterator = new Date(month);
0
       var offset = (iterator.getDay() - this.weekOffset) % 7;
0
       var inactive = offset > 0 ? 'pre beyond' : false;
0
@@ -107,7 +107,7 @@
0
         iterator.setDate(iterator.getDate() - 7);
0
         if(iterator.getDate() > 1) inactive = 'pre beyond';
0
       }
0
-
0
+
0
       calendar.select('td').each(function(day) {
0
         day.date = new Date(iterator); // Is this expensive (we unload these later)? We could store the epoch time instead.
0
         day.update(day.date.getDate()).writeAttribute('class', inactive || 'active');
0
0
0
@@ -117,16 +117,16 @@
0
           day.addClassName('selectable');
0
         if(iterator.toString() === new Date().neutral().toString()) day.addClassName('today');
0
         day.baseClass = day.readAttribute('class');
0
-
0
+
0
         iterator.setDate(iterator.getDate() + 1);
0
         if(iterator.getDate() == 1) inactive = inactive ? false : 'post beyond';
0
       }.bind(this));
0
-
0
+
0
       month.setMonth(month.getMonth() + 1);
0
     }.bind(this));
0
     return this;
0
   },
0
-
0
+
0
   _buildButtons: function() {
0
     var buttonList = new Element('ul', { id: 'timeframe_menu' });
0
     this.buttons.each(function(pair) {
0
@@ -145,7 +145,7 @@
0
     this.clearButton = new Element('span', { className: 'clear' }).update(new Element('span').update('X'));
0
     return this;
0
   },
0
-
0
+
0
   _buildFields: function() {
0
     var fieldset = new Element('div', { id: 'timeframe_fields' });
0
     this.fields.each(function(pair) {
0
0
@@ -163,9 +163,9 @@
0
     this.parseField('start').refreshField('start').parseField('end').refreshField('end').initDate = new Date(this.date);
0
     return this;
0
   },
0
-
0
+
0
   // Event registration
0
-
0
+
0
   register: function() {
0
     document.observe('click', this.eventClick.bind(this));
0
     document.observe('mousedown', this.eventMouseDown.bind(this));
0
0
@@ -175,11 +175,11 @@
0
     if(Prototype.Browser.Opera) document.observe('mousemove', this.handleMousemove.bind(this));
0
     return this._registerFieldObserver('start')._registerFieldObserver('end')._disableTextSelection();
0
   },
0
-
0
+
0
   unregister: function() {
0
     this.element.select('td').each(function(day) { day.date = day.baseClass = null; });
0
   },
0
-
0
+
0
   _registerFieldObserver: function(fieldName) {
0
     var field = this.fields.get(fieldName);
0
     field.observe('focus', function() { field.hasFocus = true; this.parseField(fieldName, true); }.bind(this));
0
@@ -187,7 +187,7 @@
0
     new Form.Element.Observer(field, 0.2, function(element, value) { if(element.hasFocus) this.parseField(fieldName, true); }.bind(this));
0
     return this;
0
   },
0
-
0
+
0
   _disableTextSelection: function() {
0
     this.element.onselectstart = function() { return false; };
0
     this.element.unselectable = 'on';
0
0
@@ -195,9 +195,9 @@
0
     this.element.style.cursor = 'default';
0
     return this;
0
   },
0
-
0
+
0
   // Fields
0
-
0
+
0
   parseField: function(fieldName, populate) {
0
     var field = this.fields.get(fieldName);
0
     var date = Date.parseToObject(this.fields.get(fieldName).value);
0
@@ -213,7 +213,7 @@
0
     this.refreshRange();
0
     return this;
0
   },
0
-
0
+
0
   refreshField: function(fieldName) {
0
     var field = this.fields.get(fieldName);
0
     var initValue = field.value;
0
0
@@ -225,8 +225,9 @@
0
     field.hasFocus = false;
0
     return this;
0
   },
0
-
0
+
0
   validateField: function(fieldName, date) {
0
+ if(!date) return;
0
     var error;
0
     if((this.earliest && date < this.earliest) || (this.latest && date > this.latest))
0
       error = 'hard';
0
0
0
@@ -236,16 +237,16 @@
0
       error = 'soft';
0
     return error;
0
   },
0
-
0
+
0
   // Event handling
0
-
0
+
0
   eventClick: function(event) {
0
     if(!event.element().ancestors) return;
0
     var el;
0
     if(el = event.findElement('a.timeframe_button'))
0
       this.handleButtonClick(event, el);
0
   },
0
-
0
+
0
   eventMouseDown: function(event) {
0
     if(!event.element().ancestors) return;
0
     var el, em;
0
@@ -257,7 +258,7 @@
0
       this.handleDateClick(el);
0
     else return;
0
   },
0
-
0
+
0
   handleButtonClick: function(event, element) {
0
     var el;
0
     var movement = this.months > 1 ? this.months - 1 : 1;
0
0
@@ -271,14 +272,14 @@
0
       this.reset();
0
     this.populate().refreshRange();
0
   },
0
-
0
+
0
   reset: function() {
0
     this.fields.get('start').value = this.fields.get('start').defaultValue || '';
0
     this.fields.get('end').value = this.fields.get('end').defaultValue || '';
0
     this.date = new Date(this.initDate);
0
     this.parseField('start').parseField('end');
0
   },
0
-
0
+
0
   handleDateClick: function(element, couldClear) {
0
     this.mousedown = this.dragging = true;
0
     if(this.stuck)
0
@@ -291,7 +292,7 @@
0
     }
0
     this.getPoint(element.date);
0
   },
0
-
0
+
0
   getPoint: function(date) {
0
     if(this.range.get('start') && this.range.get('start').toString() == date && this.range.get('end'))
0
       this.startdrag = this.range.get('end');
0
@@ -304,7 +305,7 @@
0
     }
0
     this.refreshRange();
0
   },
0
-
0
+
0
   eventMouseOver: function(event) {
0
     var el;
0
     if(!this.dragging)
0
@@ -314,7 +315,7 @@
0
       this.extendRange(el.date);
0
     else this.toggleClearButton(event);
0
   },
0
-
0
+
0
   toggleClearButton: function(event) {
0
     var el;
0
     if(event.element().ancestors && event.findElement('td.selected')) {
0
@@ -326,7 +327,7 @@
0
       this.clearButton.hide();
0
     }
0
   },
0
-
0
+
0
   extendRange: function(date) {
0
     this.clearButton.hide();
0
     if(date > this.startdrag) {
0
@@ -340,7 +341,7 @@
0
     }
0
     this.refreshRange();
0
   },
0
-
0
+
0
   eventMouseUp: function(event) {
0
     if(!this.dragging) return;
0
     if(!this.stuck) {
0
0
@@ -355,11 +356,11 @@
0
     this.mousedown = false;
0
     this.refreshRange();
0
   },
0
-
0
+
0
   refreshRange: function() {
0
     this.element.select('td').each(function(day) {
0
       day.writeAttribute('class', day.baseClass);
0
- if(this.range.get('start') this.range.get('start') <= day.date && day.date <= this.range.get('end')) {
0
+ if(this.range.get('start') && this.range.get('end') && this.range.get('start') <= day.date && day.date <= this.range.get('end')) {
0
         var baseClass = day.hasClassName('beyond') ? 'beyond_' : day.hasClassName('today') ? 'today_' : null;
0
         var state = this.stuck || this.mousedown ? 'stuck' : 'selected';
0
         if(baseClass) day.addClassName(baseClass + state);
0
@@ -397,7 +398,7 @@
0
     var day = this.getDay(), month = this.getMonth();
0
     var hours = this.getHours(), minutes = this.getMinutes();
0
     function pad(num) { return num.toPaddedString(2); };
0
-
0
+
0
     return format.gsub(/\%([aAbBcdHImMpSwyY])/, function(part) {
0
       switch(part[1]) {
0
         case 'a': return Locale.get('dayNames').invoke('substring', 0, 3)[day].escapeHTML(); break;
0
@@ -418,7 +419,7 @@
0
       }
0
     }.bind(this));
0
   },
0
-
0
+
0
   neutral: function() {
0
     return new Date(this.getFullYear(), this.getMonth(), this.getDate(), 12);
0
   }

Comments

    No one has commented yet.