• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*! Taken from django release 1.8.5
2
3Copyright (c) Django Software Foundation and individual contributors.
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without modification,
7are permitted provided that the following conditions are met:
8
9    1. Redistributions of source code must retain the above copyright notice,
10       this list of conditions and the following disclaimer.
11
12    2. Redistributions in binary form must reproduce the above copyright
13       notice, this list of conditions and the following disclaimer in the
14       documentation and/or other materials provided with the distribution.
15
16    3. Neither the name of Django nor the names of its contributors may be used
17       to endorse or promote products derived from this software without
18       specific prior written permission.
19
20THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*/
31
32// Inserts shortcut buttons after all of the following:
33//     <input type="text" class="vDateField">
34//     <input type="text" class="vTimeField">
35
36var DateTimeShortcuts = {
37    calendars: [],
38    calendarInputs: [],
39    clockInputs: [],
40    dismissClockFunc: [],
41    dismissCalendarFunc: [],
42    calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled
43    calendarDivName2: 'calendarin',  // name of <div> that contains calendar
44    calendarLinkName: 'calendarlink',// name of the link that is used to toggle
45    clockDivName: 'clockbox',        // name of clock <div> that gets toggled
46    clockLinkName: 'clocklink',      // name of the link that is used to toggle
47    shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
48    timezoneWarningClass: 'timezonewarning', // class of the warning for timezone mismatch
49    timezoneOffset: 0,
50    admin_media_prefix: '',
51    init: function() {
52        // Get admin_media_prefix by grabbing it off the window object. It's
53        // set in the admin/base.html template, so if it's not there, someone's
54        // overridden the template. In that case, we'll set a clearly-invalid
55        // value in the hopes that someone will examine HTTP requests and see it.
56        if (window.__admin_media_prefix__ != undefined) {
57            DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__;
58        } else {
59            DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/';
60        }
61
62        if (window.__admin_utc_offset__ != undefined) {
63            var serverOffset = window.__admin_utc_offset__;
64            var localOffset = new Date().getTimezoneOffset() * -60;
65            DateTimeShortcuts.timezoneOffset = localOffset - serverOffset;
66        }
67
68        var inputs = document.getElementsByTagName('input');
69        for (i=0; i<inputs.length; i++) {
70            var inp = inputs[i];
71            if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {
72                DateTimeShortcuts.addClock(inp);
73                DateTimeShortcuts.addTimezoneWarning(inp);
74            }
75            else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
76                DateTimeShortcuts.addCalendar(inp);
77                DateTimeShortcuts.addTimezoneWarning(inp);
78            }
79        }
80    },
81    // Return the current time while accounting for the server timezone.
82    now: function() {
83        if (window.__admin_utc_offset__ != undefined) {
84            var serverOffset = window.__admin_utc_offset__;
85            var localNow = new Date();
86            var localOffset = localNow.getTimezoneOffset() * -60;
87            localNow.setTime(localNow.getTime() + 1000 * (serverOffset - localOffset));
88            return localNow;
89        } else {
90            return new Date();
91        }
92    },
93    // Add a warning when the time zone in the browser and backend do not match.
94    addTimezoneWarning: function(inp) {
95        var $ = django.jQuery;
96        var warningClass = DateTimeShortcuts.timezoneWarningClass;
97        var timezoneOffset = DateTimeShortcuts.timezoneOffset / 3600;
98
99        // Only warn if there is a time zone mismatch.
100        if (!timezoneOffset)
101            return;
102
103        // Check if warning is already there.
104        if ($(inp).siblings('.' + warningClass).length)
105            return;
106
107        var message;
108        if (timezoneOffset > 0) {
109            message = ngettext(
110                'Note: You are %s hour ahead of server time.',
111                'Note: You are %s hours ahead of server time.',
112                timezoneOffset
113            );
114        }
115        else {
116            timezoneOffset *= -1
117            message = ngettext(
118                'Note: You are %s hour behind server time.',
119                'Note: You are %s hours behind server time.',
120                timezoneOffset
121            );
122        }
123        message = interpolate(message, [timezoneOffset]);
124
125        var $warning = $('<span>');
126        $warning.attr('class', warningClass);
127        $warning.text(message);
128
129        $(inp).parent()
130            .append($('<br>'))
131            .append($warning)
132    },
133    // Add clock widget to a given field
134    addClock: function(inp) {
135        var num = DateTimeShortcuts.clockInputs.length;
136        DateTimeShortcuts.clockInputs[num] = inp;
137        DateTimeShortcuts.dismissClockFunc[num] = function() { DateTimeShortcuts.dismissClock(num); return true; };
138
139        // Shortcut links (clock icon and "Now" link)
140        var shortcuts_span = document.createElement('span');
141        shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
142        inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
143        var now_link = document.createElement('a');
144        now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", -1);");
145        now_link.appendChild(document.createTextNode(gettext('Now')));
146        var clock_link = document.createElement('a');
147        clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
148        clock_link.id = DateTimeShortcuts.clockLinkName + num;
149        quickElement('img', clock_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/icon_clock.gif', 'alt', gettext('Clock'));
150        shortcuts_span.appendChild(document.createTextNode('\240'));
151        shortcuts_span.appendChild(now_link);
152        shortcuts_span.appendChild(document.createTextNode('\240|\240'));
153        shortcuts_span.appendChild(clock_link);
154
155        // Create clock link div
156        //
157        // Markup looks like:
158        // <div id="clockbox1" class="clockbox module">
159        //     <h2>Choose a time</h2>
160        //     <ul class="timelist">
161        //         <li><a href="#">Now</a></li>
162        //         <li><a href="#">Midnight</a></li>
163        //         <li><a href="#">6 a.m.</a></li>
164        //         <li><a href="#">Noon</a></li>
165        //     </ul>
166        //     <p class="calendar-cancel"><a href="#">Cancel</a></p>
167        // </div>
168
169        var clock_box = document.createElement('div');
170        clock_box.style.display = 'none';
171        clock_box.style.position = 'absolute';
172        clock_box.className = 'clockbox module';
173        clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);
174        document.body.appendChild(clock_box);
175        addEvent(clock_box, 'click', cancelEventPropagation);
176
177        quickElement('h2', clock_box, gettext('Choose a time'));
178        var time_list = quickElement('ul', clock_box);
179        time_list.className = 'timelist';
180        quickElement("a", quickElement("li", time_list), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", -1);");
181        quickElement("a", quickElement("li", time_list), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", 0);");
182        quickElement("a", quickElement("li", time_list), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", 6);");
183        quickElement("a", quickElement("li", time_list), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", 12);");
184
185        var cancel_p = quickElement('p', clock_box);
186        cancel_p.className = 'calendar-cancel';
187        quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
188        django.jQuery(document).bind('keyup', function(event) {
189            if (event.which == 27) {
190                // ESC key closes popup
191                DateTimeShortcuts.dismissClock(num);
192                event.preventDefault();
193            }
194        });
195    },
196    openClock: function(num) {
197        var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)
198        var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)
199
200        // Recalculate the clockbox position
201        // is it left-to-right or right-to-left layout ?
202        if (getStyle(document.body,'direction')!='rtl') {
203            clock_box.style.left = findPosX(clock_link) + 17 + 'px';
204        }
205        else {
206            // since style's width is in em, it'd be tough to calculate
207            // px value of it. let's use an estimated px for now
208            // TODO: IE returns wrong value for findPosX when in rtl mode
209            //       (it returns as it was left aligned), needs to be fixed.
210            clock_box.style.left = findPosX(clock_link) - 110 + 'px';
211        }
212        clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px';
213
214        // Show the clock box
215        clock_box.style.display = 'block';
216        addEvent(document, 'click', DateTimeShortcuts.dismissClockFunc[num]);
217    },
218    dismissClock: function(num) {
219       document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
220       removeEvent(document, 'click', DateTimeShortcuts.dismissClockFunc[num]);
221    },
222    handleClockQuicklink: function(num, val) {
223       var d;
224       if (val == -1) {
225           d = DateTimeShortcuts.now();
226       }
227       else {
228           d = new Date(1970, 1, 1, val, 0, 0, 0)
229       }
230       DateTimeShortcuts.clockInputs[num].value = d.strftime(get_format('TIME_INPUT_FORMATS')[0]);
231       DateTimeShortcuts.clockInputs[num].focus();
232       DateTimeShortcuts.dismissClock(num);
233    },
234    // Add calendar widget to a given field.
235    addCalendar: function(inp) {
236        var num = DateTimeShortcuts.calendars.length;
237
238        DateTimeShortcuts.calendarInputs[num] = inp;
239        DateTimeShortcuts.dismissCalendarFunc[num] = function() { DateTimeShortcuts.dismissCalendar(num); return true; };
240
241        // Shortcut links (calendar icon and "Today" link)
242        var shortcuts_span = document.createElement('span');
243        shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
244        inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
245        var today_link = document.createElement('a');
246        today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
247        today_link.appendChild(document.createTextNode(gettext('Today')));
248        var cal_link = document.createElement('a');
249        cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');
250        cal_link.id = DateTimeShortcuts.calendarLinkName + num;
251        quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/icon_calendar.gif', 'alt', gettext('Calendar'));
252        shortcuts_span.appendChild(document.createTextNode('\240'));
253        shortcuts_span.appendChild(today_link);
254        shortcuts_span.appendChild(document.createTextNode('\240|\240'));
255        shortcuts_span.appendChild(cal_link);
256
257        // Create calendarbox div.
258        //
259        // Markup looks like:
260        //
261        // <div id="calendarbox3" class="calendarbox module">
262        //     <h2>
263        //           <a href="#" class="link-previous">&lsaquo;</a>
264        //           <a href="#" class="link-next">&rsaquo;</a> February 2003
265        //     </h2>
266        //     <div class="calendar" id="calendarin3">
267        //         <!-- (cal) -->
268        //     </div>
269        //     <div class="calendar-shortcuts">
270        //          <a href="#">Yesterday</a> | <a href="#">Today</a> | <a href="#">Tomorrow</a>
271        //     </div>
272        //     <p class="calendar-cancel"><a href="#">Cancel</a></p>
273        // </div>
274        var cal_box = document.createElement('div');
275        cal_box.style.display = 'none';
276        cal_box.style.position = 'absolute';
277        cal_box.className = 'calendarbox module';
278        cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);
279        document.body.appendChild(cal_box);
280        addEvent(cal_box, 'click', cancelEventPropagation);
281
282        // next-prev links
283        var cal_nav = quickElement('div', cal_box);
284        var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');');
285        cal_nav_prev.className = 'calendarnav-previous';
286        var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');');
287        cal_nav_next.className = 'calendarnav-next';
288
289        // main box
290        var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
291        cal_main.className = 'calendar';
292        DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
293        DateTimeShortcuts.calendars[num].drawCurrent();
294
295        // calendar shortcuts
296        var shortcuts = quickElement('div', cal_box);
297        shortcuts.className = 'calendar-shortcuts';
298        quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);');
299        shortcuts.appendChild(document.createTextNode('\240|\240'));
300        quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
301        shortcuts.appendChild(document.createTextNode('\240|\240'));
302        quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);');
303
304        // cancel bar
305        var cancel_p = quickElement('p', cal_box);
306        cancel_p.className = 'calendar-cancel';
307        quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
308        django.jQuery(document).bind('keyup', function(event) {
309            if (event.which == 27) {
310                // ESC key closes popup
311                DateTimeShortcuts.dismissCalendar(num);
312                event.preventDefault();
313            }
314        });
315    },
316    openCalendar: function(num) {
317        var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
318        var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
319        var inp = DateTimeShortcuts.calendarInputs[num];
320
321        // Determine if the current value in the input has a valid date.
322        // If so, draw the calendar with that date's year and month.
323        if (inp.value) {
324            var format = get_format('DATE_INPUT_FORMATS')[0];
325            var selected = inp.value.strptime(format);
326            var year = selected.getFullYear();
327            var month = selected.getMonth() + 1;
328            var re = /\d{4}/
329            if (re.test(year.toString()) && month >= 1 && month <= 12) {
330                DateTimeShortcuts.calendars[num].drawDate(month, year, selected);
331            }
332        }
333
334        // Recalculate the clockbox position
335        // is it left-to-right or right-to-left layout ?
336        if (getStyle(document.body,'direction')!='rtl') {
337            cal_box.style.left = findPosX(cal_link) + 17 + 'px';
338        }
339        else {
340            // since style's width is in em, it'd be tough to calculate
341            // px value of it. let's use an estimated px for now
342            // TODO: IE returns wrong value for findPosX when in rtl mode
343            //       (it returns as it was left aligned), needs to be fixed.
344            cal_box.style.left = findPosX(cal_link) - 180 + 'px';
345        }
346        cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px';
347
348        cal_box.style.display = 'block';
349        addEvent(document, 'click', DateTimeShortcuts.dismissCalendarFunc[num]);
350    },
351    dismissCalendar: function(num) {
352        document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none';
353        removeEvent(document, 'click', DateTimeShortcuts.dismissCalendarFunc[num]);
354    },
355    drawPrev: function(num) {
356        DateTimeShortcuts.calendars[num].drawPreviousMonth();
357    },
358    drawNext: function(num) {
359        DateTimeShortcuts.calendars[num].drawNextMonth();
360    },
361    handleCalendarCallback: function(num) {
362        var format = get_format('DATE_INPUT_FORMATS')[0];
363        // the format needs to be escaped a little
364        format = format.replace('\\', '\\\\');
365        format = format.replace('\r', '\\r');
366        format = format.replace('\n', '\\n');
367        format = format.replace('\t', '\\t');
368        format = format.replace("'", "\\'");
369        return ["function(y, m, d) { DateTimeShortcuts.calendarInputs[",
370               num,
371               "].value = new Date(y, m-1, d).strftime('",
372               format,
373               "');DateTimeShortcuts.calendarInputs[",
374               num,
375               "].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+",
376               num,
377               ").style.display='none';}"].join('');
378    },
379    handleCalendarQuickLink: function(num, offset) {
380       var d = DateTimeShortcuts.now();
381       d.setDate(d.getDate() + offset)
382       DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
383       DateTimeShortcuts.calendarInputs[num].focus();
384       DateTimeShortcuts.dismissCalendar(num);
385    }
386}
387
388addEvent(window, 'load', DateTimeShortcuts.init);
389