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">‹</a> 264 // <a href="#" class="link-next">›</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