').appendTo(self.element);
-
- //render the different parts
- // nav links
- self._renderCalendarButtons($calendarContainer);
- // header
- self._renderCalendarHeader($calendarContainer);
- // body
- self._renderCalendarBody($calendarContainer);
-
- $weekDayColumns = $calendarContainer.find('.wc-day-column-inner');
- $weekDayColumns.each(function(i, val) {
- if (!options.readonly) {
- self._addDroppableToWeekDay($(this));
- if (options.allowEventCreation) {
- self._setupEventCreationForWeekDay($(this));
- }
- }
- });
- },
-
- /**
- * render the nav buttons on top of the calendar
- */
- _renderCalendarButtons: function($calendarContainer) {
- var self = this, options = this.options;
- if ( !options.showHeader ) return;
- if (options.buttons) {
- var calendarNavHtml = '';
-
- calendarNavHtml += '';
-
- $(calendarNavHtml).appendTo($calendarContainer);
-
- $calendarContainer.find('.wc-nav .wc-today')
- .button({
- icons: {primary: 'ui-icon-home'}})
- .click(function() {
- self.today();
- return false;
- });
-
- $calendarContainer.find('.wc-nav .wc-prev')
- .button({
- text: false,
- icons: {primary: 'ui-icon-seek-prev'}})
- .click(function() {
- self.element.weekCalendar('prev');
- return false;
- });
-
- $calendarContainer.find('.wc-nav .wc-next')
- .button({
- text: false,
- icons: {primary: 'ui-icon-seek-next'}})
- .click(function() {
- self.element.weekCalendar('next');
- return false;
- });
-
- // now add buttons to switch display
- if (this.options.switchDisplay && $.isPlainObject(this.options.switchDisplay)) {
- var $container = $calendarContainer.find('.wc-display');
- $.each(this.options.switchDisplay, function(label, option) {
- var _id = 'wc-switch-display-' + option;
- var _input = $('
');
- _label.html(label);
- _input.val(option);
- if (parseInt(self.options.daysToShow, 10) === parseInt(option, 10)) {
- _input.attr('checked', 'checked');
- }
- $container
- .append(_input)
- .append(_label);
- });
- $container.find('input').change(function() {
- self.setDaysToShow(parseInt($(this).val(), 10));
- });
- }
- $calendarContainer.find('.wc-nav, .wc-display').buttonset();
- var _height = $calendarContainer.find('.wc-nav').outerHeight();
- $calendarContainer.find('.wc-title')
- .height(_height)
- .css('line-height', _height + 'px');
- }else{
- var calendarNavHtml = '';
- calendarNavHtml += '';
- $(calendarNavHtml).appendTo($calendarContainer);
-
- }
- },
-
- /**
- * render the calendar header, including date and user header
- */
- _renderCalendarHeader: function($calendarContainer) {
- var self = this, options = this.options,
- showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
- rowspan = '', colspan = '', calendarHeaderHtml;
-
- if (showAsSeparatedUser) {
- rowspan = ' rowspan=\"2\"';
- colspan = ' colspan=\"' + options.users.length + '\" ';
- }
-
- //first row
- calendarHeaderHtml = '
';
-
- $(calendarHeaderHtml).appendTo($calendarContainer);
- },
-
- /**
- * render the calendar body.
- * Calendar body is composed of several distinct parts.
- * Each part is displayed in a separated row to ease rendering.
- * for further explanations, see each part rendering function.
- */
- _renderCalendarBody: function($calendarContainer) {
- var self = this, options = this.options,
- showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
- $calendarBody, $calendarTableTbody;
- // create the structure
- $calendarBody = '
';
- $calendarBody = $($calendarBody);
- $calendarTableTbody = $calendarBody.find('tbody');
-
- self._renderCalendarBodyTimeSlots($calendarTableTbody);
- self._renderCalendarBodyOddEven($calendarTableTbody);
- self._renderCalendarBodyFreeBusy($calendarTableTbody);
- self._renderCalendarBodyEvents($calendarTableTbody);
-
- $calendarBody.appendTo($calendarContainer);
-
- //set the column height
- $calendarContainer.find('.wc-full-height-column').height(options.timeslotHeight * options.timeslotsPerDay);
- //set the timeslot height
- $calendarContainer.find('.wc-time-slot').height(options.timeslotHeight - 1); //account for border
- //init the time row header height
- /**
- TODO if total height for an hour is less than 11px, there is a display problem.
- Find a way to handle it
- */
- $calendarContainer.find('.wc-time-header-cell').css({
- height: (options.timeslotHeight * options.timeslotsPerHour) - 11,
- padding: 5
- });
- //add the user data to every impacted column
- if (showAsSeparatedUser) {
- for (var i = 0, uLength = options.users.length; i < uLength; i++) {
- $calendarContainer.find('.wc-user-' + self._getUserIdFromIndex(i))
- .data('wcUser', options.users[i])
- .data('wcUserIndex', i)
- .data('wcUserId', self._getUserIdFromIndex(i));
- }
- }
- },
-
- /**
- * render the timeslots separation
- */
- _renderCalendarBodyTimeSlots: function($calendarTableTbody) {
- var options = this.options,
- renderRow, i, j,
- showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
- start = (options.businessHours.limitDisplay ? options.businessHours.start : 0),
- end = (options.businessHours.limitDisplay ? options.businessHours.end : 24),
- rowspan = 1;
-
- //calculate the rowspan
- if (options.displayOddEven) { rowspan += 1; }
- if (options.displayFreeBusys) { rowspan += 1; }
- if (rowspan > 1) {
- rowspan = ' rowspan=\"' + rowspan + '\"';
- }
- else {
- rowspan = '';
- }
-
- renderRow = '
';
-
- $(renderRow).appendTo($calendarTableTbody);
- },
-
- /**
- * render the odd even columns
- */
- _renderCalendarBodyOddEven: function($calendarTableTbody) {
- if (this.options.displayOddEven) {
- var options = this.options,
- renderRow = '
';
-
- $(renderRow).appendTo($calendarTableTbody);
- }
- },
-
- /**
- * render the freebusy placeholders
- */
- _renderCalendarBodyFreeBusy: function($calendarTableTbody) {
- if (this.options.displayFreeBusys) {
- var self = this, options = this.options,
- renderRow = '
';
-
- $(renderRow).appendTo($calendarTableTbody);
- }
- },
-
- /**
- * render the calendar body for event placeholders
- */
- _renderCalendarBodyEvents: function($calendarTableTbody) {
- var self = this, options = this.options,
- renderRow,
- showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
- start = (options.businessHours.limitDisplay ? options.businessHours.start : 0),
- end = (options.businessHours.limitDisplay ? options.businessHours.end : 24);
- renderRow = '
';
-
- $(renderRow).appendTo($calendarTableTbody);
- },
-
- /*
- * setup mouse events for capturing new events
- */
- _setupEventCreationForWeekDay: function($weekDay) {
- var self = this;
- var options = this.options;
- $weekDay.mousedown(function(event) {
- var $target = $(event.target);
- if ($target.hasClass('wc-day-column-inner')) {
-
- var $newEvent = $('
');
-
- $newEvent.css({lineHeight: (options.timeslotHeight - 2) + 'px', fontSize: (options.timeslotHeight / 2) + 'px'});
- $target.append($newEvent);
-
- var columnOffset = $target.offset().top;
- var clickY = event.pageY - columnOffset;
- var clickYRounded = (clickY - (clickY % options.timeslotHeight)) / options.timeslotHeight;
- var topPosition = clickYRounded * options.timeslotHeight;
- $newEvent.css({top: topPosition});
-
- if (!options.preventDragOnEventCreation) {
- $target.bind('mousemove.newevent', function(event) {
- $newEvent.show();
- $newEvent.addClass('ui-resizable-resizing');
- var height = Math.round(event.pageY - columnOffset - topPosition);
- var remainder = height % options.timeslotHeight;
- //snap to closest timeslot
- if (remainder < 0) {
- var useHeight = height - remainder;
- $newEvent.css('height', useHeight < options.timeslotHeight ? options.timeslotHeight : useHeight);
- } else {
- $newEvent.css('height', height + (options.timeslotHeight - remainder));
- }
- }).mouseup(function() {
- $target.unbind('mousemove.newevent');
- $newEvent.addClass('ui-corner-all');
- });
- }
- }
-
- }).mouseup(function(event) {
- var $target = $(event.target);
-
- var $weekDay = $target.closest('.wc-day-column-inner');
- var $newEvent = $weekDay.find('.wc-new-cal-event-creating');
-
- if ($newEvent.length) {
- var createdFromSingleClick = !$newEvent.hasClass('ui-resizable-resizing');
-
- //if even created from a single click only, default height
- if (createdFromSingleClick) {
- $newEvent.css({height: options.timeslotHeight * options.defaultEventLength}).show();
- }
- var top = parseInt($newEvent.css('top'));
- var eventDuration = self._getEventDurationFromPositionedEventElement($weekDay, $newEvent, top);
-
- $newEvent.remove();
- var newCalEvent = {start: eventDuration.start, end: eventDuration.end, title: options.newEventText};
- var showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length;
-
- if (showAsSeparatedUser) {
- newCalEvent = self._setEventUserId(newCalEvent, $weekDay.data('wcUserId'));
- }
- else if (!options.showAsSeparateUsers && options.users && options.users.length == 1) {
- newCalEvent = self._setEventUserId(newCalEvent, self._getUserIdFromIndex(0));
- }
-
- var freeBusyManager = self.getFreeBusyManagerForEvent(newCalEvent);
-
- var $renderedCalEvent = self._renderEvent(newCalEvent, $weekDay);
-
- if (!options.allowCalEventOverlap) {
- self._adjustForEventCollisions($weekDay, $renderedCalEvent, newCalEvent, newCalEvent);
- self._positionEvent($weekDay, $renderedCalEvent);
- } else {
- self._adjustOverlappingEvents($weekDay);
- }
-
- var proceed = self._trigger('beforeEventNew', event, {
- 'calEvent': newCalEvent,
- 'createdFromSingleClick': createdFromSingleClick,
- 'calendar': self.element
- });
- if (proceed) {
- options.eventNew(newCalEvent, $renderedCalEvent, freeBusyManager, self.element, event);
- }
- else {
- $($renderedCalEvent).remove();
- }
- }
- });
- },
-
- /*
- * load calendar events for the week based on the date provided
- */
- _loadCalEvents: function(dateWithinWeek) {
-
- var date, weekStartDate, weekEndDate, $weekDayColumns;
- var self = this;
- var options = this.options;
- date = this._fixMinMaxDate(dateWithinWeek || options.date);
- // if date is not provided
- // or was not set
- // or is different than old one
- if ((!date || !date.getTime) ||
- (!options.date || !options.date.getTime) ||
- date.getTime() != options.date.getTime()
- ) {
- // trigger the changedate event
- this._trigger('changedate', this.element, date);
- }
- this.options.date = date;
- weekStartDate = self._dateFirstDayOfWeek(date);
- weekEndDate = self._dateLastMilliOfWeek(date);
-
- options.calendarBeforeLoad(self.element);
-
- self.element.data('startDate', weekStartDate);
- self.element.data('endDate', weekEndDate);
-
- $weekDayColumns = self.element.find('.wc-day-column-inner');
-
- self._updateDayColumnHeader($weekDayColumns);
-
- //load events by chosen means
- if (typeof options.data == 'string') {
- if (options.loading) {
- options.loading(true);
- }
- if (_currentAjaxCall) {
- // first abort current request.
- if (!_jQuery14OrLower) {
- _currentAjaxCall.abort();
- } else {
- // due to the fact that jquery 1.4 does not detect a request was
- // aborted, we need to replace the onreadystatechange and
- // execute the "complete" callback.
- _currentAjaxCall.onreadystatechange = null;
- _currentAjaxCall.abort();
- _currentAjaxCall = null;
- if (options.loading) {
- options.loading(false);
- }
- }
- }
- var jsonOptions = self._getJsonOptions();
- jsonOptions[options.startParam || 'start'] = Math.round(weekStartDate.getTime() / 1000);
- jsonOptions[options.endParam || 'end'] = Math.round(weekEndDate.getTime() / 1000);
- _currentAjaxCall = $.ajax({
- url: options.data,
- data: jsonOptions,
- dataType: 'json',
- error: function(XMLHttpRequest, textStatus, errorThrown) {
- // only prevent error with jQuery 1.5
- // see issue #34. thanks to dapplebeforedawn
- // (https://github.com/themouette/jquery-week-calendar/issues#issue/34)
- // for 1.5+, aborted request mean errorThrown == 'abort'
- // for prior version it means !errorThrown && !XMLHttpRequest.status
- // fixes #55
- if (errorThrown != 'abort' && XMLHttpRequest.status != 0) {
- alert('unable to get data, error:' + textStatus);
- }
- },
- success: function(data) {
- self._renderEvents(data, $weekDayColumns);
- },
- complete: function() {
- _currentAjaxCall = null;
- if (options.loading) {
- options.loading(false);
- }
- }
- });
- }
- else if ($.isFunction(options.data)) {
- options.data(weekStartDate, weekEndDate,
- function(data) {
- self._renderEvents(data, $weekDayColumns);
- });
- }
- else if (options.data) {
- self._renderEvents(options.data, $weekDayColumns);
- }
-
- self._disableTextSelect($weekDayColumns);
- },
-
- /**
- * Draws a thin line which indicates the current time.
- */
- _drawCurrentHourLine: function() {
- var self = this;
- var d = MyDate(),
- options = this.options,
- businessHours = options.businessHours;
-
- self._scrollToHour(d.getHours() ,false);
- // first, we remove the old hourline if it exists
- $('.wc-hourline', this.element).remove();
-
- // the line does not need to be displayed
- if (businessHours.limitDisplay && d.getHours() > businessHours.end) {
- return;
- }
-
- // then we recreate it
- var paddingStart = businessHours.limitDisplay ? businessHours.start : 0;
- var nbHours = d.getHours() - paddingStart + d.getMinutes() / 60;
- var positionTop = nbHours * options.timeslotHeight * options.timeslotsPerHour;
- var lineWidth = $('.wc-scrollable-grid .wc-today', this.element).width() + 3;
- $('.wc-scrollable-grid .wc-today', this.element).append(
- $('
', {
- 'class': 'wc-hourline',
- style: 'top: ' + positionTop + 'px; width: ' + lineWidth + 'px'
- })
- );
- },
-
- /*
- * update the display of each day column header based on the calendar week
- */
- _updateDayColumnHeader: function($weekDayColumns) {
- var self = this;
- var options = this.options;
- var currentDay = self._cloneDate(self.element.data('startDate'));
- var showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length;
- var todayClass = 'ui-state-active wc-today';
-
- self.element.find('.wc-header td.wc-day-column-header').each(function(i, val) {
- $(this).html(self._getHeaderDate(currentDay));
- if (self._isToday(currentDay)) {
- $(this).addClass(todayClass);
- } else {
- $(this).removeClass(todayClass);
- }
- currentDay = self._addDays(currentDay, 1);
-
- });
-
- currentDay = self._cloneDate(self.element.data('startDate'));
- if (showAsSeparatedUser)
- {
- self.element.find('.wc-header td.wc-user-header').each(function(i, val) {
- if (self._isToday(currentDay)) {
- $(this).addClass(todayClass);
- } else {
- $(this).removeClass(todayClass);
- }
- currentDay = ((i + 1) % options.users.length) ? currentDay : self._addDays(currentDay, 1);
- });
- }
-
- currentDay = self._cloneDate(self.element.data('startDate'));
-
- $weekDayColumns.each(function(i, val) {
-
- $(this).data('startDate', self._cloneDate(currentDay));
- $(this).data('endDate', new Date(currentDay.getTime() + (MILLIS_IN_DAY)));
- if (self._isToday(currentDay)) {
- $(this).parent()
- .addClass(todayClass)
- .removeClass('ui-state-default');
- } else {
- $(this).parent()
- .removeClass(todayClass)
- .addClass('ui-state-default');
- }
-
- if (!showAsSeparatedUser || !((i + 1) % options.users.length)) {
- currentDay = self._addDays(currentDay, 1);
- }
- });
-
- //now update the freeBusy placeholders
- if (options.displayFreeBusys) {
- currentDay = self._cloneDate(self.element.data('startDate'));
- self.element.find('.wc-grid-row-freebusy .wc-column-freebusy').each(function(i, val) {
- $(this).data('startDate', self._cloneDate(currentDay));
- $(this).data('endDate', new Date(currentDay.getTime() + (MILLIS_IN_DAY)));
- if (!showAsSeparatedUser || !((i + 1) % options.users.length)) {
- currentDay = self._addDays(currentDay, 1);
- }
- });
- }
-
- // now update the calendar title
- if (this.options.title) {
- var date = this.options.date,
- start = self._cloneDate(self.element.data('startDate')),
- end = self._dateLastDayOfWeek(new Date(this._cloneDate(self.element.data('endDate')).getTime() - (MILLIS_IN_DAY))),
- title = this._getCalendarTitle(),
- date_format = options.dateFormat;
-
- // replace the placeholders contained in the title
- title = title.replace('%start%', self._formatDate(start, date_format));
- title = title.replace('%end%', self._formatDate(end, date_format));
- title = title.replace('%date%', self._formatDate(date, date_format));
-
- $('.wc-toolbar .wc-title', self.element).html(title);
- }
- //self._clearFreeBusys();
- },
-
- /**
- * Gets the calendar raw title.
- */
- _getCalendarTitle: function() {
- if ($.isFunction(this.options.title)) {
- return this.options.title(this.options.daysToShow);
- }
-
- return this.options.title || '';
- },
-
- /**
- * Render the events into the calendar
- */
- _renderEvents: function(data, $weekDayColumns) {
- var self = this;
- var options = this.options;
- var eventsToRender, nbRenderedEvents = 0;
-
- if (data.options) {
- var updateLayout = false;
- // update options
- $.each(data.options, function(key, value) {
- if (value !== options[key]) {
- options[key] = value;
- updateLayout = updateLayout || $.ui.weekCalendar.updateLayoutOptions[key];
- }
- });
-
- self._computeOptions();
-
- if (updateLayout) {
- var hour = self._getCurrentScrollHour();
- self.element.empty();
- self._renderCalendar();
- $weekDayColumns = self.element.find('.wc-time-slots .wc-day-column-inner');
- self._updateDayColumnHeader($weekDayColumns);
- self._resizeCalendar();
- self._scrollToHour(hour, false);
- }
- }
- this._clearCalendar();
-
- if ($.isArray(data)) {
- eventsToRender = self._cleanEvents(data);
- } else if (data.events) {
- eventsToRender = self._cleanEvents(data.events);
- self._renderFreeBusys(data);
- }
-
- $.each(eventsToRender, function(i, calEvent) {
- // render a multi day event as various event :
- // thanks to http://github.com/fbeauchamp/jquery-week-calendar
- if (!calEvent || !calEvent.start || !calEvent.end) return;
- var initialStart = new Date(calEvent.start);
- var initialEnd = new Date(calEvent.end);
- var maxHour = self.options.businessHours.limitDisplay ? self.options.businessHours.end : 24;
- var minHour = self.options.businessHours.limitDisplay ? self.options.businessHours.start : 0;
- var start = new Date(initialStart);
- var startDate = self._formatDate(start, 'Ymd');
- var endDate = self._formatDate(initialEnd, 'Ymd');
- var $weekDay;
- var isMultiday = false;
-
- while (startDate < endDate) {
- calEvent.start = start;
-
- // end of this virual calEvent is set to the end of the day
- calEvent.end.setFullYear(start.getFullYear());
- calEvent.end.setDate(start.getDate());
- calEvent.end.setMonth(start.getMonth());
- calEvent.end.setHours(maxHour, 0, 0);
-
- if (($weekDay = self._findWeekDayForEvent(calEvent, $weekDayColumns))) {
- self._renderEvent(calEvent, $weekDay);
- nbRenderedEvents += 1;
- }
-
- // start is set to the begin of the new day
- start.setDate(start.getDate() + 1);
- start.setHours(minHour, 0, 0);
-
- startDate = self._formatDate(start, 'Ymd');
- isMultiday = true;
- }
-
- if (start <= initialEnd) {
- calEvent.start = start;
- calEvent.end = initialEnd;
-
- if (((isMultiday && calEvent.start.getTime() != calEvent.end.getTime()) || !isMultiday) && ($weekDay = self._findWeekDayForEvent(calEvent, $weekDayColumns))) {
- self._renderEvent(calEvent, $weekDay);
- nbRenderedEvents += 1;
- }
- }
-
- // put back the initial start date
- calEvent.start = initialStart;
- });
-
- $weekDayColumns.each(function() {
- self._adjustOverlappingEvents($(this));
- });
-
- options.calendarAfterLoad(self.element);
-
- if (self._hourLineTimeout) {
- clearInterval(self._hourLineTimeout);
- self._hourLineTimeout = false;
- }
-
- if (options.hourLine) {
- self._drawCurrentHourLine();
-
- self._hourLineTimeout = setInterval(function() {
- self._drawCurrentHourLine();
- }, 60 * 1000); // redraw the line each minute
- }
-
- !nbRenderedEvents && options.noEvents();
- },
-
- /*
- * Render a specific event into the day provided. Assumes correct
- * day for calEvent date
- */
- _renderEvent: function(calEvent, $weekDay) {
- var self = this;
- var options = this.options;
- if (calEvent.start.getTime() > calEvent.end.getTime()) {
- return; // can't render a negative height
- }
-
- var eventClass, eventHtml, $calEventList, $modifiedEvent;
-
- eventClass = calEvent.id ? 'wc-cal-event' : 'wc-cal-event wc-new-cal-event';
- eventHtml = '
';
- eventHtml += '
';
- eventHtml += '
';
-
- $weekDay.each(function() {
- var $calEvent = $(eventHtml);
- $modifiedEvent = options.eventRender(calEvent, $calEvent);
- $calEvent = $modifiedEvent ? $modifiedEvent.appendTo($(this)) : $calEvent.appendTo($(this));
- $calEvent.css({lineHeight: (options.textSize + 2) + 'px', fontSize: options.textSize + 'px'});
-
- self._refreshEventDetails(calEvent, $calEvent);
- self._positionEvent($(this), $calEvent);
-
- //add to event list
- if ($calEventList) {
- $calEventList = $calEventList.add($calEvent);
- }
- else {
- $calEventList = $calEvent;
- }
- });
- $calEventList.show();
-
- if (!options.readonly && options.resizable(calEvent, $calEventList)) {
- self._addResizableToCalEvent(calEvent, $calEventList, $weekDay);
- }
- if (!options.readonly && options.draggable(calEvent, $calEventList)) {
- self._addDraggableToCalEvent(calEvent, $calEventList);
- }
- options.eventAfterRender(calEvent, $calEventList);
-
- return $calEventList;
-
- },
- addEvent: function() {
- return this._renderEvent.apply(this, arguments);
- },
-
- _adjustOverlappingEvents: function($weekDay) {
- var self = this;
- if (self.options.allowCalEventOverlap) {
- var groupsList = self._groupOverlappingEventElements($weekDay);
- $.each(groupsList, function() {
- var curGroups = this;
- $.each(curGroups, function(groupIndex) {
- var curGroup = this;
-
- // do we want events to be displayed as overlapping
- if (self.options.overlapEventsSeparate) {
- var newWidth = self.options.totalEventsWidthPercentInOneColumn / curGroups.length;
- var newLeft = groupIndex * newWidth;
- } else {
- // TODO what happens when the group has more than 10 elements
- var newWidth = self.options.totalEventsWidthPercentInOneColumn - ((curGroups.length - 1) * 10);
- var newLeft = groupIndex * 10;
- }
- $.each(curGroup, function() {
- // bring mouseovered event to the front
- if (!self.options.overlapEventsSeparate) {
- $(this).bind('mouseover.z-index', function() {
- var $elem = $(this);
- $.each(curGroup, function() {
- $(this).css({'z-index': '1'});
- });
- $elem.css({'z-index': '3'});
- });
- }
- $(this).css({width: newWidth + '%', left: newLeft + '%', right: 0});
- });
- });
- });
- }
- },
-
-
- /*
- * Find groups of overlapping events
- */
- _groupOverlappingEventElements: function($weekDay) {
- var $events = $weekDay.find('.wc-cal-event:visible');
- var sortedEvents = $events.sort(function(a, b) {
- return $(a).data('calEvent').start.getTime() - $(b).data('calEvent').start.getTime();
- });
-
- var lastEndTime = new Date(0, 0, 0);
- var groups = [];
- var curGroups = [];
- var $curEvent;
- $.each(sortedEvents, function() {
- $curEvent = $(this);
- //checks, if the current group list is not empty, if the overlapping is finished
- if (curGroups.length > 0) {
- if (lastEndTime.getTime() <= $curEvent.data('calEvent').start.getTime()) {
- //finishes the current group list by adding it to the resulting list of groups and cleans it
-
- groups.push(curGroups);
- curGroups = [];
- }
- }
-
- //finds the first group to fill with the event
- for (var groupIndex = 0; groupIndex < curGroups.length; groupIndex++) {
- if (curGroups[groupIndex].length > 0) {
- //checks if the event starts after the end of the last event of the group
- if (curGroups[groupIndex][curGroups[groupIndex].length - 1].data('calEvent').end.getTime() <= $curEvent.data('calEvent').start.getTime()) {
- curGroups[groupIndex].push($curEvent);
- if (lastEndTime.getTime() < $curEvent.data('calEvent').end.getTime()) {
- lastEndTime = $curEvent.data('calEvent').end;
- }
- return;
- }
- }
- }
- //if not found, creates a new group
- curGroups.push([$curEvent]);
- if (lastEndTime.getTime() < $curEvent.data('calEvent').end.getTime()) {
- lastEndTime = $curEvent.data('calEvent').end;
- }
- });
- //adds the last groups in result
- if (curGroups.length > 0) {
- groups.push(curGroups);
- }
- return groups;
- },
-
-
- /*
- * find the weekday in the current calendar that the calEvent falls within
- */
- _findWeekDayForEvent: function(calEvent, $weekDayColumns) {
-
- var $weekDay,
- options = this.options,
- showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
- user_ids = this._getEventUserId(calEvent);
-
- if (!$.isArray(user_ids)) {
- user_ids = [user_ids];
- }
-
- $weekDayColumns.each(function(index, curDay) {
- if ($(this).data('startDate').getTime() <= calEvent.start.getTime() &&
- $(this).data('endDate').getTime() >= calEvent.end.getTime() &&
- (!showAsSeparatedUser || $.inArray($(this).data('wcUserId'), user_ids) !== -1)
- ) {
- if ($weekDay) {
- $weekDay = $weekDay.add($(curDay));
- }
- else {
- $weekDay = $(curDay);
- }
- }
- });
-
- return $weekDay;
- },
-
- /*
- * update the events rendering in the calendar. Add if does not yet exist.
- */
- _updateEventInCalendar: function(calEvent) {
- var self = this;
- self._cleanEvent(calEvent);
-
- if (calEvent.id) {
- self.element.find('.wc-cal-event').each(function() {
- if ($(this).data('calEvent').id === calEvent.id || $(this).hasClass('wc-new-cal-event')) {
- $(this).remove();
- // return false;
- }
- });
- }
-
- var $weekDays = self._findWeekDayForEvent(calEvent, self.element.find('.wc-grid-row-events .wc-day-column-inner'));
- if ($weekDays) {
- $weekDays.each(function(index, weekDay) {
- var $weekDay = $(weekDay);
- var $calEvent = self._renderEvent(calEvent, $weekDay);
- self._adjustForEventCollisions($weekDay, $calEvent, calEvent, calEvent);
- self._refreshEventDetails(calEvent, $calEvent);
- self._positionEvent($weekDay, $calEvent);
- self._adjustOverlappingEvents($weekDay);
- });
- }
- },
-
- /*
- * Position the event element within the weekday based on it's start / end dates.
- */
- _positionEvent: function($weekDay, $calEvent) {
- var options = this.options;
- var calEvent = $calEvent.data('calEvent');
- var pxPerMillis = $weekDay.height() / options.millisToDisplay;
- var firstHourDisplayed = options.businessHours.limitDisplay ? options.businessHours.start : 0;
- var startMillis = this._getDSTdayShift(calEvent.start).getTime() - this._getDSTdayShift(new Date(calEvent.start.getFullYear(), calEvent.start.getMonth(), calEvent.start.getDate(), firstHourDisplayed)).getTime();
- var eventMillis = this._getDSTdayShift(calEvent.end).getTime() - this._getDSTdayShift(calEvent.start).getTime();
- var pxTop = pxPerMillis * startMillis;
- var pxHeight = pxPerMillis * eventMillis;
- //var pxHeightFallback = pxPerMillis * (60 / options.timeslotsPerHour) * 60 * 1000;
- $calEvent.css({top: pxTop, height: pxHeight || (pxPerMillis * 3600000 / options.timeslotsPerHour)});
- },
-
- /*
- * Determine the actual start and end times of a calevent based on it's
- * relative position within the weekday column and the starting hour of the
- * displayed calendar.
- */
- _getEventDurationFromPositionedEventElement: function($weekDay, $calEvent, top) {
- var options = this.options;
- var startOffsetMillis = options.businessHours.limitDisplay ? options.businessHours.start * 3600000 : 0;
- var start = new Date($weekDay.data('startDate').getTime() + startOffsetMillis + Math.round(top / options.timeslotHeight) * options.millisPerTimeslot);
- var end = new Date(start.getTime() + ($calEvent.height() / options.timeslotHeight) * options.millisPerTimeslot);
- return {start: this._getDSTdayShift(start, -1), end: this._getDSTdayShift(end, -1)};
- },
-
- /*
- * If the calendar does not allow event overlap, adjust the start or end date if necessary to
- * avoid overlapping of events. Typically, shortens the resized / dropped event to it's max possible
- * duration based on the overlap. If no satisfactory adjustment can be made, the event is reverted to
- * it's original location.
- */
- _adjustForEventCollisions: function($weekDay, $calEvent, newCalEvent, oldCalEvent, maintainEventDuration) {
- var options = this.options;
-
- if (options.allowCalEventOverlap) {
- return;
- }
- var adjustedStart, adjustedEnd;
- var self = this;
-
- $weekDay.find('.wc-cal-event').not($calEvent).each(function() {
- var currentCalEvent = $(this).data('calEvent');
-
- //has been dropped onto existing event overlapping the end time
- if (newCalEvent.start.getTime() < currentCalEvent.end.getTime() &&
- newCalEvent.end.getTime() >= currentCalEvent.end.getTime()) {
-
- adjustedStart = currentCalEvent.end;
- }
-
-
- //has been dropped onto existing event overlapping the start time
- if (newCalEvent.end.getTime() > currentCalEvent.start.getTime() &&
- newCalEvent.start.getTime() <= currentCalEvent.start.getTime()) {
-
- adjustedEnd = currentCalEvent.start;
- }
- //has been dropped inside existing event with same or larger duration
- if (oldCalEvent.resizable == false ||
- (newCalEvent.end.getTime() <= currentCalEvent.end.getTime() &&
- newCalEvent.start.getTime() >= currentCalEvent.start.getTime())) {
-
- adjustedStart = oldCalEvent.start;
- adjustedEnd = oldCalEvent.end;
- return false;
- }
-
- });
-
-
- newCalEvent.start = adjustedStart || newCalEvent.start;
-
- if (adjustedStart && maintainEventDuration) {
- newCalEvent.end = new Date(adjustedStart.getTime() + (oldCalEvent.end.getTime() - oldCalEvent.start.getTime()));
- self._adjustForEventCollisions($weekDay, $calEvent, newCalEvent, oldCalEvent);
- } else {
- newCalEvent.end = adjustedEnd || newCalEvent.end;
- }
-
-
- //reset if new cal event has been forced to zero size
- if (newCalEvent.start.getTime() >= newCalEvent.end.getTime()) {
- newCalEvent.start = oldCalEvent.start;
- newCalEvent.end = oldCalEvent.end;
- }
-
- $calEvent.data('calEvent', newCalEvent);
- },
-
- /**
- * Add draggable capabilities to an event
- */
- _addDraggableToCalEvent: function(calEvent, $calEvent) {
- var options = this.options;
-
- $calEvent.draggable({
- handle: '.wc-time',
- containment: 'div.wc-time-slots',
- snap: '.wc-day-column-inner',
- snapMode: 'inner',
- snapTolerance: options.timeslotHeight - 1,
- revert: 'invalid',
- opacity: 0.5,
- grid: [$calEvent.outerWidth() + 1, options.timeslotHeight],
- start: function(event, ui) {
- var $calEvent = ui.draggable || ui.helper;
- options.eventDrag(calEvent, $calEvent);
- }
- });
- },
-
- /*
- * Add droppable capabilites to weekdays to allow dropping of calEvents only
- */
- _addDroppableToWeekDay: function($weekDay) {
- var self = this;
- var options = this.options;
- $weekDay.droppable({
- accept: '.wc-cal-event',
- drop: function(event, ui) {
- var $calEvent = ui.draggable;
- var top = Math.round(parseInt(ui.position.top));
- var eventDuration = self._getEventDurationFromPositionedEventElement($weekDay, $calEvent, top);
- var calEvent = $calEvent.data('calEvent');
- var newCalEvent = $.extend(true, {}, calEvent, {start: eventDuration.start, end: eventDuration.end});
- var showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length;
- if (showAsSeparatedUser) {
- // we may have dragged the event on column with a new user.
- // nice way to handle that is:
- // - get the newly dragged on user
- // - check if user is part of the event
- // - if yes, nothing changes, if not, find the old owner to remove it and add new one
- var newUserId = $weekDay.data('wcUserId');
- var userIdList = self._getEventUserId(calEvent);
- var oldUserId = $(ui.draggable.parents('.wc-day-column-inner').get(0)).data('wcUserId');
- if (!$.isArray(userIdList)) {
- userIdList = [userIdList];
- }
- if ($.inArray(newUserId, userIdList) == -1) {
- // remove old user
- var _index = $.inArray(oldUserId, userIdList);
- userIdList.splice(_index, 1);
- // add new user ?
- if ($.inArray(newUserId, userIdList) == -1) {
- userIdList.push(newUserId);
- }
- }
- newCalEvent = self._setEventUserId(newCalEvent, ((userIdList.length == 1) ? userIdList[0] : userIdList));
- }
- self._adjustForEventCollisions($weekDay, $calEvent, newCalEvent, calEvent, true);
- var $weekDayColumns = self.element.find('.wc-day-column-inner');
-
- //trigger drop callback
- options.eventDrop(newCalEvent, calEvent, $calEvent);
-
- var $newEvent = self._renderEvent(newCalEvent, self._findWeekDayForEvent(newCalEvent, $weekDayColumns));
- $calEvent.hide();
-
- $calEvent.data('preventClick', true);
-
- var $weekDayOld = self._findWeekDayForEvent($calEvent.data('calEvent'), self.element.find('.wc-time-slots .wc-day-column-inner'));
-
- if ($weekDayOld.data('startDate') != $weekDay.data('startDate')) {
- self._adjustOverlappingEvents($weekDayOld);
- }
- self._adjustOverlappingEvents($weekDay);
-
- setTimeout(function() {
- $calEvent.remove();
- }, 1000);
-
- }
- });
- },
-
- /*
- * Add resizable capabilities to a calEvent
- */
- _addResizableToCalEvent: function(calEvent, $calEvent, $weekDay) {
- var self = this;
- var options = this.options;
- $calEvent.resizable({
- grid: options.timeslotHeight,
- containment: $weekDay,
- handles: 's',
- minHeight: options.timeslotHeight,
- stop: function(event, ui) {
- var $calEvent = ui.element;
- var newEnd = new Date($calEvent.data('calEvent').start.getTime() + Math.max(1, Math.round(ui.size.height / options.timeslotHeight)) * options.millisPerTimeslot);
- if (self._needDSTdayShift($calEvent.data('calEvent').start, newEnd))
- newEnd = self._getDSTdayShift(newEnd, -1);
- var newCalEvent = $.extend(true, {}, calEvent, {start: calEvent.start, end: newEnd});
- self._adjustForEventCollisions($weekDay, $calEvent, newCalEvent, calEvent);
-
- //trigger resize callback
- options.eventResize(newCalEvent, calEvent, $calEvent);
- self._refreshEventDetails(newCalEvent, $calEvent);
- self._positionEvent($weekDay, $calEvent);
- self._adjustOverlappingEvents($weekDay);
- $calEvent.data('preventClick', true);
- setTimeout(function() {
- $calEvent.removeData('preventClick');
- }, 500);
- }
- });
- $('.ui-resizable-handle', $calEvent).text('=');
- },
-
- /*
- * Refresh the displayed details of a calEvent in the calendar
- */
- _refreshEventDetails: function(calEvent, $calEvent) {
- var suffix = '';
- if (!this.options.readonly &&
- this.options.allowEventDelete &&
- this.options.deletable(calEvent,$calEvent)) {
- suffix = '
';
- }
- $calEvent.find('.wc-time').html(this.options.eventHeader(calEvent, this.element) + suffix);
- $calEvent.find('.wc-title').html(this.options.eventBody(calEvent, this.element));
- $calEvent.data('calEvent', calEvent);
- this.options.eventRefresh(calEvent, $calEvent);
- },
-
- /*
- * Clear all cal events from the calendar
- */
- _clearCalendar: function() {
- this.element.find('.wc-day-column-inner div').remove();
- this._clearFreeBusys();
- },
-
- /*
- * Scroll the calendar to a specific hour
- */
- _scrollToHour: function(hour, animate) {
- var self = this;
- var options = this.options;
- var $scrollable = this.element.find('.wc-scrollable-grid');
- var slot = hour;
- if (self.options.businessHours.limitDisplay) {
- if (hour <= self.options.businessHours.start) {
- slot = 0;
- } else if (hour >= self.options.businessHours.end) {
- slot = self.options.businessHours.end - self.options.businessHours.start - 1;
- } else {
- slot = hour - self.options.businessHours.start;
- }
- }
-
- //scroll to the hour plus some padding so that hour is in middle of viewport
- var hourHeaderHeight = this.element.find(".wc-grid-timeslot-header .wc-hour-header").outerHeight();
- var calHeight = this.element.find(".wc-scrollable-grid").outerHeight();
- var scroll = (hourHeaderHeight * slot) - calHeight/3;
- if (animate) {
- $scrollable.animate({scrollTop: scroll}, options.scrollToHourMillis);
- }
- else {
- $scrollable.animate({scrollTop: scroll}, 0);
- }
- },
-
- /*
- * find the hour (12 hour day) for a given hour index
- */
- _hourForIndex: function(index) {
- if (index === 0) { //midnight
- return 12;
- } else if (index < 13) { //am
- return index;
- } else { //pm
- return index - 12;
- }
- },
-
- _24HourForIndex: function(index) {
- if (index === 0) { //midnight
- return '00:00';
- } else if (index < 10) {
- return '0' + index + ':00';
- } else {
- return index + ':00';
- }
- },
-
- _amOrPm: function(hourOfDay) {
- return hourOfDay < 12 ? 'AM' : 'PM';
- },
-
- _isToday: function(date) {
- var clonedDate = this._cloneDate(date);
- this._clearTime(clonedDate);
- var today = MyDate();
- this._clearTime(today);
- return today.getTime() === clonedDate.getTime();
- },
-
- /*
- * Clean events to ensure correct format
- */
- _cleanEvents: function(events) {
- var self = this;
- $.each(events, function(i, event) {
- self._cleanEvent(event);
- });
- return events;
- },
-
- /*
- * Clean specific event
- */
- _cleanEvent: function(event) {
- event.start = this._cleanDate(event.start);
- event.end = this._cleanDate(event.end);
- if (!event.end) {
- event.end = this._addDays(this._cloneDate(event.start), 1);
- }
- },
-
- /*
- * Disable text selection of the elements in different browsers
- */
- _disableTextSelect: function($elements) {
- $elements.each(function() {
- $(this).attr('unselectable', 'on')
- .css({
- '-moz-user-select': '-moz-none',
- '-moz-user-select': 'none',
- '-o-user-select': 'none',
- '-khtml-user-select': 'none', /* you could also put this in a class */
- '-webkit-user-select': 'none',/* and add the CSS class here instead */
- '-ms-user-select': 'none',
- 'user-select': 'none'
- }).bind('selectstart', function () { return false; });
-
- });
- },
-
- /*
- * returns the date on the first millisecond of the week
- */
- _dateFirstDayOfWeek: function(date) {
- var self = this;
- var midnightCurrentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
- var adjustedDate = new Date(midnightCurrentDate);
- adjustedDate.setDate(adjustedDate.getDate() - self._getAdjustedDayIndex(midnightCurrentDate));
-
- return adjustedDate;
- },
-
- /*
- * returns the date on the first millisecond of the last day of the week
- */
- _dateLastDayOfWeek: function(date) {
- var self = this;
- var midnightCurrentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
- var adjustedDate = new Date(midnightCurrentDate);
- var daysToAdd = (self.options.daysToShow - 1 - self._getAdjustedDayIndex(midnightCurrentDate));
- adjustedDate.setDate(adjustedDate.getDate() + daysToAdd);
-
- return adjustedDate;
- },
-
- /**
- * fix the date if it is not within given options
- * minDate and maxDate
- */
- _fixMinMaxDate: function(date) {
- var minDate, maxDate;
- date = this._cleanDate(date);
-
- // not less than minDate
- if (this.options.minDate) {
- minDate = this._cleanDate(this.options.minDate);
- // midnight on minDate
- minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
- if (date.getTime() < minDate.getTime()) {
- this._trigger('reachedmindate', this.element, date);
- }
- date = this._cleanDate(Math.max(date.getTime(), minDate.getTime()));
- }
-
- // not more than maxDate
- if (this.options.maxDate) {
- maxDate = this._cleanDate(this.options.maxDate);
- // apply correction for max date if not startOnFirstDayOfWeek
- // to make sure no further date is displayed.
- // otherwise, the complement will still be shown
- if (!this._startOnFirstDayOfWeek()) {
- var day = maxDate.getDate() - this.options.daysToShow + 1;
- maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), day);
- }
- // microsecond before midnight on maxDate
- maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate(), 23, 59, 59, 999);
- if (date.getTime() > maxDate.getTime()) {
- this._trigger('reachedmaxdate', this.element, date);
- }
- date = this._cleanDate(Math.min(date.getTime(), maxDate.getTime()));
- }
-
- return date;
- },
-
- /*
- * gets the index of the current day adjusted based on options
- */
- _getAdjustedDayIndex: function(date) {
- if (!this._startOnFirstDayOfWeek()) {
- return 0;
- }
-
- var midnightCurrentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
- var currentDayOfStandardWeek = midnightCurrentDate.getDay();
- var days = [0, 1, 2, 3, 4, 5, 6];
- this._rotate(days, this._firstDayOfWeek());
- return days[currentDayOfStandardWeek];
- },
-
- _firstDayOfWeek: function() {
- if ($.isFunction(this.options.firstDayOfWeek)) {
- return this.options.firstDayOfWeek(this.element);
- }
- return this.options.firstDayOfWeek;
- },
-
- /*
- * returns the date on the last millisecond of the week
- */
- _dateLastMilliOfWeek: function(date) {
- var lastDayOfWeek = this._dateLastDayOfWeek(date);
- lastDayOfWeek = this._cloneDate(lastDayOfWeek);
- lastDayOfWeek.setDate(lastDayOfWeek.getDate() + 1);
- return lastDayOfWeek;
-
- },
-
- /*
- * Clear the time components of a date leaving the date
- * of the first milli of day
- */
- _clearTime: function(d) {
- d.setHours(0);
- d.setMinutes(0);
- d.setSeconds(0);
- d.setMilliseconds(0);
- return d;
- },
-
- /*
- * add specific number of days to date
- */
- _addDays: function(d, n, keepTime) {
- d.setDate(d.getDate() + n);
- if (keepTime) {
- return d;
- }
- return this._clearTime(d);
- },
-
- /*
- * Rotate an array by specified number of places.
- */
- _rotate: function(a /*array*/, p /* integer, positive integer rotate to the right, negative to the left... */) {
- for (var l = a.length, p = (Math.abs(p) >= l && (p %= l), p < 0 && (p += l), p), i, x; p; p = (Math.ceil(l / p) - 1) * p - l + (l = p)) {
- for (i = l; i > p; x = a[--i], a[i] = a[i - p], a[i - p] = x) {}
- }
- return a;
- },
-
- _cloneDate: function(d) {
- return new Date(d.getTime());
- },
-
- /**
- * Return a Date instance for different representations.
- * Valid representations are:
- * * timestamps
- * * Date objects
- * * textual representations (only these accepted by the Date
- * constructor)
- *
- * @return {Date} The clean date object.
- */
- _cleanDate: function(d) {
- if (typeof d === 'string') {
- // if is numeric
- if (!isNaN(Number(d))) {
- return this._cleanDate(parseInt(d, 10));
- }
-
- // this is a human readable date
- if (d[d.length - 1] !== 'Z') d += 'Z';
- var o = new Date(d);
- o.setTime(o.getTime() + (o.getTimezoneOffset() * 60 * 1000));
- return o;
- }
-
- if (typeof d === 'number') {
- return new Date(d);
- }
-
- return d;
- },
-
- /*
- * date formatting is adapted from
- * http://jacwright.com/projects/javascript/date_format
- */
- _formatDate: function(date, format) {
- var returnStr = '';
- for (var i = 0; i < format.length; i++) {
- var curChar = format.charAt(i);
- if (i !== 0 && format.charAt(i - 1) === '\\') {
- returnStr += curChar;
- }
- else if (this._replaceChars[curChar]) {
- returnStr += this._replaceChars[curChar](date, this);
- } else if (curChar !== '\\') {
- returnStr += curChar;
- }
- }
- return returnStr;
- },
-
- _replaceChars: {
- // Day
- d: function(date) { return (date.getDate() < 10 ? '0' : '') + date.getDate(); },
- D: function(date, calendar) { return calendar.options.shortDays[date.getDay()]; },
- j: function(date) { return date.getDate(); },
- l: function(date, calendar) { return calendar.options.longDays[date.getDay()]; },
- N: function(date) { var _d = date.getDay(); return _d ? _d : 7; },
- S: function(date) { return (date.getDate() % 10 == 1 && date.getDate() != 11 ? 'st' : (date.getDate() % 10 == 2 && date.getDate() != 12 ? 'nd' : (date.getDate() % 10 == 3 && date.getDate() != 13 ? 'rd' : 'th'))); },
- w: function(date) { return date.getDay(); },
- z: function(date) { var d = new Date(date.getFullYear(), 0, 1); return Math.ceil((date - d) / 86400000); }, // Fixed now
- // Week
- W: function(date) { var d = new Date(date.getFullYear(), 0, 1); return Math.ceil((((date - d) / 86400000) + d.getDay() + 1) / 7); }, // Fixed now
- // Month
- F: function(date, calendar) { return calendar.options.longMonths[date.getMonth()]; },
- m: function(date) { return (date.getMonth() < 9 ? '0' : '') + (date.getMonth() + 1); },
- M: function(date, calendar) { return calendar.options.shortMonths[date.getMonth()]; },
- n: function(date) { return date.getMonth() + 1; },
- t: function(date) { var d = date; return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate() }, // Fixed now, gets #days of date
- // Year
- L: function(date) { var year = date.getFullYear(); return (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)); }, // Fixed now
- o: function(date) { var d = new Date(date.valueOf()); d.setDate(d.getDate() - ((date.getDay() + 6) % 7) + 3); return d.getFullYear();}, //Fixed now
- Y: function(date) { return date.getFullYear(); },
- y: function(date) { return ('' + date.getFullYear()).substr(2); },
- // Time
- a: function(date) { return date.getHours() < 12 ? 'am' : 'pm'; },
- A: function(date) { return date.getHours() < 12 ? 'AM' : 'PM'; },
- B: function(date) { return Math.floor((((date.getUTCHours() + 1) % 24) + date.getUTCMinutes() / 60 + date.getUTCSeconds() / 3600) * 1000 / 24); }, // Fixed now
- g: function(date) { return date.getHours() % 12 || 12; },
- G: function(date) { return date.getHours(); },
- h: function(date) { return ((date.getHours() % 12 || 12) < 10 ? '0' : '') + (date.getHours() % 12 || 12); },
- H: function(date) { return (date.getHours() < 10 ? '0' : '') + date.getHours(); },
- i: function(date) { return (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); },
- s: function(date) { return (date.getSeconds() < 10 ? '0' : '') + date.getSeconds(); },
- u: function(date) { var m = date.getMilliseconds(); return (m < 10 ? '00' : (m < 100 ? '0' : '')) + m; },
- // Timezone
- e: function(date) { return 'Not Yet Supported'; },
- I: function(date) { return 'Not Yet Supported'; },
- O: function(date) { return (-date.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(date.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(date.getTimezoneOffset() / 60)) + '00'; },
- P: function(date) { return (-date.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(date.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(date.getTimezoneOffset() / 60)) + ':00'; }, // Fixed now
- T: function(date) { var m = date.getMonth(); date.setMonth(0); var result = date.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/, '$1'); date.setMonth(m); return result;},
- Z: function(date) { return -date.getTimezoneOffset() * 60; },
- // Full Date/Time
- c: function(date, calendar) { return calendar._formatDate(date, 'Y-m-d\\TH:i:sP'); }, // Fixed now
- r: function(date, calendar) { return calendar._formatDate(date, 'D, d M Y H:i:s O'); },
- U: function(date) { return date.getTime() / 1000; }
- },
-
- /* USER MANAGEMENT FUNCTIONS */
-
- getUserForId: function(id) {
- return $.extend({}, this.options.users[this._getUserIndexFromId(id)]);
- },
-
- /**
- * return the user name for header
- */
- _getUserName: function(index) {
- var self = this;
- var options = this.options;
- var user = options.users[index];
- if ($.isFunction(options.getUserName)) {
- return options.getUserName(user, index, self.element);
- }
- else {
- return user;
- }
- },
- /**
- * return the user id for given index
- */
- _getUserIdFromIndex: function(index) {
- var self = this;
- var options = this.options;
- if ($.isFunction(options.getUserId)) {
- return options.getUserId(options.users[index], index, self.element);
- }
- return index;
- },
- /**
- * returns the associated user index for given ID
- */
- _getUserIndexFromId: function(id) {
- var self = this;
- var options = this.options;
- for (var i = 0; i < options.users.length; i++) {
- if (self._getUserIdFromIndex(i) == id) {
- return i;
- }
- }
- return 0;
- },
- /**
- * return the user ids for given calEvent.
- * default is calEvent.userId field.
- */
- _getEventUserId: function(calEvent) {
- var self = this;
- var options = this.options;
- if (options.showAsSeparateUsers && options.users && options.users.length) {
- if ($.isFunction(options.getEventUserId)) {
- return options.getEventUserId(calEvent, self.element);
- }
- return calEvent.userId;
- }
- return [];
- },
- /**
- * sets the event user id on given calEvent
- * default is calEvent.userId field.
- */
- _setEventUserId: function(calEvent, userId) {
- var self = this;
- var options = this.options;
- if ($.isFunction(options.setEventUserId)) {
- return options.setEventUserId(userId, calEvent, self.element);
- }
- calEvent.userId = userId;
- return calEvent;
- },
- /**
- * return the user ids for given freeBusy.
- * default is freeBusy.userId field.
- */
- _getFreeBusyUserId: function(freeBusy) {
- var self = this;
- var options = this.options;
- if ($.isFunction(options.getFreeBusyUserId)) {
- return options.getFreeBusyUserId(freeBusy.getOption(), self.element);
- }
- return freeBusy.getOption('userId');
- },
-
- /* FREEBUSY MANAGEMENT */
-
- /**
- * ckean the free busy managers and remove all the freeBusy
- */
- _clearFreeBusys: function() {
- if (this.options.displayFreeBusys) {
- var self = this,
- options = this.options,
- $freeBusyPlaceholders = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy');
- $freeBusyPlaceholders.each(function() {
- $(this).data('wcFreeBusyManager', new FreeBusyManager({
- start: self._cloneDate($(this).data('startDate')),
- end: self._cloneDate($(this).data('endDate')),
- defaultFreeBusy: options.defaultFreeBusy || {}
- }));
- });
- self.element.find('.wc-grid-row-freebusy .wc-freebusy').remove();
- }
- },
- /**
- * retrieve placeholders for given freebusy
- */
- _findWeekDaysForFreeBusy: function(freeBusy, $weekDays) {
- var $returnWeekDays,
- options = this.options,
- showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
- self = this,
- userList = self._getFreeBusyUserId(freeBusy);
- if (!$.isArray(userList)) {
- userList = userList != 'undefined' ? [userList] : [];
- }
- if (!$weekDays) {
- $weekDays = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy');
- }
- $weekDays.each(function() {
- var manager = $(this).data('wcFreeBusyManager'),
- has_overlap = manager.isWithin(freeBusy.getStart()) ||
- manager.isWithin(freeBusy.getEnd()) ||
- freeBusy.isWithin(manager.getStart()) ||
- freeBusy.isWithin(manager.getEnd()),
- userId = $(this).data('wcUserId');
- if (has_overlap && (!showAsSeparatedUser || ($.inArray(userId, userList) != -1))) {
- $returnWeekDays = $returnWeekDays ? $returnWeekDays.add($(this)) : $(this);
- }
- });
- return $returnWeekDays;
- },
-
- /**
- * used to render all freeBusys
- */
- _renderFreeBusys: function(freeBusys) {
- if (this.options.displayFreeBusys) {
- var self = this,
- $freeBusyPlaceholders = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy'),
- freebusysToRender;
- //insert freebusys to dedicated placeholders freebusy managers
- if ($.isArray(freeBusys)) {
- freebusysToRender = self._cleanFreeBusys(freeBusys);
- } else if (freeBusys.freebusys) {
- freebusysToRender = self._cleanFreeBusys(freeBusys.freebusys);
- }
- else {
- freebusysToRender = [];
- }
-
- $.each(freebusysToRender, function(index, freebusy) {
- var $placeholders = self._findWeekDaysForFreeBusy(freebusy, $freeBusyPlaceholders);
- if ($placeholders) {
- $placeholders.each(function() {
- var manager = $(this).data('wcFreeBusyManager');
- manager.insertFreeBusy(new FreeBusy(freebusy.getOption()));
- $(this).data('wcFreeBusyManager', manager);
- });
- }
- });
-
- //now display freebusys on place holders
- self._refreshFreeBusys($freeBusyPlaceholders);
- }
- },
- /**
- * refresh freebusys for given placeholders
- */
- _refreshFreeBusys: function($freeBusyPlaceholders) {
- if (this.options.displayFreeBusys && $freeBusyPlaceholders) {
- var self = this,
- options = this.options,
- start = (options.businessHours.limitDisplay ? options.businessHours.start : 0),
- end = (options.businessHours.limitDisplay ? options.businessHours.end : 24);
-
- $freeBusyPlaceholders.each(function() {
- var $placehoder = $(this);
- var s = self._cloneDate($placehoder.data('startDate')),
- e = self._cloneDate(s);
- s.setHours(start);
- e.setHours(end);
- $placehoder.find('.wc-freebusy').remove();
- $.each($placehoder.data('wcFreeBusyManager').getFreeBusys(s, e), function() {
- self._renderFreeBusy(this, $placehoder);
- });
- });
- }
- },
- /**
- * render a freebusy item on dedicated placeholders
- */
- _renderFreeBusy: function(freeBusy, $freeBusyPlaceholder) {
- if (this.options.displayFreeBusys) {
- var self = this,
- options = this.options,
- freeBusyHtml = '
';
-
- var $fb = $(freeBusyHtml);
- $fb.data('wcFreeBusy', new FreeBusy(freeBusy.getOption()));
- this._positionFreeBusy($freeBusyPlaceholder, $fb);
- $fb = options.freeBusyRender(freeBusy.getOption(), $fb, self.element);
- if ($fb) {
- $fb.appendTo($freeBusyPlaceholder);
- }
- }
- },
- /*
- * Position the freebusy element within the weekday based on it's start / end dates.
- */
- _positionFreeBusy: function($placeholder, $freeBusy) {
- var options = this.options;
- var freeBusy = $freeBusy.data('wcFreeBusy');
- var pxPerMillis = $placeholder.height() / options.millisToDisplay;
- var firstHourDisplayed = options.businessHours.limitDisplay ? options.businessHours.start : 0;
- var startMillis = freeBusy.getStart().getTime() - new Date(freeBusy.getStart().getFullYear(), freeBusy.getStart().getMonth(), freeBusy.getStart().getDate(), firstHourDisplayed).getTime();
- var eventMillis = freeBusy.getEnd().getTime() - freeBusy.getStart().getTime();
- var pxTop = pxPerMillis * startMillis;
- var pxHeight = pxPerMillis * eventMillis;
- $freeBusy.css({top: pxTop, height: pxHeight});
- },
- /*
- * Clean freebusys to ensure correct format
- */
- _cleanFreeBusys: function(freebusys) {
- var self = this,
- freeBusyToReturn = [];
- if (!$.isArray(freebusys)) {
- var freebusys = [freebusys];
- }
- $.each(freebusys, function(i, freebusy) {
- if (!freebusy) return;
- freeBusyToReturn.push(new FreeBusy(self._cleanFreeBusy(freebusy)));
- });
- return freeBusyToReturn;
- },
-
- /*
- * Clean specific freebusy
- */
- _cleanFreeBusy: function(freebusy) {
- if (freebusy.date) {
- freebusy.start = freebusy.date;
- }
- freebusy.start = this._cleanDate(freebusy.start);
- freebusy.end = this._cleanDate(freebusy.end);
- return freebusy;
- },
-
- /**
- * retrives the first freebusy manager matching demand.
- */
- getFreeBusyManagersFor: function(date, users) {
- var calEvent = {
- start: date,
- end: date
- };
- this._setEventUserId(calEvent, users);
- return this.getFreeBusyManagerForEvent(calEvent);
- },
- /**
- * retrives the first freebusy manager for given event.
- */
- getFreeBusyManagerForEvent: function(newCalEvent) {
- var self = this,
- options = this.options,
- freeBusyManager;
- if (options.displayFreeBusys) {
- var $freeBusyPlaceHoders = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy'),
- freeBusy = new FreeBusy({start: newCalEvent.start, end: newCalEvent.end}),
- showAsSeparatedUser = options.showAsSeparateUsers && options.users && options.users.length,
- userId = showAsSeparatedUser ? self._getEventUserId(newCalEvent) : null;
- if (!$.isArray(userId)) {
- userId = [userId];
- }
- $freeBusyPlaceHoders.each(function() {
- var manager = $(this).data('wcFreeBusyManager'),
- has_overlap = manager.isWithin(freeBusy.getEnd()) ||
- manager.isWithin(freeBusy.getEnd()) ||
- freeBusy.isWithin(manager.getStart()) ||
- freeBusy.isWithin(manager.getEnd());
- if (has_overlap && (!showAsSeparatedUser || $.inArray($(this).data('wcUserId'), userId) != -1)) {
- freeBusyManager = $(this).data('wcFreeBusyManager');
- return false;
- }
- });
- }
- return freeBusyManager;
- },
- /**
- * appends the freebusys to replace the old ones.
- * @param {array|object} freeBusys freebusy(s) to apply.
- */
- updateFreeBusy: function(freeBusys) {
- var self = this,
- options = this.options;
- if (options.displayFreeBusys) {
- var $toRender,
- $freeBusyPlaceHoders = self.element.find('.wc-grid-row-freebusy .wc-column-freebusy'),
- _freeBusys = self._cleanFreeBusys(freeBusys);
-
- $.each(_freeBusys, function(index, _freeBusy) {
-
- var $weekdays = self._findWeekDaysForFreeBusy(_freeBusy, $freeBusyPlaceHoders);
- //if freebusy has a placeholder
- if ($weekdays && $weekdays.length) {
- $weekdays.each(function(index, day) {
- var manager = $(day).data('wcFreeBusyManager');
- manager.insertFreeBusy(_freeBusy);
- $(day).data('wcFreeBusyManager', manager);
- });
- $toRender = $toRender ? $toRender.add($weekdays) : $weekdays;
- }
- });
- self._refreshFreeBusys($toRender);
- }
- },
-
- /* NEW OPTIONS MANAGEMENT */
-
- /**
- * checks wether or not the calendar should be displayed starting on first day of week
- */
- _startOnFirstDayOfWeek: function() {
- return jQuery.isFunction(this.options.startOnFirstDayOfWeek) ? this.options.startOnFirstDayOfWeek(this.element) : this.options.startOnFirstDayOfWeek;
- },
-
- /**
- * finds out the current scroll to apply it when changing the view
- */
- _getCurrentScrollHour: function() {
- var self = this;
- var options = this.options;
- var $scrollable = this.element.find('.wc-scrollable-grid');
- var scroll = $scrollable.scrollTop();
- if (self.options.businessHours.limitDisplay) {
- scroll = scroll + options.businessHours.start * options.timeslotHeight * options.timeslotsPerHour;
- }
- return Math.round(scroll / (options.timeslotHeight * options.timeslotsPerHour)) + 1;
- },
- _getJsonOptions: function() {
- if ($.isFunction(this.options.jsonOptions)) {
- return $.extend({}, this.options.jsonOptions(this.element));
- }
- if ($.isPlainObject(this.options.jsonOptions)) {
- return $.extend({}, this.options.jsonOptions);
- }
- return {};
- },
- _getHeaderDate: function(date) {
- var options = this.options;
- if (options.getHeaderDate && $.isFunction(options.getHeaderDate))
- {
- return options.getHeaderDate(date, this.element);
- }
- var dayName = options.useShortDayNames ? options.shortDays[date.getDay()] : options.longDays[date.getDay()];
- return dayName + (options.headerSeparator) + this._formatDate(date, options.dateFormat);
- },
-
-
-
- /**
- * returns corrected date related to DST problem
- */
- _getDSTdayShift: function(date, shift) {
- var start = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0);
- var offset1 = start.getTimezoneOffset();
- var offset2 = date.getTimezoneOffset();
- if (offset1 == offset2)
- return date;
- shift = shift ? shift : 1;
- return new Date(date.getTime() - shift * (offset1 > offset2 ? -1 : 1) * (Math.max(offset1, offset2) - Math.min(offset1, offset2)) * 60000);
- },
- _needDSTdayShift: function(date1, date2) {
- return date1.getTimezoneOffset() != date2.getTimezoneOffset();
- }
-
-
-
- }; // end of widget function return
- })() //end of widget function closure execution
- ); // end of $.widget("ui.weekCalendar"...
-
- $.extend($.ui.weekCalendar, {
- version: '2.0-dev',
- updateLayoutOptions: {
- startOnFirstDayOfWeek: true,
- firstDayOfWeek: true,
- daysToShow: true,
- displayOddEven: true,
- timeFormat: true,
- dateFormat: true,
- use24Hour: true,
- useShortDayNames: true,
- businessHours: true,
- timeslotHeight: true,
- timeslotsPerHour: true,
- buttonText: true,
- height: true,
- shortMonths: true,
- longMonths: true,
- shortDays: true,
- longDays: true,
- textSize: true,
- users: true,
- showAsSeparateUsers: true,
- displayFreeBusys: true
- }
- });
-
- var MILLIS_IN_DAY = 86400000;
- var MILLIS_IN_WEEK = MILLIS_IN_DAY * 7;
-
- /* FREE BUSY MANAGERS */
- var FreeBusyProto = {
- getStart: function() {return this.getOption('start')},
- getEnd: function() {return this.getOption('end')},
- getOption: function() {
- if (!arguments.length) { return this.options }
- if (typeof(this.options[arguments[0]]) !== 'undefined') {
- return this.options[arguments[0]];
- }
- else if (typeof(arguments[1]) !== 'undefined') {
- return arguments[1];
- }
- return null;
- },
- setOption: function(key, value) {
- if (arguments.length == 1) {
- $.extend(this.options, arguments[0]);
- return this;
- }
- this.options[key] = value;
- return this;
- },
- isWithin: function(dateTime) {return Math.floor(dateTime.getTime() / 1000) >= Math.floor(this.getStart().getTime() / 1000) && Math.floor(dateTime.getTime() / 1000) < Math.floor(this.getEnd().getTime() / 1000)},
- isValid: function() {return this.getStart().getTime() < this.getEnd().getTime()}
- };
-
- /**
- * @constructor
- * single user freebusy manager.
- */
- var FreeBusy = function(options) {
- this.options = $.extend({}, options || {});
- };
- $.extend(FreeBusy.prototype, FreeBusyProto);
-
- var FreeBusyManager = function(options) {
- this.options = $.extend({
- defaultFreeBusy: {}
- }, options || {});
- this.freeBusys = [];
- this.freeBusys.push(new FreeBusy($.extend({
- start: this.getStart(),
- end: this.getEnd()
- }, this.options.defaultFreeBusy)));
- };
- $.extend(FreeBusyManager.prototype, FreeBusyProto, {
- /**
- * return matching freeBusys.
- * if you do not pass any argument, returns all freebusys.
- * if you only pass a start date, only matchinf freebusy will be returned.
- * if you pass 2 arguments, then all freebusys available within the time period will be returned
- * @param {Date} start [optionnal] if you do not pass end date, will return the freeBusy within which this date falls.
- * @param {Date} end [optionnal] the date where to stop the search.
- * @return {Array} an array of FreeBusy matching arguments.
- */
- getFreeBusys: function() {
- switch (arguments.length) {
- case 0:
- return this.freeBusys;
- case 1:
- var freeBusy = [];
- var start = arguments[0];
- if (!this.isWithin(start)) {
- return freeBusy;
- }
- $.each(this.freeBusys, function() {
- if (this.isWithin(start)) {
- freeBusy.push(this);
- }
- if (Math.floor(this.getEnd().getTime() / 1000) > Math.floor(start.getTime() / 1000)) {
- return false;
- }
- });
- return freeBusy;
- default:
- //we assume only 2 first args are revealants
- var freeBusy = [];
- var start = arguments[0], end = arguments[1];
- var tmpFreeBusy = new FreeBusy({start: start, end: end});
- if (end.getTime() < start.getTime() || this.getStart().getTime() > end.getTime() || this.getEnd().getTime() < start.getTime()) {
- return freeBusy;
- }
- $.each(this.freeBusys, function() {
- if (this.getStart().getTime() >= end.getTime()) {
- return false;
- }
- if (tmpFreeBusy.isWithin(this.getStart()) && tmpFreeBusy.isWithin(this.getEnd())) {
- freeBusy.push(this);
- }
- else if (this.isWithin(tmpFreeBusy.getStart()) && this.isWithin(tmpFreeBusy.getEnd())) {
- var _f = new FreeBusy(this.getOption());
- _f.setOption('end', tmpFreeBusy.getEnd());
- _f.setOption('start', tmpFreeBusy.getStart());
- freeBusy.push(_f);
- }
- else if (this.isWithin(tmpFreeBusy.getStart()) && this.getStart().getTime() < start.getTime()) {
- var _f = new FreeBusy(this.getOption());
- _f.setOption('start', tmpFreeBusy.getStart());
- freeBusy.push(_f);
- }
- else if (this.isWithin(tmpFreeBusy.getEnd()) && this.getEnd().getTime() > end.getTime()) {
- var _f = new FreeBusy(this.getOption());
- _f.setOption('end', tmpFreeBusy.getEnd());
- freeBusy.push(_f);
- }
- });
- return freeBusy;
- }
- },
- insertFreeBusy: function(freeBusy) {
- var freeBusy = new FreeBusy(freeBusy.getOption());
- //first, if inserted freebusy is bigger than manager
- if (freeBusy.getStart().getTime() < this.getStart().getTime()) {
- freeBusy.setOption('start', this.getStart());
- }
- if (freeBusy.getEnd().getTime() > this.getEnd().getTime()) {
- freeBusy.setOption('end', this.getEnd());
- }
- var start = freeBusy.getStart(), end = freeBusy.getEnd(),
- startIndex = 0, endIndex = this.freeBusys.length - 1,
- newFreeBusys = [];
- var pushNewFreeBusy = function(_f) {if (_f.isValid()) newFreeBusys.push(_f);};
-
- $.each(this.freeBusys, function(index) {
- //within the loop, we have following vars:
- // curFreeBusyItem: the current iteration freeBusy, part of manager freeBusys list
- // start: the insterted freeBusy start
- // end: the inserted freebusy end
- var curFreeBusyItem = this;
- if (curFreeBusyItem.isWithin(start) && curFreeBusyItem.isWithin(end)) {
- /*
- we are in case where inserted freebusy fits in curFreeBusyItem:
- curFreeBusyItem: *-----------------------------*
- freeBusy: *-------------*
- obviously, start and end indexes are this item.
- */
- startIndex = index;
- endIndex = index;
- if (start.getTime() == curFreeBusyItem.getStart().getTime() && end.getTime() == curFreeBusyItem.getEnd().getTime()) {
- /*
- in this case, inserted freebusy is exactly curFreeBusyItem:
- curFreeBusyItem: *-----------------------------*
- freeBusy: *-----------------------------*
-
- just replace curFreeBusyItem with freeBusy.
- */
- var _f1 = new FreeBusy(freeBusy.getOption());
- pushNewFreeBusy(_f1);
- }
- else if (start.getTime() == curFreeBusyItem.getStart().getTime()) {
- /*
- in this case inserted freebusy starts with curFreeBusyItem:
- curFreeBusyItem: *-----------------------------*
- freeBusy: *--------------*
-
- just replace curFreeBusyItem with freeBusy AND the rest.
- */
- var _f1 = new FreeBusy(freeBusy.getOption());
- var _f2 = new FreeBusy(curFreeBusyItem.getOption());
- _f2.setOption('start', end);
- pushNewFreeBusy(_f1);
- pushNewFreeBusy(_f2);
- }
- else if (end.getTime() == curFreeBusyItem.getEnd().getTime()) {
- /*
- in this case inserted freebusy ends with curFreeBusyItem:
- curFreeBusyItem: *-----------------------------*
- freeBusy: *--------------*
-
- just replace curFreeBusyItem with before part AND freeBusy.
- */
- var _f1 = new FreeBusy(curFreeBusyItem.getOption());
- _f1.setOption('end', start);
- var _f2 = new FreeBusy(freeBusy.getOption());
- pushNewFreeBusy(_f1);
- pushNewFreeBusy(_f2);
- }
- else {
- /*
- in this case inserted freebusy is within curFreeBusyItem:
- curFreeBusyItem: *-----------------------------*
- freeBusy: *--------------*
-
- just replace curFreeBusyItem with before part AND freeBusy AND the rest.
- */
- var _f1 = new FreeBusy(curFreeBusyItem.getOption());
- var _f2 = new FreeBusy(freeBusy.getOption());
- var _f3 = new FreeBusy(curFreeBusyItem.getOption());
- _f1.setOption('end', start);
- _f3.setOption('start', end);
- pushNewFreeBusy(_f1);
- pushNewFreeBusy(_f2);
- pushNewFreeBusy(_f3);
- }
- /*
- as work is done, no need to go further.
- return false
- */
- return false;
- }
- else if (curFreeBusyItem.isWithin(start) && curFreeBusyItem.getEnd().getTime() != start.getTime()) {
- /*
- in this case, inserted freebusy starts within curFreeBusyItem:
- curFreeBusyItem: *----------*
- freeBusy: *-------------------*
-
- set start index AND insert before part, we'll insert freebusy later
- */
- if (curFreeBusyItem.getStart().getTime() != start.getTime()) {
- var _f1 = new FreeBusy(curFreeBusyItem.getOption());
- _f1.setOption('end', start);
- pushNewFreeBusy(_f1);
- }
- startIndex = index;
- }
- else if (curFreeBusyItem.isWithin(end) && curFreeBusyItem.getStart().getTime() != end.getTime()) {
- /*
- in this case, inserted freebusy starts within curFreeBusyItem:
- curFreeBusyItem: *----------*
- freeBusy: *-------------------*
-
- set end index AND insert freebusy AND insert after part if needed
- */
- pushNewFreeBusy(new FreeBusy(freeBusy.getOption()));
- if (end.getTime() < curFreeBusyItem.getEnd().getTime()) {
- var _f1 = new FreeBusy(curFreeBusyItem.getOption());
- _f1.setOption('start', end);
- pushNewFreeBusy(_f1);
- }
- endIndex = index;
- return false;
- }
- });
- //now compute arguments
- var tmpFB = this.freeBusys;
- this.freeBusys = [];
-
- if (startIndex) {
- this.freeBusys = this.freeBusys.concat(tmpFB.slice(0, startIndex));
- }
- this.freeBusys = this.freeBusys.concat(newFreeBusys);
- if (endIndex < tmpFB.length) {
- this.freeBusys = this.freeBusys.concat(tmpFB.slice(endIndex + 1));
- }
-/* if(start.getDate() == 1){
- console.info('insert from '+freeBusy.getStart() +' to '+freeBusy.getEnd());
- console.log('index from '+ startIndex + ' to ' + endIndex);
- var str = [];
- $.each(tmpFB, function(i){str.push(i + ": " + this.getStart().getHours() + ' > ' + this.getEnd().getHours() + ' ' + (this.getOption('free') ? 'free' : 'busy'))});
- console.log(str.join('\n'));
-
- console.log('insert');
- var str = [];
- $.each(newFreeBusys, function(i){str.push(this.getStart().getHours() + ' > ' + this.getEnd().getHours())});
- console.log(str.join(', '));
-
- console.log('results');
- var str = [];
- $.each(this.freeBusys, function(i){str.push(i + ": " + this.getStart().getHours() + ' > ' + this.getEnd().getHours() + ' ' + (this.getOption('free') ? 'free' :'busy'))});
- console.log(str.join('\n'));
- }*/
- return this;
- }
- });
-})(jQuery);
-
diff --git a/modules-available/locationinfo/frontend/panel.html b/modules-available/locationinfo/frontend/panel.html
deleted file mode 100644
index dd5fc25d..00000000
--- a/modules-available/locationinfo/frontend/panel.html
+++ /dev/null
@@ -1,700 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/modules-available/locationinfo/inc/infopanel.inc.php b/modules-available/locationinfo/inc/infopanel.inc.php
new file mode 100644
index 00000000..66ee0ae7
--- /dev/null
+++ b/modules-available/locationinfo/inc/infopanel.inc.php
@@ -0,0 +1,219 @@
+ $lid,
+ 'name' => isset($locations[$lid]) ? $locations[$lid]['locationname'] : 'noname00.pas',
+ );
+ if (isset($overrides[$lid]) && is_array($overrides[$lid])) {
+ $config['locations'][$lid]['config'] = $overrides[$lid];
+ }
+ }
+ self::appendMachineData($config['locations'], $lids, true);
+ self::appendOpeningTimes($config['locations'], $lids);
+
+ $config['ts'] = (int)$panel['lastchange'];
+ $config['locations'] = array_values($config['locations']);
+ $config['time'] = date('Y-n-j-G-') . (int)date('i') . '-' . (int)(date('s'));
+
+ return $panel['paneltype'];
+ }
+
+ /**
+ * Gets the location info of the given locations.
+ * Append to passed array which is expected to
+ * map location ids to properties of that location.
+ * A new key 'machines' will be created in each
+ * entry of $array that will take all the machine data.
+ *
+ * @param array $array location list to populate with machine data
+ * @param bool $withPosition Defines if coords should be included or not.
+ */
+ public static function appendMachineData(&$array, $idList = false, $withPosition = false)
+ {
+ if (empty($array) && $idList === false)
+ return;
+ if ($idList === false) {
+ $idList = array_keys($array);
+ }
+
+ $positionCol = $withPosition ? 'm.position,' : '';
+ $query = "SELECT m.locationid, m.machineuuid, $positionCol m.logintime, m.lastseen, m.lastboot FROM machine m
+ WHERE m.locationid IN (:idlist)";
+ $dbquery = Database::simpleQuery($query, array('idlist' => $idList));
+
+ // Iterate over matching machines
+ while ($row = $dbquery->fetch(PDO::FETCH_ASSOC)) {
+ settype($row['locationid'], 'int');
+ if (!isset($array[$row['locationid']])) {
+ $array[$row['locationid']] = array('id' => $row['locationid'], 'machines' => array());
+ }
+ if (!isset($array[$row['locationid']]['machines'])) {
+ $array[$row['locationid']]['machines'] = array();
+ }
+ // Compact the pc data in one array.
+ $pc = array('id' => $row['machineuuid']);
+ if ($withPosition && !empty($row['position'])) {
+ $position = json_decode($row['position'], true);
+ if (isset($position['gridCol']) && isset($position['gridRow'])) {
+ $pc['x'] = $position['gridCol'];
+ $pc['y'] = $position['gridRow'];
+ if (!empty($position['overlays']) && is_array($position['overlays'])) {
+ $pc['overlays'] = $position['overlays'];
+ }
+ }
+ }
+ $pc['pcState'] = LocationInfo::getPcState($row);
+ //$pc['pcState'] = ['BROKEN', 'OFF', 'IDLE', 'OCCUPIED'][mt_rand(0,3)]; // XXX
+
+ // Add the array to the machines list.
+ $array[$row['locationid']]['machines'][] = $pc;
+ }
+ }
+
+ /**
+ * Gets the Opening time of the given locations.
+ *
+ * @param array $array list of locations, indexed by locationId
+ * @param int[] $idList list of locations
+ */
+ public static function appendOpeningTimes(&$array, $idList)
+ {
+ // First, lets get all the parent ids for the given locations
+ // in case we need to get inherited opening times
+ $allIds = self::getLocationsWithParents($idList);
+ if (empty($allIds))
+ return;
+ $res = Database::simpleQuery("SELECT locationid, openingtime FROM locationinfo_locationconfig
+ WHERE locationid IN (:lids)", array('lids' => $allIds));
+ $openingTimes = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $openingTimes[(int)$row['locationid']] = $row;
+ }
+ // Now we got all the calendars for locations and parents
+ // Iterate over the locations we're actually interested in
+ $locations = Location::getLocationsAssoc();
+ foreach ($idList as $locationId) {
+ // Start checking at actual location...
+ $currentId = $locationId;
+ while ($currentId !== 0) {
+ if (!empty($openingTimes[$currentId]['openingtime'])) {
+ $cal = json_decode($openingTimes[$currentId]['openingtime'], true);
+ if (is_array($cal)) {
+ $cal = self::formatOpeningtime($cal);
+ }
+ if (!empty($cal)) {
+ // Got a valid calendar
+ if (!isset($array[$locationId])) {
+ $array[$locationId] = array('id' => $locationId);
+ }
+ $array[$locationId]['openingtime'] = $cal;
+ break;
+ }
+ }
+ // Keep trying with parent
+ $currentId = $locations[$currentId]['parentlocationid'];
+ }
+ }
+ return;
+ }
+
+
+ /**
+ * Returns all the passed location ids and appends
+ * all their direct and indirect parent location ids.
+ *
+ * @param int[] $idList location ids
+ * @return int[] more location ids
+ */
+ private static function getLocationsWithParents($idList)
+ {
+ $locations = Location::getLocationsAssoc();
+ $allIds = $idList;
+ foreach ($idList as $id) {
+ if (isset($locations[$id]) && isset($locations[$id]['parents'])) {
+ $allIds = array_merge($allIds, $locations[$id]['parents']);
+ }
+ }
+ return array_map('intval', $allIds);
+ }
+
+// ##########
##########
+
+ /**
+ * Format the openingtime in the frontend needed format.
+ * One key per week day, wich contains an array of {
+ * 'HourOpen' => hh, 'MinutesOpen' => mm,
+ * 'HourClose' => hh, 'MinutesClose' => mm }
+ *
+ * @param array $openingtime The opening time in the db saved format.
+ * @return mixed The opening time in the frontend needed format.
+ */
+ private static function formatOpeningtime($openingtime)
+ {
+ $result = array();
+ foreach ($openingtime as $entry) {
+ $openTime = explode(':', $entry['openingtime']);
+ $closeTime = explode(':', $entry['closingtime']);
+ if (count($openTime) !== 2 || count($closeTime) !== 2)
+ continue;
+ $convertedTime = array(
+ 'HourOpen' => $openTime[0],
+ 'MinutesOpen' => $openTime[1],
+ 'HourClose' => $closeTime[0],
+ 'MinutesClose' => $closeTime[1],
+ );
+ foreach ($entry['days'] as $day) {
+ if (!isset($result[$day])) {
+ $result[$day] = array();
+ }
+ $result[$day][] = $convertedTime;
+ }
+ }
+ return $result;
+ }
+
+}
\ No newline at end of file
diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php
index 6e6cfcd7..a5feb9ed 100644
--- a/modules-available/locationinfo/inc/locationinfo.inc.php
+++ b/modules-available/locationinfo/inc/locationinfo.inc.php
@@ -64,6 +64,7 @@ class LocationInfo
'mode' => 1,
'vertical' => false,
'eco' => false,
+ 'prettytime' => true,
'scaledaysauto' => true,
'daystoshow' => 7,
'rotation' => 0,
@@ -88,10 +89,21 @@ class LocationInfo
public static function configHook($machineUuid, $panelUuid)
{
- // TODO Panel type
+ $row = Database::queryFirst('SELECT paneltype, panelconfig FROM locationinfo_panel WHERE paneluuid = :uuid',
+ array('uuid' => $panelUuid));
+ if ($row === false) {
+ // TODO: Invalid panel - what should we do?
+ } elseif ($row['paneltype'] === 'URL') {
+ // Check if we should set the insecure SSL mode (accept invalid/self signed certs etc.)
+ $data = json_decode($row['panelconfig'], true);
+ if ($data && $data['insecure-ssl']) {
+ ConfigHolder::add('SLX_BROWSER_INSECURE', '1');
+ }
+ }
ConfigHolder::add('SLX_BROWSER_URL', 'http://' . $_SERVER['SERVER_ADDR'] . '/panel/' . $panelUuid);
ConfigHolder::add('SLX_ADDONS', '', 1000);
- ConfigHolder::add('SLX_LOGOUT_TIMEOUT', 1000);
+ ConfigHolder::add('SLX_LOGOUT_TIMEOUT', '', 1000);
+ ConfigHolder::add('SLX_SCREEN_STANDBY_TIMEOUT', '', 1000);
}
}
diff --git a/modules-available/locationinfo/install.inc.php b/modules-available/locationinfo/install.inc.php
index a738a863..10422241 100644
--- a/modules-available/locationinfo/install.inc.php
+++ b/modules-available/locationinfo/install.inc.php
@@ -26,7 +26,7 @@ $t3 = $res[] = tableCreate('locationinfo_panel', "
`paneluuid` char(36) CHARACTER SET ascii NOT NULL,
`panelname` varchar(30) NOT NULL,
`locationids` varchar(20) CHARACTER SET ascii NOT NULL,
- `paneltype` enum('DEFAULT','SUMMARY') NOT NULL,
+ `paneltype` enum('DEFAULT','SUMMARY', 'URL') NOT NULL,
`panelconfig` blob NOT NULL,
`lastchange` int(10) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`paneluuid`),
@@ -67,6 +67,11 @@ if ($t1 === UPDATE_DONE) {
}
}
+if ($t3 === UPDATE_NOOP) {
+ Database::exec("ALTER TABLE `locationinfo_panel` CHANGE `paneltype`
+ `paneltype` ENUM('DEFAULT', 'SUMMARY', 'URL') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL");
+}
+
// Create response for browser
if (in_array(UPDATE_RETRY, $res)) {
diff --git a/modules-available/locationinfo/lang/de/template-tags.json b/modules-available/locationinfo/lang/de/template-tags.json
index 9b30a14a..1574d9e8 100644
--- a/modules-available/locationinfo/lang/de/template-tags.json
+++ b/modules-available/locationinfo/lang/de/template-tags.json
@@ -91,5 +91,22 @@
"lang_typeTooltip": "Legt fest um welchen Server-Typ es sich handelt",
"lang_updateRates": "Aktualisierungsintervall",
"lang_vertical": "Vertikaler Modus",
- "lang_verticalTooltip": "Legt fest, ob Kalender und Raum \u00fcbereinander angezeigt werden sollen"
+ "lang_verticalTooltip": "Legt fest, ob Kalender und Raum \u00fcbereinander angezeigt werden sollen",
+ "lang_closed": "Geschlossen",
+ "lang_free": "Frei",
+ "lang_shortSun": "So",
+ "lang_shortMon": "Mo",
+ "lang_shortTue": "Di",
+ "lang_shortWed": "Mi",
+ "lang_shortThu": "Do",
+ "lang_shortFri": "Fr",
+ "lang_shortSat": "Sa",
+ "lang_longSun": "Sonntag",
+ "lang_longMon": "Montag",
+ "lang_longTue": "Dienstag",
+ "lang_longWed": "Mittwoch",
+ "lang_longThu": "Donnerstag",
+ "lang_longFri": "Freitag",
+ "lang_longSat": "Samstag",
+ "lang_to": "bis"
}
\ No newline at end of file
diff --git a/modules-available/locationinfo/lang/en/template-tags.json b/modules-available/locationinfo/lang/en/template-tags.json
index ae308281..900839a9 100644
--- a/modules-available/locationinfo/lang/en/template-tags.json
+++ b/modules-available/locationinfo/lang/en/template-tags.json
@@ -87,5 +87,22 @@
"lang_typeTooltip": "Defines on which type of server you want to connect to",
"lang_updateRates": "Update rates",
"lang_vertical": "Vertical mode",
- "lang_verticalTooltip": "Defines whether the room and calendar are shown above each other"
+ "lang_verticalTooltip": "Defines whether the room and calendar are shown above each other",
+ "lang_closed": "Closed",
+ "lang_free": "Free",
+ "lang_shortSun": "Sun",
+ "lang_shortMon": "Mon",
+ "lang_shortTue": "Tue",
+ "lang_shortWed": "Wed",
+ "lang_shortThu": "Thu",
+ "lang_shortFri": "Fri",
+ "lang_shortSat": "Sat",
+ "lang_longSun": "Sunday",
+ "lang_longMon": "Monday",
+ "lang_longTue": "Tuesday",
+ "lang_longWed": "Wednesday",
+ "lang_longThu": "Thursday",
+ "lang_longFri": "Friday",
+ "lang_longSat": "Saturday",
+ "lang_to": "to"
}
\ No newline at end of file
diff --git a/modules-available/locationinfo/lang/pt/template-tags.json b/modules-available/locationinfo/lang/pt/template-tags.json
new file mode 100644
index 00000000..e5c2aba8
--- /dev/null
+++ b/modules-available/locationinfo/lang/pt/template-tags.json
@@ -0,0 +1,21 @@
+{
+ "lang_room": "Quarto",
+ "lang_closed": "Fechado",
+ "lang_free": "Livre",
+ "lang_shortSun": "Do",
+ "lang_shortMon": "Se",
+ "lang_shortTue": "Te",
+ "lang_shortWed": "Qu",
+ "lang_shortThu": "Qu",
+ "lang_shortFri": "Se",
+ "lang_shortSat": "Sá",
+ "lang_longSun": "Domingo",
+ "lang_longMon": "Segunda-feira",
+ "lang_longTue": "Terça-feira",
+ "lang_longWed": "Quarta-feira",
+ "lang_longThu": "Quinta-feira",
+ "lang_longFri": "Sexta-feira",
+ "lang_longSat": "Sábado",
+ "lang_to": "para"
+
+}
\ No newline at end of file
diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php
index f5e64209..f8aa1c5b 100644
--- a/modules-available/locationinfo/page.inc.php
+++ b/modules-available/locationinfo/page.inc.php
@@ -10,13 +10,16 @@ class Page_LocationInfo extends Page
*/
protected function doPreprocess()
{
+ $show = Request::any('show', '', 'string');
+ if ($show === 'panel') {
+ $this->showPanel();
+ exit(0);
+ }
User::load();
if (!User::isLoggedIn()) {
Message::addError('main.no-permission');
Util::redirect('?do=Main'); // does not return
}
-
- $show = Request::any('show', '', 'string');
$this->action = Request::post('action');
if ($this->action === 'writePanelConfig') {
$this->writePanelConfig();
@@ -49,6 +52,7 @@ class Page_LocationInfo extends Page
*/
protected function doRender()
{
+ // Do this here so we always see backend errors
$backends = $this->loadBackends();
$show = Request::get('show', '', 'string');
Render::addTemplate('page-tabs', array('class-' . $show => 'active'));
@@ -77,7 +81,7 @@ class Page_LocationInfo extends Page
{
$id = Request::post('serverid', false, 'int');
if ($id === false) {
- Messages::addError('server-id-missing');
+ Message::addError('server-id-missing');
return;
}
$res = Database::exec("DELETE FROM `locationinfo_coursebackend` WHERE serverid=:id", array('id' => $id));
@@ -90,7 +94,7 @@ class Page_LocationInfo extends Page
{
$id = Request::post('uuid', false, 'string');
if ($id === false) {
- Messages::addError('main.parameter-missing', 'uuid');
+ Message::addError('main.parameter-missing', 'uuid');
return;
}
$res = Database::exec("DELETE FROM `locationinfo_panel` WHERE paneluuid = :id", array('id' => $id));
@@ -208,29 +212,78 @@ class Page_LocationInfo extends Page
}
/**
- * Updated the config in the db.
+ * Get all location ids from the locationids parameter, which is comma separated, then split
+ * and remove any ids that don't exist. The cleaned list will be returned
+ * @param bool $failIfEmpty Show error and redirect to main page if parameter is missing or list is empty
+ * @return array list of locations from parameter
*/
- private function writePanelConfig()
+ private function getLocationIdsFromRequest($failIfEmpty)
{
- // UUID - existing or new
- $paneluuid = Request::post('uuid', false, 'string');
- if (($paneluuid === false || strlen($paneluuid) !== 36) && $paneluuid !== 'new') {
- Message::addError('invalid-panel-id', $paneluuid);
- Util::redirect('?do=locationinfo');
- }
- // Check locations
$locationids = Request::post('locationids', false, 'string');
if ($locationids === false) {
+ if (!$failIfEmpty)
+ return array();
Message::addError('main.paramter-missing', 'locationids');
Util::redirect('?do=locationinfo');
}
$locationids = explode(',', $locationids);
$all = array_map(function ($item) { return $item['locationid']; }, Location::queryLocations());
$locationids = array_filter($locationids, function ($item) use ($all) { return in_array($item, $all); });
- if (empty($locationids)) {
+ if ($failIfEmpty && empty($locationids)) {
Message::addError('main.paramter-empty', 'locationids');
Util::redirect('?do=locationinfo');
}
+ return $locationids;
+ }
+
+ /**
+ * Updated the config in the db.
+ */
+ private function writePanelConfig()
+ {
+ // UUID - existing or new
+ $paneluuid = Request::post('uuid', false, 'string');
+ if (($paneluuid === false || strlen($paneluuid) !== 36) && $paneluuid !== 'new') {
+ Message::addError('invalid-panel-id', $paneluuid);
+ Util::redirect('?do=locationinfo');
+ }
+ // Check panel type
+ $paneltype = Request::post('ptype', false, 'string');
+
+ if ($paneltype === 'DEFAULT') {
+ $params = $this->preparePanelConfigDefault();
+ } elseif ($paneltype === 'URL') {
+ $params = $this->preparePanelConfigUrl();
+ } else {
+ Message::addError('invalid-panel-type', $paneltype);
+ Util::redirect('?do=locationinfo');
+ }
+
+ if ($paneluuid === 'new') {
+ $paneluuid = Util::randomUuid();
+ $query = "INSERT INTO `locationinfo_panel` (paneluuid, panelname, locationids, paneltype, panelconfig, lastchange)
+ VALUES (:id, :name, :locationids, :type, :config, :now)";
+ } else {
+ $query = "UPDATE `locationinfo_panel`
+ SET panelname = :name, locationids = :locationids, paneltype = :type, panelconfig = :config, lastchange = :now
+ WHERE paneluuid = :id";
+ }
+ $params['id'] = $paneluuid;
+ $params['name'] = Request::post('name', '-', 'string');
+ $params['type'] = $paneltype;
+ $params['now'] = time();
+ $params['config'] = json_encode($params['config']);
+ $params['locationids'] = implode(',', $params['locationids']);
+ Database::exec($query, $params);
+
+ Message::addSuccess('config-saved');
+ Util::redirect('?do=locationinfo');
+ }
+
+ private function preparePanelConfigDefault()
+ {
+ // Check locations
+ $locationids = self::getLocationIdsFromRequest(true);
if (count($locationids) > 4) {
$locationids = array_slice($locationids, 0, 4);
}
@@ -240,6 +293,7 @@ class Page_LocationInfo extends Page
'mode' => Request::post('mode', 1, 'int'),
'vertical' => Request::post('vertical', false, 'bool'),
'eco' => Request::post('eco', false, 'bool'),
+ 'prettytime' => Request::post('prettytime', false, 'bool'),
'scaledaysauto' => Request::post('scaledaysauto', false, 'bool'),
'daystoshow' => Request::post('daystoshow', 7, 'int'),
'rotation' => Request::post('rotation', 0, 'int'),
@@ -254,27 +308,16 @@ class Page_LocationInfo extends Page
if ($conf['calupdate'] < 30) {
$conf['calupdate'] = 30;
}
+ return array('config' => $conf, 'locationids' => $locationids);
+ }
- if ($paneluuid === 'new') {
- $paneluuid = Util::randomUuid();
- $query = "INSERT INTO `locationinfo_panel` (paneluuid, panelname, locationids, paneltype, panelconfig, lastchange)
- VALUES (:id, :name, :locationids, :type, :config, :now)";
- } else {
- $query = "UPDATE `locationinfo_panel`
- SET panelname = :name, locationids = :locationids, paneltype = :type, panelconfig = :config, lastchange = :now
- WHERE paneluuid = :id";
- }
- Database::exec($query, array(
- 'id' => $paneluuid,
- 'name' => Request::post('name', '-', 'string'),
- 'locationids' => implode(',', $locationids),
- 'type' => 'DEFAULT', // TODO
- 'config' => json_encode($conf),
- 'now' => time(),
- ));
-
- Message::addSuccess('config-saved');
- Util::redirect('?do=locationinfo');
+ private function preparePanelConfigUrl()
+ {
+ $conf = array(
+ 'url' => Request::post('url', 'https://www.bwlehrpool.de/', 'string'),
+ 'insecure-ssl' => Request::post('insecure-ssl', 0, 'int'),
+ );
+ return array('config' => $conf, 'locationids' => []);
}
/**
@@ -402,6 +445,8 @@ class Page_LocationInfo extends Page
while ($row = $dbquery->fetch(PDO::FETCH_ASSOC)) {
$locid = (int)$row['locationid'];
+ if (!isset($locations[$locid]))
+ continue;
$glyph = !empty($row['openingtime']) ? 'ok' : '';
$backend = '';
if (!empty($row['serverid']) && !empty($row['serverlocationid'])) {
@@ -437,7 +482,7 @@ class Page_LocationInfo extends Page
private function showPanelsTable()
{
- $res = Database::simpleQuery('SELECT p.paneluuid, p.panelname, p.locationids,
+ $res = Database::simpleQuery('SELECT p.paneluuid, p.panelname, p.locationids, p.panelconfig,
p.paneltype FROM locationinfo_panel p
ORDER BY panelname ASC');
$hasRunmode = Module::isAvailable('runmode');
@@ -447,11 +492,16 @@ class Page_LocationInfo extends Page
$panels = array();
$locations = Location::getLocationsAssoc();
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $lids = explode(',', $row['locationids']);
- $locs = array_map(function($id) use ($locations) {
- return isset($locations[$id]) ? $locations[$id]['locationname'] : $id;
- }, $lids);
- $row['locations'] = implode(', ', $locs);
+ if ($row['paneltype'] === 'URL') {
+ $url = json_decode($row['panelconfig'], true)['url'];
+ $row['locations'] = $row['locationurl'] = $url;
+ } else {
+ $lids = explode(',', $row['locationids']);
+ $locs = array_map(function ($id) use ($locations) {
+ return isset($locations[$id]) ? $locations[$id]['locationname'] : $id;
+ }, $lids);
+ $row['locations'] = implode(', ', $locs);
+ }
$len = mb_strlen($row['panelname']);
if ($len < 5) {
$row['panelname'] .= str_repeat('…', 5 - $len);
@@ -720,6 +770,13 @@ class Page_LocationInfo extends Page
'paneltype' => 'SUMMARY',
);
$id = 'new';
+ } elseif ($id === 'new-url') {
+ // Creating new panel
+ $panel = array(
+ 'panelname' => '',
+ 'paneltype' => 'URL',
+ );
+ $id = 'new';
} else {
// Get Config data from db
$panel = Database::queryFirst("SELECT panelname, locationids, paneltype, panelconfig
@@ -756,6 +813,7 @@ class Page_LocationInfo extends Page
'mode' => $config['mode'],
'vertical_checked' => $config['vertical'] ? 'checked' : '',
'eco_checked' => $config['eco'] ? 'checked' : '',
+ 'prettytime_checked' => $config['prettytime'] ? 'checked' : '',
'scaledaysauto_checked' => $config['scaledaysauto'] ? 'checked' : '',
'daystoshow' => $config['daystoshow'],
'rotation' => $config['rotation'],
@@ -766,6 +824,14 @@ class Page_LocationInfo extends Page
'locations' => Location::getLocations(),
'locationids' => $panel['locationids'],
));
+ } elseif ($panel['paneltype'] === 'URL') {
+ Render::addTemplate('page-config-panel-url', array(
+ 'new' => $id === 'new',
+ 'uuid' => $id,
+ 'panelname' => $panel['panelname'],
+ 'url' => $config['url'],
+ 'ssl_checked' => $config['insecure-ssl'] ? 'checked' : '',
+ ));
} else { // TODO
Render::addTemplate('page-config-panel-summary', array(
'new' => $id === 'new',
@@ -779,4 +845,36 @@ class Page_LocationInfo extends Page
}
}
+ private function showPanel()
+ {
+ $uuid = Request::get('uuid', false, 'string');
+ if ($uuid === false) {
+ http_response_code(400);
+ die('Missing parameter uuid');
+ }
+ $type = InfoPanel::getConfig($uuid, $config);
+ if ($type === false) {
+ http_response_code(404);
+ die('Panel with given uuid not found');
+ }
+
+ if ($type === 'URL') {
+ Util::redirect($config['url']);
+ }
+
+ $data = array(
+ 'uuid' => $uuid,
+ 'config' => json_encode($config),
+ 'language' => $config['language'],
+ );
+
+ preg_match('#^(.*)/#', $_SERVER['PHP_SELF'], $script);
+ preg_match('#^([^?]+)/#', $_SERVER['REQUEST_URI'], $request);
+ if ($script[1] !== $request[1]) {
+ $data['dirprefix'] = $script[1] . '/';
+ }
+
+ echo Render::parse('frontend-default', $data);
+ }
+
}
diff --git a/modules-available/locationinfo/templates/frontend-default.html b/modules-available/locationinfo/templates/frontend-default.html
new file mode 100755
index 00000000..fc9c3eac
--- /dev/null
+++ b/modules-available/locationinfo/templates/frontend-default.html
@@ -0,0 +1,1763 @@
+
+
+
+
+
+ DoorSign
+
+
+
+
+
+
+
+
+
+
+
+
+ {{lang_room}}
+ {{lang_closed}}
+ {{lang_free}}
+ {{lang_shortSun}}
+ {{lang_shortMon}}
+ {{lang_shortTue}}
+ {{lang_shortWed}}
+ {{lang_shortThu}}
+ {{lang_shortFri}}
+ {{lang_shortSat}}
+ {{lang_longSun}}
+ {{lang_longMon}}
+ {{lang_longTue}}
+ {{lang_longWed}}
+ {{lang_longThu}}
+ {{lang_longFri}}
+ {{lang_longSat}}
+ {{lang_to}}
+
+
+
+
+
diff --git a/modules-available/locationinfo/templates/frontend-summary.html b/modules-available/locationinfo/templates/frontend-summary.html
new file mode 100644
index 00000000..dd5fc25d
--- /dev/null
+++ b/modules-available/locationinfo/templates/frontend-summary.html
@@ -0,0 +1,700 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules-available/locationinfo/templates/page-config-panel-default.html b/modules-available/locationinfo/templates/page-config-panel-default.html
index 4632a718..b55e3d4d 100644
--- a/modules-available/locationinfo/templates/page-config-panel-default.html
+++ b/modules-available/locationinfo/templates/page-config-panel-default.html
@@ -8,6 +8,7 @@