Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 373
- Log:
Initial import of Radiant 0.9.1, which is now packaged as a gem. This is an
import of the tagged 0.9.1 source checked out from GitHub, which isn't quite
the same as the gem distribution - but it doesn't seem to be available in an
archived form and the installed gem already has modifications, so this is
the closest I can get.
- Author:
- rool
- Date:
- Mon Mar 21 13:40:05 +0000 2011
- Size:
- 13034 Bytes
1 | /* |
2 | * dateinput.js |
3 | * |
4 | * dependencies: prototype.js, lowpro.js |
5 | * |
6 | * -------------------------------------------------------------------------- |
7 | * |
8 | * Renders a date input. To use, add the following line to application.js: |
9 | * |
10 | * Event.addBehavior({'input.date': DateInputBehavior()}); |
11 | * |
12 | * This will effectively wire all inputs with a class of "date" to the |
13 | * DateInputBehavior. |
14 | * |
15 | * This code was originally based on Dan Web's code for date_selector.js, but |
16 | * has been modified from its original form. You can find Dan's original |
17 | * code here: |
18 | * |
19 | * http://github.com/danwrong/low-pro/blob/master/behaviours/date_selector.js |
20 | * |
21 | * -------------------------------------------------------------------------- |
22 | * |
23 | * Copyright (c) 2007-2009, Five Points Solutions, Inc. |
24 | * Portions Copyright (c) 2004, Dan Web |
25 | * |
26 | * Permission is hereby granted, free of charge, to any person obtaining a |
27 | * copy of this software and associated documentation files (the "Software"), |
28 | * to deal in the Software without restriction, including without limitation |
29 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
30 | * and/or sell copies of the Software, and to permit persons to whom the |
31 | * Software is furnished to do so, subject to the following conditions: |
32 | * |
33 | * The above copyright notice and this permission notice shall be included in |
34 | * all copies or substantial portions of the Software. |
35 | * |
36 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
37 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
38 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
39 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
40 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
41 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
42 | * DEALINGS IN THE SOFTWARE. |
43 | * |
44 | */ |
45 | |
46 | DateInputBehavior = Behavior.create({ |
47 | |
48 | initialize: function(options) { |
49 | this.element.setAttribute("autocomplete", "off"); |
50 | this.calendar = null; |
51 | this.options = Object.extend(DateInputBehavior.DEFAULTS, options || {}); |
52 | this.date = this.getDate(); |
53 | this._createCalendar(); |
54 | }, |
55 | |
56 | setDate: function(value, hideCalendar) { |
57 | this.date = value; |
58 | this.element.value = this.options.setter(this.date); |
59 | var timeoutTime = 250; |
60 | if (Prototype.Browser.IE) timeoutTime = 50; |
61 | |
62 | if (hideCalendar == null) hideCalendar = true; |
63 | |
64 | if (hideCalendar && this.calendar) { |
65 | setTimeout(function() { |
66 | this.calendar.element.hide(); |
67 | this.element.select(); |
68 | }.bind(this), timeoutTime); |
69 | } |
70 | this.element.fire('date:changed'); |
71 | }, |
72 | |
73 | _createCalendar : function() { |
74 | var calendar = $div({'class': 'calendar_popup'}); |
75 | var body = $(document.getElementsByTagName('body')[0]); |
76 | body.insert(calendar); |
77 | calendar.setStyle('position: absolute'); |
78 | this.calendar = new DateInputBehavior.Calendar(calendar, this); |
79 | }, |
80 | |
81 | onclick : function(event) { |
82 | if (this._isOverWidget(event)) { |
83 | this.calendar.toggle(); |
84 | event.stop(); |
85 | } |
86 | }, |
87 | |
88 | onmouseover: function(event) { |
89 | if (this._isOverWidget(event)) this.element.setStyle("cursor: pointer"); |
90 | else this.element.setStyle("cursor: text"); |
91 | }, |
92 | |
93 | onmouseout: function(event) { |
94 | if (this._isOverWidget(event)) this.element.setStyle("cursor: text"); |
95 | else this.element.setStyle("cursor: pointer"); |
96 | }, |
97 | |
98 | onmousemove: function(event) { |
99 | this.onmouseover(event); |
100 | }, |
101 | |
102 | onkeypress: function(event) { |
103 | switch(event.keyCode) { |
104 | case Event.KEY_UP: |
105 | case 38: |
106 | case Event.KEY_DOWN: |
107 | case 63233: |
108 | case 40: |
109 | this.calendar.toggle(); |
110 | event.stop(); |
111 | break; |
112 | case Event.KEY_ESC: |
113 | this.calendar.hide(); |
114 | break; |
115 | case Event.KEY_TAB: |
116 | this.calendar.hide(); |
117 | event.stop(); |
118 | var formElements = this.element.up('form').getElements(); |
119 | var elementIndex = formElements.indexOf(this.element) + 1; |
120 | if (formElements.length > elementIndex) formElements[elementIndex].focus(); |
121 | break; |
122 | } |
123 | }, |
124 | |
125 | onkeydown: function(event) { |
126 | if (Prototype.Browser.IE) this.onkeypress(event); |
127 | if (Prototype.Browser.WebKit && (event.keyCode == 40 || event.keyCode == 38)){ |
128 | this.onkeypress(event); |
129 | } |
130 | }, |
131 | |
132 | getDate : function() { |
133 | return this.options.getter(this.element.value) || new Date; |
134 | }, |
135 | |
136 | _isOverWidget: function(event) { |
137 | var positionedOverWidget = null; |
138 | if (Prototype.Browser.IE) { |
139 | var widgetLeft = this.element.cumulativeOffset().left; |
140 | var widgetRight = this.element.cumulativeOffset().left + this.element.getDimensions().width; |
141 | positionedOverWidget = (event.pointerX() >= widgetLeft && event.pointerX() <= widgetRight); |
142 | } else { |
143 | var calendarIconWidth = parseInt(this.element.getStyle('padding-right')); |
144 | var widgetLeft = this.element.cumulativeOffset().left + this.element.getDimensions().width - calendarIconWidth; |
145 | positionedOverWidget = (event.pointerX() >= widgetLeft); |
146 | } |
147 | return positionedOverWidget; |
148 | } |
149 | }); |
150 | |
151 | DateInputBehavior.Calendar = Behavior.create({ |
152 | |
153 | initialize: function(selector) { |
154 | this.selector = selector; |
155 | this.element.hide(); |
156 | Event.observe(document, 'click', this.element.hide.bind(this.element)); |
157 | }, |
158 | |
159 | show: function() { |
160 | DateInputBehavior.Calendar.instances.invoke('hide'); |
161 | this.date = this.selector.getDate(); |
162 | this.redraw(); |
163 | this.element.setStyle({ |
164 | 'top': this.getVerticalOffset(this.selector.element) + 'px', |
165 | 'left': Math.max(this.selector.element.cumulativeOffset().left + this.selector.element.getWidth() - this.element.getWidth() - 4, this.selector.element.cumulativeOffset().left) + 'px', |
166 | 'z-index': 10001 |
167 | }); |
168 | this.element.show(); |
169 | this.active = true; |
170 | }, |
171 | |
172 | getVerticalOffset: function(selector){ |
173 | var defaultOffset = this.selector.element.cumulativeOffset().top + this.selector.element.getHeight() + 2; |
174 | var height = this.element.getHeight(); |
175 | var top = 0; |
176 | |
177 | if(document.viewport.getHeight() > defaultOffset + height) { |
178 | top = defaultOffset; |
179 | } else { |
180 | top = (defaultOffset - height - selector.getHeight() - 6); |
181 | } |
182 | |
183 | if (top < document.viewport.getScrollOffsets().top) |
184 | top = document.viewport.getScrollOffsets().top; |
185 | |
186 | return top; |
187 | }, |
188 | |
189 | hide: function() { |
190 | this.element.hide(); |
191 | this.active = false; |
192 | }, |
193 | |
194 | toggle: function() { |
195 | if (this.element.visible()) { |
196 | this.hide(); |
197 | } else { |
198 | this.show() |
199 | } |
200 | }, |
201 | |
202 | redraw: function() { |
203 | var oldMonth = this.element.down('select.month'); |
204 | if (oldMonth) Event.stopObserving(oldMonth, 'change', oldMonth._monthChanged); |
205 | |
206 | var oldYear = this.element.down('select.year'); |
207 | if (oldYear) Event.stopObserving(oldYear, 'change', oldYear._yearChanged); |
208 | |
209 | var html = '<table class="calendar" border="0" cellpadding="0" cellspacing="0">' + |
210 | ' <thead>' + |
211 | ' <tr class="month_year_navigation">' + |
212 | ' <th class="back"><a href="#">←</a></th>' + |
213 | ' <th colspan="5" class="month_year">' + this._monthYear() + '</th>' + |
214 | ' <th class="forward"><a href="#">→</a></th>' + |
215 | ' </tr>' + |
216 | ' <tr class="day_header">' + this._dayRows() + '</tr>' + |
217 | ' </thead>' + |
218 | ' <tbody>' + |
219 | this._buildDateCells() + |
220 | '</tbody></table>'; |
221 | this.element.innerHTML = ''; |
222 | var table = DOM.Builder.fromHTML(html); |
223 | this.element.insert(table); |
224 | |
225 | var newMonth = this.element.down('select.month'); |
226 | newMonth._monthChanged = this._monthChanged.bindAsEventListener(this); |
227 | Event.observe(newMonth, 'change', newMonth._monthChanged); |
228 | |
229 | var newYear = this.element.down('select.year'); |
230 | newYear._yearChanged = this._yearChanged.bindAsEventListener(this); |
231 | Event.observe(newYear, 'change', newYear._yearChanged); |
232 | }, |
233 | |
234 | onclick: function(event) { |
235 | event.stop(); |
236 | if ($(event.target.parentNode).hasClassName('day')) return this._setDate(event.target); |
237 | if ($(event.target.parentNode).hasClassName('back')) return this._backMonth(); |
238 | if ($(event.target.parentNode).hasClassName('forward')) return this._forwardMonth(); |
239 | }, |
240 | |
241 | _monthChanged: function(event) { |
242 | event.stop(); |
243 | return this._selectMonth(event.target); |
244 | }, |
245 | |
246 | _yearChanged: function(event) { |
247 | event.stop(); |
248 | return this._selectYear(event.target); |
249 | }, |
250 | |
251 | _setDate: function(source) { |
252 | if (source.innerHTML.strip() != '') { |
253 | this.date.setDate(parseInt(source.innerHTML)); |
254 | this.selector.setDate(this.date); |
255 | $A(this.element.getElementsByClassName('selected')).invoke('removeClassName', 'selected'); |
256 | source.parentNode.addClassName('selected'); |
257 | } |
258 | }, |
259 | |
260 | _backMonth: function() { |
261 | this.date.setMonth(this.date.getMonth() - 1); |
262 | this.redraw(); |
263 | return false; |
264 | }, |
265 | |
266 | _forwardMonth: function() { |
267 | this.date.setMonth(this.date.getMonth() + 1); |
268 | this.redraw(); |
269 | return false; |
270 | }, |
271 | |
272 | _selectMonth: function(combo) { |
273 | this.date.setMonth(combo.selectedIndex); |
274 | this.selector.setDate(this.date, false); |
275 | this.redraw(); |
276 | return false; |
277 | }, |
278 | |
279 | _selectYear: function(combo) { |
280 | var year = parseInt($F(combo)) |
281 | this.date.setYear(year); |
282 | this.selector.setDate(this.date, false); |
283 | this.redraw(); |
284 | return false; |
285 | }, |
286 | |
287 | _getDateFromSelector: function() { |
288 | this.date = new Date(this.selector.date.getTime()); |
289 | }, |
290 | |
291 | _firstDay: function(month, year) { |
292 | return new Date(year, month, 1).getDay(); |
293 | }, |
294 | |
295 | _monthLength: function(month, year) { |
296 | var length = DateInputBehavior.Calendar.MONTHS[month].days; |
297 | return (month == 1 && (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) ? 29 : length; |
298 | }, |
299 | |
300 | _monthYear: function() { |
301 | var currentMonth = this.date.getMonth(); |
302 | var currentYear = this.date.getFullYear(); |
303 | var todaysYear = (new Date()).getFullYear(); |
304 | var html = ''; |
305 | html += '<select class="month">'; |
306 | DateInputBehavior.Calendar.MONTHS.each(function(month, index) { |
307 | if (index == currentMonth) { |
308 | html += '<option selected="selected">' + month.label + '</option>'; |
309 | } else { |
310 | html += '<option>' + month.label + '</option>'; |
311 | } |
312 | }); |
313 | html += '</select>'; |
314 | if (!(Prototype.Browser.WebKit || Prototype.Browser.MobileSafari)) html += ' '; |
315 | html += '<select class="year">'; |
316 | for (var index = todaysYear - 100; index < todaysYear + 50; index++) { |
317 | if (index == currentYear) { |
318 | html += '<option selected="selected">' + index + '</option>'; |
319 | } else { |
320 | html += '<option>' + index + '</option>'; |
321 | } |
322 | } |
323 | html += '</select>'; |
324 | return html; |
325 | }, |
326 | |
327 | _dayRows: function() { |
328 | for (var i = 0, html='', day; day = DateInputBehavior.Calendar.DAYS[i]; i++) |
329 | html += '<th>' + day + '</th>'; |
330 | return html; |
331 | }, |
332 | |
333 | _buildDateCells: function() { |
334 | var month = this.date.getMonth(), year = this.date.getFullYear(); |
335 | var day = 1, monthLength = this._monthLength(month, year), firstDay = this._firstDay(month, year); |
336 | var html = '<tr>'; |
337 | |
338 | for (var i = 0; i < 9; i++) { |
339 | for (var j = 0; j <= 6; j++) { |
340 | |
341 | if (day <= monthLength && (i > 0 || j >= firstDay)) { |
342 | var classes = ['day']; |
343 | |
344 | if (this._compareDate(new Date, year, month, day)) classes.push('today'); |
345 | if (this._compareDate(this.selector.date, year, month, day)) classes.push('selected'); |
346 | |
347 | html += '<td class="' + classes.join(' ') + '">' + |
348 | '<a href="#">' + day++ + '</a>' + |
349 | '</td>'; |
350 | } else html += '<td></td>'; |
351 | } |
352 | |
353 | if (day > monthLength) break; |
354 | else html += '</tr><tr>'; |
355 | } |
356 | |
357 | return html + '</tr>'; |
358 | }, |
359 | |
360 | _compareDate: function(date, year, month, day) { |
361 | return date.getFullYear() == year && |
362 | date.getMonth() == month && |
363 | date.getDate() == day; |
364 | } |
365 | }); |
366 | |
367 | DateInputBehavior.DEFAULTS = { |
368 | |
369 | setter: function(date) { |
370 | return DateInputBehavior.Calendar.MONTHS[date.getMonth()].label + |
371 | ' ' + date.getDate() + ', ' + date.getFullYear(); |
372 | }, |
373 | |
374 | getter: function(value) { |
375 | var parsed = Date.parse(value); |
376 | |
377 | if (!isNaN(parsed)) return new Date(parsed); |
378 | else return null; |
379 | } |
380 | |
381 | }; |
382 | |
383 | Object.extend(DateInputBehavior.Calendar, { |
384 | |
385 | DAYS : $w('S M T W T F S'), |
386 | |
387 | MONTHS : [ |
388 | { label: 'January', days: 31 }, |
389 | { label: 'February', days: 28 }, |
390 | { label: 'March', days: 31 }, |
391 | { label: 'April', days: 30 }, |
392 | { label: 'May', days: 31 }, |
393 | { label: 'June', days: 30 }, |
394 | { label: 'July', days: 31 }, |
395 | { label: 'August', days: 31 }, |
396 | { label: 'September', days: 30 }, |
397 | { label: 'October', days: 31 }, |
398 | { label: 'November', days: 30 }, |
399 | { label: 'December', days: 31 } |
400 | ] |
401 | |
402 | }); |