/*! * Piwik - free/libre analytics platform * * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ (function ($) { Date.prototype.getWeek = function () { var onejan = new Date(this.getFullYear(), 0, 1), // needed for getDay() // use UTC times since getTime() can differ based on user's timezone onejan_utc = Date.UTC(this.getFullYear(), 0, 1), this_utc = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate()), daysSinceYearStart = (this_utc - onejan_utc) / 86400000; // constant is millisecs in one day return Math.ceil((daysSinceYearStart + onejan.getDay()) / 7); }; var currentYear, currentMonth, currentDay, currentDate, currentWeek; function setCurrentDate(dateStr) { var splitDate = dateStr.split("-"); currentYear = splitDate[0]; currentMonth = splitDate[1] - 1; currentDay = splitDate[2]; currentDate = new Date(currentYear, currentMonth, currentDay); currentWeek = currentDate.getWeek(); } if(!piwik.currentDateString) { // eg. Login form return; } setCurrentDate(piwik.currentDateString); var todayDate = new Date; var todayMonth = todayDate.getMonth(); var todayYear = todayDate.getFullYear(); var todayDay = todayDate.getDate(); // min/max date for picker var piwikMinDate = new Date(piwik.minDateYear, piwik.minDateMonth - 1, piwik.minDateDay), piwikMaxDate = new Date(piwik.maxDateYear, piwik.maxDateMonth - 1, piwik.maxDateDay); // we start w/ the current period var selectedPeriod = piwik.period; function isDateInCurrentPeriod(date) { // if the selected period isn't the current period, don't highlight any dates if (selectedPeriod != piwik.period) { return [true, '']; } var valid = false; var dateMonth = date.getMonth(); var dateYear = date.getFullYear(); var dateDay = date.getDate(); // we don't color dates in the future if (dateMonth == todayMonth && dateYear == todayYear && dateDay > todayDay ) { return [true, '']; } // we don't color dates before the minimum date if (dateYear < piwik.minDateYear || ( dateYear == piwik.minDateYear && ( (dateMonth == piwik.minDateMonth - 1 && dateDay < piwik.minDateDay) || (dateMonth < piwik.minDateMonth - 1) ) ) ) { return [true, '']; } // we color all day of the month for the same year for the month period if (piwik.period == "month" && dateMonth == currentMonth && dateYear == currentYear ) { valid = true; } // we color all day of the year for the year period else if (piwik.period == "year" && dateYear == currentYear ) { valid = true; } else if (piwik.period == "week" && date.getWeek() == currentWeek && dateYear == currentYear ) { valid = true; } else if (piwik.period == "day" && dateDay == currentDay && dateMonth == currentMonth && dateYear == currentYear ) { valid = true; } if (valid) { return [true, 'ui-datepicker-current-period']; } return [true, '']; } piwik.getBaseDatePickerOptions = function (defaultDate) { return { showOtherMonths: false, dateFormat: 'yy-mm-dd', firstDay: 1, minDate: piwikMinDate, maxDate: piwikMaxDate, prevText: "", nextText: "", currentText: "", defaultDate: defaultDate, changeMonth: true, changeYear: true, stepMonths: 1, // jquery-ui-i18n 1.7.2 lacks some translations, so we use our own dayNamesMin: [ _pk_translate('Intl_Day_Min_StandAlone_7'), _pk_translate('Intl_Day_Min_StandAlone_1'), _pk_translate('Intl_Day_Min_StandAlone_2'), _pk_translate('Intl_Day_Min_StandAlone_3'), _pk_translate('Intl_Day_Min_StandAlone_4'), _pk_translate('Intl_Day_Min_StandAlone_5'), _pk_translate('Intl_Day_Min_StandAlone_6')], dayNamesShort: [ _pk_translate('Intl_Day_Short_StandAlone_7'), // start with sunday _pk_translate('Intl_Day_Short_StandAlone_1'), _pk_translate('Intl_Day_Short_StandAlone_2'), _pk_translate('Intl_Day_Short_StandAlone_3'), _pk_translate('Intl_Day_Short_StandAlone_4'), _pk_translate('Intl_Day_Short_StandAlone_5'), _pk_translate('Intl_Day_Short_StandAlone_6')], dayNames: [ _pk_translate('Intl_Day_Long_StandAlone_7'), // start with sunday _pk_translate('Intl_Day_Long_StandAlone_1'), _pk_translate('Intl_Day_Long_StandAlone_2'), _pk_translate('Intl_Day_Long_StandAlone_3'), _pk_translate('Intl_Day_Long_StandAlone_4'), _pk_translate('Intl_Day_Long_StandAlone_5'), _pk_translate('Intl_Day_Long_StandAlone_6')], monthNamesShort: [ _pk_translate('Intl_Month_Short_StandAlone_1'), _pk_translate('Intl_Month_Short_StandAlone_2'), _pk_translate('Intl_Month_Short_StandAlone_3'), _pk_translate('Intl_Month_Short_StandAlone_4'), _pk_translate('Intl_Month_Short_StandAlone_5'), _pk_translate('Intl_Month_Short_StandAlone_6'), _pk_translate('Intl_Month_Short_StandAlone_7'), _pk_translate('Intl_Month_Short_StandAlone_8'), _pk_translate('Intl_Month_Short_StandAlone_9'), _pk_translate('Intl_Month_Short_StandAlone_10'), _pk_translate('Intl_Month_Short_StandAlone_11'), _pk_translate('Intl_Month_Short_StandAlone_12')], monthNames: [ _pk_translate('Intl_Month_Long_StandAlone_1'), _pk_translate('Intl_Month_Long_StandAlone_2'), _pk_translate('Intl_Month_Long_StandAlone_3'), _pk_translate('Intl_Month_Long_StandAlone_4'), _pk_translate('Intl_Month_Long_StandAlone_5'), _pk_translate('Intl_Month_Long_StandAlone_6'), _pk_translate('Intl_Month_Long_StandAlone_7'), _pk_translate('Intl_Month_Long_StandAlone_8'), _pk_translate('Intl_Month_Long_StandAlone_9'), _pk_translate('Intl_Month_Long_StandAlone_10'), _pk_translate('Intl_Month_Long_StandAlone_11'), _pk_translate('Intl_Month_Long_StandAlone_12')] }; }; var updateDate; function getDatePickerOptions() { var result = piwik.getBaseDatePickerOptions(currentDate); result.beforeShowDay = isDateInCurrentPeriod; result.stepMonths = selectedPeriod == 'year' ? 12 : 1; result.onSelect = function () { updateDate.apply(this, arguments); }; return result; } $(function () { var datepickerElem = $('#datepicker').datepicker(getDatePickerOptions()), periodLabels = $('#periodString').find('.period-type label'), periodTooltip = $('#periodString').find('.period-click-tooltip').html(); var toggleWhitespaceHighlighting = function (klass, toggleTop, toggleBottom) { var viewedYear = $('.ui-datepicker-year', datepickerElem).val(), viewedMonth = +$('.ui-datepicker-month', datepickerElem).val(), // convert to int w/ '+' firstOfViewedMonth = new Date(viewedYear, viewedMonth, 1), lastOfViewedMonth = new Date(viewedYear, viewedMonth + 1, 0); // only highlight dates between piwik.minDate... & piwik.maxDate... // we select the cells to highlight by checking whether the first & last of the // currently viewed month are within the min/max dates. if (firstOfViewedMonth >= piwikMinDate) { $('tbody>tr:first-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleTop); } if (lastOfViewedMonth < piwikMaxDate) { $('tbody>tr:last-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleBottom); } }; // 'this' is the table cell var highlightCurrentPeriod = function () { switch (selectedPeriod) { case 'day': // highlight this link $('a', $(this)).addClass('ui-state-hover'); break; case 'week': var row = $(this).parent(); // highlight parent row (the week) $('a', row).addClass('ui-state-hover'); // toggle whitespace if week goes into previous or next month. we check if week is on // top or bottom row. var toggleTop = row.is(':first-child'), toggleBottom = row.is(':last-child'); toggleWhitespaceHighlighting('ui-state-hover', toggleTop, toggleBottom); break; case 'month': // highlight all parent rows (the month) $('a', $(this).parent().parent()).addClass('ui-state-hover'); break; case 'year': // highlight table (month + whitespace) $('a', $(this).parent().parent()).addClass('ui-state-hover'); toggleWhitespaceHighlighting('ui-state-hover', true, true); break; } }; var unhighlightAllDates = function () { // make sure nothing is highlighted $('.ui-state-active,.ui-state-hover', datepickerElem).removeClass('ui-state-active ui-state-hover'); // color whitespace if (piwik.period == 'year') { var viewedYear = $('.ui-datepicker-year', datepickerElem).val(), toggle = selectedPeriod == 'year' && currentYear == viewedYear; toggleWhitespaceHighlighting('ui-datepicker-current-period', toggle, toggle); } else if (piwik.period == 'week') { var toggleTop = $('tr:first-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period'), toggleBottom = $('tr:last-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period'); toggleWhitespaceHighlighting('ui-datepicker-current-period', toggleTop, toggleBottom); } }; updateDate = function (dateText) { piwikHelper.showAjaxLoading('ajaxLoadingCalendar'); // select new dates in calendar setCurrentDate(dateText); piwik.period = selectedPeriod; // make sure it's called after jquery-ui is done, otherwise everything we do will // be undone. setTimeout(unhighlightAllDates, 1); datepickerElem.datepicker('refresh'); // Let broadcast do its job: // It will replace date value to both search query and hash and load the new page. broadcast.propagateNewPage('date=' + dateText + '&period=' + selectedPeriod); }; var toggleMonthDropdown = function (disable) { if (typeof disable === 'undefined') { disable = selectedPeriod == 'year'; } // enable/disable month dropdown based on period == year $('.ui-datepicker-month', datepickerElem).attr('disabled', disable); }; var togglePeriodPickers = function (showSingle) { $('#periodString').find('.period-date').toggle(showSingle); $('#periodString').find('.period-range').toggle(!showSingle); }; // // setup datepicker // unhighlightAllDates(); // // hook up event slots // // highlight current period when mouse enters date datepickerElem.on('mouseenter', 'tbody td', function () { if ($(this).hasClass('ui-state-hover')) // if already highlighted, do nothing { return; } // unhighlight if cell is disabled/blank, unless the period is year if ($(this).hasClass('ui-state-disabled') && selectedPeriod != 'year') { unhighlightAllDates(); // if period is week, then highlight the current week if (selectedPeriod == 'week') { highlightCurrentPeriod.call(this); } } else { highlightCurrentPeriod.call(this); } }); // make sure cell stays highlighted when mouse leaves cell (overrides jquery-ui behavior) datepickerElem.on('mouseleave', 'tbody td', function () { $('a', this).addClass('ui-state-hover'); }); // unhighlight everything when mouse leaves table body (can't do event on tbody, for some reason // that fails, so we do two events, one on the table & one on thead) datepickerElem.on('mouseleave', 'table', unhighlightAllDates) .on('mouseenter', 'thead', unhighlightAllDates); // make sure whitespace is clickable when the period makes it appropriate datepickerElem.on('click', 'tbody td.ui-datepicker-other-month', function () { if ($(this).hasClass('ui-state-hover')) { var row = $(this).parent(), tbody = row.parent(); if (row.is(':first-child')) { // click on first of the month $('a', tbody).first().click(); } else { // click on last of month $('a', tbody).last().click(); } } }); var reloading = false; var changePeriodOnClick = function (periodInput) { if (reloading) // if a click event resulted in reloading, don't reload again { return; } var url = periodInput.val(), period = broadcast.getValueFromUrl('period', url); // if clicking on the selected period, change the period but not the date if (selectedPeriod == period && selectedPeriod != 'range') { // only reload if current period is different from selected if (piwik.period != selectedPeriod && !reloading) { reloading = true; selectedPeriod = period; updateDate(piwik.currentDateString); } return true; } return false; }; $("#otherPeriods").find("label,input").on('dblclick', function (e) { var id = $(e.target).attr('for'); changePeriodOnClick($('#' + id)); }); $("#otherPeriods").find("label,input").on('dblclick', function (e) { var id = $(e.target).attr('for'); changePeriodOnClick($('#' + id)); }); // Apply date range button will reload the page with the selected range $('#calendarApply') .on('click', function () { var $selectedPeriod = $('#periodMore [name=period]:checked'); if (!$selectedPeriod.is('#period_id_range')) { changePeriodOnClick($selectedPeriod); return true; } var dateFrom = $('#inputCalendarFrom').val(), dateTo = $('#inputCalendarTo').val(), oDateFrom = $.datepicker.parseDate('yy-mm-dd', dateFrom), oDateTo = $.datepicker.parseDate('yy-mm-dd', dateTo); if (!isValidDate(oDateFrom) || !isValidDate(oDateTo) || oDateFrom > oDateTo) { $('#alert').find('h2').text(_pk_translate('General_InvalidDateRange')); piwikHelper.modalConfirm('#alert', {}); return false; } piwikHelper.showAjaxLoading('ajaxLoadingCalendar'); broadcast.propagateNewPage('period=range&date=' + dateFrom + ',' + dateTo); }) .show(); // when non-range period is clicked, change the period & refresh the date picker $("#otherPeriods").find("input").on('click', function (e) { var request_URL = $(e.target).val(), period = broadcast.getValueFromUrl('period', request_URL), lastPeriod = selectedPeriod; if (changePeriodOnClick($(e.target))) { return true; } // switch the selected period selectedPeriod = period; // remove tooltips from the period inputs periodLabels.each(function () { $(this).attr('title', '').removeClass('selected-period-label'); }); // range periods are handled in an event handler below if (period == 'range') { return true; } // set the tooltip of the current period if (period != piwik.period) // don't add tooltip for current period { $(this).parent().find('label[for=period_id_' + period + ']') .attr('title', periodTooltip).addClass('selected-period-label'); } // toggle the right selector controls (show period selector datepicker & hide 'apply range' button) togglePeriodPickers(true); // set months step to 12 for year period (or set back to 1 if leaving year period) if (selectedPeriod == 'year' || lastPeriod == 'year') { // setting stepMonths will change the month in view back to the selected date. to avoid // we set the selected date to the month in view. var currentMonth = $('.ui-datepicker-month', datepickerElem).val(), currentYear = $('.ui-datepicker-year', datepickerElem).val(); datepickerElem .datepicker('option', 'stepMonths', selectedPeriod == 'year' ? 12 : 1) .datepicker('setDate', new Date(currentYear, currentMonth)); } datepickerElem.datepicker('refresh'); // must be last datepicker call, otherwise cells get highlighted unhighlightAllDates(); toggleMonthDropdown(); return true; }); // clicking left/right re-enables the month dropdown, so we disable it again $(datepickerElem).on('click', '.ui-datepicker-next,.ui-datepicker-prev', function () { unhighlightAllDates(); // make sure today's date isn't highlighted & toggle extra year highlighting toggleMonthDropdown(selectedPeriod == 'year'); }); function onDateRangeSelect(dateText, inst) { var toOrFrom = inst.id == 'calendarFrom' ? 'From' : 'To'; $('#inputCalendar' + toOrFrom).val(dateText); } // this will trigger to change only the period value on search query and hash string. $("#period_id_range").on('click', function (e) { togglePeriodPickers(false); var options = getDatePickerOptions(); // Custom Date range callback options.onSelect = onDateRangeSelect; // Do not highlight the period options.beforeShowDay = ''; // Create both calendars options.defaultDate = piwik.startDateString; $('#calendarFrom').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.startDateString)); // Technically we should trigger the onSelect event on the calendar, but I couldn't find how to do that // So calling the onSelect bind function manually... //$('#calendarFrom').trigger('dateSelected'); // or onSelect onDateRangeSelect(piwik.startDateString, { "id": "calendarFrom" }); // Same code for the other calendar options.defaultDate = piwik.endDateString; $('#calendarTo').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.endDateString)); onDateRangeSelect(piwik.endDateString, { "id": "calendarTo" }); // If not called, the first date appears light brown instead of dark brown $('.ui-state-hover').removeClass('ui-state-hover'); // Bind the input fields to update the calendar's date when date is manually changed $('#inputCalendarFrom, #inputCalendarTo') .keyup(function (e) { var fromOrTo = this.id == 'inputCalendarFrom' ? 'From' : 'To'; var dateInput = $(this).val(); try { var newDate = $.datepicker.parseDate('yy-mm-dd', dateInput); } catch (e) { return; } $("#calendar" + fromOrTo).datepicker("setDate", newDate); if (e.keyCode == 13) { $('#calendarApply').click(); } }); return true; }); function isValidDate(d) { if (Object.prototype.toString.call(d) !== "[object Date]") return false; return !isNaN(d.getTime()); } if (piwik.period == 'range') { $("#period_id_range").click(); } }); }(jQuery));