mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-29 10:24:20 +01:00
Merge branch 'calendrier' into dev
This commit is contained in:
commit
608ed8b64f
@ -20,7 +20,7 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ngCooki
|
||||
'ui.select', 'ui.calendar', 'angularMoment', 'Devise', 'DeviseModal', 'angular-growl', 'xeditable',
|
||||
'checklist-model', 'unsavedChanges', 'angular-loading-bar', 'ngTouch', 'angular-google-analytics',
|
||||
'angularUtils.directives.dirDisqus', 'summernote', 'elasticsearch', 'angular-medium-editor', 'naif.base64',
|
||||
'minicolors', 'pascalprecht.translate', 'ngFitText']).
|
||||
'minicolors', 'pascalprecht.translate', 'ngFitText', 'ngAside']).
|
||||
config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfigProvider", "AnalyticsProvider", "uibDatepickerPopupConfig", "$provide", "$translateProvider",
|
||||
function($httpProvider, AuthProvider, growlProvider, unsavedWarningsConfigProvider, AnalyticsProvider, uibDatepickerPopupConfig, $provide, $translateProvider) {
|
||||
|
||||
|
@ -67,6 +67,7 @@
|
||||
//= require messageformat/messageformat
|
||||
//= require angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat
|
||||
//= require ngFitText/dist/ng-FitText.min
|
||||
//= require angular-aside/dist/js/angular-aside
|
||||
//= require_tree ./controllers
|
||||
//= require_tree ./services
|
||||
//= require_tree ./directives
|
||||
|
@ -4,8 +4,8 @@
|
||||
# Controller used in the calendar management page
|
||||
##
|
||||
|
||||
Application.Controllers.controller "AdminCalendarController", ["$scope", "$state", "$uibModal", "moment", "Availability", 'Slot', 'Setting', 'growl', 'dialogs', 'availabilitiesPromise', 'bookingWindowStart', 'bookingWindowEnd', 'machinesPromise', '_t'
|
||||
($scope, $state, $uibModal, moment, Availability, Slot, Setting, growl, dialogs, availabilitiesPromise, bookingWindowStart, bookingWindowEnd, machinesPromise, _t) ->
|
||||
Application.Controllers.controller "AdminCalendarController", ["$scope", "$state", "$uibModal", "moment", "Availability", 'Slot', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', 'machinesPromise', '_t', 'uiCalendarConfig', 'CalendarConfig'
|
||||
($scope, $state, $uibModal, moment, Availability, Slot, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, machinesPromise, _t, uiCalendarConfig, CalendarConfig) ->
|
||||
|
||||
|
||||
|
||||
@ -17,9 +17,6 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
# The bookings can be positioned every half hours
|
||||
BOOKING_SNAP = '00:30:00'
|
||||
|
||||
# The calendar will be initialized positioned under 9:00 AM
|
||||
DEFAULT_CALENDAR_POSITION = '09:00:00'
|
||||
|
||||
# We do not allow the creation of slots that are not a multiple of 60 minutes
|
||||
SLOT_MULTIPLE = 60
|
||||
|
||||
@ -36,40 +33,17 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
## bind the availabilities slots with full-Calendar events
|
||||
$scope.eventSources = []
|
||||
$scope.eventSources.push
|
||||
events: availabilitiesPromise
|
||||
url: '/api/availabilities'
|
||||
textColor: 'black'
|
||||
|
||||
## after fullCalendar loads, provides access to its methods through $scope.calendar.fullCalendar()
|
||||
$scope.calendar = null
|
||||
|
||||
## fullCalendar (v2) configuration
|
||||
$scope.calendarConfig =
|
||||
timezone: Fablab.timezone
|
||||
lang: Fablab.fullcalendar_locale
|
||||
header:
|
||||
left: 'month agendaWeek'
|
||||
center: 'title'
|
||||
right: 'today prev,next'
|
||||
firstDay: 1 # Week start on monday (France)
|
||||
scrollTime: DEFAULT_CALENDAR_POSITION
|
||||
$scope.calendarConfig = CalendarConfig
|
||||
slotDuration: BASE_SLOT
|
||||
snapDuration: BOOKING_SNAP
|
||||
allDayDefault: false
|
||||
minTime: "00:00:00"
|
||||
maxTime: "24:00:00"
|
||||
height: 'auto'
|
||||
buttonIcons:
|
||||
prev: 'left-single-arrow'
|
||||
next: 'right-single-arrow'
|
||||
timeFormat:
|
||||
agenda:'H:mm'
|
||||
month: 'H(:mm)'
|
||||
axisFormat: 'H:mm'
|
||||
|
||||
allDaySlot: false
|
||||
defaultView: 'agendaWeek'
|
||||
selectable: true
|
||||
selecHelper: true
|
||||
minTime: moment.duration(moment(bookingWindowStart.setting.value).format('HH:mm:ss'))
|
||||
maxTime: moment.duration(moment(bookingWindowEnd.setting.value).format('HH:mm:ss'))
|
||||
select: (start, end, jsEvent, view) ->
|
||||
calendarSelectCb(start, end, jsEvent, view)
|
||||
eventClick: (event, jsEvent, view)->
|
||||
@ -77,10 +51,6 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
eventRender: (event, element, view) ->
|
||||
eventRenderCb(event, element)
|
||||
|
||||
## fullCalendar time bounds (up & down)
|
||||
$scope.calendarConfig.minTime = moment.duration(moment(bookingWindowStart.setting.value).format('HH:mm:ss'))
|
||||
$scope.calendarConfig.maxTime = moment.duration(moment(bookingWindowEnd.setting.value).format('HH:mm:ss'))
|
||||
|
||||
|
||||
|
||||
##
|
||||
@ -141,7 +111,7 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
# update the machine_ids attribute
|
||||
$scope.availability.machine_ids = data.machine_ids
|
||||
$scope.availability.title = data.title
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
# notify the admin
|
||||
growl.success(_t('the_machine_was_successfully_removed_from_the_slot'))
|
||||
, (data, status) -> # failed
|
||||
@ -180,7 +150,7 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
end: -> end
|
||||
# when the modal is closed, we send the slot to the server for saving
|
||||
modalInstance.result.then (availability) ->
|
||||
$scope.calendar.fullCalendar 'renderEvent',
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'renderEvent',
|
||||
id: availability.id
|
||||
title: availability.title,
|
||||
start: availability.start_at
|
||||
@ -189,12 +159,13 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
backgroundColor: availability.backgroundColor
|
||||
borderColor: availability.borderColor
|
||||
tag_ids: availability.tag_ids
|
||||
tags: availability.tags
|
||||
machine_ids: availability.machine_ids
|
||||
, true
|
||||
, ->
|
||||
$scope.calendar.fullCalendar('unselect')
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar('unselect')
|
||||
|
||||
$scope.calendar.fullCalendar('unselect')
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar('unselect')
|
||||
|
||||
|
||||
|
||||
@ -209,7 +180,7 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
# if the user has clicked on the delete event button, delete the event
|
||||
if ($(jsEvent.target).hasClass('remove-event'))
|
||||
Availability.delete id: event.id, ->
|
||||
$scope.calendar.fullCalendar 'removeEvents', event.id
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'removeEvents', event.id
|
||||
for _event, i in $scope.eventSources[0].events
|
||||
if _event.id == event.id
|
||||
$scope.eventSources[0].events.splice(i,1)
|
||||
@ -231,12 +202,12 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
# @see http://fullcalendar.io/docs/event_rendering/eventRender/
|
||||
##
|
||||
eventRenderCb = (event, element) ->
|
||||
if event.tag_ids.length > 0
|
||||
Availability.get {id: event.id}, (avail) ->
|
||||
html = ''
|
||||
for tag in avail.tags
|
||||
html += "<span class='label label-success text-white'>#{tag.name}</span> "
|
||||
element.find('.fc-title').append("<br/>"+html)
|
||||
if event.tags.length > 0
|
||||
html = ''
|
||||
for tag in event.tags
|
||||
html += "<span class='label label-success text-white'>#{tag.name}</span> "
|
||||
element.find('.fc-title').append("<br/>"+html)
|
||||
return
|
||||
|
||||
]
|
||||
|
||||
|
168
app/assets/javascripts/controllers/calendar.coffee
Normal file
168
app/assets/javascripts/controllers/calendar.coffee
Normal file
@ -0,0 +1,168 @@
|
||||
'use strict'
|
||||
|
||||
##
|
||||
# Controller used in the public calendar global
|
||||
##
|
||||
|
||||
Application.Controllers.controller "CalendarController", ["$scope", "$state", "$aside", "moment", "Availability", 'Slot', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', '_t', 'uiCalendarConfig', 'CalendarConfig', 'trainingsPromise', 'machinesPromise',
|
||||
($scope, $state, $aside, moment, Availability, Slot, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, _t, uiCalendarConfig, CalendarConfig, trainingsPromise, machinesPromise) ->
|
||||
|
||||
|
||||
### PRIVATE STATIC CONSTANTS ###
|
||||
currentMachineEvent = null
|
||||
machinesPromise.forEach((m) -> m.checked = true)
|
||||
trainingsPromise.forEach((t) -> t.checked = true)
|
||||
|
||||
## check all formation/machine is select in filter
|
||||
isSelectAll = (type, scope) ->
|
||||
scope[type].length == scope[type].filter((t) -> t.checked).length
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## List of trainings
|
||||
$scope.trainings = trainingsPromise
|
||||
|
||||
## List of machines
|
||||
$scope.machines = machinesPromise
|
||||
|
||||
## add availabilities source to event sources
|
||||
$scope.eventSources = []
|
||||
|
||||
## filter availabilities if have change
|
||||
$scope.filterAvailabilities = (filter, scope) ->
|
||||
scope ||= $scope
|
||||
scope.filter = $scope.filter =
|
||||
trainings: isSelectAll('trainings', scope)
|
||||
machines: isSelectAll('machines', scope)
|
||||
evt: filter.evt
|
||||
dispo: filter.dispo
|
||||
$scope.calendarConfig.events = availabilitySourceUrl()
|
||||
|
||||
|
||||
## a variable for formation/machine/event/dispo checkbox is or not checked
|
||||
$scope.filter =
|
||||
trainings: isSelectAll('trainings', $scope)
|
||||
machines: isSelectAll('machines', $scope)
|
||||
evt: true
|
||||
dispo: true
|
||||
|
||||
## toggle to select all formation/machine
|
||||
$scope.toggleFilter = (type, filter) ->
|
||||
$scope[type].forEach((t) -> t.checked = filter[type])
|
||||
$scope.filterAvailabilities(filter, $scope)
|
||||
|
||||
$scope.openFilterAside = ->
|
||||
$aside.open
|
||||
templateUrl: 'filterAside.html'
|
||||
placement: 'right'
|
||||
size: 'md'
|
||||
backdrop: false
|
||||
resolve:
|
||||
trainings: ->
|
||||
$scope.trainings
|
||||
machines: ->
|
||||
$scope.machines
|
||||
filter: ->
|
||||
$scope.filter
|
||||
toggleFilter: ->
|
||||
$scope.toggleFilter
|
||||
filterAvailabilities: ->
|
||||
$scope.filterAvailabilities
|
||||
controller: ['$scope', '$uibModalInstance', 'trainings', 'machines', 'filter', 'toggleFilter', 'filterAvailabilities', ($scope, $uibModalInstance, trainings, machines, filter, toggleFilter, filterAvailabilities) ->
|
||||
$scope.trainings = trainings
|
||||
$scope.machines = machines
|
||||
$scope.filter = filter
|
||||
|
||||
$scope.toggleFilter = (type, filter) ->
|
||||
toggleFilter(type, filter)
|
||||
|
||||
$scope.filterAvailabilities = (filter) ->
|
||||
filterAvailabilities(filter, $scope)
|
||||
|
||||
$scope.close = (e) ->
|
||||
$uibModalInstance.dismiss()
|
||||
e.stopPropagation()
|
||||
]
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
calendarEventClickCb = (event, jsEvent, view) ->
|
||||
## current calendar object
|
||||
calendar = uiCalendarConfig.calendars.calendar
|
||||
if event.available_type == 'machines'
|
||||
currentMachineEvent = event
|
||||
calendar.fullCalendar('changeView', 'agendaDay')
|
||||
calendar.fullCalendar('gotoDate', event.start)
|
||||
else
|
||||
if event.available_type == 'event'
|
||||
$state.go('app.public.events_show', {id: event.event_id})
|
||||
else if event.available_type == 'training'
|
||||
$state.go('app.public.training_show', {id: event.training_id})
|
||||
else
|
||||
$state.go('app.public.machines_show', {id: event.machine_id})
|
||||
|
||||
## agendaDay view: disable slotEventOverlap
|
||||
## agendaWeek view: enable slotEventOverlap
|
||||
toggleSlotEventOverlap = (view) ->
|
||||
# set defaultView, because when we change slotEventOverlap
|
||||
# ui-calendar will trigger rerender calendar
|
||||
$scope.calendarConfig.defaultView = view.type
|
||||
today = if currentMachineEvent then currentMachineEvent.start else moment().utc().startOf('day')
|
||||
if today > view.start and today < view.end and today != view.start
|
||||
$scope.calendarConfig.defaultDate = today
|
||||
else
|
||||
$scope.calendarConfig.defaultDate = view.start
|
||||
if view.type == 'agendaDay'
|
||||
$scope.calendarConfig.slotEventOverlap = false
|
||||
else
|
||||
$scope.calendarConfig.slotEventOverlap = true
|
||||
|
||||
## function is called when calendar view is rendered or changed
|
||||
viewRenderCb = (view, element) ->
|
||||
toggleSlotEventOverlap(view)
|
||||
if view.type == 'agendaDay'
|
||||
# get availabilties by 1 day for show machine slots
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents')
|
||||
|
||||
eventRenderCb = (event, element) ->
|
||||
if event.tags.length > 0
|
||||
html = ''
|
||||
for tag in event.tags
|
||||
html += "<span class='label label-success text-white'>#{tag.name}</span> "
|
||||
element.find('.fc-title').append("<br/>"+html)
|
||||
return
|
||||
|
||||
getFilter = ->
|
||||
t = $scope.trainings.filter((t) -> t.checked).map((t) -> t.id)
|
||||
m = $scope.machines.filter((m) -> m.checked).map((m) -> m.id)
|
||||
{t: t, m: m, evt: $scope.filter.evt, dispo: $scope.filter.dispo}
|
||||
|
||||
availabilitySourceUrl = ->
|
||||
"/api/availabilities/public?#{$.param(getFilter())}"
|
||||
|
||||
initialize = ->
|
||||
## fullCalendar (v2) configuration
|
||||
$scope.calendarConfig = CalendarConfig
|
||||
events: availabilitySourceUrl()
|
||||
slotEventOverlap: true
|
||||
header:
|
||||
left: 'month agendaWeek agendaDay'
|
||||
center: 'title'
|
||||
right: 'today prev,next'
|
||||
minTime: moment.duration(moment(bookingWindowStart.setting.value).format('HH:mm:ss'))
|
||||
maxTime: moment.duration(moment(bookingWindowEnd.setting.value).format('HH:mm:ss'))
|
||||
defaultView: if window.innerWidth <= 480 then 'agendaDay' else 'agendaWeek'
|
||||
eventClick: (event, jsEvent, view)->
|
||||
calendarEventClickCb(event, jsEvent, view)
|
||||
viewRender: (view, element) ->
|
||||
viewRenderCb(view, element)
|
||||
eventRender: (event, element, view) ->
|
||||
eventRenderCb(event, element)
|
||||
|
||||
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
@ -268,38 +268,26 @@ Application.Controllers.controller "ShowMachineController", ['$scope', '$state',
|
||||
# This controller workflow is pretty similar to the trainings reservation controller.
|
||||
##
|
||||
|
||||
Application.Controllers.controller "ReserveMachineController", ["$scope", "$state", '$stateParams', "$uibModal", '_t', "moment", 'Machine', 'Auth', 'dialogs', '$timeout', 'Price', 'Member', 'Availability', 'Slot', 'Setting', 'CustomAsset', 'plansPromise', 'groupsPromise', 'growl', 'settingsPromise',
|
||||
($scope, $state, $stateParams, $uibModal, _t, moment, Machine, Auth, dialogs, $timeout, Price, Member, Availability, Slot, Setting, CustomAsset, plansPromise, groupsPromise, growl, settingsPromise) ->
|
||||
Application.Controllers.controller "ReserveMachineController", ["$scope", "$state", '$stateParams', "$uibModal", '_t', "moment", 'Machine', 'Auth', 'dialogs', '$timeout', 'Price', 'Member', 'Availability', 'Slot', 'Setting', 'CustomAsset', 'plansPromise', 'groupsPromise', 'growl', 'settingsPromise', 'uiCalendarConfig', 'CalendarConfig'
|
||||
($scope, $state, $stateParams, $uibModal, _t, moment, Machine, Auth, dialogs, $timeout, Price, Member, Availability, Slot, Setting, CustomAsset, plansPromise, groupsPromise, growl, settingsPromise, uiCalendarConfig, CalendarConfig) ->
|
||||
|
||||
|
||||
|
||||
### PRIVATE STATIC CONSTANTS ###
|
||||
|
||||
# The calendar is divided in slots of 60 minutes
|
||||
BASE_SLOT = '01:00:00'
|
||||
|
||||
# The calendar will be initialized positioned under 9:00 AM
|
||||
DEFAULT_CALENDAR_POSITION = '09:00:00'
|
||||
|
||||
# The user is unable to modify his already booked reservation 1 day before it occurs
|
||||
PREVENT_BOOKING_MODIFICATION_DELAY = 1
|
||||
|
||||
# Slot already booked by the current user
|
||||
FREE_SLOT_BORDER_COLOR = '#e4cd78'
|
||||
FREE_SLOT_BORDER_COLOR = '<%= AvailabilityHelper::MACHINE_COLOR %>'
|
||||
|
||||
# Slot already booked by another user
|
||||
UNAVAILABLE_SLOT_BORDER_COLOR = '#1d98ec'
|
||||
UNAVAILABLE_SLOT_BORDER_COLOR = '<%= AvailabilityHelper::MACHINE_IS_RESERVED_BY_USER %>'
|
||||
|
||||
# Slot free to be booked
|
||||
BOOKED_SLOT_BORDER_COLOR = '#b2e774'
|
||||
BOOKED_SLOT_BORDER_COLOR = '<%= AvailabilityHelper::IS_RESERVED_BY_CURRENT_USER %>'
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## after fullCalendar loads, provides access to its methods through $scope.calendar.fullCalendar()
|
||||
$scope.calendar = null
|
||||
|
||||
## bind the machine availabilities with full-Calendar events
|
||||
$scope.eventSources = []
|
||||
|
||||
@ -343,31 +331,9 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.machine = {}
|
||||
|
||||
## fullCalendar (v2) configuration
|
||||
$scope.calendarConfig =
|
||||
timezone: Fablab.timezone
|
||||
lang: Fablab.fullcalendar_locale
|
||||
header:
|
||||
left: 'month agendaWeek'
|
||||
center: 'title'
|
||||
right: 'today prev,next'
|
||||
firstDay: 1 # Week start on monday (France)
|
||||
scrollTime: DEFAULT_CALENDAR_POSITION
|
||||
slotDuration: BASE_SLOT
|
||||
allDayDefault: false
|
||||
minTime: '00:00:00'
|
||||
maxTime: '24:00:00'
|
||||
height: 'auto'
|
||||
buttonIcons:
|
||||
prev: 'left-single-arrow'
|
||||
next: 'right-single-arrow'
|
||||
timeFormat:
|
||||
agenda:'H:mm'
|
||||
month: 'H(:mm)'
|
||||
axisFormat: 'H:mm'
|
||||
|
||||
allDaySlot: false
|
||||
defaultView: 'agendaWeek'
|
||||
editable: false
|
||||
$scope.calendarConfig = CalendarConfig
|
||||
minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss'))
|
||||
maxTime: moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss'))
|
||||
eventClick: (event, jsEvent, view) ->
|
||||
calendarEventClickCb(event, jsEvent, view)
|
||||
eventRender: (event, element, view) ->
|
||||
@ -391,12 +357,6 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
## Global config: delay in hours before a booking while the cancellation is forbidden
|
||||
$scope.cancelBookingDelay = parseInt(settingsPromise.booking_cancel_delay)
|
||||
|
||||
## Global config: calendar window in the morning
|
||||
$scope.calendarConfig.minTime = moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss'))
|
||||
|
||||
## Global config: calendar window in the evening
|
||||
$scope.calendarConfig.maxTime = moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss'))
|
||||
|
||||
|
||||
|
||||
##
|
||||
@ -412,7 +372,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.slotToModify.title = if $scope.currentUser.role isnt 'admin' then _t('i_ve_reserved') else _t('not_available')
|
||||
$scope.slotToModify.backgroundColor = 'white'
|
||||
$scope.slotToModify = null
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
@ -425,7 +385,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.slotToPlace.backgroundColor = 'white'
|
||||
$scope.slotToPlace.title = ''
|
||||
$scope.slotToPlace = null
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
@ -458,7 +418,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.slotToModify.can_modify = false
|
||||
$scope.slotToModify = null
|
||||
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
, (err) -> # failure
|
||||
growl.error(_t('unable_to_change_the_reservation'))
|
||||
console.error(err)
|
||||
@ -476,7 +436,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.slotToModify.backgroundColor = 'white'
|
||||
$scope.slotToModify = null
|
||||
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
@ -532,8 +492,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.plansAreShown = false
|
||||
updateCartPrice()
|
||||
$timeout ->
|
||||
$scope.calendar.fullCalendar 'refetchEvents'
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'refetchEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
@ -732,7 +692,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.slotToModify = event
|
||||
event.backgroundColor = '#eee'
|
||||
event.title = _t('i_change')
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
else if type == 'cancel'
|
||||
dialogs.confirm
|
||||
resolve:
|
||||
@ -750,7 +710,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.canceledSlot.is_reserved = false
|
||||
$scope.canceledSlot.can_modify = false
|
||||
$scope.canceledSlot = null
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
, -> # error while canceling
|
||||
growl.error _t('cancellation_failed')
|
||||
, ->
|
||||
@ -758,7 +718,7 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
$scope.selectedPlan = null
|
||||
$scope.modifiedSlots = null
|
||||
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
updateCartPrice()
|
||||
|
||||
@ -930,8 +890,8 @@ Application.Controllers.controller "ReserveMachineController", ["$scope", "$stat
|
||||
Auth._currentUser.subscribed_plan = angular.copy($scope.selectedPlan)
|
||||
$scope.plansAreShown = false
|
||||
|
||||
$scope.calendar.fullCalendar 'refetchEvents'
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'refetchEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@ Application.Controllers.controller "MainNavController", ["$scope", "$location",
|
||||
{
|
||||
state: 'app.public.machines_list'
|
||||
linkText: 'reserve_a_machine'
|
||||
linkIcon: 'calendar'
|
||||
linkIcon: 'cogs'
|
||||
}
|
||||
{
|
||||
state: 'app.public.trainings_list'
|
||||
@ -28,6 +28,11 @@ Application.Controllers.controller "MainNavController", ["$scope", "$location",
|
||||
linkText: 'events_registrations'
|
||||
linkIcon: 'tags'
|
||||
}
|
||||
{
|
||||
state: 'app.public.calendar'
|
||||
linkText: 'public_calendar'
|
||||
linkIcon: 'calendar'
|
||||
}
|
||||
{
|
||||
state: 'app.public.projects_list'
|
||||
linkText: 'projects_gallery'
|
||||
|
@ -51,35 +51,23 @@ Application.Controllers.controller "ShowTrainingController", ['$scope', '$state'
|
||||
# training can be reserved during the reservation process (the shopping cart may contains only one training and a subscription).
|
||||
##
|
||||
|
||||
Application.Controllers.controller "ReserveTrainingController", ["$scope", "$state", '$stateParams', '$filter', '$compile', "$uibModal", 'Auth', 'dialogs', '$timeout', 'Price', 'Availability', 'Slot', 'Member', 'Setting', 'CustomAsset', 'availabilityTrainingsPromise', 'plansPromise', 'groupsPromise', 'growl', 'settingsPromise', 'trainingPromise', '_t'
|
||||
, ($scope, $state, $stateParams, $filter, $compile, $uibModal, Auth, dialogs, $timeout, Price, Availability, Slot, Member, Setting, CustomAsset, availabilityTrainingsPromise, plansPromise, groupsPromise, growl, settingsPromise, trainingPromise, _t) ->
|
||||
Application.Controllers.controller "ReserveTrainingController", ["$scope", "$state", '$stateParams', '$filter', '$compile', "$uibModal", 'Auth', 'dialogs', '$timeout', 'Price', 'Availability', 'Slot', 'Member', 'Setting', 'CustomAsset', 'availabilityTrainingsPromise', 'plansPromise', 'groupsPromise', 'growl', 'settingsPromise', 'trainingPromise', '_t', 'uiCalendarConfig', 'CalendarConfig'
|
||||
($scope, $state, $stateParams, $filter, $compile, $uibModal, Auth, dialogs, $timeout, Price, Availability, Slot, Member, Setting, CustomAsset, availabilityTrainingsPromise, plansPromise, groupsPromise, growl, settingsPromise, trainingPromise, _t, uiCalendarConfig, CalendarConfig) ->
|
||||
|
||||
|
||||
|
||||
### PRIVATE STATIC CONSTANTS ###
|
||||
|
||||
# The calendar is divided in slots of 60 minutes
|
||||
BASE_SLOT = '01:00:00'
|
||||
|
||||
# The calendar will be initialized positioned under 9:00 AM
|
||||
DEFAULT_CALENDAR_POSITION = '09:00:00'
|
||||
|
||||
# The user is unable to modify his already booked reservation 1 day before it occurs
|
||||
PREVENT_BOOKING_MODIFICATION_DELAY = 1
|
||||
|
||||
# Color of the selected event backgound
|
||||
SELECTED_EVENT_BG_COLOR = '#ffdd00'
|
||||
|
||||
# Slot already booked by the current user
|
||||
FREE_SLOT_BORDER_COLOR = '#bd7ae9'
|
||||
FREE_SLOT_BORDER_COLOR = '<%= AvailabilityHelper::TRAINING_COLOR %>'
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## after fullCalendar loads, provides access to its methods through $scope.calendar.fullCalendar()
|
||||
$scope.calendar = null
|
||||
|
||||
## bind the trainings availabilities with full-Calendar events
|
||||
$scope.eventSources = [ { events: availabilityTrainingsPromise, textColor: 'black' } ]
|
||||
|
||||
@ -120,35 +108,13 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.training = trainingPromise
|
||||
|
||||
## fullCalendar (v2) configuration
|
||||
$scope.calendarConfig =
|
||||
timezone: Fablab.timezone
|
||||
lang: Fablab.fullcalendar_locale
|
||||
header:
|
||||
left: 'month agendaWeek'
|
||||
center: 'title'
|
||||
right: 'today prev,next'
|
||||
firstDay: 1 # Week start on monday (France)
|
||||
scrollTime: DEFAULT_CALENDAR_POSITION
|
||||
slotDuration: BASE_SLOT
|
||||
allDayDefault: false
|
||||
minTime: '00:00:00'
|
||||
maxTime: '24:00:00'
|
||||
height: 'auto'
|
||||
buttonIcons:
|
||||
prev: 'left-single-arrow'
|
||||
next: 'right-single-arrow'
|
||||
timeFormat:
|
||||
agenda:'H:mm'
|
||||
month: 'H(:mm)'
|
||||
axisFormat: 'H:mm'
|
||||
|
||||
allDaySlot: false
|
||||
defaultView: 'agendaWeek'
|
||||
editable: false
|
||||
$scope.calendarConfig = CalendarConfig
|
||||
minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss'))
|
||||
maxTime: moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss'))
|
||||
eventClick: (event, jsEvent, view) ->
|
||||
calendarEventClickCb(event, jsEvent, view)
|
||||
eventAfterAllRender: (view)->
|
||||
$scope.events = $scope.calendar.fullCalendar 'clientEvents'
|
||||
$scope.events = uiCalendarConfig.calendars.calendar.fullCalendar 'clientEvents'
|
||||
eventRender: (event, element, view) ->
|
||||
eventRenderCb(event, element, view)
|
||||
|
||||
@ -160,8 +126,6 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.moveBookingDelay = parseInt(settingsPromise.booking_move_delay)
|
||||
$scope.enableBookingCancel = (settingsPromise.booking_cancel_enable == "true")
|
||||
$scope.cancelBookingDelay = parseInt(settingsPromise.booking_cancel_delay)
|
||||
$scope.calendarConfig.minTime = moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss'))
|
||||
$scope.calendarConfig.maxTime = moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss'))
|
||||
|
||||
|
||||
|
||||
@ -174,7 +138,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
Member.get {id: $scope.ctrl.member.id}, (member) ->
|
||||
$scope.ctrl.member = member
|
||||
Availability.trainings {trainingId: $stateParams.id, member_id: $scope.ctrl.member.id}, (trainings) ->
|
||||
$scope.calendar.fullCalendar 'removeEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'removeEvents'
|
||||
$scope.eventSources.push
|
||||
events: trainings
|
||||
textColor: 'black'
|
||||
@ -210,8 +174,8 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.selectedPlan = null
|
||||
$scope.trainingIsValid = false
|
||||
$timeout ->
|
||||
$scope.calendar.fullCalendar 'refetchEvents'
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.fullCalendar 'refetchEvents'
|
||||
uiCalendarConfig.calendars.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
@ -283,7 +247,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.slotToModify.title = if $scope.currentUser.role isnt 'admin' then $scope.slotToModify.training.name + " - " + _t('i_ve_reserved') else $scope.slotToModify.training.name
|
||||
$scope.slotToModify.backgroundColor = 'white'
|
||||
$scope.slotToModify = null
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
@ -296,7 +260,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.slotToPlace.backgroundColor = 'white'
|
||||
$scope.slotToPlace.title = $scope.slotToPlace.training.name
|
||||
$scope.slotToPlace = null
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
@ -329,7 +293,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.slotToModify.can_modify = false
|
||||
$scope.slotToModify.is_completed = false if $scope.slotToModify.is_completed
|
||||
$scope.slotToModify = null
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
, -> # failure
|
||||
growl.error('an_error_occured_preventing_the_booked_slot_from_being_modified')
|
||||
|
||||
@ -345,7 +309,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.slotToModify.title = if $scope.currentUser.role isnt 'admin' then $scope.slotToModify.training.name + " - " + _t('i_ve_reserved') else $scope.slotToModify.training.name
|
||||
$scope.slotToModify.backgroundColor = 'white'
|
||||
$scope.slotToModify = null
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
@ -426,7 +390,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
angular.forEach $scope.events, (e)->
|
||||
if event.id != e.id
|
||||
e.backgroundColor = 'white'
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
# two if below for move training reserved
|
||||
# if training isnt reserved and have a training to modify and same training and not complete
|
||||
else if !event.is_reserved && $scope.slotToModify && slotCanBePlaced(event)
|
||||
@ -436,7 +400,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.slotToPlace = event
|
||||
event.backgroundColor = '#bbb'
|
||||
event.title = event.training.name + ' - ' + _t('i_shift')
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
# if training reserved can modify
|
||||
else if event.is_reserved and (slotCanBeModified(event) or slotCanBeCanceled(event)) and !$scope.slotToModify and !$scope.selectedTraining
|
||||
event.movable = slotCanBeModified(event)
|
||||
@ -454,7 +418,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.slotToModify = event
|
||||
event.backgroundColor = '#eee'
|
||||
event.title = event.training.name + ' - ' + _t('i_change')
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
else if type == 'cancel'
|
||||
dialogs.confirm
|
||||
resolve:
|
||||
@ -473,7 +437,7 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
$scope.canceledSlot.can_modify = false
|
||||
$scope.canceledSlot.is_completed = false if event.is_completed
|
||||
$scope.canceledSlot = null
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
, -> # error while canceling
|
||||
growl.error _t('cancellation_failed')
|
||||
, -> # canceled
|
||||
@ -488,11 +452,12 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
# @see http://fullcalendar.io/docs/event_rendering/eventRender/
|
||||
##
|
||||
eventRenderCb = (event, element, view)->
|
||||
element.attr(
|
||||
'uib-popover': $filter('humanize')($filter('simpleText')(event.training.description), 70)
|
||||
'popover-trigger': 'mouseenter'
|
||||
)
|
||||
$compile(element)($scope)
|
||||
# Comment these codes for show a popup of description, because we add feature page of training
|
||||
#element.attr(
|
||||
# 'uib-popover': $filter('humanize')($filter('simpleText')(event.training.description), 70)
|
||||
# 'popover-trigger': 'mouseenter'
|
||||
#)
|
||||
#$compile(element)($scope)
|
||||
|
||||
|
||||
|
||||
@ -635,8 +600,8 @@ Application.Controllers.controller "ReserveTrainingController", ["$scope", "$sta
|
||||
Auth._currentUser.training_credits = angular.copy(reservation.user.training_credits)
|
||||
Auth._currentUser.machine_credits = angular.copy(reservation.user.machine_credits)
|
||||
|
||||
$scope.calendar.fullCalendar 'refetchEvents'
|
||||
$scope.calendar.fullCalendar 'rerenderEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'refetchEvents'
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar 'rerenderEvents'
|
||||
|
||||
|
||||
|
||||
|
@ -112,7 +112,7 @@ Application.Filters.filter "breakFilter", [ ->
|
||||
##
|
||||
Application.Filters.filter "simpleText", [ ->
|
||||
(text) ->
|
||||
if text != undefined
|
||||
if text?
|
||||
text = text.replace(/<br\s*\/?>/g, '\n')
|
||||
text.replace(/<\/?\w+[^>]*>/g, '')
|
||||
else
|
||||
|
@ -497,6 +497,30 @@ angular.module('application.router', ['ui.router']).
|
||||
Translations.query(['app.public.events_show', 'app.shared.member_select', 'app.shared.stripe', 'app.shared.valid_reservation_modal']).$promise
|
||||
]
|
||||
|
||||
# calendar global (trainings, machines and events)
|
||||
.state 'app.public.calendar',
|
||||
url: '/calendar'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "calendar/calendar.html" %>'
|
||||
controller: 'CalendarController'
|
||||
resolve:
|
||||
bookingWindowStart: ['Setting', (Setting)->
|
||||
Setting.get(name: 'booking_window_start').$promise
|
||||
]
|
||||
bookingWindowEnd: ['Setting', (Setting)->
|
||||
Setting.get(name: 'booking_window_end').$promise
|
||||
]
|
||||
trainingsPromise: ['Training', (Training)->
|
||||
Training.query().$promise
|
||||
]
|
||||
machinesPromise: ['Machine', (Machine)->
|
||||
Machine.query().$promise
|
||||
]
|
||||
translations: [ 'Translations', (Translations) ->
|
||||
Translations.query(['app.public.calendar']).$promise
|
||||
]
|
||||
|
||||
# --- namespace /admin/... ---
|
||||
# calendar
|
||||
.state 'app.admin.calendar',
|
||||
@ -506,9 +530,6 @@ angular.module('application.router', ['ui.router']).
|
||||
templateUrl: '<%= asset_path "admin/calendar/calendar.html" %>'
|
||||
controller: 'AdminCalendarController'
|
||||
resolve:
|
||||
availabilitiesPromise: ['Availability', (Availability)->
|
||||
Availability.query().$promise
|
||||
]
|
||||
bookingWindowStart: ['Setting', (Setting)->
|
||||
Setting.get(name: 'booking_window_start').$promise
|
||||
]
|
||||
|
38
app/assets/javascripts/services/calendar.coffee
Normal file
38
app/assets/javascripts/services/calendar.coffee
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'CalendarConfig', [->
|
||||
(options = {}) ->
|
||||
# The calendar is divided in slots of 1 hour
|
||||
BASE_SLOT = '01:00:00'
|
||||
|
||||
# The calendar will be initialized positioned under 9:00 AM
|
||||
DEFAULT_CALENDAR_POSITION = '09:00:00'
|
||||
|
||||
defaultOptions =
|
||||
timezone: Fablab.timezone
|
||||
lang: Fablab.fullcalendar_locale
|
||||
header:
|
||||
left: 'month agendaWeek'
|
||||
center: 'title'
|
||||
right: 'today prev,next'
|
||||
firstDay: 1 # Week start on monday (France)
|
||||
scrollTime: DEFAULT_CALENDAR_POSITION
|
||||
slotDuration: BASE_SLOT
|
||||
allDayDefault: false
|
||||
minTime: "00:00:00"
|
||||
maxTime: "24:00:00"
|
||||
height: 'auto'
|
||||
buttonIcons:
|
||||
prev: 'left-single-arrow'
|
||||
next: 'right-single-arrow'
|
||||
timeFormat:
|
||||
agenda:'H:mm'
|
||||
month: 'H(:mm)'
|
||||
axisFormat: 'H:mm'
|
||||
|
||||
allDaySlot: false
|
||||
defaultView: 'agendaWeek'
|
||||
editable: false
|
||||
|
||||
Object.assign({}, defaultOptions, options)
|
||||
]
|
@ -6,6 +6,7 @@
|
||||
.bg-token { background-color: rgba(230, 208, 137, 0.49); }
|
||||
.bg-machine { background-color: $beige; }
|
||||
.bg-formation { background-color: $violet; }
|
||||
.bg-event { background-color: $japonica; }
|
||||
.bg-atelier { background-color: $blue; }
|
||||
.bg-stage { background-color: $violet; }
|
||||
.bg-success { background-color: $brand-success; }
|
||||
@ -35,3 +36,6 @@
|
||||
.text-blue { color: $blue; }
|
||||
.text-muted { color: $text-muted; }
|
||||
.text-danger, .red { color: $red !important; }
|
||||
.text-purple { color: $violet !important; }
|
||||
.text-japonica { color: $japonica !important; }
|
||||
.text-beige { color: $beige !important; }
|
||||
|
@ -513,4 +513,4 @@ padding: 10px;
|
||||
z-index:10;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -604,4 +604,14 @@ body.container{
|
||||
display: inherit;
|
||||
text-align: center;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-filter {
|
||||
h3 {
|
||||
line-height: 2.1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-filter-aside {
|
||||
padding: 20px;
|
||||
}
|
||||
|
@ -91,7 +91,7 @@
|
||||
cursor: pointer;
|
||||
z-index: 9999;
|
||||
text-align: right;
|
||||
.training-reserve &, .machine-reserve & { display: none; }
|
||||
.training-reserve &, .machine-reserve &, .public-calendar & { display: none; }
|
||||
}
|
||||
|
||||
.fc-v-event.fc-end {
|
||||
@ -102,6 +102,15 @@
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.calendar-filter {
|
||||
.badge {
|
||||
cursor: pointer;
|
||||
|
||||
&.inactive {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -342,6 +342,7 @@ p, .widget p {
|
||||
|
||||
@media screen and (min-width: $screen-lg-min) {
|
||||
.b-r-lg {border-right: 1px solid $border-color; }
|
||||
.hide-b-r-lg { border: none !important; }
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
*= require bootstrap-switch/dist/css/bootstrap3/bootstrap-switch.min
|
||||
*= require summernote/dist/summernote
|
||||
*= require jquery-minicolors/jquery.minicolors.css
|
||||
*= require angular-aside/dist/css/angular-aside
|
||||
*/
|
||||
|
||||
@import "app.functions";
|
||||
|
@ -43,6 +43,7 @@ $blue: $brand-info;
|
||||
$green: $brand-success;
|
||||
$beige: #e4cd78;
|
||||
$violet: #bd7ae9;
|
||||
$japonica: #dd7e6b;
|
||||
|
||||
$border-color: #dddddd;
|
||||
$header-bg: $bg-gray;
|
||||
|
62
app/assets/templates/calendar/calendar.html.erb
Normal file
62
app/assets/templates/calendar/calendar.html.erb
Normal file
@ -0,0 +1,62 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md hide-b-r-lg">
|
||||
<section class="heading-title">
|
||||
<h1 translate>{{ 'calendar' }}</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md hidden-lg">
|
||||
<div class="heading-actions wrapper">
|
||||
<button type="button" class="btn btn-default m-t m-b" ng-click="openFilterAside()">
|
||||
<span class="fa fa-filter"></span> {{ 'filter-calendar' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="row no-gutter">
|
||||
<div class="hidden-lg">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-9">
|
||||
<div ui-calendar="calendarConfig" ng-model="eventSources" calendar="calendar" class="wrapper-lg public-calendar"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3 hidden-md hidden-sm hidden-xs">
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3 translate>{{ 'filter-calendar' }}</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg auto wrapper calendar-filter">
|
||||
<ng-include src="'<%= asset_path 'calendar/filter.html' %>'"></ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<script type="text/ng-template" id="filterAside.html">
|
||||
<div class="widget">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="close($event)"><span>×</span></button>
|
||||
<h1 class="modal-title" translate>{{ 'filter-calendar' }}</h1>
|
||||
</div>
|
||||
<div class="modal-body widget-content calendar-filter calendar-filter-aside">
|
||||
<ng-include src="'<%= asset_path 'calendar/filter.html' %>'"></ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
28
app/assets/templates/calendar/filter.html.erb
Normal file
28
app/assets/templates/calendar/filter.html.erb
Normal file
@ -0,0 +1,28 @@
|
||||
<div>
|
||||
<div class="row">
|
||||
<h3 class="col-md-11 col-sm-11 col-xs-11 text-purple" translate>{{ 'trainings' }}</h3>
|
||||
<input class="col-md-1 col-sm-1 col-xs-1" type="checkbox" ng-model="filter.trainings" ng-change="toggleFilter('trainings', filter)">
|
||||
</div>
|
||||
<div ng-repeat="t in trainings" class="row">
|
||||
<span class="col-md-11 col-sm-11 col-xs-11">{{::t.name}}</span>
|
||||
<input class="col-md-1 col-sm-1 col-xs-1" type="checkbox" ng-model="t.checked" ng-change="filterAvailabilities(filter)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-t">
|
||||
<div class="row">
|
||||
<h3 class="col-md-11 col-sm-11 col-xs-11 text-beige" translate>{{ 'machines' }}</h3>
|
||||
<input class="col-md-1 col-sm-1 col-xs-1" type="checkbox" ng-model="filter.machines" ng-change="toggleFilter('machines', filter)">
|
||||
</div>
|
||||
<div ng-repeat="m in machines" class="row">
|
||||
<span class="col-md-11 col-sm-11 col-xs-11">{{::m.name}}</span>
|
||||
<input class="col-md-1 col-sm-1 col-xs-1" type="checkbox" ng-model="m.checked" ng-change="filterAvailabilities(filter)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-t row">
|
||||
<h3 class="col-md-11 col-sm-11 col-xs-11 text-japonica" translate>{{ 'events' }}</h3>
|
||||
<input class="col-md-1 col-sm-1 col-xs-1" type="checkbox" ng-model="filter.evt" ng-change="filterAvailabilities(filter)">
|
||||
</div>
|
||||
<div class="m-t row">
|
||||
<h3 class="col-md-11 col-sm-11 col-xs-11 text-black" translate>{{ 'show_no_disponible' }}</h3>
|
||||
<input class="col-md-1 col-sm-1 col-xs-1" type="checkbox" ng-model="filter.dispo" ng-change="filterAvailabilities(filter)">
|
||||
</div>
|
@ -10,7 +10,7 @@
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>{{ event.title }} <span class="v-middle badge text-xs bg-formation">{{event.categories[0].name}}</span></h1>
|
||||
<h1>{{ event.title }} <span class="v-middle badge text-xs bg-event">{{event.categories[0].name}}</span></h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
class API::AvailabilitiesController < API::ApiController
|
||||
before_action :authenticate_user!
|
||||
before_action :authenticate_user!, except: [:public]
|
||||
before_action :set_availability, only: [:show, :update, :destroy, :reservations]
|
||||
respond_to :json
|
||||
|
||||
@ -8,7 +8,47 @@ class API::AvailabilitiesController < API::ApiController
|
||||
|
||||
def index
|
||||
authorize Availability
|
||||
start_date = ActiveSupport::TimeZone[params[:timezone]].parse(params[:start])
|
||||
end_date = ActiveSupport::TimeZone[params[:timezone]].parse(params[:end]).end_of_day
|
||||
@availabilities = Availability.includes(:machines,:tags,:trainings).where.not(available_type: 'event')
|
||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||
end
|
||||
|
||||
def public
|
||||
start_date = ActiveSupport::TimeZone[params[:timezone]].parse(params[:start])
|
||||
end_date = ActiveSupport::TimeZone[params[:timezone]].parse(params[:end]).end_of_day
|
||||
@reservations = Reservation.includes(:slots, user: [:profile]).references(:slots, :user).where('slots.start_at >= ? AND slots.end_at <= ?', start_date, end_date)
|
||||
if in_same_day(start_date, end_date)
|
||||
@training_and_event_availabilities = Availability.includes(:tags, :trainings, :event, :slots).where(available_type: ['training', 'event'])
|
||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||
@machine_availabilities = Availability.includes(:tags, :machines).where(available_type: 'machines')
|
||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||
@machine_slots = []
|
||||
@machine_availabilities.each do |a|
|
||||
a.machines.each do |machine|
|
||||
if params[:m] and params[:m].include?(machine.id.to_s)
|
||||
((a.end_at - a.start_at)/SLOT_DURATION.minutes).to_i.times do |i|
|
||||
slot = Slot.new(start_at: a.start_at + (i*SLOT_DURATION).minutes, end_at: a.start_at + (i*SLOT_DURATION).minutes + SLOT_DURATION.minutes, availability_id: a.id, availability: a, machine: machine, title: machine.name)
|
||||
slot = verify_machine_is_reserved(slot, @reservations, current_user, '')
|
||||
@machine_slots << slot
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@availabilities = [].concat(@training_and_event_availabilities).concat(@machine_slots)
|
||||
else
|
||||
|
||||
@availabilities = Availability.includes(:tags, :machines, :trainings, :event, :slots)
|
||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||
@availabilities.each do |a|
|
||||
if a.available_type != 'machines'
|
||||
a = verify_training_event_is_reserved(a, @reservations)
|
||||
end
|
||||
end
|
||||
end
|
||||
machine_ids = params[:m] || []
|
||||
@title_filter = {machine_ids: machine_ids.map(&:to_i)}
|
||||
@availabilities = filter_availabilites(@availabilities)
|
||||
end
|
||||
|
||||
def show
|
||||
@ -106,7 +146,7 @@ class API::AvailabilitiesController < API::ApiController
|
||||
|
||||
# finally, we merge the availabilities with the reservations
|
||||
@availabilities.each do |a|
|
||||
a = verify_training_is_reserved(a, @reservations)
|
||||
a = verify_training_event_is_reserved(a, @reservations)
|
||||
end
|
||||
end
|
||||
|
||||
@ -138,31 +178,35 @@ class API::AvailabilitiesController < API::ApiController
|
||||
def verify_machine_is_reserved(slot, reservations, user, user_role)
|
||||
reservations.each do |r|
|
||||
r.slots.each do |s|
|
||||
if s.start_at == slot.start_at and s.canceled_at == nil
|
||||
slot.id = s.id
|
||||
slot.is_reserved = true
|
||||
slot.title = t('availabilities.not_available')
|
||||
slot.can_modify = true if user_role === 'admin'
|
||||
slot.reservation = r
|
||||
end
|
||||
if s.start_at == slot.start_at and r.user == user and s.canceled_at == nil
|
||||
slot.title = t('availabilities.i_ve_reserved')
|
||||
slot.can_modify = true
|
||||
slot.is_reserved_by_current_user = true
|
||||
if slot.machine.id == r.reservable_id
|
||||
if s.start_at == slot.start_at and s.canceled_at == nil
|
||||
slot.id = s.id
|
||||
slot.is_reserved = true
|
||||
slot.title = "#{slot.machine.name} - #{t('availabilities.not_available')}"
|
||||
slot.can_modify = true if user_role === 'admin'
|
||||
slot.reservation = r
|
||||
end
|
||||
if s.start_at == slot.start_at and r.user == user and s.canceled_at == nil
|
||||
slot.title = "#{slot.machine.name} - #{t('availabilities.i_ve_reserved')}"
|
||||
slot.can_modify = true
|
||||
slot.is_reserved_by_current_user = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
slot
|
||||
end
|
||||
|
||||
def verify_training_is_reserved(availability, reservations)
|
||||
def verify_training_event_is_reserved(availability, reservations)
|
||||
user = current_user
|
||||
reservations.each do |r|
|
||||
r.slots.each do |s|
|
||||
if s.start_at == availability.start_at and s.canceled_at == nil and availability.trainings.first.id == r.reservable_id
|
||||
if ((availability.available_type == 'training' and availability.trainings.first.id == r.reservable_id) or (availability.available_type == 'event' and availability.event.id == r.reservable_id)) and s.start_at == availability.start_at and s.canceled_at == nil
|
||||
availability.slot_id = s.id
|
||||
availability.is_reserved = true
|
||||
availability.can_modify = true if r.user == user
|
||||
if r.user == user
|
||||
availability.is_reserved = true
|
||||
availability.can_modify = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -177,4 +221,40 @@ class API::AvailabilitiesController < API::ApiController
|
||||
def is_subscription_year(user)
|
||||
user.subscription and user.subscription.plan.interval == 'year' and user.subscription.expired_at >= Time.now
|
||||
end
|
||||
|
||||
def in_same_day(start_date, end_date)
|
||||
(end_date.to_date - start_date.to_date).to_i == 1
|
||||
end
|
||||
|
||||
def filter_availabilites(availabilities)
|
||||
availabilities_filtered = []
|
||||
availabilities.to_ary.each do |a|
|
||||
# machine slot
|
||||
if !a.try(:available_type)
|
||||
availabilities_filtered << a
|
||||
else
|
||||
# training
|
||||
if params[:t] and a.available_type == 'training'
|
||||
if params[:t].include?(a.trainings.first.id.to_s)
|
||||
availabilities_filtered << a
|
||||
end
|
||||
end
|
||||
# machines
|
||||
if params[:m] and a.available_type == 'machines'
|
||||
if (params[:m].map(&:to_i) & a.machine_ids).any?
|
||||
availabilities_filtered << a
|
||||
end
|
||||
end
|
||||
# event
|
||||
if params[:evt] and params[:evt] == 'true' and a.available_type == 'event'
|
||||
availabilities_filtered << a
|
||||
end
|
||||
end
|
||||
end
|
||||
availabilities_filtered.delete_if do |a|
|
||||
if params[:dispo] == 'false'
|
||||
a.is_reserved or (a.try(:is_completed) and a.is_completed)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
36
app/helpers/availability_helper.rb
Normal file
36
app/helpers/availability_helper.rb
Normal file
@ -0,0 +1,36 @@
|
||||
module AvailabilityHelper
|
||||
MACHINE_COLOR = '#e4cd78'
|
||||
TRAINING_COLOR = '#bd7ae9'
|
||||
EVENT_COLOR = '#dd7e6b'
|
||||
IS_RESERVED_BY_CURRENT_USER = '#b2e774'
|
||||
MACHINE_IS_RESERVED_BY_USER = '#1d98ec'
|
||||
IS_COMPLETED = '#eeeeee'
|
||||
|
||||
def availability_border_color(availability)
|
||||
if availability.available_type == 'machines'
|
||||
MACHINE_COLOR
|
||||
elsif availability.available_type == 'training'
|
||||
TRAINING_COLOR
|
||||
else
|
||||
EVENT_COLOR
|
||||
end
|
||||
end
|
||||
|
||||
def machines_slot_border_color(slot)
|
||||
slot.is_reserved ? (slot.is_reserved_by_current_user ? IS_RESERVED_BY_CURRENT_USER : IS_COMPLETED) : MACHINE_COLOR
|
||||
end
|
||||
|
||||
def trainings_events_border_color(availability)
|
||||
if availability.is_reserved
|
||||
IS_RESERVED_BY_CURRENT_USER
|
||||
elsif availability.is_completed
|
||||
IS_COMPLETED
|
||||
else
|
||||
if availability.available_type == 'training'
|
||||
TRAINING_COLOR
|
||||
else
|
||||
EVENT_COLOR
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -39,9 +39,14 @@ class Availability < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def title
|
||||
def title(filter = {})
|
||||
if available_type == 'machines'
|
||||
machines.map(&:name).join(' - ')
|
||||
if filter[:machine_ids]
|
||||
return machines.to_ary.delete_if {|m| !filter[:machine_ids].include?(m.id)}.map(&:name).join(' - ')
|
||||
end
|
||||
return machines.map(&:name).join(' - ')
|
||||
elsif available_type == 'event'
|
||||
event.name
|
||||
else
|
||||
trainings.map(&:name).join(' - ')
|
||||
end
|
||||
@ -51,15 +56,23 @@ class Availability < ActiveRecord::Base
|
||||
# if haven't defined a nb_total_places, places are unlimited
|
||||
def is_completed
|
||||
return false if nb_total_places.blank?
|
||||
nb_total_places <= slots.to_a.select {|s| s.canceled_at == nil }.size
|
||||
if available_type == 'training'
|
||||
nb_total_places <= slots.to_a.select {|s| s.canceled_at == nil }.size
|
||||
elsif available_type == 'event'
|
||||
event.nb_free_places == 0
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: refactoring this function for avoid N+1 query
|
||||
def nb_total_places
|
||||
if read_attribute(:nb_total_places).present?
|
||||
read_attribute(:nb_total_places)
|
||||
else
|
||||
trainings.first.nb_total_places unless trainings.empty?
|
||||
if available_type == 'training'
|
||||
if read_attribute(:nb_total_places).present?
|
||||
read_attribute(:nb_total_places)
|
||||
else
|
||||
trainings.first.nb_total_places unless trainings.empty?
|
||||
end
|
||||
elsif available_type == 'event'
|
||||
event.nb_total_places
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -7,6 +7,10 @@ json.array!(@availabilities) do |availability|
|
||||
json.machine_ids availability.machine_ids
|
||||
json.training_ids availability.training_ids
|
||||
json.backgroundColor 'white'
|
||||
json.borderColor availability.available_type == 'machines' ? '#e4cd78' : '#bd7ae9'
|
||||
json.borderColor availability_border_color(availability)
|
||||
json.tag_ids availability.tag_ids
|
||||
json.tags availability.tags do |t|
|
||||
json.id t.id
|
||||
json.name t.name
|
||||
end
|
||||
end
|
||||
|
@ -6,7 +6,7 @@ json.array!(@slots) do |slot|
|
||||
json.end slot.end_at.iso8601
|
||||
json.is_reserved slot.is_reserved
|
||||
json.backgroundColor 'white'
|
||||
json.borderColor slot.is_reserved ? (slot.is_reserved_by_current_user ? '#b2e774' : '#1d98ec') : '#e4cd78'
|
||||
json.borderColor machines_slot_border_color(slot)
|
||||
|
||||
json.availability_id slot.availability_id
|
||||
json.machine do
|
||||
|
48
app/views/api/availabilities/public.json.jbuilder
Normal file
48
app/views/api/availabilities/public.json.jbuilder
Normal file
@ -0,0 +1,48 @@
|
||||
json.array!(@availabilities) do |availability|
|
||||
json.id availability.id
|
||||
json.start availability.start_at.iso8601
|
||||
json.end availability.end_at.iso8601
|
||||
json.textColor 'black'
|
||||
json.backgroundColor 'white'
|
||||
# availability object
|
||||
if availability.try(:available_type)
|
||||
json.title availability.title(@title_filter)
|
||||
if availability.available_type == 'event'
|
||||
json.event_id availability.event.id
|
||||
end
|
||||
if availability.available_type == 'training'
|
||||
json.training_id availability.trainings.first.id
|
||||
end
|
||||
json.available_type availability.available_type
|
||||
json.tag_ids availability.tag_ids
|
||||
json.tags availability.tags do |t|
|
||||
json.id t.id
|
||||
json.name t.name
|
||||
end
|
||||
|
||||
if availability.available_type != 'machines'
|
||||
json.borderColor trainings_events_border_color(availability)
|
||||
if availability.is_reserved
|
||||
json.is_reserved true
|
||||
json.title "#{availability.title}' - #{t('trainings.i_ve_reserved')}"
|
||||
elsif availability.is_completed
|
||||
json.is_completed true
|
||||
json.title "#{availability.title} - #{t('trainings.completed')}"
|
||||
end
|
||||
else
|
||||
json.borderColor availability_border_color(availability)
|
||||
end
|
||||
|
||||
# machine slot object ( here => availability = slot )
|
||||
else
|
||||
json.title availability.title
|
||||
json.machine_id availability.machine.id
|
||||
json.borderColor machines_slot_border_color(availability)
|
||||
json.tag_ids availability.availability.tag_ids
|
||||
json.tags availability.availability.tags do |t|
|
||||
json.id t.id
|
||||
json.name t.name
|
||||
end
|
||||
json.is_reserved availability.is_reserved
|
||||
end
|
||||
end
|
@ -4,7 +4,7 @@ json.end_at @availability.end_at.iso8601
|
||||
json.available_type @availability.available_type
|
||||
json.machine_ids @availability.machine_ids
|
||||
json.backgroundColor 'white'
|
||||
json.borderColor @availability.available_type == 'machines' ? '#e4cd78' : '#bd7ae9'
|
||||
json.borderColor availability_border_color(@availability)
|
||||
json.title @availability.title
|
||||
json.tag_ids @availability.tag_ids
|
||||
json.tags @availability.tags do |t|
|
||||
|
@ -4,15 +4,13 @@ json.array!(@availabilities) do |a|
|
||||
if a.is_reserved
|
||||
json.is_reserved true
|
||||
json.title "#{a.trainings[0].name}' - #{t('trainings.i_ve_reserved')}"
|
||||
json.borderColor '#b2e774'
|
||||
elsif a.is_completed
|
||||
json.is_completed true
|
||||
json.title "#{a.trainings[0].name} - #{t('trainings.completed')}"
|
||||
json.borderColor '#eeeeee'
|
||||
else
|
||||
json.title a.trainings[0].name
|
||||
json.borderColor '#bd7ae9'
|
||||
end
|
||||
json.borderColor trainings_events_border_color(a)
|
||||
json.start a.start_at.iso8601
|
||||
json.end a.end_at.iso8601
|
||||
json.backgroundColor 'white'
|
||||
|
@ -18,7 +18,7 @@
|
||||
"angular-bootstrap": "~0.14.3",
|
||||
"angular-ui-router": ">=0.2.15",
|
||||
"fullcalendar": "=2.3.1",
|
||||
"angular-ui-calendar": "0.9.0-beta.1",
|
||||
"angular-ui-calendar": "1.0.1",
|
||||
"moment": "=2.10.6",
|
||||
"angular-moment": ">=0.10.3",
|
||||
"ngUpload": ">=0.5.11",
|
||||
@ -53,7 +53,8 @@
|
||||
"angular-translate-interpolation-messageformat": "~2.8.1",
|
||||
"messageformat": "=0.1.8",
|
||||
"moment-timezone": "~0.5.0",
|
||||
"ngFitText": "~4.1.1"
|
||||
"ngFitText": "~4.1.1",
|
||||
"angular-aside": "^1.3.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"jquery": ">=1.10.2",
|
||||
|
@ -32,6 +32,7 @@ en:
|
||||
events_registrations: "Events registrations"
|
||||
projects_gallery: "Projects gallery"
|
||||
subscriptions: "Subscriptions"
|
||||
public_calendar: "Calendar"
|
||||
|
||||
# left menu (admin)
|
||||
trainings_monitoring: "Trainings monitoring"
|
||||
@ -227,3 +228,8 @@ en:
|
||||
book: "Book"
|
||||
change_the_reservation: "Change the reservation"
|
||||
you_can_shift_this_reservation_on_the_following_slots: "You can shift this reservation on the following slots:"
|
||||
|
||||
calendar:
|
||||
calendar: "Calendar"
|
||||
show_no_disponible: "Show the slots no disponibles"
|
||||
filter-calendar: "Filter calendar"
|
||||
|
@ -32,6 +32,7 @@ fr:
|
||||
events_registrations: "Inscriptions aux évènements"
|
||||
projects_gallery: "Galerie de projets"
|
||||
subscriptions: "Abonnements"
|
||||
public_calendar: "Calendrier"
|
||||
|
||||
# menu de gauche (partie admin)
|
||||
trainings_monitoring: "Suivi formations"
|
||||
@ -229,3 +230,8 @@ fr:
|
||||
book: "Réserver"
|
||||
change_the_reservation: "Modifier la réservation"
|
||||
you_can_shift_this_reservation_on_the_following_slots: "Vous pouvez déplacer cette réservation sur les créneaux suivants :"
|
||||
|
||||
calendar:
|
||||
calendar: "Calendrier"
|
||||
show_no_disponible: "Afficher les crénaux non disponibles"
|
||||
filter-calendar: "Filtrer le calendrier"
|
||||
|
@ -29,6 +29,7 @@ en:
|
||||
confirmation_required: "Confirmation required"
|
||||
description: "Description"
|
||||
machines: "Machines"
|
||||
events: "Events"
|
||||
materials: "Materials"
|
||||
date: "Date"
|
||||
price: "Price"
|
||||
@ -94,6 +95,7 @@ en:
|
||||
description_is_required: "Description is required."
|
||||
name_is_required: "Name is required."
|
||||
all_themes: "All themes"
|
||||
filter: 'Filter'
|
||||
|
||||
messages:
|
||||
you_will_lose_any_unsaved_modification_if_you_quit_this_page: "You will lose any unsaved modification if you quit this page"
|
||||
|
@ -29,6 +29,7 @@ fr:
|
||||
confirmation_required: "Confirmation requise"
|
||||
description: "Description"
|
||||
machines: "Machines"
|
||||
events: "Évènements"
|
||||
materials: "Matériaux"
|
||||
date: "Date"
|
||||
price: "Prix"
|
||||
@ -94,6 +95,7 @@ fr:
|
||||
description_is_required: "La description est requise."
|
||||
name_is_required: "Le nom est requis."
|
||||
all_themes: "Toutes les thématiques"
|
||||
filter: 'Filtre'
|
||||
|
||||
messages:
|
||||
you_will_lose_any_unsaved_modification_if_you_quit_this_page: "Vous perdrez les modifications non enregistrées si vous quittez cette page"
|
||||
|
@ -66,6 +66,7 @@ Rails.application.routes.draw do
|
||||
get 'machines/:machine_id', action: 'machine', on: :collection
|
||||
get 'trainings/:training_id', action: 'trainings', on: :collection
|
||||
get 'reservations', on: :member
|
||||
get 'public', on: :collection
|
||||
end
|
||||
|
||||
resources :groups, only: [:index, :create, :update, :destroy]
|
||||
|
49
vendor/assets/components/angular-aside/.bower.json
vendored
Normal file
49
vendor/assets/components/angular-aside/.bower.json
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "angular-aside",
|
||||
"version": "1.3.2",
|
||||
"homepage": "https://github.com/dbtek/angular-aside",
|
||||
"author": {
|
||||
"name": "İsmail Demirbilek",
|
||||
"email": "ce.demirbilek@gmail.com"
|
||||
},
|
||||
"description": "Off canvas side menu to use with ui-bootstrap.",
|
||||
"main": [
|
||||
"dist/js/angular-aside.js",
|
||||
"dist/css/angular-aside.css"
|
||||
],
|
||||
"keywords": [
|
||||
"aside",
|
||||
"off",
|
||||
"canvas",
|
||||
"menu",
|
||||
"ui",
|
||||
"bootstrap"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"Gruntfile.js",
|
||||
"karma.conf.js",
|
||||
"package.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular-bootstrap": ">=0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": ">=1.4.0"
|
||||
},
|
||||
"_release": "1.3.2",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "1.3.2",
|
||||
"commit": "6093a98f325fbb606325da92a32b74b84aee7254"
|
||||
},
|
||||
"_source": "https://github.com/dbtek/angular-aside.git",
|
||||
"_target": "^1.3.2",
|
||||
"_originalSource": "angular-aside",
|
||||
"_direct": true
|
||||
}
|
21
vendor/assets/components/angular-aside/LICENSE
vendored
Normal file
21
vendor/assets/components/angular-aside/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 İsmail Demirbilek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
55
vendor/assets/components/angular-aside/README.md
vendored
Normal file
55
vendor/assets/components/angular-aside/README.md
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
angular-aside ![bower version](http://img.shields.io/bower/v/angular-aside.svg) [![npm version](https://badge.fury.io/js/angular-aside.svg)](https://www.npmjs.com/package/angular-aside)
|
||||
=============
|
||||
|
||||
Off canvas side menu for use with ui-bootstrap 0.14+. Extends ui-bootstrap's `$uibModal` provider.
|
||||
|
||||
:information_desk_person: Please use v1.2.x for ui-bootstrap versions 0.13 and below.
|
||||
|
||||
###[Live Demo](http://plnkr.co/edit/G7vMSv?p=preview)
|
||||
|
||||
##Install
|
||||
|
||||
#### Bower:
|
||||
```bash
|
||||
$ bower install angular-aside
|
||||
```
|
||||
Then, include css/js in html.
|
||||
|
||||
#### NPM
|
||||
```bash
|
||||
$ npm install angular-aside
|
||||
```
|
||||
|
||||
##Usage
|
||||
|
||||
```js
|
||||
angular.module('myApp', ['ui.bootstrap', 'ngAside']);
|
||||
```
|
||||
|
||||
```js
|
||||
angular.module('myApp')
|
||||
.controller('MyController', function($scope, $aside) {
|
||||
var asideInstance = $aside.open({
|
||||
templateUrl: 'aside.html',
|
||||
controller: 'AsideCtrl',
|
||||
placement: 'left',
|
||||
size: 'lg'
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Supports all configuration that `$uibModal` has. Can be used with both `template` and `templateUrl`. For more info hit **Modal** section on [angular-ui bootstrap](http://angular-ui.github.io/bootstrap) documentation.
|
||||
|
||||
|
||||
##Additional Config
|
||||
- `placement` - Aside placement can be `'left'`, `'right'`, `'top'`, or `'bottom'`.
|
||||
|
||||
|
||||
##Credits
|
||||
- [Angular UI Bootstrap](angular-ui.github.io/bootstrap/)
|
||||
- [Animate.css](http://daneden.github.io/animate.css/)
|
||||
|
||||
|
||||
##Author
|
||||
|
||||
İsmail Demirbilek ([@dbtek](https://twitter.com/dbtek))
|
39
vendor/assets/components/angular-aside/bower.json
vendored
Normal file
39
vendor/assets/components/angular-aside/bower.json
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "angular-aside",
|
||||
"version": "1.3.2",
|
||||
"homepage": "https://github.com/dbtek/angular-aside",
|
||||
"author": {
|
||||
"name": "İsmail Demirbilek",
|
||||
"email": "ce.demirbilek@gmail.com"
|
||||
},
|
||||
"description": "Off canvas side menu to use with ui-bootstrap.",
|
||||
"main": [
|
||||
"dist/js/angular-aside.js",
|
||||
"dist/css/angular-aside.css"
|
||||
],
|
||||
"keywords": [
|
||||
"aside",
|
||||
"off",
|
||||
"canvas",
|
||||
"menu",
|
||||
"ui",
|
||||
"bootstrap"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"Gruntfile.js",
|
||||
"karma.conf.js",
|
||||
"package.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular-bootstrap": ">=0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": ">=1.4.0"
|
||||
}
|
||||
}
|
151
vendor/assets/components/angular-aside/demo.html
vendored
Normal file
151
vendor/assets/components/angular-aside/demo.html
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="asideApp">
|
||||
<head>
|
||||
<link data-require="bootstrap-css@3.2.0" data-semver="3.2.0" rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css"/>
|
||||
<link rel="stylesheet" href="src/styles/animate.css"/>
|
||||
<link rel="stylesheet" href="src/styles/aside.css"/>
|
||||
<style type="text/css">
|
||||
/* Styles go here */
|
||||
body {
|
||||
padding: 30px 15px 0;
|
||||
}
|
||||
|
||||
#downloadbtn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.about-links {
|
||||
color: #95a5a6;
|
||||
}
|
||||
.about-links a {
|
||||
border-bottom: 1px dotted;
|
||||
text-decoration: none;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.about-links li {line-height: 30px;}
|
||||
.about-links li:hover, .about-links li:hover a { color: #666; }
|
||||
.about-links .love:hover i { color: #e74c3c; }
|
||||
.about-links .prospectus:hover i { color: #f39c12; }
|
||||
.about-links .cypher:hover i { color: #16a085; }
|
||||
.about-links .lab:hover i { color: #8e44ad; }
|
||||
</style>
|
||||
<script src="https://code.angularjs.org/1.4.7/angular.js"></script>
|
||||
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.14.3.min.js"></script>
|
||||
<script src="dist/js/angular-aside.min.js"></script>
|
||||
<script>
|
||||
angular.module('asideApp', ['ui.bootstrap', 'ngAside'])
|
||||
.controller('MainCtrl', function($scope, $aside) {
|
||||
$scope.openAside = function(position) {
|
||||
$aside.open({
|
||||
templateUrl: 'aside.html',
|
||||
placement: position,
|
||||
backdrop: true,
|
||||
controller: function($scope, $modalInstance) {
|
||||
$scope.ok = function(e) {
|
||||
$modalInstance.close();
|
||||
e.stopPropagation();
|
||||
};
|
||||
$scope.cancel = function(e) {
|
||||
$modalInstance.dismiss();
|
||||
e.stopPropagation();
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script type="text/ng-template" id="aside.html">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">ngAside</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Look im in aside.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" ng-click="ok($event)">OK</button>
|
||||
<button class="btn btn-warning" ng-click="cancel($event)">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body ng-controller="MainCtrl">
|
||||
<div class="container">
|
||||
<div class="jumbotron panel panel-default">
|
||||
<h1>Angular Aside</h1>
|
||||
<p>
|
||||
Off canvas side menu to use with ui-bootstrap. Extends ui-bootstrap's $modal provider.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/dbtek/angular-aside" class="btn btn-primary btn-lg" id="downloadbtn">
|
||||
<span class="glyphicon glyphicon-download"></span> Download
|
||||
</a>
|
||||
<br />
|
||||
<a href="https://twitter.com/share" class="twitter-share-button" data-url="http://github.com/dbtek/angular-aside" data-via="dbtek">Tweet</a>
|
||||
<iframe src="http://ghbtns.com/github-btn.html?user=dbtek&repo=angular-aside&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="107" height="20"></iframe>
|
||||
</p>
|
||||
<div>
|
||||
<ul class="text-center about-links list-inline">
|
||||
<li class="love">
|
||||
made with <i class="fa fa-heart"></i> by <a href="http://twitter.com/dbtek" onclick="_gaq.push(['_trackEvent', 'Outbound Link', 'View twitter account']);">@dbtek</a>
|
||||
</li>
|
||||
<li class="lab">
|
||||
<i class="fa fa-flask"></i>
|
||||
<a href="https://github.com/dbtek/angular-aside">github project</a>
|
||||
</li>
|
||||
<li class="cypher">
|
||||
<i class="fa fa-code"></i>
|
||||
<a href="http://opensource.org/licenses/MIT">license: MIT</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="jumbotron panel panel-default">
|
||||
<h2>Demo</h2>
|
||||
<p>
|
||||
<span class="pull-left">
|
||||
<button type="button" class="btn btn-default btn-lg" ng-click="openAside('left')">
|
||||
<span class="glyphicon glyphicon-align-justify"></span> Left
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-lg" ng-click="openAside('top')">
|
||||
Up <span class="glyphicon glyphicon-arrow-down"></span>
|
||||
</button>
|
||||
</span>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-default btn-lg" ng-click="openAside('bottom')">
|
||||
Down <span class="glyphicon glyphicon-arrow-up"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-lg" ng-click="openAside('right')">
|
||||
Right <span class="glyphicon glyphicon-align-justify"></span>
|
||||
</button>
|
||||
</span>
|
||||
</p>
|
||||
<div class="clearfix"></div>
|
||||
<br />
|
||||
<!-- plunk-hr -->
|
||||
<div class="text-center">
|
||||
<ins class="adsbygoogle text-center"
|
||||
style="display:inline-block;width:320px;height:100px"
|
||||
data-ad-client="ca-pub-7616772085785107"
|
||||
data-ad-slot="4180012826"></ins>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script async src="http://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
|
||||
<script>
|
||||
(adsbygoogle = window.adsbygoogle || []).push({});
|
||||
</script>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','http://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-46512232-6', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script>
|
||||
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
|
||||
</body>
|
||||
</html>
|
421
vendor/assets/components/angular-aside/dist/css/angular-aside.css
vendored
Normal file
421
vendor/assets/components/angular-aside/dist/css/angular-aside.css
vendored
Normal file
@ -0,0 +1,421 @@
|
||||
/*!
|
||||
* angular-aside - v1.3.2
|
||||
* https://github.com/dbtek/angular-aside
|
||||
* 2015-11-17
|
||||
* Copyright (c) 2015 İsmail Demirbilek
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
Animate.css - http://daneden.me/animate
|
||||
Licensed under the MIT license - http://opensource.org/licenses/MIT
|
||||
|
||||
Copyright (c) 2014 Daniel Eden
|
||||
*/
|
||||
|
||||
@-webkit-keyframes fadeInLeft {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInLeft {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
-ms-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInLeft {
|
||||
-webkit-animation-name: fadeInLeft;
|
||||
animation-name: fadeInLeft;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeInRight {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInRight {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
-ms-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInRight {
|
||||
-webkit-animation-name: fadeInRight;
|
||||
animation-name: fadeInRight;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeInTop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInTop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
-ms-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInTop {
|
||||
-webkit-animation-name: fadeInTop;
|
||||
animation-name: fadeInTop;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeInBottom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInBottom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
-ms-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInBottom {
|
||||
-webkit-animation-name: fadeInBottom;
|
||||
animation-name: fadeInBottom;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeOutLeft {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutLeft {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeOutLeft {
|
||||
-webkit-animation-name: fadeOutLeft;
|
||||
animation-name: fadeOutLeft;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeOutRight {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutRight {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeOutRight {
|
||||
-webkit-animation-name: fadeOutRight;
|
||||
animation-name: fadeOutRight;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeOutUp {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutUp {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeOutUp {
|
||||
-webkit-animation-name: fadeOutUp;
|
||||
animation-name: fadeOutUp;
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes fadeOutDown {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutDown {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeOutDown {
|
||||
-webkit-animation-name: fadeOutDown;
|
||||
animation-name: fadeOutDown;
|
||||
}
|
||||
/* Common */
|
||||
|
||||
.ng-aside {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.ng-aside .modal-dialog {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ng-aside.fade .modal-dialog {
|
||||
-o-transition: none;
|
||||
-moz-transition: none;
|
||||
-ms-transition: none;
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
/*CSS transforms*/
|
||||
-o-transform: none;
|
||||
-moz-transform: none;
|
||||
-ms-transform: none;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.ng-aside .modal-dialog .modal-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Horizontal */
|
||||
|
||||
.ng-aside.horizontal {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal .modal-dialog .modal-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal .modal-dialog {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.modal-open .ng-aside.horizontal.left .modal-dialog {
|
||||
animation: fadeOutLeft 250ms;
|
||||
-webkit-animation: fadeOutLeft 250ms;
|
||||
-moz-animation: fadeOutLeft 250ms;
|
||||
-o-animation: fadeOutLeft 250ms;
|
||||
-ms-animation: fadeOutLeft 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.left.in .modal-dialog {
|
||||
animation: fadeInLeft 400ms;
|
||||
-webkit-animation: fadeInLeft 400ms;
|
||||
-moz-animation: fadeInLeft 400ms;
|
||||
-o-animation: fadeInLeft 400ms;
|
||||
-ms-animation: fadeInLeft 400ms;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.left .modal-dialog {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.right .modal-dialog {
|
||||
animation: fadeOutRight 400ms;
|
||||
-webkit-animation: fadeOutRight 400ms;
|
||||
-moz-animation: fadeOutRight 400ms;
|
||||
-o-animation: fadeOutRight 400ms;
|
||||
-ms-animation: fadeOutRight 400ms;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.right.in .modal-dialog {
|
||||
animation: fadeInRight 250ms;
|
||||
-webkit-animation: fadeInRight 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.right .modal-dialog {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* Vertical */
|
||||
|
||||
.ng-aside.vertical {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ng-aside.vertical .modal-dialog {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.ng-aside.vertical .modal-dialog .modal-content {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.top .modal-dialog {
|
||||
animation: fadeOutUp 250ms;
|
||||
-webkit-animation: fadeOutUp 250ms;
|
||||
-webkit-animation: fadeOutUp 250ms;
|
||||
-moz-animation: fadeOutUp 250ms;
|
||||
-o-animation: fadeOutUp 250ms;
|
||||
-ms-animation: fadeOutUp 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.top.in .modal-dialog {
|
||||
animation: fadeInTop 250ms;
|
||||
-webkit-animation: fadeInTop 250ms;
|
||||
-webkit-animation: fadeInTop 250ms;
|
||||
-moz-animation: fadeInTop 250ms;
|
||||
-o-animation: fadeInTop 250ms;
|
||||
-ms-animation: fadeInTop 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.bottom .modal-dialog {
|
||||
animation: fadeOutDown 250ms;
|
||||
-webkit-animation: fadeOutDown 250ms;
|
||||
-webkit-animation: fadeOutDown 250ms;
|
||||
-moz-animation: fadeOutDown 250ms;
|
||||
-o-animation: fadeOutDown 250ms;
|
||||
-ms-animation: fadeOutDown 250ms;
|
||||
}
|
||||
.ng-aside.vertical.bottom.in .modal-dialog {
|
||||
animation: fadeInBottom 250ms;
|
||||
-webkit-animation: fadeInBottom 250ms;
|
||||
-webkit-animation: fadeInBottom 250ms;
|
||||
-moz-animation: fadeInBottom 250ms;
|
||||
-o-animation: fadeInBottom 250ms;
|
||||
-ms-animation: fadeInBottom 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.bottom .modal-dialog {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.top .modal-dialog {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.ng-aside.vertical .modal-dialog .modal-content {
|
||||
width: 100%;
|
||||
}
|
14
vendor/assets/components/angular-aside/dist/css/angular-aside.min.css
vendored
Normal file
14
vendor/assets/components/angular-aside/dist/css/angular-aside.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
60
vendor/assets/components/angular-aside/dist/js/angular-aside.js
vendored
Normal file
60
vendor/assets/components/angular-aside/dist/js/angular-aside.js
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
/*!
|
||||
* angular-aside - v1.3.2
|
||||
* https://github.com/dbtek/angular-aside
|
||||
* 2015-11-17
|
||||
* Copyright (c) 2015 İsmail Demirbilek
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngAside
|
||||
* @description
|
||||
* Main module for aside component.
|
||||
* @function
|
||||
* @author İsmail Demirbilek
|
||||
*/
|
||||
angular.module('ngAside', ['ui.bootstrap.modal']);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('ngAside')
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name ngAside.services:$aside
|
||||
* @description
|
||||
* Factory to create a uibModal instance to use it as aside. It simply wraps $uibModal by overriding open() method and sets a class on modal window.
|
||||
* @function
|
||||
*/
|
||||
.factory('$aside', ['$uibModal', function($uibModal) {
|
||||
var defaults = this.defaults = {
|
||||
placement: 'left'
|
||||
};
|
||||
|
||||
var asideFactory = {
|
||||
// override open method
|
||||
open: function(config) {
|
||||
var options = angular.extend({}, defaults, config);
|
||||
// check placement is set correct
|
||||
if(['left', 'right', 'bottom', 'top'].indexOf(options.placement) === -1) {
|
||||
options.placement = defaults.placement;
|
||||
}
|
||||
var vertHoriz = ['left', 'right'].indexOf(options.placement) === -1 ? 'vertical' : 'horizontal';
|
||||
// set aside classes
|
||||
options.windowClass = 'ng-aside ' + vertHoriz + ' ' + options.placement + (options.windowClass ? ' ' + options.windowClass : '');
|
||||
delete options.placement
|
||||
return $uibModal.open(options);
|
||||
}
|
||||
};
|
||||
|
||||
// create $aside as extended $uibModal
|
||||
var $aside = angular.extend({}, $uibModal, asideFactory);
|
||||
return $aside;
|
||||
}]);
|
||||
})();
|
8
vendor/assets/components/angular-aside/dist/js/angular-aside.min.js
vendored
Normal file
8
vendor/assets/components/angular-aside/dist/js/angular-aside.min.js
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* angular-aside - v1.3.2
|
||||
* https://github.com/dbtek/angular-aside
|
||||
* 2015-11-17
|
||||
* Copyright (c) 2015 İsmail Demirbilek
|
||||
* License: MIT
|
||||
*/
|
||||
!function(){"use strict";angular.module("ngAside",["ui.bootstrap.modal"])}(),function(){"use strict";angular.module("ngAside").factory("$aside",["$uibModal",function(a){var b=this.defaults={placement:"left"},c={open:function(c){var d=angular.extend({},b,c);-1===["left","right","bottom","top"].indexOf(d.placement)&&(d.placement=b.placement);var e=-1===["left","right"].indexOf(d.placement)?"vertical":"horizontal";return d.windowClass="ng-aside "+e+" "+d.placement+(d.windowClass?" "+d.windowClass:""),delete d.placement,a.open(d)}},d=angular.extend({},a,c);return d}])}();
|
2
vendor/assets/components/angular-aside/index.js
vendored
Normal file
2
vendor/assets/components/angular-aside/index.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
require('./dist/js/angular-aside');
|
||||
module.exports = 'ngAside';
|
13
vendor/assets/components/angular-aside/src/scripts/app.js
vendored
Normal file
13
vendor/assets/components/angular-aside/src/scripts/app.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngAside
|
||||
* @description
|
||||
* Main module for aside component.
|
||||
* @function
|
||||
* @author İsmail Demirbilek
|
||||
*/
|
||||
angular.module('ngAside', ['ui.bootstrap.modal']);
|
||||
})();
|
37
vendor/assets/components/angular-aside/src/scripts/services/aside.js
vendored
Normal file
37
vendor/assets/components/angular-aside/src/scripts/services/aside.js
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('ngAside')
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name ngAside.services:$aside
|
||||
* @description
|
||||
* Factory to create a uibModal instance to use it as aside. It simply wraps $uibModal by overriding open() method and sets a class on modal window.
|
||||
* @function
|
||||
*/
|
||||
.factory('$aside', function($uibModal) {
|
||||
var defaults = this.defaults = {
|
||||
placement: 'left'
|
||||
};
|
||||
|
||||
var asideFactory = {
|
||||
// override open method
|
||||
open: function(config) {
|
||||
var options = angular.extend({}, defaults, config);
|
||||
// check placement is set correct
|
||||
if(['left', 'right', 'bottom', 'top'].indexOf(options.placement) === -1) {
|
||||
options.placement = defaults.placement;
|
||||
}
|
||||
var vertHoriz = ['left', 'right'].indexOf(options.placement) === -1 ? 'vertical' : 'horizontal';
|
||||
// set aside classes
|
||||
options.windowClass = 'ng-aside ' + vertHoriz + ' ' + options.placement + (options.windowClass ? ' ' + options.windowClass : '');
|
||||
delete options.placement
|
||||
return $uibModal.open(options);
|
||||
}
|
||||
};
|
||||
|
||||
// create $aside as extended $uibModal
|
||||
var $aside = angular.extend({}, $uibModal, asideFactory);
|
||||
return $aside;
|
||||
});
|
||||
})();
|
263
vendor/assets/components/angular-aside/src/styles/animate.css
vendored
Normal file
263
vendor/assets/components/angular-aside/src/styles/animate.css
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
/*!
|
||||
Animate.css - http://daneden.me/animate
|
||||
Licensed under the MIT license - http://opensource.org/licenses/MIT
|
||||
|
||||
Copyright (c) 2014 Daniel Eden
|
||||
*/
|
||||
|
||||
@-webkit-keyframes fadeInLeft {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInLeft {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
-ms-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInLeft {
|
||||
-webkit-animation-name: fadeInLeft;
|
||||
animation-name: fadeInLeft;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeInRight {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInRight {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
-ms-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInRight {
|
||||
-webkit-animation-name: fadeInRight;
|
||||
animation-name: fadeInRight;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeInTop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInTop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
-ms-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInTop {
|
||||
-webkit-animation-name: fadeInTop;
|
||||
animation-name: fadeInTop;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeInBottom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInBottom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
-ms-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInBottom {
|
||||
-webkit-animation-name: fadeInBottom;
|
||||
animation-name: fadeInBottom;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeOutLeft {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutLeft {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeOutLeft {
|
||||
-webkit-animation-name: fadeOutLeft;
|
||||
animation-name: fadeOutLeft;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeOutRight {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutRight {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeOutRight {
|
||||
-webkit-animation-name: fadeOutRight;
|
||||
animation-name: fadeOutRight;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeOutUp {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutUp {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeOutUp {
|
||||
-webkit-animation-name: fadeOutUp;
|
||||
animation-name: fadeOutUp;
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes fadeOutDown {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutDown {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeOutDown {
|
||||
-webkit-animation-name: fadeOutDown;
|
||||
animation-name: fadeOutDown;
|
||||
}
|
150
vendor/assets/components/angular-aside/src/styles/aside.css
vendored
Normal file
150
vendor/assets/components/angular-aside/src/styles/aside.css
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
/* Common */
|
||||
|
||||
.ng-aside {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.ng-aside .modal-dialog {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ng-aside.fade .modal-dialog {
|
||||
-o-transition: none;
|
||||
-moz-transition: none;
|
||||
-ms-transition: none;
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
/*CSS transforms*/
|
||||
-o-transform: none;
|
||||
-moz-transform: none;
|
||||
-ms-transform: none;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.ng-aside .modal-dialog .modal-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Horizontal */
|
||||
|
||||
.ng-aside.horizontal {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal .modal-dialog .modal-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal .modal-dialog {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.modal-open .ng-aside.horizontal.left .modal-dialog {
|
||||
animation: fadeOutLeft 250ms;
|
||||
-webkit-animation: fadeOutLeft 250ms;
|
||||
-moz-animation: fadeOutLeft 250ms;
|
||||
-o-animation: fadeOutLeft 250ms;
|
||||
-ms-animation: fadeOutLeft 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.left.in .modal-dialog {
|
||||
animation: fadeInLeft 400ms;
|
||||
-webkit-animation: fadeInLeft 400ms;
|
||||
-moz-animation: fadeInLeft 400ms;
|
||||
-o-animation: fadeInLeft 400ms;
|
||||
-ms-animation: fadeInLeft 400ms;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.left .modal-dialog {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.right .modal-dialog {
|
||||
animation: fadeOutRight 400ms;
|
||||
-webkit-animation: fadeOutRight 400ms;
|
||||
-moz-animation: fadeOutRight 400ms;
|
||||
-o-animation: fadeOutRight 400ms;
|
||||
-ms-animation: fadeOutRight 400ms;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.right.in .modal-dialog {
|
||||
animation: fadeInRight 250ms;
|
||||
-webkit-animation: fadeInRight 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.horizontal.right .modal-dialog {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* Vertical */
|
||||
|
||||
.ng-aside.vertical {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ng-aside.vertical .modal-dialog {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.ng-aside.vertical .modal-dialog .modal-content {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.top .modal-dialog {
|
||||
animation: fadeOutUp 250ms;
|
||||
-webkit-animation: fadeOutUp 250ms;
|
||||
-webkit-animation: fadeOutUp 250ms;
|
||||
-moz-animation: fadeOutUp 250ms;
|
||||
-o-animation: fadeOutUp 250ms;
|
||||
-ms-animation: fadeOutUp 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.top.in .modal-dialog {
|
||||
animation: fadeInTop 250ms;
|
||||
-webkit-animation: fadeInTop 250ms;
|
||||
-webkit-animation: fadeInTop 250ms;
|
||||
-moz-animation: fadeInTop 250ms;
|
||||
-o-animation: fadeInTop 250ms;
|
||||
-ms-animation: fadeInTop 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.bottom .modal-dialog {
|
||||
animation: fadeOutDown 250ms;
|
||||
-webkit-animation: fadeOutDown 250ms;
|
||||
-webkit-animation: fadeOutDown 250ms;
|
||||
-moz-animation: fadeOutDown 250ms;
|
||||
-o-animation: fadeOutDown 250ms;
|
||||
-ms-animation: fadeOutDown 250ms;
|
||||
}
|
||||
.ng-aside.vertical.bottom.in .modal-dialog {
|
||||
animation: fadeInBottom 250ms;
|
||||
-webkit-animation: fadeInBottom 250ms;
|
||||
-webkit-animation: fadeInBottom 250ms;
|
||||
-moz-animation: fadeInBottom 250ms;
|
||||
-o-animation: fadeInBottom 250ms;
|
||||
-ms-animation: fadeInBottom 250ms;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.bottom .modal-dialog {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.ng-aside.vertical.top .modal-dialog {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.ng-aside.vertical .modal-dialog .modal-content {
|
||||
width: 100%;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-ui-calendar",
|
||||
"version": "0.9.0-beta.1",
|
||||
"version": "1.0.1",
|
||||
"description": "A complete AngularJS directive for the Arshaw FullCalendar.",
|
||||
"author": "https://github.com/angular-ui/ui-calendar/graphs/contributors",
|
||||
"license": "MIT",
|
||||
@ -16,21 +16,27 @@
|
||||
"package.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "~1.2.x",
|
||||
"fullcalendar": "~2.x"
|
||||
"angular": "1.3.15",
|
||||
"jquery": "2.x",
|
||||
"fullcalendar": "2.3.1",
|
||||
"moment": "2.*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "~1.x",
|
||||
"bootstrap-css": "2.3.1",
|
||||
"jquery-ui": "~1.10.3"
|
||||
"bootstrap-css": "2.3.1"
|
||||
},
|
||||
"_release": "0.9.0-beta.1",
|
||||
"resolutions": {
|
||||
"jquery": "~2.x",
|
||||
"angular": "1.3.15"
|
||||
},
|
||||
"_release": "1.0.1",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "0.9.0-beta.1",
|
||||
"commit": "80a32ee00a6a327ea1446451d725fdd30a4535e9"
|
||||
"tag": "1.0.1",
|
||||
"commit": "9c658bcf574be738bbe24258992da263eafc3095"
|
||||
},
|
||||
"_source": "git://github.com/angular-ui/ui-calendar.git",
|
||||
"_target": "0.9.0-beta.1",
|
||||
"_originalSource": "angular-ui-calendar"
|
||||
"_source": "https://github.com/angular-ui/ui-calendar.git",
|
||||
"_target": "1.0.1",
|
||||
"_originalSource": "angular-ui-calendar",
|
||||
"_direct": true
|
||||
}
|
@ -1,50 +1,54 @@
|
||||
# ui-calendar directive [![Build Status](https://travis-ci.org/angular-ui/ui-calendar.png?branch=master)](https://travis-ci.org/angular-ui/ui-calendar)
|
||||
# ui-calendar directive [![Build Status](https://travis-ci.org/angular-ui/ui-calendar.svg?branch=master)](https://travis-ci.org/angular-ui/ui-calendar)
|
||||
|
||||
[![Join the chat at https://gitter.im/angular-ui/ui-calendar](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular-ui/ui-calendar?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
A complete AngularJS directive for the Arshaw FullCalendar.
|
||||
|
||||
# Requirements
|
||||
|
||||
- ([AngularJS](http://code.angularjs.org/1.2.1/angular.js))
|
||||
- ([fullcalendar.js 2.0 and it's dependencies](http://arshaw.com/fullcalendar/download/))
|
||||
- optional - ([gcal-plugin](http://arshaw.com/js/fullcalendar-1.5.3/fullcalendar/gcal.js))
|
||||
|
||||
# Testing
|
||||
|
||||
We use karma and grunt to ensure the quality of the code.
|
||||
|
||||
npm install -g grunt-cli
|
||||
npm install
|
||||
bower install
|
||||
grunt
|
||||
|
||||
# Usage
|
||||
|
||||
We use [bower](http://twitter.github.com/bower/) for dependency management. Add
|
||||
Using [bower](http://bower.io) run:
|
||||
|
||||
bower install --save angular-ui-calendar
|
||||
|
||||
Alternatively you can add it to your `bower.json` like this:
|
||||
|
||||
dependencies: {
|
||||
"angular-ui-calendar": "latest"
|
||||
}
|
||||
|
||||
To your `components.json` file. Then run
|
||||
And then run
|
||||
|
||||
bower install
|
||||
|
||||
This will copy the ui-calendar files into your `components` folder, along with its dependencies. Load the script files in your application:
|
||||
This will copy the ui-calendar files into your `components` folder, along with its dependencies. Load the script and style files in your application:
|
||||
|
||||
<script type="text/javascript" src="bower_components/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="bower_components/jquery-ui/ui/jquery-ui.js"></script>
|
||||
<script type="text/javascript" src="bower_components/angular/angular.js"></script>
|
||||
<link rel="stylesheet" href="bower_components/fullcalendar/dist/fullcalendar.css"/>
|
||||
<!-- jquery, moment, and angular have to get included before fullcalendar -->
|
||||
<script type="text/javascript" src="bower_components/jquery/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/moment/min/moment.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/angular/angular.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/angular-ui-calendar/src/calendar.js"></script>
|
||||
<script type="text/javascript" src="bower_components/fullcalendar/fullcalendar.js"></script>
|
||||
<script type="text/javascript" src="bower_components/fullcalendar/gcal.js"></script>
|
||||
<script type="text/javascript" src="bower_components/fullcalendar/dist/fullcalendar.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/fullcalendar/dist/gcal.js"></script>
|
||||
|
||||
Add the calendar module as a dependency to your application module:
|
||||
|
||||
var myAppModule = angular.module('MyApp', ['ui.calendar'])
|
||||
var app = angular.module('App', ['ui.calendar'])
|
||||
|
||||
Apply the directive to your div elements. The calendar must be supplied an array of decoumented event sources to render itself:
|
||||
Apply the directive to your div elements. The calendar must be supplied an array of documented event sources to render itself:
|
||||
|
||||
<div ui-calendar ng-model="eventSources"></div>
|
||||
|
||||
Define your model in a scope e.g.
|
||||
|
||||
$scope.eventSources = [];
|
||||
|
||||
## Options
|
||||
|
||||
All the Arshaw Fullcalendar options can be passed through the directive. This even means function objects that are declared on the scope.
|
||||
@ -73,20 +77,20 @@ All the Arshaw Fullcalendar options can be passed through the directive. This ev
|
||||
|
||||
The ui-calendar directive plays nicely with ng-model.
|
||||
|
||||
An Event Sources objects needs to be created to pass into ng-model. This object will be watched for changes and update the calendar accordingly, giving the calendar some Angular Magic.
|
||||
An Event Sources objects needs to be created to pass into ng-model. This object's values will be watched for changes. If a change occurs, then that specific calendar will call the appropriate fullCalendar method.
|
||||
|
||||
The ui-calendar directive expects the eventSources object to be any type allowed in the documentation for the fullcalendar. [docs](http://arshaw.com/fullcalendar/docs/event_data/Event_Source_Object/)
|
||||
Note that all calendar options which are functions that are passed into the calendar are wrapped in an apply automatically.
|
||||
|
||||
## Accessing the calendar object
|
||||
|
||||
To avoid potential issues, by default the calendar object is not available in the parent scope. Access the object by declaring a calendar attribute name:
|
||||
It is possible to access a specific calendar object by declaring a name for it on the uiCalendar directive. In this next line we are naming the calendar 'myCalendar'. This will be attached to the uiCalendarConfig constant object, that can be accessed via DI.
|
||||
|
||||
<div ui-calendar="calendarOptions" ng-model="eventSources" calendar="myCalendar">
|
||||
|
||||
Now the calendar object is available in the parent scope:
|
||||
Now the calendar object is available in uiCalendarConfig.calendars:
|
||||
|
||||
$scope.myCalendar.fullCalendar
|
||||
uiCalendarConfig.calendars.myCalendar
|
||||
|
||||
This allows you to declare any number of calendar objects with distinct names.
|
||||
|
||||
@ -102,9 +106,13 @@ If you need to automatically re-render other event data, you can use `calendar-w
|
||||
returns "" + event.price;
|
||||
}
|
||||
|
||||
<ui-calendar calendar-watch-event="extraEventSignature" ... >
|
||||
<ui-calendar calendar-watch-event="extraEventSignature(event)" ... >
|
||||
// will now watch for price
|
||||
|
||||
### Adding new events issue
|
||||
|
||||
When adding new events to the calendar they can disappear when switching months. To solve this add `stick: true` to the event object being added to the scope.
|
||||
|
||||
## Watching the displayed date range of the calendar
|
||||
|
||||
There is no mechanism to $watch the displayed date range on the calendar due to the JQuery nature of fullCalendar. If you want
|
||||
@ -121,9 +129,22 @@ in a service, instead of letting fullCalendar pull them via AJAX), you can add t
|
||||
}
|
||||
};
|
||||
|
||||
# Minify
|
||||
|
||||
grunt minify
|
||||
|
||||
## Documentation for the Calendar
|
||||
|
||||
The calendar works alongside of all the documentation represented [here](http://arshaw.com/fullcalendar/docs)
|
||||
|
||||
## PR's R always Welcome
|
||||
Make sure that if a new feature is added, that the proper tests are created.
|
||||
|
||||
# Testing
|
||||
|
||||
We use karma and grunt to ensure the quality of the code.
|
||||
|
||||
npm install -g grunt-cli
|
||||
npm install
|
||||
bower install
|
||||
grunt
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-ui-calendar",
|
||||
"version": "0.9.0-beta.1",
|
||||
"version": "1.0.1",
|
||||
"description": "A complete AngularJS directive for the Arshaw FullCalendar.",
|
||||
"author": "https://github.com/angular-ui/ui-calendar/graphs/contributors",
|
||||
"license": "MIT",
|
||||
@ -16,12 +16,17 @@
|
||||
"package.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "~1.2.x",
|
||||
"fullcalendar": "~2.x"
|
||||
"angular": "1.3.15",
|
||||
"jquery": "2.x",
|
||||
"fullcalendar": "2.3.1",
|
||||
"moment": "2.*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "~1.x",
|
||||
"bootstrap-css": "2.3.1",
|
||||
"jquery-ui": "~1.10.3"
|
||||
"bootstrap-css": "2.3.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"jquery": "~2.x",
|
||||
"angular": "1.3.15"
|
||||
}
|
||||
}
|
||||
|
@ -9,48 +9,56 @@
|
||||
*/
|
||||
|
||||
angular.module('ui.calendar', [])
|
||||
.constant('uiCalendarConfig', {})
|
||||
.controller('uiCalendarCtrl', ['$scope', '$timeout', function($scope, $timeout){
|
||||
.constant('uiCalendarConfig', {calendars: {}})
|
||||
.controller('uiCalendarCtrl', ['$scope',
|
||||
'$locale', function(
|
||||
$scope,
|
||||
$locale){
|
||||
|
||||
var sourceSerialId = 1,
|
||||
eventSerialId = 1,
|
||||
sources = $scope.eventSources,
|
||||
var sources = $scope.eventSources,
|
||||
extraEventSignature = $scope.calendarWatchEvent ? $scope.calendarWatchEvent : angular.noop,
|
||||
|
||||
wrapFunctionWithScopeApply = function(functionToWrap){
|
||||
var wrapper;
|
||||
|
||||
if (functionToWrap){
|
||||
wrapper = function(){
|
||||
// This happens outside of angular context so we need to wrap it in a timeout which has an implied apply.
|
||||
// In this way the function will be safely executed on the next digest.
|
||||
return function(){
|
||||
// This may happen outside of angular context, so create one if outside.
|
||||
|
||||
if ($scope.$root.$$phase) {
|
||||
return functionToWrap.apply(this, arguments);
|
||||
} else {
|
||||
var args = arguments;
|
||||
var _this = this;
|
||||
$timeout(function(){
|
||||
functionToWrap.apply(_this, args);
|
||||
var self = this;
|
||||
return $scope.$root.$apply(function(){
|
||||
return functionToWrap.apply(self, args);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
this.eventsFingerprint = function(e) {
|
||||
var eventSerialId = 1;
|
||||
// @return {String} fingerprint of the event object and its properties
|
||||
this.eventFingerprint = function(e) {
|
||||
if (!e._id) {
|
||||
e._id = eventSerialId++;
|
||||
}
|
||||
// This extracts all the information we need from the event. http://jsperf.com/angular-calendar-events-fingerprint/3
|
||||
return "" + e._id + (e.id || '') + (e.title || '') + (e.url || '') + (+e.start || '') + (+e.end || '') +
|
||||
(e.allDay || '') + (e.className || '') + extraEventSignature(e) || '';
|
||||
(e.allDay || '') + (e.className || '') + extraEventSignature({event: e}) || '';
|
||||
};
|
||||
|
||||
this.sourcesFingerprint = function(source) {
|
||||
return source.__id || (source.__id = sourceSerialId++);
|
||||
var sourceSerialId = 1, sourceEventsSerialId = 1;
|
||||
// @return {String} fingerprint of the source object and its events array
|
||||
this.sourceFingerprint = function(source) {
|
||||
var fp = '' + (source.__id || (source.__id = sourceSerialId++)),
|
||||
events = angular.isObject(source) && source.events;
|
||||
if (events) {
|
||||
fp = fp + '-' + (events.__id || (events.__id = sourceEventsSerialId++));
|
||||
}
|
||||
return fp;
|
||||
};
|
||||
|
||||
// @return {Array} all events from all sources
|
||||
this.allEvents = function() {
|
||||
// return sources.flatten(); but we don't have flatten
|
||||
// do sources.map(&:events).flatten(), but we don't have flatten
|
||||
var arraySources = [];
|
||||
for (var i = 0, srcLen = sources.length; i < srcLen; i++) {
|
||||
var source = sources[i];
|
||||
@ -61,7 +69,7 @@ angular.module('ui.calendar', [])
|
||||
// event source as object, ie extended form
|
||||
var extEvent = {};
|
||||
for(var key in source){
|
||||
if(key !== '_uiCalId' && key !== 'events'){
|
||||
if(key !== '_id' && key !== 'events'){
|
||||
extEvent[key] = source[key];
|
||||
}
|
||||
}
|
||||
@ -71,14 +79,17 @@ angular.module('ui.calendar', [])
|
||||
arraySources.push(source.events);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.prototype.concat.apply([], arraySources);
|
||||
};
|
||||
|
||||
// Track changes in array by assigning id tokens to each element and watching the scope for changes in those tokens
|
||||
// arguments:
|
||||
// arraySource array of function that returns array of objects to watch
|
||||
// tokenFn function(object) that returns the token for a given object
|
||||
// Track changes in array of objects by assigning id tokens to each element and watching the scope for changes in the tokens
|
||||
// @param {Array|Function} arraySource array of objects to watch
|
||||
// @param tokenFn {Function} that returns the token for a given object
|
||||
// @return {Object}
|
||||
// subscribe: function(scope, function(newTokens, oldTokens))
|
||||
// called when source has changed. return false to prevent individual callbacks from firing
|
||||
// onAdded/Removed/Changed:
|
||||
// when set to a callback, called each item where a respective change is detected
|
||||
this.changeWatcher = function(arraySource, tokenFn) {
|
||||
var self;
|
||||
var getTokens = function() {
|
||||
@ -92,8 +103,12 @@ angular.module('ui.calendar', [])
|
||||
}
|
||||
return result;
|
||||
};
|
||||
// returns elements in that are in a but not in b
|
||||
// subtractAsSets([4, 5, 6], [4, 5, 7]) => [6]
|
||||
|
||||
// @param {Array} a
|
||||
// @param {Array} b
|
||||
// @return {Array} elements in that are in a but not in b
|
||||
// @example
|
||||
// subtractAsSets([6, 100, 4, 5], [4, 5, 7]) // [6, 100]
|
||||
var subtractAsSets = function(a, b) {
|
||||
var result = [], inB = {}, i, n;
|
||||
for (i = 0, n = b.length; i < n; i++) {
|
||||
@ -110,6 +125,7 @@ angular.module('ui.calendar', [])
|
||||
// Map objects to tokens and vice-versa
|
||||
var map = {};
|
||||
|
||||
// Compare newTokens to oldTokens and call onAdded, onRemoved, and onChanged handlers for each affected event respectively.
|
||||
var applyChanges = function(newTokens, oldTokens) {
|
||||
var i, n, el, token;
|
||||
var replacedTokens = {};
|
||||
@ -138,9 +154,10 @@ angular.module('ui.calendar', [])
|
||||
}
|
||||
};
|
||||
return self = {
|
||||
subscribe: function(scope, onChanged) {
|
||||
subscribe: function(scope, onArrayChanged) {
|
||||
scope.$watch(getTokens, function(newTokens, oldTokens) {
|
||||
if (!onChanged || onChanged(newTokens, oldTokens) !== false) {
|
||||
var notify = !(onArrayChanged && onArrayChanged(newTokens, oldTokens) === false);
|
||||
if (notify) {
|
||||
applyChanges(newTokens, oldTokens);
|
||||
}
|
||||
}, true);
|
||||
@ -156,7 +173,7 @@ angular.module('ui.calendar', [])
|
||||
|
||||
angular.extend(config, uiCalendarConfig);
|
||||
angular.extend(config, calendarSettings);
|
||||
|
||||
|
||||
angular.forEach(config, function(value,key){
|
||||
if (typeof value === 'function'){
|
||||
config[key] = wrapFunctionWithScopeApply(config[key]);
|
||||
@ -165,26 +182,31 @@ angular.module('ui.calendar', [])
|
||||
|
||||
return config;
|
||||
};
|
||||
}])
|
||||
.directive('uiCalendar', ['uiCalendarConfig', '$locale', function(uiCalendarConfig, $locale) {
|
||||
// Configure to use locale names by default
|
||||
var tValues = function(data) {
|
||||
// convert {0: "Jan", 1: "Feb", ...} to ["Jan", "Feb", ...]
|
||||
var r, k;
|
||||
r = [];
|
||||
for (k in data) {
|
||||
r[k] = data[k];
|
||||
}
|
||||
return r;
|
||||
};
|
||||
var dtf = $locale.DATETIME_FORMATS;
|
||||
uiCalendarConfig = angular.extend({
|
||||
monthNames: tValues(dtf.MONTH),
|
||||
monthNamesShort: tValues(dtf.SHORTMONTH),
|
||||
dayNames: tValues(dtf.DAY),
|
||||
dayNamesShort: tValues(dtf.SHORTDAY)
|
||||
}, uiCalendarConfig || {});
|
||||
|
||||
this.getLocaleConfig = function(fullCalendarConfig) {
|
||||
if (!fullCalendarConfig.lang || fullCalendarConfig.useNgLocale) {
|
||||
// Configure to use locale names by default
|
||||
var tValues = function(data) {
|
||||
// convert {0: "Jan", 1: "Feb", ...} to ["Jan", "Feb", ...]
|
||||
var r, k;
|
||||
r = [];
|
||||
for (k in data) {
|
||||
r[k] = data[k];
|
||||
}
|
||||
return r;
|
||||
};
|
||||
var dtf = $locale.DATETIME_FORMATS;
|
||||
return {
|
||||
monthNames: tValues(dtf.MONTH),
|
||||
monthNamesShort: tValues(dtf.SHORTMONTH),
|
||||
dayNames: tValues(dtf.DAY),
|
||||
dayNamesShort: tValues(dtf.SHORTDAY)
|
||||
};
|
||||
}
|
||||
return {};
|
||||
};
|
||||
}])
|
||||
.directive('uiCalendar', ['uiCalendarConfig', function(uiCalendarConfig) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {eventSources:'=ngModel',calendarWatchEvent: '&'},
|
||||
@ -193,8 +215,9 @@ angular.module('ui.calendar', [])
|
||||
|
||||
var sources = scope.eventSources,
|
||||
sourcesChanged = false,
|
||||
eventSourcesWatcher = controller.changeWatcher(sources, controller.sourcesFingerprint),
|
||||
eventsWatcher = controller.changeWatcher(controller.allEvents, controller.eventsFingerprint),
|
||||
calendar,
|
||||
eventSourcesWatcher = controller.changeWatcher(sources, controller.sourceFingerprint),
|
||||
eventsWatcher = controller.changeWatcher(controller.allEvents, controller.eventFingerprint),
|
||||
options = null;
|
||||
|
||||
function getOptions(){
|
||||
@ -203,8 +226,12 @@ angular.module('ui.calendar', [])
|
||||
|
||||
fullCalendarConfig = controller.getFullCalendarConfig(calendarSettings, uiCalendarConfig);
|
||||
|
||||
var localeFullCalendarConfig = controller.getLocaleConfig(fullCalendarConfig);
|
||||
angular.extend(localeFullCalendarConfig, fullCalendarConfig);
|
||||
options = { eventSources: sources };
|
||||
angular.extend(options, fullCalendarConfig);
|
||||
angular.extend(options, localeFullCalendarConfig);
|
||||
//remove calendars from options
|
||||
options.calendars = null;
|
||||
|
||||
var options2 = {};
|
||||
for(var o in options){
|
||||
@ -216,51 +243,60 @@ angular.module('ui.calendar', [])
|
||||
}
|
||||
|
||||
scope.destroy = function(){
|
||||
if(scope.calendar && scope.calendar.fullCalendar){
|
||||
scope.calendar.fullCalendar('destroy');
|
||||
if(calendar && calendar.fullCalendar){
|
||||
calendar.fullCalendar('destroy');
|
||||
}
|
||||
if(attrs.calendar) {
|
||||
scope.calendar = scope.$parent[attrs.calendar] = $(elm).html('');
|
||||
calendar = uiCalendarConfig.calendars[attrs.calendar] = $(elm).html('');
|
||||
} else {
|
||||
scope.calendar = $(elm).html('');
|
||||
calendar = $(elm).html('');
|
||||
}
|
||||
};
|
||||
|
||||
scope.init = function(){
|
||||
scope.calendar.fullCalendar(options);
|
||||
calendar.fullCalendar(options);
|
||||
if(attrs.calendar) {
|
||||
uiCalendarConfig.calendars[attrs.calendar] = calendar;
|
||||
}
|
||||
};
|
||||
|
||||
eventSourcesWatcher.onAdded = function(source) {
|
||||
scope.calendar.fullCalendar('addEventSource', source);
|
||||
sourcesChanged = true;
|
||||
calendar.fullCalendar('addEventSource', source);
|
||||
sourcesChanged = true;
|
||||
};
|
||||
|
||||
eventSourcesWatcher.onRemoved = function(source) {
|
||||
scope.calendar.fullCalendar('removeEventSource', source);
|
||||
calendar.fullCalendar('removeEventSource', source);
|
||||
sourcesChanged = true;
|
||||
};
|
||||
|
||||
eventSourcesWatcher.onChanged = function(source) {
|
||||
calendar.fullCalendar('refetchEvents');
|
||||
sourcesChanged = true;
|
||||
};
|
||||
|
||||
eventsWatcher.onAdded = function(event) {
|
||||
scope.calendar.fullCalendar('renderEvent', event);
|
||||
calendar.fullCalendar('renderEvent', event, (event.stick ? true : false));
|
||||
};
|
||||
|
||||
eventsWatcher.onRemoved = function(event) {
|
||||
scope.calendar.fullCalendar('removeEvents', function(e) {
|
||||
return e._id === event._id;
|
||||
});
|
||||
calendar.fullCalendar('removeEvents', event._id);
|
||||
};
|
||||
|
||||
eventsWatcher.onChanged = function(event) {
|
||||
event._start = $.fullCalendar.moment(event.start);
|
||||
event._end = $.fullCalendar.moment(event.end);
|
||||
scope.calendar.fullCalendar('updateEvent', event);
|
||||
var clientEvents = calendar.fullCalendar('clientEvents', event._id);
|
||||
for (var i = 0; i < clientEvents.length; i++) {
|
||||
var clientEvent = clientEvents[i];
|
||||
clientEvent = angular.extend(clientEvent, event);
|
||||
calendar.fullCalendar('updateEvent', clientEvent);
|
||||
}
|
||||
};
|
||||
|
||||
eventSourcesWatcher.subscribe(scope);
|
||||
eventsWatcher.subscribe(scope, function(newTokens, oldTokens) {
|
||||
eventsWatcher.subscribe(scope, function() {
|
||||
if (sourcesChanged === true) {
|
||||
sourcesChanged = false;
|
||||
// prevent incremental updates in this case
|
||||
// return false to prevent onAdded/Removed/Changed handlers from firing in this case
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@ -271,4 +307,4 @@ angular.module('ui.calendar', [])
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
|
Loading…
Reference in New Issue
Block a user